Core implementation for drawing a Sankey (alluvial) diagram using
geom_alluvium (or geom_flow)
and geom_stratum. This function takes a single
data frame (no split_by support) and returns a ggplot object
with faceting applied.
The function supports five input formats (in_form) which control how
the data columns are interpreted. Nodes (strata) are rendered as vertical
blocks whose fill colour and alpha can be customised independently of the
links (alluvia / flows) connecting them. Link colours can match node
colours or use a separate palette; link borders can be set to a fixed colour
or to follow the fill colour (links_color = ".fill").
Automatic legend resolution determines whether nodes on different x-axis positions receive a merged legend or separate legends, based on overlaps between stratum values across positions.
Usage
SankeyPlotAtomic(
data,
in_form = c("auto", "long", "lodes", "wide", "alluvia", "counts"),
x,
x_sep = "_",
y = NULL,
stratum = NULL,
stratum_sep = "_",
alluvium = NULL,
alluvium_sep = "_",
flow = FALSE,
nodes_color = "grey30",
links_fill_by = NULL,
links_fill_by_sep = "_",
links_name = NULL,
links_color = "gray80",
nodes_palette = "Paired",
nodes_palcolor = NULL,
palreverse = FALSE,
nodes_alpha = 1,
nodes_label = FALSE,
nodes_width = 0.25,
nodes_label_miny = 0,
nodes_legend = c("auto", "separate", "merge", "none"),
expand = c(0, 0, 0, 0),
links_palette = "Paired",
links_palcolor = NULL,
links_alpha = 0.6,
legend.box = "vertical",
keep_empty = TRUE,
x_text_angle = 0,
aspect.ratio = 1,
legend.position = "right",
legend.direction = "vertical",
flip = FALSE,
theme = "theme_this",
theme_args = list(),
title = NULL,
subtitle = NULL,
xlab = NULL,
ylab = NULL,
facet_by = NULL,
facet_scales = "fixed",
facet_ncol = NULL,
facet_nrow = NULL,
facet_byrow = TRUE,
...
)Arguments
- data
A data frame.
- in_form
A character string specifying the input data format. One of
"auto"(default),"long","lodes","wide","alluvia", or"counts"."long"is an alias for"lodes";"wide"is an alias for"alluvia". See thedataparameter ofSankeyPlotfor format descriptions.- x
A character vector of column name(s) for the x-axis categories. Each unique value or concatenated pair represents a time point, state, or position along the horizontal axis. Behaviour depends on
in_form: for"lodes"at least one column is expected; for"alluvia"and"counts"at least two columns are required. In the latter two casesx_sepis not used.- x_sep
A character string to join multiple
xcolumns whenin_formis"lodes"or auto-determined as lodes. Default"_".- y
A character string specifying the numeric column for the y-axis (frequency / value). When
NULL(default), the count of observations per combination ofx,stratum,alluvium,links_fill_by, andfacet_byis computed automatically. Ignored whenin_formis"counts".- stratum
A character string specifying the column that defines the node categories at each x-axis position. Each unique value becomes a stratum (node block) at each x position. When
NULL, defaults tolinks_fill_by. Multiple columns are concatenated withstratum_sep. Ignored in"alluvia"format.- stratum_sep
A character string to join multiple
stratumcolumns. Default"_".- alluvium
A character string specifying the column that identifies individual flows (alluvia) across x-axis positions. Each unique value represents a single observational unit tracked across positions. When
NULLin"counts"format, an auto-generated identifier is created. Multiple columns are concatenated withalluvium_sep. Ignored in"alluvia"format.- alluvium_sep
A character string to join multiple
alluviumcolumns. Default"_".- flow
A logical value. When
FALSE(default),geom_alluvium()is used for the links. WhenTRUE,geom_flow()is used instead, which draws the flows with a directional gradient between x positions.- nodes_color
A character string specifying the border colour of the node (stratum) rectangles. Use the special value
".fill"to match the border colour to the node fill colour. Default"grey30".- links_fill_by
A character string specifying the column that determines the fill colour of the links (alluvia / flows). When
NULLin"lodes"format, defaults toalluvium. In"counts"format with the"."prefix, this parameter is required. Multiple columns are concatenated withlinks_fill_by_sep.- links_fill_by_sep
A character string to join multiple
links_fill_bycolumns. Default"_".- links_name
A character string for the legend title of the link fill scale. When
NULL(default), thelinks_fill_bycolumn name is used.- links_color
A character string specifying the border colour of the links (alluvia / flows). Use the special value
".fill"to match the link border colour to the link fill colour. Default"gray80".- nodes_palette
A character string specifying the colour palette for the node (stratum) fill. Passed to
palette_this(). Default"Paired".- nodes_palcolor
A character vector of custom colours for the node fill, used as
palcolorinpalette_this(). WhenNULL(default), the palette colours are used directly.- palreverse
A logical value indicating whether to reverse the palette. Default is FALSE.
- nodes_alpha
A numeric value in \([0, 1]\) controlling the transparency of the node (stratum) fill. Default
1.- nodes_label
A logical value. When
TRUE, stratum labels are drawn inside each node usinggeom_label()withStatStratum. DefaultFALSE.- nodes_width
A numeric value (typically 0–1) specifying the width of the node (stratum) rectangles as a fraction of the x-axis spacing. Default
0.25.- nodes_label_miny
A numeric value specifying the minimum y (frequency) threshold for displaying node labels. Nodes with y-values below this threshold are not labelled. Default
0.- nodes_legend
Controls how the node legend is displayed. One of:
"auto"(default)Automatically determined: if
nodes_label = TRUE, or ifstratumis identical tolinks_fill_bywith matching colours, the legend is hidden. Otherwise, overlapping stratum values across x positions are checked: any overlap produces a merged legend; no overlap produces separate legends per x position."merge"A single merged legend for all nodes.
"separate"One legend per x-axis position, generated via separate
scale_fill_manual()layers."none"No node legend is shown.
- expand
The values to expand the x and y axes. It is like CSS padding. When a single value is provided, it is used for both axes on both sides. When two values are provided, the first value is used for the top/bottom side and the second value is used for the left/right side. When three values are provided, the first value is used for the top side, the second value is used for the left/right side, and the third value is used for the bottom side. When four values are provided, the values are used for the top, right, bottom, and left sides, respectively. You can also use a named vector to specify the values for each side. When the axis is discrete, the values will be applied as 'add' to the 'expansion' function. When the axis is continuous, the values will be applied as 'mult' to the 'expansion' function. See also https://ggplot2.tidyverse.org/reference/expansion.html
- links_palette
A character string specifying the colour palette for the link fill. Passed to
palette_this(). Default"Paired".- links_palcolor
A character vector of custom colours for the link fill, used as
palcolorinpalette_this(). WhenNULL(default), the palette colours are used directly.- links_alpha
A numeric value in \([0, 1]\) controlling the transparency of the link fill. Default
0.6.- legend.box
A character string specifying the arrangement of legend boxes, either
"vertical"(default) or"horizontal".- keep_empty
One of FALSE, TRUE and "level". It can also take a named list to specify different behavior for different columns. Without a named list, the behavior applies to the categorical/character columns used on the plot, for example, the
x,group_by,fill_by, etc.FALSE(default): Drop empty factor levels from the data before plotting.TRUE: Keep empty factor levels and show them as a separate category in the plot."level": Keep empty factor levels, but do not show them in the plot. But they will be assigned colors from the palette to maintain consistency across multiple plots. Alias:levels
- x_text_angle
A numeric value specifying the angle of the x-axis text.
- aspect.ratio
A numeric value specifying the aspect ratio of the plot.
- legend.position
A character string specifying the position of the legend. if
waiver(), for single groups, the legend will be "none", otherwise "right".- legend.direction
A character string specifying the direction of the legend.
- flip
A logical value. When
TRUE,coord_flip()is applied to swap the x and y axes. DefaultFALSE.- theme
A character string or a theme class (i.e. ggplot2::theme_classic) specifying the theme to use. Default is "theme_this".
- theme_args
A list of arguments to pass to the theme function.
- title
A character string specifying the title of the plot. A function can be used to generate the title based on the default title. This is useful when split_by is used and the title needs to be dynamic.
- subtitle
A character string specifying the subtitle of the plot.
- xlab
A character string specifying the x-axis label.
- ylab
A character string specifying the y-axis label.
- facet_by
A character string specifying the column name of the data frame to facet the plot. Otherwise, the data will be split by
split_byand generate multiple plots and combine them into one usingpatchwork::wrap_plots- facet_scales
Whether to scale the axes of facets. Default is "fixed" Other options are "free", "free_x", "free_y". See
ggplot2::facet_wrap- facet_ncol
A numeric value specifying the number of columns in the facet. When facet_by is a single column and facet_wrap is used.
- facet_nrow
A numeric value specifying the number of rows in the facet. When facet_by is a single column and facet_wrap is used.
- facet_byrow
A logical value indicating whether to fill the plots by row. Default is TRUE.
- ...
Additional arguments passed to
geom_alluvium()orgeom_flow(), depending on theflowsetting. Forgeom_flowwith a distinctlinks_fill_bycolumn, passingstat = "alluvium"preserves the fill variable.
Architecture
Format resolution —
in_formis matched and aliased ("long"→"lodes","wide"→"alluvia").Data parsing by format — one of five code paths executes:
Lodes / long —
x,stratum,alluvium, andlinks_fill_byare validated viacheck_columns(). Multi-column inputs are concatenated with their respective separators. WhenyisNULL, counts are computed per (x,stratum,alluvium,links_fill_by,facet_by).Counts (x without
"."prefix) — numericxcolumns are pivoted to long form viapivot_longer(), creating the y-axis from the cell values.alluviumandstratumdefault tolinks_fill_by.Counts with source node (x with
"."as first element,is_flowcounts) — same as counts but also injectslinks_fill_byvalues as an additional first column of nodes, visualising how flows originate from source groups.Alluvia / wide — validated via
is_alluvia_form()and converted to lodes form viato_lodes_form().stratumandalluviumare ignored.Auto / fallback — when no other branch matches, the lodes path is attempted and validated via
is_lodes_form().
Palette assignment —
palette_this()resolves colours for both nodes (nodes_palette/nodes_palcolor) and links (links_palette/links_palcolor).Flow-counts guide logic — when
is_flowcountsisTRUE, the links guide is suppressed if the first-column node colours match the link colours, avoiding redundant legends. When the palettes are identical but colours differ (too few colours in the palette), the first-column node colours are reused.Legend auto-detection — when
nodes_legend = "auto": ifnodes_label = TRUEor ifstratumandlinks_fill_byshare identical values and colours, the nodes legend is hidden. Otherwise, overlapping stratum values across x-axis positions are checked: any overlap triggers a merged legend; no overlap produces separate legends per position.Base ggplot — constructed with
aes(x = x, stratum = stratum, alluvium = alluvium, y = y).Separate node fill layers — when
stratumdiffers fromlinks_fill_byandnodes_legend = "separate", ageom_col()+scale_fill_manual()layer pair is added per x-axis position, each followed bynew_scale_fill()to produce independent legends.Link rendering —
geom_alluvium()(default) orgeom_flow()whenflow = TRUE. Whenlinks_color = ".fill", the colour aesthetic is mapped tolinks_fill_byand the colour scale guide is suppressed. Forgeom_flowwith a distinctstratumandlinks_fill_by,stat = "alluvium"is forced to preserve the fill variable through the flow stat transformation.Link fill scale —
scale_fill_manual()withlinks_colorsand the resolvedlinks_guide.Node rendering —
geom_stratum()withnodes_color(border) andnodes_alpha. Whennodes_color = ".fill", the colour aesthetic maps tostratum.Node fill scale —
scale_fill_manual()withnodes_colors. The guide is"none"whennodes_legendis"none"or"separate"(already handled by per-position layers); otherwise"legend".Labels — when
nodes_label = TRUE,geom_label()withstat = StatStratumandmin.y = nodes_label_miny.Axes, theme, labels —
scale_x_discrete(),scale_y_continuous(), custom theme, plot title / subtitle, axis labels, and aesthetic adjustments (aspect ratio, legend position / direction, grid removal, x-text angle).Coordinate flip — when
flip = TRUE,coord_flip()swaps the axes.Dimension calculation —
calculate_plot_dimensions()computesheightandwidthattributes from the number of x-axis positions, legend metrics, and flip state, scaled byaspect.ratio.Faceting —
facet_plot()wraps the result whenfacet_byis provided.
