API reference#

The page follows the tasks of the capabilities tour: plot, lay out, read the inputs, save, and pick colors. Every entry links to the tour sections that use it, and its examples run as tests, so they match the current version. draw, plot, legend and get_artists load matplotlib on first access, so import alluvial alone does not import it.

alluvial.__version__#

The package version, a string such as "0.7.0": print(alluvial.__version__).

Plotting#

alluvial.plot(networks, ax=None, **kwargs)#

Lay out and draw in one call: draw(layout(networks, **layout_kwargs), ax, **draw_kwargs).

Parameters:
  • networks (iterable of str or PathLike, infomap.Infomap, dict or NetworkFile, or a pandas DataFrame) – Networks from left to right, as for alluvial.layout: paths, run infomap.Infomap objects, Infomap JSON dicts, NetworkFile objects, dicts mapping nodes to modules (alluvial.partition(...)), or a long-format DataFrame with one network per network value (alluvial.from_dataframe).

  • ax (matplotlib.axes.Axes, optional) – Target axes; by default a new figure sized by content (see alluvial.draw): taller with more modules, wider with more networks and longer labels.

  • **kwargs – Keyword arguments of alluvial.layout (including LayoutOptions fields) and alluvial.draw.

Returns:

get_artists(ax) returns the created artists.

Return type:

matplotlib.axes.Axes

Examples

>>> import alluvial
>>> ax = alluvial.plot([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])

Layout and drawing keywords go together, here on an axes of your own:

>>> import matplotlib.pyplot as plt
>>> fig, ax = plt.subplots(figsize=(3.4, 2.4), layout="constrained")
>>> ax = alluvial.plot(
...     [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], ax=ax, colors="similarity", palette="tab10"
... )

Used in: 1.2 Infomap output files · 6.1 Axes, fonts and figure sizes · 6.5 Saving figures

alluvial.draw(geometry, ax=None, *, palette=None, default_color=None, module_labels=True, network_labels=True, fontsize=None, network_fontsize=None, max_label_lines=None, max_label_width='auto', avoid_label_overlap=True, label_pad=3.0, streamline_alpha=0.8, gradient=True, edgecolor='face', linewidth=0.3, rasterized=False, hierarchy=True, legend=False, label_colors='auto', module_kw=None, streamline_kw=None, hierarchy_kw=None, text_kw=None, label_kw=None, network_label_kw=None)#

Draw a laid-out alluvial diagram on a matplotlib Axes.

The shapes are in layout px (data coordinates, y up); every text size, gap and margin is in points. At each draw the labels and the axes limits are fitted to the axes size, so the diagram fills the axes, every text stays inside it and font sizes are exact at any figure size (also under layout="constrained" and after resizing).

Parameters:
  • geometry (AlluvialGeometry) – Result of alluvial.layout.

  • ax (matplotlib.axes.Axes, optional) – Target axes, drawn on at its size. By default a new figure is created, sized by content: 1.0 + 0.28 in per module of the network with the most modules (between 2 and 6 in) tall, and 0.4 + 1.1 in per further network wide, plus the width of the labels (at most as much again).

  • palette (sequence of colors, str or Colormap, optional) – Highlight colors, or a matplotlib colormap (name). Qualitative maps are used as is, continuous maps are sampled to the needed number of colors. Defaults to geometry.palette. Indices beyond the palette cycle it, with a warning.

  • default_color (color, optional) – Color of unhighlighted modules; defaults to the web app’s gray.

  • module_labels (bool, default True) – Draw module name labels (first and last network) and network names (below each network).

  • network_labels (bool, default True) – Draw module name labels (first and last network) and network names (below each network).

  • fontsize (float or str, optional) – Module label size in points or a relative size; defaults to "small" (rcParams["font.size"] / 1.2).

  • network_fontsize (float or str, optional) – Network name size; defaults to "medium" (rcParams["font.size"]).

  • max_label_lines (int, optional) – Cap on the names (lines) per module label. By default as many as fit (the web app rule: one line per 1.8 em of module height, an odd number).

  • max_label_width (float, "auto" or None, default "auto") – Longer label lines are truncated with an ellipsis to this width in points. "auto" keeps each label column within a quarter of the axes width; None never truncates.

  • avoid_label_overlap (bool, default True) – Where labels of neighboring modules would overlap, drop lines of the smaller module, then its whole label. False reproduces the web app, which lets them overlap.

  • label_pad (float, default 3) – Gap between a module and its label, in points.

  • streamline_alpha (float, default 0.8) – Streamline opacity.

  • gradient (bool, default True) – Fill streamlines whose ends differ in color with a horizontal gradient (as the web app does; a small embedded image, clipped to the streamline). If False they use the color of their left end, all vector.

  • edgecolor (default "face", 0.3) – Module edge style. The default hairline in the face color hides the antialiasing seams between stacked groups and at streamline ends; use linewidth=0 for exact web app rectangles.

  • linewidth (default "face", 0.3) – Module edge style. The default hairline in the face color hides the antialiasing seams between stacked groups and at streamline ends; use linewidth=0 for exact web app rectangles.

  • rasterized (bool, default False) – Rasterize modules and streamlines (texts stay vector) in PDF/SVG output, for very large diagrams.

  • hierarchy (bool or {"shadow", "outline"}, default True) – How to show the parent modules of expanded sub-modules (NetworkGeometry.outlines, from layout(module_level=...) or expand=), as the web app does: "shadow" (or True) draws a gray box behind each parent, offset right and down by 5 px per level; "outline" a thin solid dark gray rounded box around it (the web app’s is dotted). False or None draws nothing. A diagram of top modules has no outlines either way.

  • legend (bool, default False) – Add a legend of the highlight labels (layout(highlight=...)) above the diagram; see alluvial.legend.

  • label_colors (bool or "auto", default "auto") – Color each module label line by its node’s highlight color, as the web app does (black when gray). "auto" does so only for a diagram with highlight labels (layout(highlight=...)), where the color tells which names belong to a group.

  • module_kw (dict, optional) – Extra Rectangle / streamline properties (e.g. {"hatch": "//", "edgecolor": "white"}, {"alpha": 0.6}). A hatch is drawn in the edge color, which is the face color by default.

  • streamline_kw (dict, optional) – Extra Rectangle / streamline properties (e.g. {"hatch": "//", "edgecolor": "white"}, {"alpha": 0.6}). A hatch is drawn in the edge color, which is the face color by default.

  • hierarchy_kw (dict, optional) – Extra properties of the hierarchy shadows or outlines (e.g. {"facecolor": "0.85"}, {"edgecolor": "k", "linewidth": 1.5}, {"linestyle": "--"}).

  • text_kw (dict, optional) – Extra Text properties for all labels (e.g. {"family": "serif"}).

  • label_kw (dict, optional) – Extra Text properties for module labels or network names only (override text_kw).

  • network_label_kw (dict, optional) – Extra Text properties for module labels or network names only (override text_kw).

