https://github.com/aureliennicosiaulaval/ggcircular
A ggplot2 extension for circular, axial and directional data.
https://github.com/aureliennicosiaulaval/ggcircular
circular-statistics data-visualization directional-data ggplot2 r
Last synced: about 2 months ago
JSON representation
A ggplot2 extension for circular, axial and directional data.
- Host: GitHub
- URL: https://github.com/aureliennicosiaulaval/ggcircular
- Owner: AurelienNicosiaULaval
- License: other
- Created: 2026-05-30T22:10:12.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-31T02:24:40.000Z (about 2 months ago)
- Last Synced: 2026-05-31T04:09:51.302Z (about 2 months ago)
- Topics: circular-statistics, data-visualization, directional-data, ggplot2, r
- Language: R
- Homepage: https://aureliennicosiaulaval.github.io/ggcircular/
- Size: 10.3 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.Rmd
- Changelog: NEWS.md
- License: LICENSE
Awesome Lists containing this project
README
---
output: github_document
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
fig.width = 7,
fig.height = 5,
dpi = 160,
out.width = "100%",
message = FALSE,
warning = FALSE
)
```

# ggcircular
[](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/R-CMD-check.yaml)
[](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/pkgdown.yaml)
[](https://lifecycle.r-lib.org/articles/stages.html#experimental)
[](https://github.com/AurelienNicosiaULaval/ggcircular/releases)
[](LICENSE)
[](DESCRIPTION)
[](https://aureliennicosiaulaval.github.io/ggcircular/)
`ggcircular` is a `ggplot2` extension for circular, axial and directional
data. It provides layers, scales, coordinate helpers, summaries and diagnostics
for angles measured on a periodic scale.
The package is designed for exploratory graphics, teaching examples and
reproducible statistical workflows involving directions, bearings, orientations,
times of day, turn angles and other circular measurements.
## Installation
### Not on CRAN yet
`ggcircular` is not on CRAN yet. Install it from GitHub while the API is being
stabilized for a first CRAN submission.
Install the development release from GitHub:
```{r, eval = FALSE}
install.packages("remotes")
remotes::install_github("AurelienNicosiaULaval/ggcircular")
```
Or clone with SSH and install locally:
```bash
git clone git@github.com:AurelienNicosiaULaval/ggcircular.git
cd ggcircular
R -q -e 'devtools::install(upgrade = "never")'
```
## Quick Start
```{r setup}
library(ggplot2)
library(dplyr)
library(ggcircular)
```
```{r quick-start}
wind_directions |>
filter(season == "winter") |>
ggplot(aes(x = direction)) +
geom_rose(aes(y = after_stat(density), fill = after_stat(density)), bins = 24, alpha = 0.78) +
geom_circular_density(linewidth = 1.1, colour = "#123C4A") +
geom_mean_direction(length = "resultant", colour = "#E4572E", linewidth = 1.1) +
scale_x_circular_compass() +
coord_circular(zero = "north", direction = "clockwise") +
labs(fill = "density", title = "Winter wind directions") +
theme_circular()
```
## What It Does
| Workflow | Main helpers |
| --- | --- |
| Rose diagrams and circular histograms | `geom_rose()`, `stat_rose()` |
| Circular density estimation | `geom_circular_density()`, `stat_circular_density()` |
| Mean direction and concentration | `geom_mean_direction()`, `circular_summary()`, `estimate_kappa()` |
| Circular confidence intervals and tests | `circular_mean_ci()`, `rayleigh_test()`, `watson_williams_test()`, `stat_circular_test()` |
| Axial orientations modulo pi | `axial = TRUE` in summaries and layers |
| Theoretical circular distributions | `stat_vonmises()`, `stat_wrapped_normal()`, `stat_uniform_circular()` |
| Mixtures of von Mises components | `fit_vonmises_mixture()`, `stat_vonmises_mixture()` |
| Movement and state-angle graphics | `mutate_directional_features()`, `geom_direction_arrow()`, `plot_state_angles()` |
| Angular model diagnostics | `circular_residuals()`, `circular_model_diagnostics()`, `autoplot()` methods |
| Spherical and posterior helpers | `spherical_summary()`, `as_circular_draws()`, `summarise_circular_draws()` |
## Design Principles
- Angles are stored and computed in radians.
- Scales handle display labels in radians, degrees, hours or compass labels.
- Directional data use period `2 * pi`.
- Axial data use period `pi` through `axial = TRUE`.
- Heavy packages remain optional and are accessed with explicit availability checks.
- Outputs are standard `ggplot` objects, tibbles or familiar test objects.
## Conventions for Directions and Bearings
The default mathematical convention is `zero = "east"` with angles increasing
counterclockwise. This matches the usual unit circle.
Compass bearings use `zero = "north"` with angles increasing clockwise. Use
`scale_x_circular_compass()` together with
`coord_circular(zero = "north", direction = "clockwise")` for bearing-like
data such as wind direction or movement headings.
Axial data, such as unoriented lines, are different again: `0` and `pi`
represent the same orientation. Use `axial = TRUE` in summaries and layers for
these data.
## Summaries
`circular_summary()` respects existing `dplyr` groups and returns mean direction,
resultant length, circular variance, circular standard deviation and an
estimated von Mises concentration parameter. `estimate_kappa()` is a descriptive
piecewise approximation from the sample resultant length, not a full inferential
fit.
```{r summaries}
wind_directions |>
circular_summary(direction, season) |>
mutate(
mean_degrees = round(rad_to_deg(mean), 1),
Rbar = round(Rbar, 3),
kappa = round(kappa, 2)
) |>
select(season, n, mean_degrees, Rbar, kappa)
```
## Axial Data
Axial observations identify opposite directions. For example, an orientation of
0 radians is equivalent to an orientation of pi radians. Use `axial = TRUE` to
compute and display these data modulo pi.
```{r axial, fig.height = 3.6}
ggplot(axial_orientations, aes(x = orientation, fill = group)) +
geom_rose(bins = 18, axial = TRUE, alpha = 0.72) +
geom_mean_direction(axial = TRUE, colour = "#123C4A", linewidth = 1) +
scale_x_circular_degrees(limits = c(0, pi)) +
coord_circular() +
facet_wrap(~ group) +
theme_circular()
```
## Directional Movement
`ggcircular` includes helpers for bearings, turn angles and state-specific
angular distributions.
```{r movement, fig.height = 3.6}
animal_steps |>
filter(!is.na(turn_angle)) |>
ggplot(aes(x = turn_angle, fill = state)) +
geom_rose(bins = 24, alpha = 0.72) +
geom_mean_direction(colour = "#123C4A", linewidth = 1) +
scale_x_circular_degrees(
breaks = deg_to_rad(c(0, 90, 180, 270)),
labels = c("0", "90", "180", "270")
) +
coord_circular() +
facet_wrap(~ state) +
theme_circular()
```
## Mixtures of von Mises Distributions
Finite mixtures are fitted with an expectation-maximization routine and can be
drawn directly on top of empirical rose diagrams. These fits are descriptive and
depend on initialization, so use `seed`, `nstart` and diagnostic output when the
mixture is substantively important.
```{r mixture}
set.seed(2026)
fit_mix <- fit_vonmises_mixture(
wind_directions$direction,
k = 2,
init = "spaced",
nstart = 3,
seed = 2026
)
ggplot(wind_directions, aes(x = direction)) +
geom_rose(aes(y = after_stat(density)), bins = 24, alpha = 0.42) +
stat_vonmises_mixture(fit = fit_mix, linewidth = 1.2, colour = "#123C4A") +
scale_x_circular_degrees() +
coord_circular() +
theme_circular()
```
```{r mixture-table}
tidy_circular(fit_mix) |>
mutate(
mu_degrees = round(rad_to_deg(mu), 1),
kappa = round(kappa, 2),
proportion = round(proportion, 3)
) |>
select(component, proportion, mu_degrees, kappa)
```
## Tests and Intervals
```{r tests}
circular_mean_ci(
wind_directions$direction,
method = "bootstrap",
R = 399,
seed = 2026
) |>
mutate(across(c(mean, lower, upper), rad_to_deg))
```
```{r rayleigh}
rayleigh <- rayleigh_test(wind_directions$direction)
tibble::tibble(
statistic = unname(rayleigh$statistic),
n = unname(rayleigh$parameter),
p_value = rayleigh$p.value,
method = rayleigh$method
)
```
## Optional Model Integrations
The package keeps heavier modeling ecosystems in `Suggests`. When available,
these integrations add diagnostics without making them hard dependencies.
```{r angular-models, eval = FALSE}
fit <- CircularRegression::consensus(direction ~ speed, data = wind_directions)
circular_model_diagnostics(fit)
autoplot(fit, type = "residuals_density")
autoplot(fit, type = "fitted_observed")
```
Optional helpers currently target:
- `CircularRegression`-style angular, consensus and two-step objects through
S3 class support.
- `momentuHMM` state probabilities and Viterbi states.
- `posterior` draw objects through `posterior::as_draws_df()`.
- `circular` tests when classical circular test implementations are available.
## Experimental Features
The following pieces are intentionally available but still experimental:
- angular model diagnostics for optional external model classes;
- finite mixtures of von Mises distributions;
- `momentuHMM` state-angle adapters;
- spherical summaries and posterior draw helpers.
Experimental functions are documented and tested, but their return columns may
still be refined before a CRAN release if validation reveals a better public
contract.
## Statistical Limitations
`ggcircular` is primarily a visualization and diagnostics package. It does not
replace specialist inference workflows for circular statistics.
- The automatic density bandwidth is a simple heuristic.
- `circular_mean_ci()` is unreliable when the mean resultant length is close to
zero because the mean direction is weakly identified.
- `rayleigh_test()` is mainly sensitive to unimodal departures from uniformity.
- `watson_williams_test()` relies on strong assumptions about group
concentration and uses the optional `circular` implementation.
- Multimodal data should usually be inspected with density or mixture graphics,
not summarized only by one mean direction.
## CRAN Readiness
The package is being prepared for a first CRAN submission. The release checklist
currently includes:
- `R CMD check --as-cran` on the final source tarball;
- `--run-donttest` checks;
- hard-dependency checks with `_R_CHECK_FORCE_SUGGESTS_=false`;
- full-suggests checks when optional packages are available;
- Linux R-devel, Linux R-release, macOS R-release and Windows R-release checks;
- vignette build timing and source tarball size checks.
Longer articles are built for pkgdown and excluded from the CRAN tarball.
## Vignettes
Start with:
```{r, eval = FALSE}
vignette("ggcircular", package = "ggcircular")
```
Then see the pkgdown articles:
- [Getting started](https://aureliennicosiaulaval.github.io/ggcircular/articles/ggcircular.html)
- [Rose diagrams](https://aureliennicosiaulaval.github.io/ggcircular/articles/rose-diagrams.html)
- [Circular density](https://aureliennicosiaulaval.github.io/ggcircular/articles/circular-density.html)
- [Mean direction and uncertainty](https://aureliennicosiaulaval.github.io/ggcircular/articles/mean-direction-and-uncertainty.html)
- [Axial data](https://aureliennicosiaulaval.github.io/ggcircular/articles/axial-data.html)
- [Movement data](https://aureliennicosiaulaval.github.io/ggcircular/articles/movement-data.html)
- [Circular distributions](https://aureliennicosiaulaval.github.io/ggcircular/articles/circular-distributions.html)
- [Model diagnostics](https://aureliennicosiaulaval.github.io/ggcircular/articles/model-diagnostics.html)
- [Spherical and posterior helpers](https://aureliennicosiaulaval.github.io/ggcircular/articles/spherical-and-posterior.html)
- [Scientific validation notes](https://aureliennicosiaulaval.github.io/ggcircular/articles/validation.html)
- [Comparative validation](https://aureliennicosiaulaval.github.io/ggcircular/articles/validation-comparative.html)
## Contributing and Support
Contributions are welcome through focused GitHub issues and pull requests. See
[`CONTRIBUTING.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/CONTRIBUTING.md),
[`SUPPORT.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/SUPPORT.md)
and
[`CODE_OF_CONDUCT.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/CODE_OF_CONDUCT.md)
for contribution, support and conduct guidelines.
## Development Status
`ggcircular` is currently experimental. The public API is usable, tested and
documented, but may still evolve as more angular model classes and validation
cases are added.
Current checks:
- Local `devtools::test()` passes.
- Local `devtools::check(document = FALSE, args = "--as-cran", build_args = "--no-manual")`
is used before release commits.
- GitHub Actions runs hard-dependency checks with `_R_CHECK_FORCE_SUGGESTS_=false`
and full-suggests checks when optional packages are available.
- GitHub Actions includes Linux R-devel plus Linux, macOS and Windows R-release.
- `pkgdown` builds and publishes the website from `main`.
## References
- Fisher, N. I. (1993). [*Statistical Analysis of Circular Data*](https://doi.org/10.1017/CBO9780511564345).
Cambridge University Press.
- Jammalamadaka, S. R., and Sengupta, A. (2001). [*Topics in Circular
Statistics*](https://doi.org/10.1142/4031). World Scientific.
- Pewsey, A., Neuhäuser, M., and Ruxton, G. D. (2013). [*Circular Statistics in
R*](https://books.google.com/books/about/Circular_Statistics_in_R.html?id=qeadAAAAQBAJ).
Oxford University Press.
- Wickham, H. (2016). [*ggplot2: Elegant Graphics for Data
Analysis*](https://doi.org/10.1007/978-3-319-24277-4). Springer.