# ggalluvial draws alluvial diagrams as ggplot2 layers you compose yourself

> ggalluvial is a ggplot2 extension for alluvial plots, replacing the single-call approach of the alluvial package with separate stats and geoms for strata, alluvia, lodes and flows, and requiring a single data frame with explicitly declared dimensions. It suits R users who already build charts in ggplot2, and the cost is that the plot is assembled layer by layer rather than produced by one function.

**corybrunson/ggalluvial** — ggplot2 extension for alluvial plots

- Repository: https://github.com/corybrunson/ggalluvial
- Website: http://corybrunson.github.io/ggalluvial/
- Stars: 526 · Forks: 34
- Language: R
- License: GPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/corybrunson-ggalluvial

## Why the alluvial package's single function became four layers

ggalluvial is a ggplot2 extension for alluvial plots, and its Design section is explicit about how it differs from the package it was inspired by, the alluvial package.

The three differences are specific and each one is a consequence of the ggplot2 framework.

The alluvial package understands a variety of inputs, vectors, lists and data frames, whereas ggalluvial requires a single data frame. The alluvial package uses each variable of those inputs as a dimension of the data, whereas ggalluvial requires the user to specify the dimensions, either as separate aesthetics or as key-value pairs.

And the third difference is the important one. The alluvial package produces both the alluvia, which link cohorts across multiple dimensions, and what this package calls the strata, which partition the data along each dimension, in a single function. ggalluvial instead relies on separate layers, the stats and geoms, to produce strata, alluvia, and alluvial segments called lodes and flows.

So the vocabulary differs as well as the API. A stratum is a partition along one dimension. An alluvium is a cohort tracked across dimensions. A lode and a flow are the two halves of an alluvial segment, and separating them is what lets you draw the ribbon and the box as independent layers.

That layer separation is also where the payoff is. There is one more design decision on top of it: ggalluvial arranges these layers vertically without gaps, so that the secondary plotting axis indicates the cumulative values of the strata at each dimension. That axis is a genuine addition rather than a decoration, because it is what lets a reader see how the population shrinks from one dimension to the next.

The topics the repository carries make the intended use clear as well: alluvial diagrams, alluvial plots, categorical data visualization, ggplot2, and repeated measures data.

## The Titanic example, and what each aesthetic does

The README's worked example uses the Titanic dataset, and it is worth reading because the plot is assembled the way any other ggplot2 chart is, layer by layer.

```r
ggplot(data = titanic_wide,
       aes(axis1 = Class, axis2 = Sex, axis3 = Age,
           y = Freq)) +
  scale_x_discrete(limits = c("Class", "Sex", "Age"), expand = c(.2, .05)) +
  xlab("Demographic") +
  geom_alluvium(aes(fill = Survived)) +
  geom_stratum() +
  geom_text(stat = "stratum", aes(label = after_stat(stratum))) +
  theme_minimal() +
  ggtitle("passengers on the maiden voyage of the Titanic",
          "stratified by demographics and survival")
```

The mechanics are worth walking through. The data starts wide, from data.frame(Titanic), where each row is a combination of Class, Sex, Age and Survived with a Freq count, and the dimensions are declared as axis1, axis2 and axis3, with y = Freq carrying the count that gives each stratum its height.

The discrete scale then names the axes, with limits = c("Class", "Sex", "Age") and a slight expansion so the labels have room. The strata are drawn by geom_stratum(), which gives the boxes at each dimension, and the ribbons between them by geom_alluvium(), which is where the fill aesthetic takes over and encodes Survived. The text labels come from a separate geom_text layer, using after_stat(stratum) to label each stratum with its own computed value rather than a column from the data.

That last point is a small illustration of the whole design. Because the text is its own layer with its own stat, you can label with whatever the stat computed, place it, style it and remove it independently. In the alluvial package's single-function model there is no such separation.

The theme and title calls at the end are ordinary ggplot2, which is the point of the extension: an alluvial plot is a ggplot, not a special object.

## Wide and long formats, and the conversion between them

The data format is the second decision, and the README treats it as something you choose rather than something you have.

The Titanic example starts in wide format, where each row is a combination of values across the dimensions with a frequency. ggalluvial also recognises data in long format, and can convert between the two with to_lodes_form.

```r
titanic_long <- to_lodes_form(data.frame(Titanic),
                              key = "Demographic",
                              axes = 1:3)
```

The two arguments do the work that the aesthetics do in the wide case. The key names the variable that identifies which dimension a row belongs to, so the output carries a Demographic column with values Class, Sex and Age, and the axes argument says which columns of the original frame are the dimensions, here 1:3.