Returns:

The axes drawn on, with the axis turned off. get_artists(ax) returns the created artists.

Return type:

matplotlib.axes.Axes

Examples

>>> import alluvial
>>> import matplotlib.pyplot as plt
>>> geometry = alluvial.layout([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])
>>> fig, ax = plt.subplots(figsize=(3.4, 2.4), layout="constrained")
>>> ax = alluvial.draw(geometry, ax, fontsize=6, max_label_lines=3)
>>> fig.savefig("alluvial.pdf")

Used in: 2 Layout options · 3.1 Color modes · 6.2 Label options · 6.3 Palettes, colormaps, gradients and styles

alluvial.legend(ax, *, other=None, **kwargs)#

Add a legend of the highlight labels (layout(highlight=...)) of the diagram drawn on ax.

By default the legend is a row of color swatches centered above the diagram, inside the axes. It takes more rows when it is wider than the axes, and the fit makes room for it at every draw. Passing loc or bbox_to_anchor places it like any matplotlib legend instead (no room is made). Calling it again replaces the legend.

Parameters:
  • ax (matplotlib.axes.Axes) – An axes draw or plot drew on.

  • other (str, optional) – Also add an entry in the default (gray) color with this label, e.g. "other".

  • **kwargs – Axes.legend keywords (e.g. title, fontsize, ncols, frameon).

Returns:

None (with a warning) if the geometry has no highlight labels and no other.

Return type:

matplotlib.legend.Legend or None

Examples

>>> import alluvial
>>> networks = [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}]
>>> ax = alluvial.plot(networks, highlight={"group a": ["a"]})
>>> legend = alluvial.legend(ax, other="other", title="Nodes")
>>> [text.get_text() for text in legend.get_texts()]
['group a', 'other']

Used in: 3.4 Highlight groups and legends

alluvial.get_artists(ax)#

The AlluvialArtists of the last diagram drawn on ax.

Examples

>>> import alluvial
>>> ax = alluvial.plot([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])
>>> artists = alluvial.get_artists(ax)
>>> for text in artists.network_labels:
...     text.set_fontweight("bold")
>>> for patch in artists.streamlines:
...     patch.set_alpha(0.5)
Parameters:

ax (Axes)

Return type:

AlluvialArtists

Used in: 6.4 Restyling with get_artists

class alluvial.mpl.AlluvialArtists(geometry, modules=<factory>, streamlines=<factory>, module_labels=<factory>, network_labels=<factory>, outlines=<factory>, legend=None, _colors=None)#

The artists draw added to an axes, for restyling after the fact. Get it with get_artists(ax).

Every artist also carries a gid (kept as the element id in SVG output): module:<network id>:<module id>:<group>, streamline:<streamline id>, module-label:<network id>:<module id>:<line>, network-label:<network id> and outline:<network id>:<module path>.

Parameters:
  • geometry (AlluvialGeometry)

  • modules (list[Rectangle])

  • streamlines (list[Artist])

  • module_labels (list[Text])

  • network_labels (list[Text])

  • outlines (list[Patch])

  • legend (Legend | None)

  • _colors (Any)

geometry#

The geometry that was drawn.

Type:

AlluvialGeometry

modules#

One rectangle per module group, in geometry order.

Type:

list of matplotlib.patches.Rectangle

streamlines#

One per streamline in geometry (draw) order: a PathPatch, or a clipped AxesImage for gradients.

Type:

list of matplotlib.artist.Artist

module_labels#

One text per label line. Lines that do not fit are invisible; the fitter decides at every draw.

Type:

list of matplotlib.text.Text

network_labels#

One per network with a name.

Type:

list of matplotlib.text.Text

outlines#

One per super-module box (NetworkGeometry.outlines) when drawn with hierarchy: a shadow Rectangle or a dotted FancyBboxPatch.

Type:

list of matplotlib.patches.Patch

legend#

The highlight legend, from draw(..., legend=True) or alluvial.legend(ax).

Type:

matplotlib.legend.Legend or None

Examples

>>> import alluvial
>>> ax = alluvial.plot([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])
>>> artists = alluvial.get_artists(ax)
>>> len(artists.modules), len(artists.streamlines), [text.get_text() for text in artists.network_labels]
(4, 3, ['2020', '2021'])
>>> artists.network_labels[0].get_gid()
'network-label:net0'

Used in: 6.4 Restyling with get_artists

Layout and geometry#

alluvial.layout(networks, *, names=None, ids=None, identifier='id', split_layers=False, colors='module_id', color_network=None, module_level=1, expand=None, highlight=None, highlight_colors=None, scheme=None, tone=None, offset=None, restore_state=True, module_order=None, module_names=None, show_bipartite_nodes=None, **options)#

Lay out an alluvial diagram of one or more partitioned networks.

