MyNixOS website logo
Description

A 'ggplot2' Extension for Circular and Directional Data.

Provides a 'ggplot2' grammar for circular, axial and directional data, including rose diagrams, circular densities, mean directions, confidence arcs, theoretical circular distributions and movement data visualizations.
ggcircular hex logo

ggcircular

R-CMD-check pkgdown Lifecycle:experimental GitHubrelease License:MIT R>=4.1.0 pkgdownsite

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:

install.packages("remotes")
remotes::install_github("AurelienNicosiaULaval/ggcircular")

Or clone with SSH and install locally:

git clone [email protected]:AurelienNicosiaULaval/ggcircular.git
cd ggcircular
R -q -e 'devtools::install(upgrade = "never")'

Quick Start

library(ggplot2)
library(dplyr)
library(ggcircular)
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

WorkflowMain helpers
Rose diagrams and circular histogramsgeom_rose(), stat_rose()
Circular density estimationgeom_circular_density(), stat_circular_density()
Mean direction and concentrationgeom_mean_direction(), circular_summary(), estimate_kappa()
Circular confidence intervals and testscircular_mean_ci(), rayleigh_test(), watson_williams_test(), stat_circular_test()
Axial orientations modulo piaxial = TRUE in summaries and layers
Theoretical circular distributionsstat_vonmises(), stat_wrapped_normal(), stat_uniform_circular()
Mixtures of von Mises componentsfit_vonmises_mixture(), stat_vonmises_mixture()
Movement and state-angle graphicsmutate_directional_features(), geom_direction_arrow(), plot_state_angles()
Angular model diagnosticscircular_residuals(), circular_model_diagnostics(), autoplot() methods
Spherical and posterior helpersspherical_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.

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)
#> # A tibble: 4 × 5
#>   season     n mean_degrees  Rbar kappa
#>   <chr>  <int>        <dbl> <dbl> <dbl>
#> 1 fall     131        310.  0.811  3
#> 2 spring   115        135.  0.802  2.89
#> 3 summer   138        223.  0.87   4.15
#> 4 winter   116         48.2 0.904  5.52

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.

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.

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.

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()
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)
#> # A tibble: 2 × 4
#>   component proportion mu_degrees kappa
#>       <int>      <dbl>      <dbl> <dbl>
#> 1         1      0.328       51.6  1.45
#> 2         2      0.672      232.   0.77

Tests and Intervals

circular_mean_ci(
  wind_directions$direction,
  method = "bootstrap",
  R = 399,
  seed = 2026
) |>
  mutate(across(c(mean, lower, upper), rad_to_deg))
#> # A tibble: 1 × 7
#>    mean lower upper level method        n   Rbar
#>   <dbl> <dbl> <dbl> <dbl> <chr>     <int>  <dbl>
#> 1  235.  139.  354.  0.95 bootstrap   500 0.0494
rayleigh <- rayleigh_test(wind_directions$direction)

tibble::tibble(
  statistic = unname(rayleigh$statistic),
  n = unname(rayleigh$parameter),
  p_value = rayleigh$p.value,
  method = rayleigh$method
)
#> # A tibble: 1 × 4
#>   statistic     n p_value method
#>       <dbl> <int>   <dbl> <chr>
#> 1      1.22   500   0.295 Rayleigh test of circular uniformity

Optional Model Integrations

The package keeps heavier modeling ecosystems in Suggests. When available, these integrations add diagnostics without making them hard dependencies.

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:

vignette("ggcircular", package = "ggcircular")

Then see the pkgdown articles:

Contributing and Support

Contributions are welcome through focused GitHub issues and pull requests. See CONTRIBUTING.md, SUPPORT.md and 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

Metadata

Version

0.1.0

License

Unknown

Platforms (79)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    wasip1
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasip1
  • wasm64-wasip1
  • x86_64-cygwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows