Note
Go to the end to download the full example code.
DAG Visualization with Mermaid¶
NeuroDAGs can render any pipeline or derivative definition as an interactive Mermaid diagram saved to a standalone HTML file.
Two levels of detail are available:
Pipeline DAG — high-level view: one node per derivative, edges showing inter-derivative dependencies.
Derivative DAG — fine-grained view: every computation node and data reference inside a single derivative.
Two layout engines are supported:
ELK (default) — orthogonal edge routing with active crossing minimisation. Significantly cleaner for dense pipelines with many interconnected derivatives. Requires internet access to load the ELK bundle from the CDN when the HTML is opened.
dagre — right-angle step edges, no CDN dependency. Use for offline environments.
Setup¶
import yaml
Sample pipeline¶
We define a three-step pipeline in YAML — the same format used in a real
pipeline.yml file.
PIPELINE_YAML = """\
DerivativeDefinitions:
BasicPrep:
nodes:
- id: 0
derivative: SourceFile
- id: 1
node: basic_preprocessing
args:
mne_object: id.0
filter_args: {l_freq: 1.0, h_freq: 80.0}
epoch_config: {duration: 2.0, overlap: 0.0}
Spectrum:
nodes:
- id: 0
derivative: BasicPrep.fif
- id: 1
node: mne_spectrum_array
args:
meeg: id.0
method: welch
BandPower:
save: false
for_dataframe: true
nodes:
- id: 0
derivative: Spectrum.nc
- id: 1
node: extract_data_var
args: {dataset_like: id.0, data_var: spectrum}
- id: 2
node: bandpower
args:
psd_like: id.1
relative: true
bands:
delta: [1.0, 4.0]
theta: [4.0, 8.0]
alpha: [8.0, 13.0]
beta: [13.0, 30.0]
- id: 3
node: aggregate_across_dimension
args: {xarray_data: id.2, dim: epochs, operation: mean}
"""
pipeline_config = yaml.safe_load(PIPELINE_YAML)
Pipeline-level Mermaid diagram¶
pipeline_to_mermaid() returns the raw Mermaid string.
pipeline_to_html() saves it to a self-contained HTML
file that renders in any browser.
from neurodags.mermaid import pipeline_to_html, pipeline_to_mermaid
mermaid_str = pipeline_to_mermaid(pipeline_config)
print("Pipeline Mermaid diagram:")
print(mermaid_str)
Pipeline Mermaid diagram:
%% Pipeline DAG
flowchart TD
BasicPrep["BasicPrep"]
Spectrum["Spectrum"]
BandPower["BandPower"]
BasicPrep --> Spectrum
Spectrum --> BandPower
Save to HTML with ELK layout (default)¶
ELK produces orthogonal edges with crossing minimisation — the clearest layout for pipelines with many interconnected derivatives. Requires CDN access when the HTML file is opened in a browser.
import tempfile
from pathlib import Path
out_dir = Path(tempfile.mkdtemp(prefix="neurodags_mermaid_"))
pipeline_html = pipeline_to_html(
pipeline_config,
output_path=out_dir / "pipeline_dag_elk.html",
title="My Pipeline DAG (ELK)",
auto_open=False, # set True to open in browser
layout="elk", # default — orthogonal routing via ELK
)
print(f"Pipeline DAG (ELK) saved to: {pipeline_html}")
Pipeline DAG (ELK) saved to: /tmp/neurodags_mermaid_y3eh9q4b/pipeline_dag_elk.html
Save to HTML with dagre layout (offline fallback)¶
dagre uses right-angle step edges and loads no external resources — suitable for air-gapped environments or when CDN access is unavailable.
pipeline_html_dagre = pipeline_to_html(
pipeline_config,
output_path=out_dir / "pipeline_dag_dagre.html",
title="My Pipeline DAG (dagre)",
auto_open=False,
layout="dagre", # offline fallback, no CDN dependency
)
print(f"Pipeline DAG (dagre) saved to: {pipeline_html_dagre}")
Pipeline DAG (dagre) saved to: /tmp/neurodags_mermaid_y3eh9q4b/pipeline_dag_dagre.html
Derivative-level Mermaid diagram¶
Zoom into a single derivative to see every node and data reference.
Node shapes:
Circle
(((...)))—SourceFile(raw input).Cylinder
[(...)]— upstream derivative artifact (cached on disk).Rectangle
[...]— computation node.
from neurodags.mermaid import derivative_to_html, derivative_to_mermaid
deriv_name = "BandPower"
deriv_def = pipeline_config["DerivativeDefinitions"][deriv_name]
mermaid_str = derivative_to_mermaid(deriv_def, deriv_name)
print(f"\n{deriv_name} derivative Mermaid diagram:")
print(mermaid_str)
BandPower derivative Mermaid diagram:
%% BandPower
flowchart TD
id0[("Spectrum.nc")]
id1["extract_data_var"]
id2["bandpower"]
id3["aggregate_across_dimension"]
id0 --> id1
id1 --> id2
id2 --> id3
Save the derivative diagram to HTML (ELK layout).
deriv_html = derivative_to_html(
deriv_def,
deriv_name,
output_path=out_dir / f"{deriv_name}_dag.html",
auto_open=False,
layout="elk",
)
print(f"{deriv_name} DAG saved to: {deriv_html}")
BandPower DAG saved to: /tmp/neurodags_mermaid_y3eh9q4b/BandPower_dag.html
Using a real pipeline.yml¶
In a real project, load your config from disk and call the same functions:
import yaml
from neurodags.mermaid import pipeline_to_html, derivative_to_html
with open("pipeline.yml") as f:
config = yaml.safe_load(f)
# Full pipeline overview — ELK layout (default)
pipeline_to_html(config, output_path="pipeline_dag.html", auto_open=True)
# Offline fallback — dagre layout
pipeline_to_html(config, output_path="pipeline_dag.html", layout="dagre", auto_open=True)
# Single derivative detail
derivative_to_html(
config["DerivativeDefinitions"]["BandPower"],
"BandPower",
output_path="bandpower_dag.html",
auto_open=True,
)
CLI equivalents:
neurodags dag pipeline.yml
neurodags dag pipeline.yml --html pipeline_dag.html # ELK layout (default)
neurodags dag pipeline.yml --html pipeline_dag.html --layout dagre # offline fallback
neurodags dag pipeline.yml --derivative BandPower --html bandpower_dag.html
Direct Mermaid string access¶
Use the raw string functions when you need to embed diagrams in Jupyter
notebooks or custom HTML templates. Pass layout= to save_mermaid_html
to select the layout engine for the wrapper HTML.
from neurodags.mermaid import save_mermaid_html
custom_diagram = """ flowchart TD
A["load_raw"] --> B["filter"]
B --> C["epoch"]
C --> D[("BasicPrep.fif")]"""
out = save_mermaid_html(
custom_diagram,
output_path=out_dir / "custom_dag.html",
title="Custom DAG",
layout="elk",
)
print(f"Custom DAG saved to: {out}")
Custom DAG saved to: /tmp/neurodags_mermaid_y3eh9q4b/custom_dag.html
Total running time of the script: (0 minutes 0.089 seconds)