Parameters:
  • networks (iterable of str or PathLike, infomap.Infomap, dict or NetworkFile, or a pandas DataFrame) – Networks from left to right. Paths are read by suffix (.tree, .ftree, .stree, .clu, .json); an infomap.Infomap must already have run. A .zip of network files (the web app’s zip upload, or save_diagram) contributes its files in archive order. A dict is Infomap JSON when its "nodes" is a list of dicts; any other dict is a partition {node: module} without Infomap (alluvial.partition): nodes are ids or names matched across networks by key, a module is an int, a path string "1:2" or a tuple of ints, and every node gets flow 1/N. A long-format DataFrame with the columns network, node, module and optionally flow gives one network per network value (alluvial.from_dataframe, which also takes other column names). When no input carries flows (only such dicts, or partitions and frames without flows), module_size and sort_modules_by default to "nodes".

  • names (sequence of str, optional) – Network names, one per network (after splitting layers; numbers are converted with str). Defaults to the file basename, "network {i}" for objects (a partition’s name if given), and "<name> layer <k>"-style names for split layers.

  • ids (sequence of str, optional) – Network ids, one per network (after splitting layers): unique, without "--". By default the file stem, "net{i}" for objects or the NetworkFile id, with colliding ids made unique as out, out_2, … An input that is split gets that suffix before splitting (out_2_layer1, …).

  • identifier ({"id", "name"}, default "id") – How nodes are matched across networks: stateId ?? id, or the node name. Applies to paths, dicts and Infomap objects; a NetworkFile keeps the identifiers it was read with.

  • split_layers (bool or sequence of bool, default False) – Split multilayer networks into one network per layer (alluvial.read(..., split_layers=True)); a sequence gives one flag per input. Split layers match their nodes by physical id.

  • colors ({"similarity", "module_id", "nodes", "layer", "physical_id", "hierarchy"} or None, default "module_id") – Coloring operation applied after the build, as in the web app: by module id (the same id gets the same color in every network), matching modules across networks, every node by its module in one network (color_network), by layer id (multilayer networks), by physical node id (state or multilayer networks), or by the module hierarchy (c3’s hierarchy(): sub-modules get colors close to their parent’s, and the same module path gets the same color in every network; use it with module_level/expand, and scheme, tone and offset set the top-module colors). None keeps the input colors (gray unless the input carries colors). Not allowed with highlight. The default is None when the input carries colors: Infomap JSON with a top-level colors (as written by save_diagram).

  • color_network (int or str, optional) – With colors="nodes", the network whose modules color the nodes (in every network), as the web app’s selected network: an index, a network id, or a name given with names= when exactly one network has it. Default the first network. Only allowed with colors="nodes".

  • module_level (int or sequence of int, default 1) – Show sub-modules down to this level (one value, or one per network): every visible module whose sub-modules go that deep is expanded, as when clicking modules in the web app. 1 shows the top modules. With colors="similarity" a sub-module that is not paired with a module of a neighboring network can stay gray, as in the web app.

  • expand (iterable of (network, module_id), optional) – Modules to expand first, in order. network is an index or a network id, module_id a path string like "1" or "1:2" (looked up when its turn comes, so [(0, "1"), (0, "1:2")] works).

  • highlight (mapping or callable, optional) – Highlight groups of nodes, each in its own color (the rest stays gray). Either {label: identifiers} (matched against node identifiers, ints allowed; an identifier may belong to one label only), or a callable (NodeInfo) -> label or None evaluated once per node. Labels are kept in mapping order (or first-appearance order) in AlluvialGeometry.highlight_labels, for draw(..., legend=True).

  • highlight_colors (sequence or mapping, optional) – Colors of the highlight labels (a list aligned with the labels, or {label: color}): color strings (any matplotlib color name, "#rrggbb" or "rgb(r, g, b)") or RGB tuples in [0, 1]. Defaults to the scheme.

  • scheme (str or sequence of colors, optional) – The color scheme of the coloring operations: a web-app scheme name (alluvial.SCHEMES lists them, e.g. "Paired", "Tableau20", "C3 Sinebow"; default "C3 Oklch", as in the web app) or a list of colors. Also the default highlight_colors. The C3 schemes ("C3 Oklch", "C3 Turbo", "C3 Rainbow", "C3 Sinebow", "C3 Viridis") are computed with c3 (https://mapequation.github.io/c3).

  • tone (str, optional) – The c3 tone: "vivid", "bright", "strong", "deep", "dark", "soft", "pastel" or "muted", the lightness and chroma of the C3 schemes and of colors="hierarchy" with a C3 or an interpolated (diverging, sequential or cyclical) scheme. Other schemes ignore it. Default "vivid", as in the web app (or the stored tone of a saved diagram).

  • offset (float, optional) – Where c3 puts the first color, in degrees around the hue source: its hue on the OKLCH hue circle ("C3 Oklch"), else 360 is the whole scheme. Applies where tone does. Default c3’s (245 for "C3 Oklch", else 0), or the stored offset of a saved diagram when its scheme is used.

  • restore_state (bool, default True) – Restore what a saved diagram (save_diagram or the web app’s “Download diagram”) stores besides the networks, colors and expanded modules: the options, the color scheme with its tone and offset, the custom module orders and the module names, as the web app opens it. Explicit keywords win: a layout option, scheme, tone or offset given here replaces the stored one (another scheme drops the stored offset), and module_order/module_names replace the stored orders/names. Files from different saves, or not all files of one save, restore their module orders and names but not the options (warned). False loads the files as plain Infomap JSON (with their colors and expanded modules).

  • module_order (mapping, optional) – {network: [module_id, ...]}: put these visible modules of a network (index, id, or a name given with names= as a string when exactly one network has it) at these positions, listed bottom to top. The unlisted visible modules keep their relative order above them; an ... (Ellipsis) in the list stands for them, so ["3", ...] puts module 3 at the bottom, [..., "3"] at the top and ["2", ..., "5"] both. Applied after the coloring as one reorder of the web app’s store (one layout however many modules move; “move up/down” in the web app gives the same order), so sort_modules_by sets the start. Module ids are those shown after expand/module_level (e.g. "1:2"); hidden modules cannot be placed.

  • module_names (mapping or callable, optional) – Custom module names, shown instead of the node names (as the web app’s module name field): either {(network, module_id): name} (network as for module_order) or a callable (ModuleInfo) -> str or None called for every module (also hidden ones) after all other steps. Names are kept in ModuleGeometry.name (save_diagram stores them); the labels are drawn on the first and last network only, as in the web app.

  • show_bipartite_nodes (bool, optional) – False hides the second node type of bipartite networks (node id >= the network’s bipartiteStartId), as the web app’s “Show bipartite nodes” switch. Default True (or the stored value of a saved diagram).

  • **options – Fields of LayoutOptions.

Returns:

Frozen geometry in the web app’s SVG coordinates (y down).

Return type:

AlluvialGeometry

Raises:
  • ValueError – For an Infomap object that has not run, an unreadable or unsupported file, no networks, an invalid option value, a module to expand that does not exist or has no sub-modules, colors with highlight, an unknown color_network or one without colors="nodes", or a module_order/module_names entry with an unknown network or module, a module given twice, or (for module_order) a hidden module.

  • FileNotFoundError – For a path that does not exist.

  • TypeError – For an unknown keyword, a single network not wrapped in a list, or an argument of the wrong type.

Warns:

UserWarning – For state networks matched by state id next to networks matched by node id (identifier="id"), a module_level deeper than a network’s module tree, highlight identifiers that match no node, duplicate node identifiers within a network, highlight labels drawn gray or in a shared color, and a diagram where no node flow is positive.

Notes

The steps run in the web app’s store, in this order: build, expand, module_level, the coloring (colors or highlight), module_order, then module_names. Each is a sequence of the store’s own operations, so the geometry matches the web app bit for bit.

Examples

>>> import alluvial
>>> geometry = alluvial.layout([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])
>>> [network.name for network in geometry.networks]
['2020', '2021']
>>> [len(network.modules) for network in geometry.networks], len(geometry.streamlines)
([2, 2], 3)

Used in: 1 Inputs · 2 Layout options · 3.1 Color modes · 4 Hierarchy · 7 Geometry access

class alluvial.LayoutOptions(height=600.0, module_width=80.0, streamline_fraction=2.0, streamline_threshold=1.0, flow_threshold=0.005, margin_exponent=4.0, zero_margins=False, vertical_align='bottom', module_size='flow', sort_modules_by='flow')#

Layout options with the web app’s defaults. Validated on construction.

Parameters:
  • height (float, default 600) – Diagram height in layout px. Must be > 0.

  • module_width (float, default 80) – Width of each network’s module column. Must be > 0.

  • streamline_fraction (float, default 2) – Gap between networks as a multiple of module_width. Must be >= 0.

  • streamline_threshold (float, default 1) – Streamlines with mean height <= this (px, not scaled with height) are not drawn. Must be >= 0.

  • flow_threshold (float, default 0.005) – Fraction of each network’s flow to hide, smallest modules first. Must be in [0, 1).

  • margin_exponent (float, default 4) – Module margins are 2 ** (margin_exponent - 2 * difference_index).

  • zero_margins (bool, default False) – Put no margin between modules.

  • vertical_align ({"bottom", "justify"}, default "bottom") – "justify" stretches the margins so every network spans the full height. "top" is rejected (the web app never implemented it).

  • module_size ({"flow", "nodes"}, default "flow") – Module height measure.

  • sort_modules_by ({"flow", "nodes", "node_id", "highlight_index"}, default "flow") – Module order key (descending). "node_id" is the mean node id.

Raises:
  • TypeError – If a value has the wrong type.

  • ValueError – If a value is out of range or not one of the allowed choices.

Examples

Pass the fields as keywords to layout or plot; the geometry keeps the options it ran with:

>>> import alluvial
>>> geometry = alluvial.layout([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], height=300, module_width=40)
>>> geometry.options.height, geometry.options.module_width
(300.0, 40.0)
>>> alluvial.LayoutOptions(vertical_align="justify").to_ts()["verticalAlign"]
'justify'
to_ts()#

The options under the web app’s option names and values, as a saved diagram stores them.

Return type:

dict[str, Any]

classmethod from_ts(options)#

Build from the web app’s option names (the inverse of to_ts()). Unknown keys raise ValueError.

Parameters:

options (Mapping[str, Any])

Return type:

LayoutOptions

Used in: 2 Layout options

class alluvial.AlluvialGeometry(width, height, networks, streamlines, palette=(), options=None, highlight_labels=())#

The complete diagram geometry.

Parameters:
width, height

Diagram size in layout px.

Type:

float

networks#

Left to right.

Type:

tuple of NetworkGeometry

streamlines#

In draw order (getLinks order per network pair, pairs left to right).

Type:

tuple of StreamlineGeometry

palette#

The highlight colors in effect; highlight_index i maps to palette[i].

Type:

tuple of str

options#

The options the layout ran with.

Type:

LayoutOptions or None

highlight_labels#

The labels of layout(highlight=...) in label order (empty otherwise), for legends.

Type:

tuple of HighlightLabel

Examples

>>> import alluvial
>>> geometry = alluvial.layout([{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], names=["2020", "2021"])
>>> geometry.width, geometry.height
(320.0, 600.0)
>>> module = geometry.networks[0].modules[0]
>>> module.id, module.label.text.splitlines()
('1', ['a', 'b'])

It converts to plain dicts and lists (e.g. for JSON) and back:

>>> alluvial.AlluvialGeometry.from_dict(geometry.to_dict()) == geometry
True
classmethod from_dict(data)#

Inverse of to_dict().

Parameters:

data (dict[str, Any])

Return type:

AlluvialGeometry

Used in: 7 Geometry access

The geometry is a tree of frozen dataclasses: AlluvialGeometry.networks holds the NetworkGeometry of each network, with its ModuleGeometry modules, their GroupGeometry highlight groups and Label labels, and the OutlineGeometry boxes of the module hierarchy; AlluvialGeometry.streamlines holds the StreamlineGeometry between adjacent networks. Each has to_dict().

class alluvial.geometry.NetworkGeometry(id, name, index, x, y, width, height, flow, name_label, modules, outlines=())#

One network column. modules are the visible modules in layout order (bottom first). outlines are the super-module boxes of expanded modules in breadth-first order (empty when every module is a top module).

Parameters:
class alluvial.geometry.ModuleGeometry(id, path, network_index, flow, x, y, width, height, highlight_index, groups, label, largest_leaf_names, name_candidates=(), level=1, max_level=1, order=None, name_highlight_indices=(), name_insignificant=(), name='')#

A visible module of a network.

Parameters:
id#

Module path string, e.g. "3".

Type:

str

path#

Module path components.

Type:

tuple of int

network_index#

Index of the network (left to right).

Type:

int

highlight_index#

The highlight index of the module’s largest group.

Type:

int

groups#

In the web app’s group order: significant first, then by highlight index.

Type:

tuple of GroupGeometry

label#

Name label; only modules of the first (anchor "end") and last (anchor "start") network have one.

Type:

Label or None

largest_leaf_names#

Label names in descending flow (aggregated to physical names for state networks), capped by the web app’s lines-per-module rule at layout px (label.text joins them).

Type:

tuple of str

name_candidates#

For labeled modules only: largest_leaf_names followed by the next largest names, up to NAME_CANDIDATES in total. Renderers that size labels at their own scale (alluvial.mpl) pick lines from these, so a tall axes or a small font can show more names than a 600 px web view.

Type:

tuple of str

level#

The module level (1 for top modules; deeper after module_level/expand).

Type:

int

max_level#

The deepest level the module could be expanded to; > level means it has sub-modules.

Type:

int

order#

Index of the module in the network’s hierarchical draw order (breadth first, interleaved with NetworkGeometry.outlines). In a flat diagram it is the index in modules.

Type:

int or None

name_highlight_indices#

Highlight index of each label name, aligned with name_candidates (or largest_leaf_names when there are no candidates). The web app colors each label line with it (black for -1).

Type:

tuple of int

name_insignificant#

Whether each label name is an insignificant node (drawn brighter); always False for aggregated names.

Type:

tuple of bool

name#

The module’s custom name (restored from a saved web-app diagram), or "". The web app shows it, when set, as the module’s label instead of the largest leaf names (label keeps the leaf names).

Type:

str

class alluvial.geometry.GroupGeometry(highlight_index, insignificant, x, y, width, height, flow)#

A highlight group: a colored slice of a module. highlight_index -1 is the default color.

Parameters:
class alluvial.geometry.Label(text, x, y, anchor, center_line=0)#

A text label at (x, y).

Parameters:
text#

Text; multi-line module labels join the names with "\n" in top-to-bottom order.

Type:

str

x, y

Anchor point. For module labels y is the module’s vertical center and line center_line is vertically centered on it (SVG dominant-baseline: middle). For network names y is network.height + 20 and the web app puts the first baseline one network font size below it.

Type:

float

anchor#

SVG text-anchor.

Type:

{“start”, “end”, “middle”}

center_line#

Index of the line centered on y (ceil(n / 2) - 1 for an n-line module label, else 0).

Type:

int

class alluvial.geometry.OutlineGeometry(path, level, max_level, x, y, width, height, order)#

A super-module box: the union of the visible modules below one parent module path.

Parameters:
path#

The parent module path, e.g. "1" or "1:2".

Type:

str

level#

Its module level (path has level components).

Type:

int

max_level#

The deepest module level in its subtree (sets the web app’s shadow/outline offset).

Type:

int

x, y, width, height

The raw union rect of the visible descendant modules (no render offset).

Type:

float

order#

Index in the network’s hierarchical draw order (breadth first, interleaved with the modules’ order).

Type:

int

class alluvial.geometry.StreamlineGeometry(id, left, right, x0, y0, h0, x1, y1, h1, avg_height, highlight_index, left_highlight_index, right_highlight_index, left_insignificant=False, right_insignificant=False)#

A streamline between two adjacent networks.

Parameters:
id#

The web app’s link id (the left streamline node’s id).

Type:

str

left, right

(network_index, module_id, highlight_index) of the group at each end.

Type:

tuple

x0, y0, h0

Left end: x (module right edge), top y and height.

Type:

float

x1, y1, h1

Right end: x (module left edge), top y and height.

Type:

float

avg_height#

(h0 + h1) / 2.

Type:

float

highlight_index#

max(left_highlight_index, right_highlight_index); the draw-order key.

Type:

int

left_insignificant, right_insignificant

Whether the group at each end is insignificant (drawn with a brighter color).

Type:

bool

path()#

Closed outline as (code, (x, y)) pairs with matplotlib Path codes, in layout coordinates.

Same points as the web app’s SVG path: bottom edge left to right, up the right end, top edge back, close. Top edges below 1e-6 snap to 0.

Return type:

list[tuple[int, tuple[float, float]]]

svg_path()#

The SVG path string exactly as the web app formats it.

Return type:

str

Used in: 7 Geometry access

Inputs#

alluvial.partition(modules, flows=None, *, name=None)#

One network from a mapping of node ids or names to modules, without Infomap.

Parameters:
  • modules (mapping) – {node: module}. A node is an id (int) or a name (str); nodes are matched across networks by this key (whatever layout(identifier=...) says). A module is a positive int (a top module), a tree path string "1:2" (sub-module 2 of module 1) or a tuple or list of positive ints (1, 2); module ids start at 1, as in Infomap. The mapping order is the node order.

  • flows (mapping, optional) – {node: flow} with one non-negative finite number per node of modules. The flows are normalized to sum to 1, as Infomap’s flows do. Without flows every node gets flow 1/N, N being the number of nodes.

  • name (str, optional) – Network name (and id, unless it is empty or contains "--"). layout names an unnamed partition "network {i}", as other objects.

Returns:

Nodes in mapping order. An int node keeps its value as node id; a named node gets its 1-based position as node id and the key as name. A node’s path is its module’s path plus its position in that module, as in Infomap’s .tree files: "2:1:3" is the third node of sub-module "2:1".

Return type:

NetworkFile

Raises:
  • ValueError – For no nodes, a module value of another kind (or below 1), a key that is neither str nor int, two keys with the same string (1 and "1"), or flows that are negative, not finite, missing for a node, given for a node not in modules, or all zero.

  • TypeError – If modules or flows is not a mapping, or name is not a string.

Notes

A diagram of only flow-less networks (partition without flows, plain {node: module} dicts in layout) sizes and sorts its modules by node count: module_size and sort_modules_by default to "nodes" there, unless given.

Examples

>>> import alluvial
>>> nf = alluvial.partition({"Ann": 1, "Bo": 1, "Cy": "2:1"}, name="2020")
>>> [(n.name, n.path) for n in nf.nodes]
[('Ann', '1:1'), ('Bo', '1:2'), ('Cy', '2:1:1')]
>>> [n.flow for n in alluvial.partition({1: 1, 2: (1, 2)}).nodes]
[0.5, 0.5]
>>> [n.flow for n in alluvial.partition({"Ann": 1, "Bo": 2}, flows={"Ann": 3, "Bo": 1}).nodes]
[0.75, 0.25]

Plain dicts work directly as networks in layout and plot:

>>> geom = alluvial.layout([{"Ann": 1, "Bo": 1, "Cy": 2}, {"Ann": 1, "Bo": 2, "Cy": "2:1"}], names=["2020", "2021"])
>>> [network.name for network in geom.networks]
['2020', '2021']

Used in: 1.1 Dicts, partition() and DataFrames

alluvial.from_dataframe(df, *, network='network', node='node', module='module', flow=None)#

Networks from a long-format data frame with one row per (network, node), without Infomap.

Parameters:
  • df (pandas.DataFrame) – The rows. pandas is not required by alluvial; any data frame with columns and column access works.

  • network (str, default "network", "node", "module") – Column names: the network of a row, its node (id or name, matched across networks) and its module (as in partition: an int, a path string "1:2" or a tuple of ints).

  • node (str, default "network", "node", "module") – Column names: the network of a row, its node (id or name, matched across networks) and its module (as in partition: an int, a path string "1:2" or a tuple of ints).

  • module (str, default "network", "node", "module") – Column names: the network of a row, its node (id or name, matched across networks) and its module (as in partition: an int, a path string "1:2" or a tuple of ints).

  • flow (str, optional) – The flow column. If None (the default), a column named "flow" is used when there is one; otherwise every node gets flow 1/N. Flows are normalized to sum to 1 per network.

Returns:

One partition per network, in order of first appearance, or in category order for a pandas Categorical network column. A network’s name is str(value).

Return type:

list of NetworkFile

Raises:
  • ValueError – For a missing column, a node twice in one network, an empty frame, or any value partition rejects.

  • TypeError – If df is not a data frame.

Examples

>>> import alluvial, pandas as pd
>>> df = pd.DataFrame({"year": [2020, 2020, 2021, 2021], "who": ["Ann", "Bo", "Ann", "Bo"],
...                    "community": [1, 2, 1, 1]})
>>> networks = alluvial.from_dataframe(df, network="year", node="who", module="community")
>>> [(nf.name, [n.path for n in nf.nodes]) for nf in networks]
[('2020', ['1:1', '2:1']), ('2021', ['1:1', '1:2'])]

With the default column names, pass the frame straight to layout or plot:

>>> df = df.rename(columns={"year": "network", "who": "node", "community": "module"})
>>> [network.name for network in alluvial.layout(df).networks]
['2020', '2021']

Used in: 1.1 Dicts, partition() and DataFrames

alluvial.read(source: str | PathLike[str] | Any, *, name: str | None = None, identifier: Literal['id', 'name'] = 'id', states: bool | None = None, network_id: str | None = None, split_layers: Literal[False] = False) → NetworkFile#
alluvial.read(source: str | PathLike[str] | Any, *, name: str | None = None, identifier: Literal['id', 'name'] = 'id', states: bool | None = None, network_id: str | None = None, split_layers: Literal[True]) → list[NetworkFile]

Read one network into a NetworkFile (or, with split_layers=True, one per layer).

Parameters:
  • source (str, PathLike, dict, infomap.Infomap or NetworkFile) – A .tree/.ftree/.stree/.clu path (format from the suffix; Infomap’s -o states files *_states.tree/.ftree/.clu included), an Infomap -o json file (.json) or its parsed dict, an infomap.Infomap that has run, an infomap Result (from im.run(), infomap.run(...) or Network.run(); infomap >= 2.14), or a NetworkFile (returned unchanged unless split). A .zip of network files (the web app’s zip upload, or save_diagram), a directory of network files, or a list of paths gives a list, one NetworkFile per file, in archive (file-name, list) order, or in saved order when they are the files of one saved diagram.

  • name (str, optional) – Network name. Defaults to the file basename, or "network" for objects. For JSON a top-level name comes before these defaults.

  • identifier ({"id", "name"}, default "id") – Node identity across networks, as in the web app: "id" uses state_id if present else the node id, "name" the node name. See State networks below. Ignored with split_layers=True.

  • states (bool, optional) – Infomap objects only: read state nodes. None picks them for state/memory and multilayer networks (like write_tree(states=True)); False reads the physical level (write_tree(states=False)).

  • network_id (str, optional) – Network id. Defaults to the file stem for paths, or name (else "network") for objects. For JSON a top-level string id comes before these defaults.

  • split_layers (bool, default False) – Split a multilayer state network into one network per layer, like the web app’s “split layers” (alluvial.io.split_layers). Layers are ordered by layer id, named "Layer <k>" (or from a JSON layers list), get the ids "<network_id>_layer<k>" and match their nodes by physical id.

Returns:

A list (one per layer) if split_layers is true. Pass it on unpacked: layout([*read(path, split_layers=True)]).

Return type:

NetworkFile or list of NetworkFile

Raises:
  • ValueError – For an unsupported file (Infomap input .net, .csv, Newick, JSON that is not Infomap output, a saved diagram in the 2019 web-app format), a malformed file, a node whose flow is not a finite number, an Infomap object that has not been run, or split_layers=True on a network that is not a multilayer state network.

  • TypeError – For other sources, or an infomap older than 2.14.

Warns:

UserWarning – If several nodes share an identifier (the engine then matches the last one, as in the web app; checked per layer after a split), if a tree file has node names with a double quote (Infomap writes them unescaped, so the id column is misread), or if identifier="name" is combined with split_layers=True.

Notes

State networks. Networks are matched node by node through identifier, exactly as in the web app.

  • identifier="id" matches state nodes by state id. That is right for memory networks whose state ids mean the same thing in every network (you assign them in the *States section or with add_state_node). For multilayer networks Infomap assigns the state ids itself, in input order, so the same (layer, node) can get different state ids in two runs: do not match multilayer networks by "id" unless they were built from identically ordered input.

  • identifier="name" matches by physical node name: Infomap writes the physical name (or the physical id when unnamed) for every state node. A physical node with several state nodes then gives duplicate identifiers (warned; matching uses the last one).

  • The physical level (states=False or a write_tree(states=False) file) has one row per physical node and module; a node whose state nodes sit in several modules still appears more than once.

  • split_layers=True shows how one multilayer partition changes across its layers: each layer is a network, and its nodes are matched by physical node id (the identifier mode does not apply, as in the web app). A non-split network next to the layers matches them only if its identifiers are physical ids too, e.g. a first-order .tree read with identifier="id".

Examples

A parsed Infomap JSON (infomap -o json), here written inline:

>>> import alluvial
>>> nf = alluvial.read(
...     {
...         "nodes": [
...             {"id": 1, "name": "a", "path": [1, 1], "flow": 0.5},
...             {"id": 2, "name": "b", "path": [2, 1], "flow": 0.5},
...         ]
...     },
...     name="2020",
... )
>>> nf.name, nf.format, [node.name for node in nf.nodes]
('2020', 'json', ['a', 'b'])

A .clu file of Infomap (node id, module, flow). The id defaults to the file stem and the name to the file name:

>>> import pathlib, tempfile
>>> with tempfile.TemporaryDirectory() as tmp:
...     path = pathlib.Path(tmp) / "2021.clu"
...     _ = path.write_text("# node_id module flow\n1 1 0.5\n2 1 0.5\n")
...     nf = alluvial.read(path)
>>> nf.id, nf.name, nf.format, [node.path for node in nf.nodes]
('2021', '2021.clu', 'clu', ['1', '1'])

Used in: 1.2 Infomap output files · 1.3 Infomap JSON · 1.4 Infomap results and objects · 1.7 The web app’s zip, directories and lists of files

alluvial.split_layers(nf, *, id_format='{id}_layer{layer}')#

Split a multilayer state network into one network per layer, like the web app’s “split layers”.

Nodes are matched across the layers by physical node id (as the web app does), whatever identifiers nf was read with. Streamlines then connect the same physical node in adjacent layers.

Parameters:
  • nf (NetworkFile) – A multilayer network read at the state level (its first node has a layer_id). Not mutated: the layers get copies of the nodes.

  • id_format (str, default "{id}_layer{layer}") – Network id of each layer, formatted with id (nf.id) and layer (the layer id as a string, e.g. "3"). The result must not contain "--".

Returns:

In layer id order: non-negative integer ids ascending, then any other ids in first-seen order (only hand-made input has those). Each layer copies every field of nf (header, modules …) and sets id, name (nf.layers entry with a strictly equal id, else "Layer <id>"), nodes (file order), layer_id, original_id = nf.id, the statistics of its nodes and is_expanded = True.

Return type:

list of NetworkFile

Raises:

ValueError – If nf is not multilayer.

Notes

Nodes without a layer_id form a last "Layer undefined" network, as in the web app. A JSON layerId: null reads as missing (the web app would make a "Layer null" network).

Examples

A multilayer state network with two layers, as Infomap JSON:

>>> import alluvial
>>> nf = alluvial.read(
...     {
...         "nodes": [
...             {"id": 1, "stateId": 1, "layerId": 1, "name": "a", "path": [1, 1], "flow": 0.25},
...             {"id": 2, "stateId": 2, "layerId": 1, "name": "b", "path": [1, 2], "flow": 0.25},
...             {"id": 1, "stateId": 3, "layerId": 2, "name": "a", "path": [1, 1], "flow": 0.25},
...             {"id": 2, "stateId": 4, "layerId": 2, "name": "b", "path": [2, 1], "flow": 0.25},
...         ]
...     },
...     network_id="net",
... )
>>> layers = alluvial.split_layers(nf)
>>> [(layer.id, layer.name) for layer in layers]
[('net_layer1', 'Layer 1'), ('net_layer2', 'Layer 2')]
>>> [len(network.modules) for network in alluvial.layout(layers).networks]
[1, 2]

Used in: 1.6 Multilayer networks and split_layers

class alluvial.NetworkFile(id, name, format, nodes, ...)#

A parsed network file, as alluvial.read and alluvial.partition return it.

Besides the attributes below, it holds the file’s Infomap header (codelength, directed, …) and statistics (is_state_network, is_multilayer, num_top_modules, …).

Parameters:
  • id (str)

  • name (str)

  • format (str)

  • nodes (list[Node])

  • filename (str)

  • size (int)

  • last_modified (int)

  • have_modules (bool)

  • version (str | None)

  • args (str | None)

  • started_at (str | None)

  • completed_in (float | None)

  • codelength (float | None)

  • relative_codelength_savings (float | None)

  • flow_model (str | None)

  • higher_order (bool | None)

  • state_level (bool | None)

  • clu_level (int | None)

  • bipartite_start_id (int | None)

  • directed (bool | None)

  • modules (list[InfomapModule] | None)

  • flow_distribution (dict[Any, float] | None)

  • is_state_network (bool | None)

  • is_multilayer (bool | None)

  • is_expanded (bool | None)

  • num_top_modules (int | None)

  • num_levels (int | None)

  • num_layers (int | None)

  • network (str | None)

  • num_trials (int | None)

  • two_level (bool | None)

  • layer_id (int | None)

  • original_id (str | None)

  • layers (list[dict[str, Any]] | None)

  • colors (Any)

  • raw_modules (list[Any] | None)

  • alluvial (Any)

id#

Network id. Must be unique within a diagram and must not contain "--"; readers default it to the file stem.

Type:

str

name#

Display name; readers default it to the file basename.

Type:

str

format#

"tree", "ftree", "stree", "clu", …

Type:

str

nodes#

Node rows in file order (the order decides module creation order and every downstream tie-break).

Type:

list of Node

Examples

>>> import dataclasses, alluvial
>>> nf = alluvial.partition({"a": 1, "b": 1, "c": 2}, name="2020")
>>> nf.id, nf.name, nf.num_top_modules
('2020', '2020', 2)
>>> renamed = dataclasses.replace(nf, id="first", name="First year")
>>> [network.name for network in alluvial.layout([renamed, alluvial.partition({"a": 1, "b": 2, "c": 2})]).networks]
['First year', 'network 1']

Used in: 1.8 NetworkFile, names, ids and identifiers

class alluvial.NodeInfo(network_index, network_id, identifier, id, state_id, layer_id, name, flow, path, metadata)#

A read-only view of one input node, passed to a layout(highlight=callable).

Parameters:
network_index#

Index of the network (left to right, after splitting layers).

Type:

int

network_id#

Its network id.

Type:

str

identifier#

The string nodes are matched by across networks (see layout(identifier=...)).

Type:

str

id#

The (physical) node id.

Type:

int or float

state_id, layer_id

State and layer ids of state and multilayer networks.

Type:

int or None

name#

The node name.

Type:

str or None

flow#

The node flow.

Type:

float

path#

The module path, e.g. "1:2:3" (the last part is the node’s rank in its module).

Type:

str

metadata#

The node’s metadata, read-only.

Type:

Mapping or None

Examples

>>> import alluvial
>>> geometry = alluvial.layout(
...     [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}],
...     highlight=lambda node: "a" if node.identifier == "a" else None,
... )
>>> [label.label for label in geometry.highlight_labels]
['a']

Used in: 3.4 Highlight groups and legends

class alluvial.ModuleInfo(network_index, network_id, network_name, id, path, level, flow, visible, node_names)#

A read-only view of one module, passed to a layout(module_names=callable).

Parameters:
network_index#

Index of the network (left to right, after splitting layers).

Type:

int

network_id#

Its network id.

Type:

str

network_name#

Its network name.

Type:

str

id#

The module id (path string), e.g. "3" or "1:2" after module_level/expand.

Type:

str

path#

The module path components.

Type:

tuple of int

level#

The module level (1 for top modules).

Type:

int

flow#

The module flow.

Type:

float

visible#

Whether the module is drawn (its flow is above the flow threshold).

Type:

bool

node_names#

The names of its nodes in descending flow (nodes without a name are left out).

Type:

tuple of str

Examples

>>> import alluvial
>>> geometry = alluvial.layout(
...     [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}], module_names=lambda module: f"M{module.id}"
... )
>>> [module.name for module in geometry.networks[0].modules]
['M1', 'M2']

