{"id":50593118,"url":"https://github.com/aureliennicosiaulaval/ggcircular","last_synced_at":"2026-06-05T12:01:11.783Z","repository":{"id":361522545,"uuid":"1254687423","full_name":"AurelienNicosiaULaval/ggcircular","owner":"AurelienNicosiaULaval","description":"A ggplot2 extension for circular, axial and directional data.","archived":false,"fork":false,"pushed_at":"2026-05-31T02:24:40.000Z","size":10846,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-31T04:09:51.302Z","etag":null,"topics":["circular-statistics","data-visualization","directional-data","ggplot2","r"],"latest_commit_sha":null,"homepage":"https://aureliennicosiaulaval.github.io/ggcircular/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AurelienNicosiaULaval.png","metadata":{"files":{"readme":"README.Rmd","changelog":"NEWS.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T22:10:12.000Z","updated_at":"2026-05-31T02:22:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AurelienNicosiaULaval/ggcircular","commit_stats":null,"previous_names":["aureliennicosiaulaval/ggcircular"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/AurelienNicosiaULaval/ggcircular","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AurelienNicosiaULaval%2Fggcircular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AurelienNicosiaULaval%2Fggcircular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AurelienNicosiaULaval%2Fggcircular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AurelienNicosiaULaval%2Fggcircular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AurelienNicosiaULaval","download_url":"https://codeload.github.com/AurelienNicosiaULaval/ggcircular/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AurelienNicosiaULaval%2Fggcircular/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33939227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["circular-statistics","data-visualization","directional-data","ggplot2","r"],"created_at":"2026-06-05T12:01:09.960Z","updated_at":"2026-06-05T12:01:11.776Z","avatar_url":"https://github.com/AurelienNicosiaULaval.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  fig.width = 7,\n  fig.height = 5,\n  dpi = 160,\n  out.width = \"100%\",\n  message = FALSE,\n  warning = FALSE\n)\n```\n\n\u003cimg src=\"man/figures/logo.png\" align=\"right\" width=\"170\" alt=\"ggcircular hex logo\" /\u003e\n\n# ggcircular\n\n[![R-CMD-check](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/R-CMD-check.yaml)\n[![pkgdown](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/pkgdown.yaml/badge.svg)](https://github.com/AurelienNicosiaULaval/ggcircular/actions/workflows/pkgdown.yaml)\n[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)\n[![GitHub release](https://img.shields.io/github/v/release/AurelienNicosiaULaval/ggcircular?label=release)](https://github.com/AurelienNicosiaULaval/ggcircular/releases)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![R \u003e= 4.1.0](https://img.shields.io/badge/R-%3E%3D%204.1.0-276DC3.svg)](DESCRIPTION)\n[![pkgdown site](https://img.shields.io/badge/docs-pkgdown-1f425f.svg)](https://aureliennicosiaulaval.github.io/ggcircular/)\n\n`ggcircular` is a `ggplot2` extension for circular, axial and directional\ndata. It provides layers, scales, coordinate helpers, summaries and diagnostics\nfor angles measured on a periodic scale.\n\nThe package is designed for exploratory graphics, teaching examples and\nreproducible statistical workflows involving directions, bearings, orientations,\ntimes of day, turn angles and other circular measurements.\n\n## Installation\n\n### Not on CRAN yet\n\n`ggcircular` is not on CRAN yet. Install it from GitHub while the API is being\nstabilized for a first CRAN submission.\n\nInstall the development release from GitHub:\n\n```{r, eval = FALSE}\ninstall.packages(\"remotes\")\nremotes::install_github(\"AurelienNicosiaULaval/ggcircular\")\n```\n\nOr clone with SSH and install locally:\n\n```bash\ngit clone git@github.com:AurelienNicosiaULaval/ggcircular.git\ncd ggcircular\nR -q -e 'devtools::install(upgrade = \"never\")'\n```\n\n## Quick Start\n\n```{r setup}\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ggcircular)\n```\n\n```{r quick-start}\nwind_directions |\u003e\n  filter(season == \"winter\") |\u003e\n  ggplot(aes(x = direction)) +\n  geom_rose(aes(y = after_stat(density), fill = after_stat(density)), bins = 24, alpha = 0.78) +\n  geom_circular_density(linewidth = 1.1, colour = \"#123C4A\") +\n  geom_mean_direction(length = \"resultant\", colour = \"#E4572E\", linewidth = 1.1) +\n  scale_x_circular_compass() +\n  coord_circular(zero = \"north\", direction = \"clockwise\") +\n  labs(fill = \"density\", title = \"Winter wind directions\") +\n  theme_circular()\n```\n\n## What It Does\n\n| Workflow | Main helpers |\n| --- | --- |\n| Rose diagrams and circular histograms | `geom_rose()`, `stat_rose()` |\n| Circular density estimation | `geom_circular_density()`, `stat_circular_density()` |\n| Mean direction and concentration | `geom_mean_direction()`, `circular_summary()`, `estimate_kappa()` |\n| Circular confidence intervals and tests | `circular_mean_ci()`, `rayleigh_test()`, `watson_williams_test()`, `stat_circular_test()` |\n| Axial orientations modulo pi | `axial = TRUE` in summaries and layers |\n| Theoretical circular distributions | `stat_vonmises()`, `stat_wrapped_normal()`, `stat_uniform_circular()` |\n| Mixtures of von Mises components | `fit_vonmises_mixture()`, `stat_vonmises_mixture()` |\n| Movement and state-angle graphics | `mutate_directional_features()`, `geom_direction_arrow()`, `plot_state_angles()` |\n| Angular model diagnostics | `circular_residuals()`, `circular_model_diagnostics()`, `autoplot()` methods |\n| Spherical and posterior helpers | `spherical_summary()`, `as_circular_draws()`, `summarise_circular_draws()` |\n\n## Design Principles\n\n- Angles are stored and computed in radians.\n- Scales handle display labels in radians, degrees, hours or compass labels.\n- Directional data use period `2 * pi`.\n- Axial data use period `pi` through `axial = TRUE`.\n- Heavy packages remain optional and are accessed with explicit availability checks.\n- Outputs are standard `ggplot` objects, tibbles or familiar test objects.\n\n## Conventions for Directions and Bearings\n\nThe default mathematical convention is `zero = \"east\"` with angles increasing\ncounterclockwise. This matches the usual unit circle.\n\nCompass bearings use `zero = \"north\"` with angles increasing clockwise. Use\n`scale_x_circular_compass()` together with\n`coord_circular(zero = \"north\", direction = \"clockwise\")` for bearing-like\ndata such as wind direction or movement headings.\n\nAxial data, such as unoriented lines, are different again: `0` and `pi`\nrepresent the same orientation. Use `axial = TRUE` in summaries and layers for\nthese data.\n\n## Summaries\n\n`circular_summary()` respects existing `dplyr` groups and returns mean direction,\nresultant length, circular variance, circular standard deviation and an\nestimated von Mises concentration parameter. `estimate_kappa()` is a descriptive\npiecewise approximation from the sample resultant length, not a full inferential\nfit.\n\n```{r summaries}\nwind_directions |\u003e\n  circular_summary(direction, season) |\u003e\n  mutate(\n    mean_degrees = round(rad_to_deg(mean), 1),\n    Rbar = round(Rbar, 3),\n    kappa = round(kappa, 2)\n  ) |\u003e\n  select(season, n, mean_degrees, Rbar, kappa)\n```\n\n## Axial Data\n\nAxial observations identify opposite directions. For example, an orientation of\n0 radians is equivalent to an orientation of pi radians. Use `axial = TRUE` to\ncompute and display these data modulo pi.\n\n```{r axial, fig.height = 3.6}\nggplot(axial_orientations, aes(x = orientation, fill = group)) +\n  geom_rose(bins = 18, axial = TRUE, alpha = 0.72) +\n  geom_mean_direction(axial = TRUE, colour = \"#123C4A\", linewidth = 1) +\n  scale_x_circular_degrees(limits = c(0, pi)) +\n  coord_circular() +\n  facet_wrap(~ group) +\n  theme_circular()\n```\n\n## Directional Movement\n\n`ggcircular` includes helpers for bearings, turn angles and state-specific\nangular distributions.\n\n```{r movement, fig.height = 3.6}\nanimal_steps |\u003e\n  filter(!is.na(turn_angle)) |\u003e\n  ggplot(aes(x = turn_angle, fill = state)) +\n  geom_rose(bins = 24, alpha = 0.72) +\n  geom_mean_direction(colour = \"#123C4A\", linewidth = 1) +\n  scale_x_circular_degrees(\n    breaks = deg_to_rad(c(0, 90, 180, 270)),\n    labels = c(\"0\", \"90\", \"180\", \"270\")\n  ) +\n  coord_circular() +\n  facet_wrap(~ state) +\n  theme_circular()\n```\n\n## Mixtures of von Mises Distributions\n\nFinite mixtures are fitted with an expectation-maximization routine and can be\ndrawn directly on top of empirical rose diagrams. These fits are descriptive and\ndepend on initialization, so use `seed`, `nstart` and diagnostic output when the\nmixture is substantively important.\n\n```{r mixture}\nset.seed(2026)\n\nfit_mix \u003c- fit_vonmises_mixture(\n  wind_directions$direction,\n  k = 2,\n  init = \"spaced\",\n  nstart = 3,\n  seed = 2026\n)\n\nggplot(wind_directions, aes(x = direction)) +\n  geom_rose(aes(y = after_stat(density)), bins = 24, alpha = 0.42) +\n  stat_vonmises_mixture(fit = fit_mix, linewidth = 1.2, colour = \"#123C4A\") +\n  scale_x_circular_degrees() +\n  coord_circular() +\n  theme_circular()\n```\n\n```{r mixture-table}\ntidy_circular(fit_mix) |\u003e\n  mutate(\n    mu_degrees = round(rad_to_deg(mu), 1),\n    kappa = round(kappa, 2),\n    proportion = round(proportion, 3)\n  ) |\u003e\n  select(component, proportion, mu_degrees, kappa)\n```\n\n## Tests and Intervals\n\n```{r tests}\ncircular_mean_ci(\n  wind_directions$direction,\n  method = \"bootstrap\",\n  R = 399,\n  seed = 2026\n) |\u003e\n  mutate(across(c(mean, lower, upper), rad_to_deg))\n```\n\n```{r rayleigh}\nrayleigh \u003c- rayleigh_test(wind_directions$direction)\n\ntibble::tibble(\n  statistic = unname(rayleigh$statistic),\n  n = unname(rayleigh$parameter),\n  p_value = rayleigh$p.value,\n  method = rayleigh$method\n)\n```\n\n## Optional Model Integrations\n\nThe package keeps heavier modeling ecosystems in `Suggests`. When available,\nthese integrations add diagnostics without making them hard dependencies.\n\n```{r angular-models, eval = FALSE}\nfit \u003c- CircularRegression::consensus(direction ~ speed, data = wind_directions)\n\ncircular_model_diagnostics(fit)\n\nautoplot(fit, type = \"residuals_density\")\nautoplot(fit, type = \"fitted_observed\")\n```\n\nOptional helpers currently target:\n\n- `CircularRegression`-style angular, consensus and two-step objects through\n  S3 class support.\n- `momentuHMM` state probabilities and Viterbi states.\n- `posterior` draw objects through `posterior::as_draws_df()`.\n- `circular` tests when classical circular test implementations are available.\n\n## Experimental Features\n\nThe following pieces are intentionally available but still experimental:\n\n- angular model diagnostics for optional external model classes;\n- finite mixtures of von Mises distributions;\n- `momentuHMM` state-angle adapters;\n- spherical summaries and posterior draw helpers.\n\nExperimental functions are documented and tested, but their return columns may\nstill be refined before a CRAN release if validation reveals a better public\ncontract.\n\n## Statistical Limitations\n\n`ggcircular` is primarily a visualization and diagnostics package. It does not\nreplace specialist inference workflows for circular statistics.\n\n- The automatic density bandwidth is a simple heuristic.\n- `circular_mean_ci()` is unreliable when the mean resultant length is close to\n  zero because the mean direction is weakly identified.\n- `rayleigh_test()` is mainly sensitive to unimodal departures from uniformity.\n- `watson_williams_test()` relies on strong assumptions about group\n  concentration and uses the optional `circular` implementation.\n- Multimodal data should usually be inspected with density or mixture graphics,\n  not summarized only by one mean direction.\n\n## CRAN Readiness\n\nThe package is being prepared for a first CRAN submission. The release checklist\ncurrently includes:\n\n- `R CMD check --as-cran` on the final source tarball;\n- `--run-donttest` checks;\n- hard-dependency checks with `_R_CHECK_FORCE_SUGGESTS_=false`;\n- full-suggests checks when optional packages are available;\n- Linux R-devel, Linux R-release, macOS R-release and Windows R-release checks;\n- vignette build timing and source tarball size checks.\n\nLonger articles are built for pkgdown and excluded from the CRAN tarball.\n\n## Vignettes\n\nStart with:\n\n```{r, eval = FALSE}\nvignette(\"ggcircular\", package = \"ggcircular\")\n```\n\nThen see the pkgdown articles:\n\n- [Getting started](https://aureliennicosiaulaval.github.io/ggcircular/articles/ggcircular.html)\n- [Rose diagrams](https://aureliennicosiaulaval.github.io/ggcircular/articles/rose-diagrams.html)\n- [Circular density](https://aureliennicosiaulaval.github.io/ggcircular/articles/circular-density.html)\n- [Mean direction and uncertainty](https://aureliennicosiaulaval.github.io/ggcircular/articles/mean-direction-and-uncertainty.html)\n- [Axial data](https://aureliennicosiaulaval.github.io/ggcircular/articles/axial-data.html)\n- [Movement data](https://aureliennicosiaulaval.github.io/ggcircular/articles/movement-data.html)\n- [Circular distributions](https://aureliennicosiaulaval.github.io/ggcircular/articles/circular-distributions.html)\n- [Model diagnostics](https://aureliennicosiaulaval.github.io/ggcircular/articles/model-diagnostics.html)\n- [Spherical and posterior helpers](https://aureliennicosiaulaval.github.io/ggcircular/articles/spherical-and-posterior.html)\n- [Scientific validation notes](https://aureliennicosiaulaval.github.io/ggcircular/articles/validation.html)\n- [Comparative validation](https://aureliennicosiaulaval.github.io/ggcircular/articles/validation-comparative.html)\n\n## Contributing and Support\n\nContributions are welcome through focused GitHub issues and pull requests. See\n[`CONTRIBUTING.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/CONTRIBUTING.md),\n[`SUPPORT.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/SUPPORT.md)\nand\n[`CODE_OF_CONDUCT.md`](https://github.com/AurelienNicosiaULaval/ggcircular/blob/main/CODE_OF_CONDUCT.md)\nfor contribution, support and conduct guidelines.\n\n## Development Status\n\n`ggcircular` is currently experimental. The public API is usable, tested and\ndocumented, but may still evolve as more angular model classes and validation\ncases are added.\n\nCurrent checks:\n\n- Local `devtools::test()` passes.\n- Local `devtools::check(document = FALSE, args = \"--as-cran\", build_args = \"--no-manual\")`\n  is used before release commits.\n- GitHub Actions runs hard-dependency checks with `_R_CHECK_FORCE_SUGGESTS_=false`\n  and full-suggests checks when optional packages are available.\n- GitHub Actions includes Linux R-devel plus Linux, macOS and Windows R-release.\n- `pkgdown` builds and publishes the website from `main`.\n\n## References\n\n- Fisher, N. I. (1993). [*Statistical Analysis of Circular Data*](https://doi.org/10.1017/CBO9780511564345).\n  Cambridge University Press.\n- Jammalamadaka, S. R., and Sengupta, A. (2001). [*Topics in Circular\n  Statistics*](https://doi.org/10.1142/4031). World Scientific.\n- Pewsey, A., Neuhäuser, M., and Ruxton, G. D. (2013). [*Circular Statistics in\n  R*](https://books.google.com/books/about/Circular_Statistics_in_R.html?id=qeadAAAAQBAJ).\n  Oxford University Press.\n- Wickham, H. (2016). [*ggplot2: Elegant Graphics for Data\n  Analysis*](https://doi.org/10.1007/978-3-319-24277-4). Springer.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faureliennicosiaulaval%2Fggcircular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faureliennicosiaulaval%2Fggcircular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faureliennicosiaulaval%2Fggcircular/lists"}