The result has columns for Survived, Freq, alluvium, Demographic and stratum. The alluvium column is the identifier that groups rows belonging to the same cohort across dimensions, and the stratum column is the label within a dimension. Those two identifiers are what the geoms use to draw the ribbons and the boxes, and understanding that they are data columns rather than computed positions is what makes the rest of the API make sense.

Once converted, the long form draws with the same layers, but with the mapping expressed directly:

```r
ggplot(data = titanic_long,
       aes(x = Demographic, stratum = stratum, alluvium = alluvium,
           y = Freq, label = stratum)) +
  geom_alluvium(aes(fill = Survived)) +
  geom_stratum() + geom_text(stat = "stratum") +
  theme_minimal() +
  ggtitle("passengers on the maiden voyage of the Titanic",
          "stratified by demographics and survival")
```

The difference is that the long form names the x axis with the key variable and passes stratum and alluvium as aesthetics, so the mapping is explicit in one place. The wide form infers the axes from axis1, axis2 and axis3 and derives the same identifiers internally.

The choice is a real one. Wide format is closer to a frequency table, which is how this kind of data often arrives. Long format is closer to a tidy dataset and generalises better when the number of dimensions varies, since the dimensions are values of the key column rather than fixed columns.

## Installing from CRAN, from GitHub, and from the optimization branch

Installation has three routes, and the README is careful to say which is which.

The latest stable release comes from CRAN:

```r
install.packages("ggalluvial")
```

The development version comes from the default main branch on GitHub:

```r
remotes::install_github("corybrunson/ggalluvial@main", build_vignettes = TRUE)
```

That command has a prerequisite worth knowing about. To build the vignettes, the imported packages alluvial, ggfittext and ggrepel must be installed, and to skip that step you leave build_vignettes unspecified or set it to FALSE. So the cost of the vignettes is three extra packages, and the vignettes are where the package explains its data formats.

The third route is a branch, and this is the part that needs a warning. The optimization branch contains a development version with experimental functions to reduce the number or area of alluvial overlaps, tracked as issue #6 in the repository. It installs with:

```r
remotes::install_github("corybrunson/ggalluvial", ref = "optimization")
```

But the README notes that this branch has not kept pace with the main branch or with recent upgrades on CRAN. That is the honest way to describe a branch that solves a real problem, the crowded-ribbon problem, while remaining behind the main branch and recent CRAN upgrades.

The branch structure is also worth understanding for a different reason. The cran branch holds the version most recently submitted to CRAN and is duplicated in the master branch, from which the website is built. The README is generated from README.rmd, so the source of the documentation you are reading is an R Markdown file, and the documentation site at corybrunson.github.io/ggalluvial is built from the master branch.

For a user who wants a stable chart, CRAN is the sensible default. The optimization branch is worth knowing about because it names a genuine weakness in the base package, and the fact that the fix exists on a branch that has not been merged is a reasonable signal about the state of that work.

## The vignettes that document the formats, and the limits they explain

The documentation strategy in this package is a good model for a ggplot2 extension, because the hard part is not the drawing.

The main entry point is the technical vignette, which the README recommends for detailed discussion of the data formats recognised by ggalluvial and for several examples that illustrate its flexibility and its limitations. In an R package, that is not the same thing as the reference manual, and it is where the design decisions live.

```r
vignette(topic = "ggalluvial", package = "ggalluvial")
```

Three more vignettes cover specific problems. Labeling small strata, under the topic labels, is about how to label strata of a wide range of heights in an alluvial plot. The Order of the Rectangles, under order-rectangles, covers how to control the positioning of strata and lodes. And Tooltips for ggalluvial plots in Shiny apps, under shiny, covers how to incorporate alluvial plots into interactive apps.

Those three are worth naming as a list because they tell you what the package's rough edges are. Small strata are hard to label, because a very short stratum has nowhere to put text. Rectangle order is a real problem, because the vertical arrangement of boxes within a dimension determines whether the ribbons cross. And Shiny support is an add-on rather than a given, since the hover information an interactive plot needs has to be added deliberately.

The object documentation fills in the rest. The README points you at help() for any layer, stat_* or geom_*, for the conversion functions to_*_form, and for the data sets installed with the package, which are vaccinations and majors.

There is also a short form for people who want a quick orientation rather than a reference. Three cheat sheets and demos by students in Joyce Robbins's Exploratory Data Analysis and Visualization Community Contribution Project are linked, an introduction by Meggie Wen and a cheat sheet by Qingyi Zhang.

## Where ggalluvial is the wrong choice

There are two cases where a different tool is the better answer, and they are worth naming clearly.