Used in: 5 Module names and order

class alluvial.HighlightLabel(label, highlight_index, color)#

One label of layout(highlight=...): its text, its highlight index (-1: drawn in the default color) and the color string registered for it.

Examples

>>> import alluvial
>>> networks = [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}]
>>> geometry = alluvial.layout(networks, highlight={"group a": ["a"]}, highlight_colors=["tab:red"])
>>> geometry.highlight_labels
(HighlightLabel(label='group a', highlight_index=0, color='tab:red'),)
Parameters:
  • label (str)

  • highlight_index (int)

  • color (str)

Used in: 3.4 Highlight groups and legends

Saving#

alluvial.save_diagram(networks, path, *, loose=False, **layout_kwargs)#

Lay out networks like layout and save the diagram as the web app saves it (“Download diagram”).

The files are “alluvial network files” (formatVersion 1): one Infomap-JSON file per network (00_<name>.json, …) with the network name, the palette (colors), each node’s color index and module level, Infomap’s own top-level data, and an alluvial block with the options, the color scheme with its c3 tone and offset, the module names and any custom module order. The web app opens them (drop the zip, or all files, on “Load networks”) with the same picture, and alluvial.read/layout load them back exactly. Other Infomap-JSON readers ignore the block. The text equals the web app’s for the same diagram, apart from generator.

