# ggalt: the awkward ggplot2 geoms that do not fit the standard grammar

> ggalt is an R package of extra geoms, stats, scales and fonts for ggplot2, covering X-splines, horizon charts, 2d density contours, averaged shifted histograms, lollipops, dumbbells and the ProPublica StateFace font. It suits a data analyst who needs one of those specific marks, and it is a single authored package rather than a maintained extension ecosystem.

**hrbrmstr/ggalt** — :earth_americas: Extra Coordinate Systems, Geoms,  Statistical Transformations & Scales for 'ggplot2'

- Repository: https://github.com/hrbrmstr/ggalt
- Website: https://cran.r-project.org/web/packages/ggalt/vignettes/ggalt_examples.html
- Stars: 690 · Forks: 101
- Language: R
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/hrbrmstr-ggalt

## A list of geoms that ggplot2's grammar does not have a slot for

ggalt is described as a compendium of geoms, coords, stats, scales and fonts for ggplot2, and the description names the areas it covers: splines, 1d and 2d densities, univariate average shifted histograms, and the StateFace open source font from ProPublica.

The function list is the real specification of the package, and it is worth reading as a list of awkward marks:

- geom_ubar for uniform width bar charts
- geom_horizon for horizon charts, modified from a ggTimeSeries implementation
- geom_xspline and stat_xspline to connect control points or observations with an X-spline
- geom_bkde and stat_bkde for a smooth density estimate, using KernSmooth::bkde
- geom_bkde2d and stat_bkde2d for contours from a 2d density estimate, using KernSmooth::bkde2D
- stat_ash to compute and display a univariate averaged shifted histogram with a polynomial kernel, using ash::ash1 and ash::bin1
- geom_encircle to automatically enclose points in a polygon
- byte_format and helpers, for turning a number like 10000 into 10 Kb
- geom_lollipop and geom_dumbbell, described as dead easy lollipops and dumbbell plots, horizontal or vertical
- stat_stepribbon for step ribbons
- annotation_ticks to add minor ticks to the identity, exp(1) and exp(10) axis scales independently of each other
- geom_spikelines, which draws a pair of segments originating from the same c(x,y) to the respective axes, instead of geom_vline and geom_hline

That list is the argument for the package. Every entry is a mark that a competent R user could draw in ggplot2 with some effort, and that a user who needs one of them will otherwise draw badly.

geom_stateface deserves its own line, because it is the odd one out. It is a font rather than a mark, it uses ProPublica's StateFace typeface, and it is the only item in the list that is about how the text looks rather than how the data is drawn. The packages that pair with it are KernSmooth for the density estimates, ash for the averaged shifted histogram, and the font itself, which is why the package's dependency footprint is larger than the geoms alone would suggest.

## The X-spline pair, and the spline_shape that changes the curve

The geom_xspline and stat_xspline pair is the part of ggalt with a real parameter to tune, and the examples in the project documentation show it well.

The baseline plot is a scatter with a straight line, and then the X-spline is layered on top of it:

``` r
ggplot(dat, aes(x, y, group=group, color=factor(group))) +
  geom_point(color="black") +
  geom_smooth(se=FALSE, linetype="dashed", size=0.5) +
  geom_xspline(spline_shape=-0.4, size=0.5)
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
```

The spline_shape argument is what changes the character of the curve, and the documentation walks it through a series of values: -0.4, 0.4, 1 and 0, each rendered in turn so the reader can see the shape change rather than infer it. A negative value pulls the curve one way, a positive value the other, and zero gives a flatter path through the control points.

The size argument is passed through to the segment, so the same knob controls the line weight as in any other geom.

One detail in those examples is worth flagging for anyone copying them. The output includes a warning that using the size aesthetic for lines was deprecated in ggplot2 3.4.0, with the note to use linewidth instead, and a second warning for the same deprecation in the default_aes field of a geom. The examples were written before that change and still work, but the current advice is linewidth.