The first is when you want a finished diagram from one call. The alluvial package, which ggalluvial was inspired by, does exactly that, and it accepts a wider range of inputs including vectors and lists, and it infers the dimensions from the variables rather than requiring you to declare them. If your use is a single static chart in a script and you do not want to reason about layers, that package is the shorter path. The cost of ggalluvial's approach is that the geometry is assembled by you, and if you are new to ggplot2 that assembly is the learning curve.

The second is when your data is not categorical. Alluvial plots show how a population splits and recombines across dimensions. If you have a continuous variable, a time series, or a measure you want to correlate against another, this is the wrong family of chart, and no amount of configuration will change that.

There is a third, quieter limitation, which is the crowded-ribbon problem the optimization branch exists to address. When many flows pass through the same stratum, the ribbons overlap into an unreadable mass, and the base package has no answer to it. The README's framing of the branch is honest about the status, so if a busy plot is your use case, budget for the branch being out of date or for choosing a different visualisation.

For comparison, the alternatives differ in what they cost. Building the diagram by hand in base ggplot with geom_rect and geom_polygon gives you complete control and no package, at the price of writing the aggregation yourself. A dedicated alluvial package gives you the finished chart and less control. ggalluvial sits between, and its bet is that the control is worth the assembly work for people who already live in ggplot2.

There is also a maintenance point. The listed releases are v0.12.1, an alignment bug fix from August 2020, v0.12.2, a faceting bug fix from September 2020, and v0.12.6, a dplyr deprecation fix from February 2026. The gap between them is long, and the last push to the repository was on 2026-09-23. The package is on CRAN, has a JOSS paper and a Zenodo DOI, and carries the GPL-3.0 licence. Whether a dormant period matters to you depends on whether your ggplot2 and dplyr versions have moved since the last release.

## Conclusion

Adopt ggalluvial if your charts are already built in ggplot2 and your question involves how categorical values flow across several dimensions, such as a cohort over time or a multi-way frequency table, since the layer-based design means you can fill, label and annotate the plot with the rest of your ggplot vocabulary. Do not adopt it if you want a single function that returns a finished diagram, because that is the exact thing this package deliberately does not do, and expect to read the technical vignette rather than a reference card. Before you commit, work out whether your data is wide or long, since the wide format requires the dimensions to be named as aesthetics while the long format requires key and axes arguments to to_lodes_form, and check the releases, where v0.12.2 was a faceting bug fix and v0.12.6 a dplyr deprecation fix. The licence is GPL-3.0, the package is on CRAN as ggalluvial, and the last push was on 2026-09-23.

## FAQ

### What is ggalluvial and what does it do?

It is a ggplot2 extension for alluvial plots, which visualise frequency distributions over time or frequency tables involving several categorical variables. Unlike the alluvial package it was inspired by, it uses separate stats and geoms to produce strata, alluvia, lodes and flows.

### How do I install ggalluvial?

The latest stable release installs from CRAN with install.packages("ggalluvial"). The development version comes from the main branch with remotes::install_github("corybrunson/ggalluvial@main", build_vignettes = TRUE), which needs the alluvial, ggfittext and ggrepel packages to build the vignettes.

### What is the difference between wide and long format data in ggalluvial?

The wide format is a frequency table where each row is a combination of dimension values with a frequency, and the dimensions are named as axis1, axis2 and so on in the aesthetics. The long format names the dimension variable as a key and lists the dimension columns as axes, and to_lodes_form converts between the two.

### What are strata, alluvia, lodes and flows in ggalluvial?

Strata are the parts that partition the data along each dimension, and alluvia are the cohorts that link across multiple dimensions. Lodes and flows are the two halves of an alluvial segment, separated so the ribbon and the box can be drawn as independent layers.

### Where can I read about ggalluvial's data formats and limitations?

In the technical vignette, which the README recommends for detailed discussion of the data formats recognised by the package and for examples illustrating its flexibility and limitations. Three further vignettes cover labeling small strata, controlling the order of the rectangles, and adding tooltips in Shiny apps.

### What is the optimization branch for in ggalluvial?

It contains a development version with experimental functions to reduce the number or area of alluvial overlaps, tracked as issue 6 in the repository. The README warns that the branch has not kept pace with the main branch or with recent upgrades on CRAN, so it is research code rather than a supported release.

## Sources

- [corybrunson/ggalluvial on GitHub](https://github.com/corybrunson/ggalluvial)
- [License: GPL-3.0](https://github.com/corybrunson/ggalluvial/blob/main/LICENSE)
- [Project website](http://corybrunson.github.io/ggalluvial/)
- [README](https://github.com/corybrunson/ggalluvial/blob/main/README.md)
- [Releases](https://github.com/corybrunson/ggalluvial/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/corybrunson-ggalluvial