Parameters:
  • networks (Iterable[Any]) – As for layout (names, colors, color_network, module_level, highlight, scheme, tone, offset, module_order, module_names, layout options, …). The writer reads the engine store after these steps, as the web app saves its own store.

  • **layout_kwargs – As for layout (names, colors, color_network, module_level, highlight, scheme, tone, offset, module_order, module_names, layout options, …). The writer reads the engine store after these steps, as the web app saves its own store.

  • path (str or PathLike) – Where to write. By default a zip (.zip is appended if path has another suffix). With loose=True, a directory (created if needed) for the loose files. A .json path writes the one file of a single-network diagram.

  • loose (bool, default False) – Write loose files to a directory instead of a zip.

Returns:

The path written (the zip, the directory or the .json file).

Return type:

pathlib.Path

Raises:
  • TypeError – If networks is an AlluvialGeometry (pass the networks and layout’s keywords instead).

  • ValueError – For a .json path and a diagram of more than one network.

Examples

>>> import alluvial, pathlib, tempfile
>>> networks = [{"a": 1, "b": 1, "c": 2}, {"a": 1, "b": 2, "c": 2}]
>>> with tempfile.TemporaryDirectory() as tmp:
...     path = alluvial.save_diagram(networks, pathlib.Path(tmp) / "diagram.zip", names=["2020", "2021"])
...     [network.name for network in alluvial.read(path)]
['2020', '2021']

