Written and maintained by CASRAI Editorial Board
Published
On this page
What it is used for in research
A Sankey diagram is a specialized directed flow visualization where the width of connecting bands is proportional to the quantity of flow between categorical states or process stages. In academic research, investigators use Sankey diagrams to depict multi-stage transitions, cohort attrition, energy or mass balances, and categorical sequence data. Unlike standard static flowcharts, Sankey diagrams preserve quantitative proportions across every junction, enabling readers to identify primary conduits, bottlenecks, and loss pathways across complex experimental or observational workflows.
Primary research applications across scientific disciplines include:
- Clinical Trial & Epidemiological Cohorts: Visualizing participant progress through clinical protocols, tracking attrition from initial screening and randomization through intervention arms to completed follow-up or loss to follow-up.
- Life Cycle Assessment (LCA) & Energy Systems: Mapping raw material extraction, transformation, transmission losses, and final end-use consumption in environmental engineering and circular economy studies.
- Bioinformatics & Systems Biology: Tracing gene expression classifications, microbiome taxonomic shifts across treatment timepoints, or multi-omics data integration flows.
- Education & Career Trajectories: Analyzing longitudinal survey data, student retention across degree milestones, and interdisciplinary researcher mobility patterns.
While researchers working in Python often rely on Plotly or Matplotlib (see our guide to Sankey diagrams in Python), the R statistical environment provides two specialized ecosystems. For interactive exploratory dashboards and web-based supplementary materials, the networkD3 package produces dynamic D3.js JavaScript widgets. For static, high-resolution vector figures destined for print manuscripts, the ggalluvial package extends the Grammar of Graphics to generate publication-grade scientific figures directly within ggplot2.
Install or access (official source only)
Both primary packages are available directly from the Comprehensive R Archive Network (CRAN) and can be installed into an active R session (version 3.0.0 or higher) using the standard CRAN package manager. For comprehensive package setup rules, review our tutorial on how to install libraries in R.
Run the following command in the R console to install the packages from an official CRAN mirror:
# Install official CRAN packages
install.packages("networkD3")
install.packages("ggalluvial")
install.packages("dplyr")
To access the installed libraries and verify version compatibility, load them into your script or R Markdown/Quarto document:
# Load libraries into environment
library(networkD3)
library(ggalluvial)
library(dplyr)
# Verify loaded versions
packageVersion("networkD3") # Current CRAN release: 0.4
packageVersion("ggalluvial") # Current CRAN release: 0.12.5
Step-by-step task (numbered, with menu names or commands exactly as in the current docs)
Building a Sankey diagram in R involves preparing research flow data, mapping entities to package-specific structures, and rendering the final graphic. The steps below detail how to construct both an interactive D3 diagram and a static manuscript-ready vector figure.
Step 1: Structure the Raw Research Data
Real-world research observations typically start as tabular data frames in R recording categorical patient or specimen states across consecutive experimental stages. Create a representative clinical cohort dataset recording participant progression across three study milestones:
# Sample research cohort: Screening -> Intervention -> 12-Month Outcome
cohort_data <- data.frame(
Screening = c("Eligible", "Eligible", "Eligible", "Ineligible"),
Intervention = c("Treatment Arm", "Treatment Arm", "Control Arm", "Excluded"),
Outcome = c("Remission", "Relapse", "Remission", "None"),
Count = c(45, 15, 30, 20)
)
Step 2: Method A — Build an Interactive D3 Diagram with networkD3
The networkD3::sankeyNetwork() function requires two distinct data frames: a Nodes table containing unique entity labels, and a Links table specifying connection pairs with zero-indexed integer IDs and numeric flow weights.
- Derive Pairwise Aggregations: Transform multi-stage cohort data into pairwise source-to-target links:
# Stage 1 to Stage 2 links link1 <- cohort_data %>% group_by(source = Screening, target = Intervention) %>% summarise(value = sum(Count), .groups = "drop") # Stage 2 to Stage 3 links link2 <- cohort_data %>% filter(Intervention != "Excluded") %>% group_by(source = Intervention, target = Outcome) %>% summarise(value = sum(Count), .groups = "drop") # Combine all pairwise transitions links_raw <- bind_rows(link1, link2) - Construct Node Lookup with Zero-Indexed Mapping: Because JavaScript arrays count from 0 while R vectors count from 1, failure to subtract 1 will cause node lookup errors or omit the final node:
# Create unique node list nodes <- data.frame( name = unique(c(links_raw$source, links_raw$target)), stringsAsFactors = FALSE ) # Map string labels to zero-indexed integer identifiers links <- links_raw %>% mutate( IDsource = match(source, nodes$name) - 1, IDtarget = match(target, nodes$name) - 1 ) - Execute sankeyNetwork: Render the interactive widget with explicit styling and unit labels:
# Generate interactive Sankey diagram sankey_widget <- sankeyNetwork( Links = links, Nodes = nodes, Source = "IDsource", Target = "IDtarget", Value = "value", NodeID = "name", units = "Patients", fontSize = 12, fontFamily = "Arial", nodeWidth = 20, nodePadding = 12, sinksRight = FALSE ) # View in RStudio Viewer or web browser sankey_widget - Export Standalone HTML: Save the interactive widget for web supplements using
saveNetwork():# Export to standalone HTML file saveNetwork(sankey_widget, file = "figure1_interactive.html", selfcontained = TRUE)
Step 3: Method B — Build a Static Vector Diagram with ggalluvial
For peer-reviewed journal submission requiring high-resolution vector graphics (PDF/EPS), use ggalluvial to preserve native ggplot2 thematic control.
- Specify Alluvial Coordinates: Map multi-stage variables directly to categorical axis aesthetics inside
aes():library(ggplot2) library(ggalluvial) # Construct static alluvial/Sankey plot p_static <- ggplot( data = cohort_data, aes(y = Count, axis1 = Screening, axis2 = Intervention, axis3 = Outcome) ) + geom_alluvium(aes(fill = Intervention), width = 1/4, knot.pos = 0.4, alpha = 0.7) + geom_stratum(width = 1/4, fill = "grey92", color = "grey30") + geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 3.2, fontface = "bold") + scale_x_discrete(limits = c("Screening", "Intervention", "Outcome"), expand = c(0.12, 0.05)) + scale_fill_brewer(palette = "Set2") + theme_minimal(base_size = 11) + theme( legend.position = "bottom", panel.grid.major.x = element_blank(), panel.grid.minor = element_blank(), axis.text.y = element_text(color = "black") ) + labs( title = "Participant Flow Across Study Milestones", y = "Number of Enrolled Participants", x = "Trial Stage", fill = "Treatment Allocation" ) print(p_static) - Export Vector Figures for Manuscript Submission: Use
ggsave()to output publication-compliant PDF and TIFF files:# Export publication-ready vector PDF (for peer review) ggsave("Figure1_Cohort_Flow.pdf", plot = p_static, width = 7.5, height = 5.0, units = "in") # Export high-resolution raster TIFF (300 DPI) ggsave("Figure1_Cohort_Flow.tiff", plot = p_static, width = 7.5, height = 5.0, units = "in", dpi = 300)
Output and how to read it
A Sankey diagram displays flows through three interconnected visual elements:
- Nodes (Strata): Vertical rectangular blocks representing discrete categories, experimental conditions, or milestone stages. The vertical height of each stratum represents the cumulative total volume or sample size passing through that condition.
- Links (Alluvia / Ribbons): Curved bands spanning between nodes. The thickness of each link corresponds strictly to the magnitude of the transition value ($y$-aesthetic or
Valueparameter). A wider ribbon indicates a greater proportion of items transitioning between those two states. - Flow Conservation: In balanced systems (e.g., energy or financial budgets), the total incoming ribbon width at an intermediate node equals the total outgoing ribbon width. In cohort or attrition workflows, differences between incoming and outgoing widths visually highlight dropouts, treatment non-compliance, or system loss.
When reviewing interactive networkD3 output, hovering over any ribbon activates an inspection tooltip displaying the source node, target node, and exact numeric magnitude formatted with the user-defined units string. Clicking and dragging vertical node bars allows the researcher to manually optimize vertical spacing and resolve overlapping link trajectories. In static ggalluvial output, color shading tracks specific cohorts across all axes simultaneously, allowing readers to trace sub-populations from inception to study conclusion.
Troubleshooting
When constructing flow diagrams in R, users encounter recurring configuration challenges stemming from data indexing, topological constraints, or graphic parameters. The table below provides verified solutions to primary failure modes:
| Error or Symptom | Underlying Technical Cause | Verified Corrective Action |
|---|---|---|
Error in if (links$source[i] == links$target[i]) or layout hangs |
Circular flow or feedback loop in networkD3. D3’s iterative relaxation layout algorithm requires a strict Directed Acyclic Graph (DAG). |
Remove self-referential transitions or disambiguate repeated categories across stages by appending stage suffixes (e.g., Remission_T1 and Remission_T2). |
Blank diagram or missing terminal nodes in networkD3 |
1-based indexing passed to JavaScript. D3 requires 0-indexed node IDs (ranging from $0$ to $N-1$). | Subtract 1 when mapping character labels to node indices: IDsource = match(source, nodes$name) - 1. |
| Overlapping node labels or cramped strata in dense multi-stage plots | Insufficient vertical whitespace between closely spaced categorical strata. | Increase nodePadding in sankeyNetwork() (e.g., set to 15 or 20). In ggalluvial, adjust stratum width to 1/4 or reduce label font size in geom_text(). |
Missing ribbons or broken flows across intermediate axes in ggalluvial |
Missing values (NA) in intermediate stages causing cohort tracking fragmentation. |
Explicitly impute missing milestone steps as a distinct category (e.g., “Discontinued” or “Lost to Follow-up”) prior to plotting, or set na.rm = FALSE. |
Free alternatives (table)
Several free and open-source packages in the R and scientific computing ecosystem support flow and alluvial visualizations. The matrix below compares functional characteristics to assist researchers in selecting the appropriate tool:
| Tool / Package | Rendering Engine | Primary Data Structure | Output Format | Best Research Use Case | CRAN Status |
|---|---|---|---|---|---|
ggalluvial |
ggplot2 / Grid graphics | Wide multi-axis or long lode data frames | Vector PDF, SVG, EPS, PNG | Academic manuscripts, grant proposals, multi-panel figures | Official CRAN package |
networkD3 |
D3.js JavaScript | Pairwise 0-indexed Node-Link data frames | Standalone interactive HTML | Web reports, Shiny applications, online supplementary data | Official CRAN package |
plotly (R) |
Plotly.js / WebGL | Zero-indexed source, target, and value arrays | Interactive HTML, SVG/PNG via Kaleido | Complex multi-attribute exploratory web applications | Official CRAN package |
ggsankey |
ggplot2 / Grid graphics | Long format via make_long() helper |
Vector PDF, SVG, PNG | Custom curved flow ribbons with distinct start/end widths | GitHub package |
| Python Plotly | Plotly.js / JavaScript | Dictionary structures and Pandas DataFrames | Interactive HTML, static vector PDF/SVG | Integrated Python data pipelines and Jupyter notebooks (see Plotly vs Matplotlib) | PyPI / Conda |
FAQ
Can I create a Sankey diagram using base R graphics without external packages?
Base R graphics (such as plot(), lines(), or polygon()) do not include native primitives for computing cubic spline ribbons or automatic iterative layout relaxation. While it is theoretically possible to manually calculate polynomial coordinates and draw filled polygons, doing so requires hundreds of lines of custom mathematical code. Using CRAN-verified packages such as ggalluvial or networkD3 is standard scientific practice.
How do I convert an interactive networkD3 Sankey diagram into a static publication figure?
To export a networkD3 widget as a high-resolution static image, save the widget to an HTML file using saveNetwork(widget, "sankey.html") and then render it to an image format using the CRAN package webshot2 via webshot2::webshot("sankey.html", "sankey.png", vwidth = 1200, vheight = 800). However, for peer-reviewed journal submission requiring vector PDF or EPS formats, generating the plot directly with ggalluvial and exporting via ggsave() is strongly recommended.
What is the difference between a Sankey diagram and an alluvial diagram in R?
In data visualization terminology, a classic Sankey diagram visualizes continuous flows (such as energy, fluid, or money) across arbitrary network topologies with node-level flow conservation. An alluvial plot is a specialized structured form of Sankey diagram designed for repeated-measures categorical data, where categorical dimensions are arranged along discrete parallel vertical axes and ribbons track cohorts transitioning from axis to axis. In R practice, ggalluvial specializes in axis-based categorical cohorts, while networkD3 supports arbitrary directed network flow structures.
Why does my networkD3 Sankey diagram render as a completely blank box?
A blank output window almost always indicates an indexing discrepancy between R and JavaScript. If your IDsource or IDtarget columns contain 1-based indices (or character strings instead of integers), JavaScript fails silently to bind links to nodes. Always verify that your link identifiers are integers starting at 0, and ensure no NA values or missing node names exist in the link list.
Sources (official docs)
- Brunson, J. C., & Read, Q. D. (2023). ggalluvial: Alluvial Plots in ‘ggplot2’. R package version 0.12.5. Comprehensive R Archive Network (CRAN). https://cran.r-project.org/package=ggalluvial (Accessed 2026-11-10).
- Gandrud, C., Allaire, J. J., Russell, K., & Yetman, C. J. (2020). networkD3: D3 JavaScript Network Graphs from R. R package version 0.4. Comprehensive R Archive Network (CRAN). https://cran.r-project.org/package=networkD3 (Accessed 2026-11-10).
- Plotly Technologies Inc. (2026). Sankey Diagrams in R: Open Source Graphing Library. Plotly R Documentation. https://plotly.com/r/sankey-diagram/ (Accessed 2026-11-10).
- The R Foundation for Statistical Computing. (2026). R: A Language and Environment for Statistical Computing. Comprehensive R Archive Network. https://www.r-project.org/ (Accessed 2026-11-10).







