Calling wompwomp from Inside ggalluvial

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(ggalluvial)
library(wompwomp)
set.seed(43)

The vignette("plotting") workflow sorts and colours the data frame first and hands the result to ggplot2. Development versions of ggalluvial can instead call wompwomp from within the alluvial layers themselves, through the sort_strata and color_strata parameters of stat_stratum(), stat_alluvium() and stat_flow(). The parameter names an engine; "wompwomp" runs sort_to_uncross(column_method = "none") for sort_strata and get_lode_clusters() for color_strata, the latter exposed as the computed variable cluster. sort_strata must be given the same value in every layer of a plot, since each layer computes its own positions; wompwomp is still run only once per panel and the layers share the result.

We use the same toy data as the other vignettes.

df <- data.frame(
  tissue = c(
    "BRAIN", "BRAIN", "BRAIN",
    "STOMACH", "STOMACH", "STOMACH", "STOMACH", "STOMACH", "STOMACH",
    "HEART", "HEART", "HEART", "HEART", "HEART", "HEART", "HEART",
    "T CELL", "T CELL",
    "B CELL", "B CELL", "B CELL", "B CELL", "B CELL", "B CELL", "B CELL", "B CELL", "B CELL"
  ),
  cluster = c(
    1, 1, 2,
    1, 2, 2, 2, 2, 2,
    1, 3, 3, 3, 3, 3, 3,
    4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4
  )
)

df |> 
  dplyr::group_by(tissue, cluster) |> 
  dplyr::summarize(value = dplyr::n()) |> 
  dplyr::ungroup() |> 
  dplyr::mutate(dplyr::across(c(tissue, cluster), as.character)) |> 
  print() -> clus_df_gather
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by tissue and cluster.
## ℹ Output is grouped by tissue.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(tissue, cluster))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
## # A tibble: 8 × 3
##   tissue  cluster value
##   <chr>   <chr>   <int>
## 1 B CELL  4           9
## 2 BRAIN   1           2
## 3 BRAIN   2           1
## 4 HEART   1           1
## 5 HEART   3           6
## 6 STOMACH 1           1
## 7 STOMACH 2           5
## 8 T CELL  4           2

Sorting and colouring inside the layers

clus_df_gather |>
  ggplot(aes(y = value, axis1 = tissue, axis2 = cluster)) +
  geom_alluvium(aes(fill = tissue), width = 1/12,
                sort_strata = "wompwomp") +
  geom_stratum(aes(fill = after_stat(cluster)), width = 1/12, color = "grey",
               sort_strata = "wompwomp", color_strata = "wompwomp") +
  geom_label(stat = "stratum", aes(label = after_stat(stratum)),
             sort_strata = "wompwomp") +
  scale_x_discrete(
    limits = c("tissue", "cluster"),
    expand = c(.05, .05)
  )

Choosing and tuning an algorithm

To choose an algorithm other than the default, or to tune it, pass a list: the method selects among sort_to_uncross()’s (or get_lode_clusters()’s) methods, and the remaining named elements are that function’s own arguments. Everything else wompwomp exposes lives in sort_to_uncross_options() and get_lode_clusters_options(), so those knobs travel in options — passing one directly is an error that names the constructor to use.

# arguments of sort_to_uncross() / get_lode_clusters()
geom_stratum(sort_strata = list("wompwomp", method = "tsp", alpha = 3))
geom_stratum(color_strata = list("wompwomp", method = "advanced",
                                 resolution = 2))

# anything from the *_options() constructors goes through `options`
geom_stratum(sort_strata = list(
  "wompwomp",
  options = sort_to_uncross_options(weighted_metric = FALSE)
))
geom_stratum(color_strata = list(
  "wompwomp", method = "left",
  options = get_lode_clusters_options(cutoff = 0.3)
))

Note that method = "tsp" starts the solver from a random tour, so seed it when a plot has to be redrawn identically; the default "neighbornet" is deterministic.

Axis order

The order of the axes stays outside the layers: position scales are trained before statistical transformations run, so a stat_*() that permuted x would move the strata out from under their axis labels. ggalluvial::sort_axes() returns the recommended axis order instead, for use in scale_x_discrete(limits = ) — exactly where the axis order is written above.

axis_order <- ggalluvial::sort_axes(
  clus_df_gather, axes = c("tissue", "cluster"), weight = "value",
  engine = "wompwomp"          # or list("wompwomp", method = "neighbornet")
)

It runs sort_to_uncross() with column_method set and reads off only the column order. The strata are sorted while it searches, because candidate axis orders are compared by the crossings they induce; that inner ordering defaults to "neighbornet" and can be set with strata_method. Applying the result re-sorts the strata against the new axis order, since which strata minimise crossings depends on which axes end up adjacent.

Sorting the axes and then the strata is two passes over a problem sort_to_uncross(method = , column_method = ) optimises jointly, and the column-ordering heuristic does not guarantee an improvement — on some data it returns an order no better than the one you started with. Check with compute_crossing_objective() before adopting it, or call sort_to_uncross() directly and plot the result the way vignette("plotting") does.

sessionInfo()
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04.1 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] wompwomp_1.0.0    ggalluvial_0.12.6 ggplot2_4.0.3     dplyr_1.2.1      
## [5] rmarkdown_2.32   
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       jsonlite_2.0.0     compiler_4.6.1     Rcpp_1.1.2        
##  [5] tidyselect_1.2.1   jquerylib_0.1.4    scales_1.4.0       yaml_2.3.12       
##  [9] fastmap_1.2.0      R6_2.6.1           generics_0.1.4     igraph_2.3.4      
## [13] knitr_1.52         MASS_7.3-66        polyclip_1.10-7    tibble_3.3.1      
## [17] maketools_1.3.2    bslib_0.12.0       pillar_1.11.1      RColorBrewer_1.1-3
## [21] rlang_1.3.0        utf8_1.2.6         cachem_1.1.0       xfun_0.62         
## [25] sass_0.4.10        sys_3.4.3          S7_0.2.2           otel_0.2.0        
## [29] cli_3.6.6          tweenr_2.0.3       withr_3.0.3        magrittr_2.0.5    
## [33] digest_0.6.39      grid_4.6.1         ggforce_0.5.0      lifecycle_1.0.5   
## [37] vctrs_0.7.3        evaluate_1.0.5     glue_1.8.1         farver_2.1.2      
## [41] buildtools_1.0.0   tools_4.6.1        pkgconfig_2.0.3    htmltools_0.5.9