Used in: 8 Save and load

Colors#

alluvial.SCHEMES: tuple[str, ...]#

the values of scheme=.

Type:

The names of the web app’s color schemes, in the order of its scheme menu

Used in: 3.2 Color schemes · 3.3 Tones and offsets

Pass a name as scheme= to layout, plot or save_diagram, or pass a list of colors instead. The C3 schemes are computed with c3, which shows what they and its tones look like; tone= and offset= apply to them. The other schemes are those of the web app’s scheme menu. To draw with any matplotlib colormap instead, pass palette= to draw or plot.

Group

Schemes

ColorBrewer

Accent, Category10, Dark2, Paired, Pastel1, Pastel2, Set1, Set2, Set3, Tableau10

Seaborn

Deep, Muted, Pastel, Bright, Dark, Colorblind

Matplotlib

Tableau20, Tableau20b, Tableau20c

C3

C3 Oklch, C3 Turbo, C3 Rainbow, C3 Sinebow, C3 Viridis

Natural

Mural

Uniform

Flare, Crest

Diverging

Brown-Green, Purple-Green, Pink-Green, Purple-Orange, Red-Blue, Red-Gray, Red-Yellow-Blue, Red-Yellow-Green, Spectral

Sequential Multi

Blue-Green, Blue-Purple, Green-Blue, Orange-Red, Purple-Blue-Green, Purple-Blue, Purple-Red, Red-Purple, Yellow-Green-Blue, Yellow-Green, Yellow-Orange-Brown, Yellow-Orange-Red, Cividis, Cubehelix, Warm, Cool, Turbo, Viridis, Magma, Inferno, Plasma

Sequential Single

Blues, Greens, Greys, Purples, Reds, Oranges

Cyclical

Rainbow, Sinebow

The c3 tones (tone=): vivid (default), bright, strong, deep, dark, soft, pastel, muted.