Alluvial diagrams#

alluvial is a Python package for drawing alluvial diagrams with matplotlib: diagrams of how the modules (communities) of a network change, split and merge across time steps, layers or methods. Each diagram is made of ordinary matplotlib artists on your own axes, ready for a paper figure as vector PDF or SVG.

Alluvial diagram of synthetic citation networks, 2001 to 2007, in which neurology, psychology and part of molecular & cell biology merge into neuroscience

Features#

  • Native Infomap support. Pass the results of infomap.run (or infomap.Infomap objects that have run), or Infomap’s output files (.tree, .ftree, .stree, .clu, .json), and go from data to a paper figure in a few lines. Sub-modules, state, memory and multilayer networks work too.

  • Any partition. Plain dicts that map nodes to modules, and pandas DataFrames with one row per node, need no Infomap at all.

  • The mapequation.org layout. The layout is the one of the mapequation.org alluvial web app, so module order, sizes, margins, streamlines and colors match it, and alluvial.save_diagram writes files the web app opens for editing by hand.

  • Made for papers. Font sizes in points from rcParams, labels fitted to the axes, highlighted groups with a legend, custom module names and order, and the web app’s color schemes.

Install#

pip install alluvial

Requires Python >= 3.11 and matplotlib >= 3.7. Results of infomap.run and infomap.Infomap objects work whenever infomap >= 2.14 is installed; pip install "alluvial[infomap]" is only a shortcut that also installs a compatible infomap. Files written by any infomap version read without it.

Quick start#

Map each node to its module, one dict per network. Nodes are matched across networks by name, and modules are sized by their number of nodes:

import alluvial

# Who works in which team, two years running.
teams_2020 = {"Ann": 1, "Bo": 1, "Cy": 1, "Di": 1, "Ed": 2, "Flo": 2, "Gus": 2, "Hal": 3, "Ivy": 3}
teams_2021 = {"Ann": 1, "Bo": 1, "Cy": 1, "Di": 2, "Ed": 2, "Flo": 2, "Gus": 2, "Hal": 2, "Ivy": 3}

ax = alluvial.plot([teams_2020, teams_2021], names=["2020", "2021"])
ax.figure.savefig("alluvial.png", dpi=150)
Alluvial diagram of two partitions of nine people into teams

A module can also be a path in a module tree, such as "1:2" (sub-module 2 of module 1). alluvial.partition() adds flows that weight the nodes, and a DataFrame with the columns network, node, module and optionally flow is a whole diagram: alluvial.plot(df).

From Infomap#

Pass the Result of infomap.run, or the files Infomap writes (an infomap.Infomap that has run works too):

import infomap

import alluvial

links_2020 = [(1, 2), (1, 3), (2, 3), (3, 4), (4, 5), (4, 6), (5, 6)]
links_2021 = [(1, 2), (1, 3), (2, 3), (3, 5), (3, 6), (4, 5), (4, 6), (5, 6), (4, 7), (7, 8), (8, 4)]
results = [infomap.run(links, seed=1, two_level=True) for links in (links_2020, links_2021)]

ax = alluvial.plot(results, names=["2020", "2021"])
ax = alluvial.plot(["2020.tree", "2021.tree"])  # or Infomap's output files

Networks go from left to right, and each one can be a path to an Infomap output file (.tree, .ftree, .stree, .clu or -o json .json), the Result of infomap.run or an infomap.Infomap that has run, an Infomap JSON dict, a dict of modules, or an alluvial.NetworkFile from alluvial.read() or alluvial.partition(). The tutorial walks through a complete analysis, and capabilities covers every feature.