## Horizon charts, and the S3 methods that get in the way

geom_horizon draws horizon charts, the multi-band form where a series is split into overlapping layers so that many series fit in one small panel. The example in the project documentation is carved from a published visualisation script, and it is a real piece of work: the data is a tab-separated file of activity over time, the peaks are normalised per activity, a three-point rolling average is applied, the series are faceted, and the colour scale is a discrete viridis scale with percent labels.

The chart is built with a theme from a different package, and the panel spacing is negative to remove the gaps between facets, and the strip text is rotated.

What makes the example worth quoting is the warning it produces. Loading ggalt after ggplot2 and gridExtra prints this:

``` r
## Registered S3 methods overwritten by 'ggalt':
##   method                  from   
##   grobHeight.absoluteGrob ggplot2
##   grobWidth.absoluteGrob  ggplot2
##   grobX.absoluteGrob      ggplot2
##   grobY.absoluteGrob      ggplot2
```

So ggalt registers S3 methods for the grid grob functions that ggplot2 also provides, and loading the package replaces them. That is a real integration cost: a package that takes over methods another package owns is a source of subtle rendering differences depending on load order, and this is the kind of thing you find out about from the startup message rather than from a changelog entry.

The horizon chart itself is the modified ggTimeSeries implementation, and the documentation credits the original project.

## Installation, the version, and the one line the author wants you to read

Installation is two calls to install.packages, with a note in front of them:

``` r
# you'll want to see the vignettes, trust me
install.packages("ggplot2")
install.packages("ggalt")
# OR: devtools::install_github("hrbrmstr/ggalt")
```

The comment is characteristic of the package's voice, and it points at something real, because the vignettes on the CRAN page are where the geoms are actually explained. The homepage of the project is the ggalt_examples.html vignette on CRAN, so a new user is pointed at examples rather than at a manual.

The version is printed in the usage section with a small block that also shows how the package announces itself:

``` r
# current verison
packageVersion("ggalt")
## [1] '0.8.0'
```

The spelling of verison in that comment is in the original, and it is a small reminder that this is a personal project with a personal touch rather than a corporate one.

The repository is a standard R package layout: DESCRIPTION, NAMESPACE, R/, man/, tests/, vignettes/, inst/, docs/, a ggalt.Rproj project file, README.Rmd with README.md generated from it, NEWS.md, a CRAN-SUBMISSION file, and a LICENSE file. The presence of CRAN-SUBMISSION is the marker that this is a package submitted to CRAN, and a CONDUCT.md alongside it.

## A release history with one long gap and one recent jump

The release list is three entries and it tells a story.

v0.1.1, named Beryl Storm, was released on 2016-01-05. v0.4.0, named Serious Sunshine, followed on 2017-02-15. And v0.8.0 came on 2026-05-13.

So there is a nine year gap between 0.4.0 and 0.8.0, and the naming convention of the older releases, colour and weather words, is a joke that the recent release drops. The last push to the repository was on 2026-05-13, the same day as the 0.8.0 tag, so the current work is the 0.8.0 line rather than something ahead of it.

The jump from 0.4 to 0.8 in one release is also worth noticing. It is a substantial version change for a package whose function list reads as additive, and a user upgrading from 0.4.0 is crossing four minor versions of a library with a single primary author.

The licence is recorded as not one that GitHub can classify, which usually means a custom file rather than a standard SPDX identifier, and the LICENSE file in the repository root is the place to check. For a package that is mostly plotting code, that is unlikely to be a blocker, but it is the kind of detail that matters if an analysis script is going into a published report or a commercial deliverable.

The package is on CRAN, which is the more consequential fact for an R user, because CRAN's incoming checks are what make an R package safe to depend on. The current status on the CRAN page is the thing to look at before pinning a version.

## Where ggalt is the wrong tool

The first limit is the catalogue itself. ggalt is a list of about fifteen specific marks, and if the chart you need is not one of them, the package gives you nothing. Unlike a theme package or a scale package, it is not a layer you put under everything, and there is no general extension point that makes it more useful than the individual geoms it contains.

The second is the single-author dependency. The function list is a set of personal choices, and the maintenance history shows a nine year gap between two releases. A team that adopts ggalt for one geom has made a long-term bet on one person's time, and the release naming and the readme's tone are part of what the project is.

The third is the S3 method overwrite. Loading ggalt after ggplot2 replaces four grob methods that ggplot2 also registers. For a package that mostly draws unusual marks, that is an outsized side effect, and it means a project using ggalt has a load-order dependency on ggplot2 that it did not ask for. A session where ggplot2 is loaded after ggalt behaves differently from one where it is loaded before.

The fourth is the dependency chain for the density and histogram geoms. geom_bkde and stat_bkde call KernSmooth::bkde, the 2d variants call KernSmooth::bkde2D, and stat_ash calls ash::ash1 and ash::bin1. So a plot that needs a smooth density estimate pulls in KernSmooth, and one that needs an averaged shifted histogram pulls in ash. Those are small packages, but they are additional installs that a user of the other geoms never asked for, and they are imported rather than reimplemented, so the behaviour is KernSmooth's and ash's rather than ggalt's.

The fifth is the state of the examples. The output in the project documentation still shows the deprecated use of the size aesthetic for lines, and while it works, copying an example that emits deprecation warnings is how a new codebase starts life with noise in the log. The current ggplot2 advice is linewidth, so treat the snippets as illustrations of the shape argument rather than as code to paste.

## Conclusion

ggalt is worth installing if your next chart is an X-spline, a horizon chart, a density contour, a lollipop or a dumbbell, because each of those marks takes real work to build with stock ggplot2 and this package supplies them as ordinary geoms you can add to a plot. It is the wrong choice if you need a chart type that is not in the list, and it is the wrong choice if you need a package with a community behind it. Before you depend on it, check the three things the repository tells you: that the current release is 0.8.0 from 2026-05-13, with the previous release being 0.4.0 from 2017, so the version line has moved once in a decade rather than continuously; that the package is on CRAN with a vignette at the ggalt_examples.html page; and that the licence file is not a standard one that GitHub can classify, so the terms are worth reading before the package goes into a published analysis.

## FAQ

### How do I install ggalt?

Install it from CRAN with install.packages("ggplot2") and install.packages("ggalt"), or from GitHub with devtools::install_github("hrbrmstr/ggalt"). The project documentation asks you to install ggplot2 as well, since ggalt extends it, and points you at the vignettes on the CRAN page for the examples.

### Which extra geoms and stats does ggalt provide?

The list includes geom_ubar for uniform width bars, geom_horizon for horizon charts, geom_xspline and stat_xspline for X-splines, geom_bkde, stat_bkde, geom_bkde2d and stat_bkde2d for density estimates and contours, stat_ash for averaged shifted histograms, geom_encircle, geom_lollipop, geom_dumbbell, stat_stepribbon, annotation_ticks, geom_spikelines and geom_stateface for the ProPublica StateFace font.

### Does loading ggalt change how ggplot2 behaves?

Yes. The project documentation shows a startup message saying that Registered S3 methods overwritten by 'ggalt', listing grobHeight.absoluteGrob, grobWidth.absoluteGrob, grobX.absoluteGrob and grobY.absoluteGrob, all from ggplot2. So the load order between the two packages matters for rendering behaviour.

## Sources

- [hrbrmstr/ggalt on GitHub](https://github.com/hrbrmstr/ggalt)
- [Issues](https://github.com/hrbrmstr/ggalt/issues)
- [Project website](https://cran.r-project.org/web/packages/ggalt/vignettes/ggalt_examples.html)
- [README](https://github.com/hrbrmstr/ggalt/blob/master/README.md)
- [Releases](https://github.com/hrbrmstr/ggalt/releases)

---

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