Circular correlation "wheel" plots in native R.
circlecorR draws correlation wheel plots with variables arranged around a
ring, grouped and colour-tiled by category, connected by curved links whose
colour maps to the correlation coefficient using
circlize.
A traditional correlation matrix is mostly redundant. A diagonal of
self-correlations, two mirror-image triangles, and blocks of within-category
correlations bury the between-category relationships we care about. The
wheel, first seen in Gharibans et al. (2019), keeps only the relationships of
interest. Hiding self- and within-category correlations also removes them from the
multiple-comparison family, which improves statistical power (see
vignette("circlecorR")).

Install the released version from CRAN:
install.packages("circlecorR")
Or the development version from GitHub:
# install.packages("remotes")
remotes::install_github("kriz98/circlecorR", build_vignettes = TRUE)
Dependencies (circlize and psych) install automatically with either
command above.
circlecorR is designed to work straight from a data frame, one row per
subject, one column per variable, with no separate step to build
correlation matrices yourself. Hand that data frame to corr_wheel() and it
computes the correlations and p-values for you. The only other thing you
supply is groups, which both selects the variables and orders them around
the wheel (extra columns like IDs are ignored).
library(circlecorR)
# `gastro_symptoms` is a synthetic example dataset bundled with the package --
# swap it for your own data frame (one row per subject) to use your own data.
groups <- list(
Demographics = c("Age", "BMI"),
Metrics = c("Amplitude", "Fed-Fasted AR", "Frequency", "GA-RI"),
Symptoms = c("Nausea", "Early satiety", "Bloating",
"Upper GI pain", "Lower GI pain", "Heartburn"),
Scores = c("GCSI", "PAGI-SYM", "PAGI-QoL", "EQ-5D")
)
corr_wheel(
gastro_symptoms, # one row per subject -- use your own data here
groups = groups,
method = "pearson",
adjust = "hochberg",
sig_level = 0.05,
r_threshold = 0.3,
r_limits = c(-0.6, 0.6)
)
png("correlation_wheel.png", width = 2400, height = 2000, res = 300)
corr_wheel(gastro_symptoms, groups = groups, r_threshold = 0.3, r_limits = c(-0.6, 0.6))
dev.off()
See vignette("circlecorR") for the full tour.
Everything the reference figure controls is a plain argument:
| What | Argument | Example |
|---|---|---|
| Category assignment & order | groups |
named list or variable = category vector |
| Colour scheme (category colours + link palette, together) | scheme |
"colorblind", "ocean", "vivid", "alimetry", or list(colors=, palette=)
|
| Category colours | colors |
c(Symptoms = "#55A868", Scores = "#C44E52") (overrides scheme per category) |
| Pretty variable labels | labels |
c("GA-RI" = "Rhythm index") |
| Significance cutoff | sig_level |
0.05 |
| Multiple-comparison adjustment | adjust |
"holm", "hochberg", "BH", "none"
|
| Minimum |r| shown | r_threshold |
0.3 |
| Hide within-category links | hide_within_group |
TRUE / FALSE
|
| Colour-scale range | r_limits |
c(-0.5, 0.5) |
| Link colour ramp | palette |
c("#2166AC", "white", "#B2182B") (overrides scheme's) |
| Block size (thickness) | tile_height |
0.06 (thin) … 0.12 (thick) |
| Line size (width) | link_lwd |
1.6, 3
|
| Rotation / spacing |
start_degree, group_gap, node_gap
|
|
| Legend / colour bar |
legend, colorbar
|
Built-in schemes are listed with corr_wheel_schemes() and inspected/tweaked
with corr_wheel_scheme(); see vignette("circlecorR") for examples.
corr_wheel() returns (invisibly) the ordered variables, resolved group and
colour maps, the colour function, and the masked matrix actually plotted — handy
for reproducibility or building a caption.
gastro_symptoms is a bundled synthetic dataset — fully simulated, not
patient data — mimicking a gastric-symptom study, one row per subject. It's
loaded automatically with the package (LazyData: true), so it's available
directly as soon as you library(circlecorR) — no data() call needed. See
?gastro_symptoms for details, or use it as a template for your own data's
shape.
A link between variables i and j is drawn only if all hold:
hide_within_group = TRUE), and never
the same variable (self-correlations are always excluded);p_adjusted <= sig_level; and|r| >= r_threshold.The correlations left after step 1 form the family of comparisons. The
adjust correction is applied over only that family — so redundant self-
and within-category correlations don't inflate the count, and power improves.
corr_wheel() returns n_tests (the family size) and the family-adjusted
p-matrix for reporting.
The correlation wheel was introduced in:
Gharibans AA, Coleman TP, Mousa H, Kunkel DC. Spatial patterns from high-resolution electrogastrography correlate with severity of symptoms in patients with functional dyspepsia and gastroparesis. Clin Gastroenterol Hepatol. 2019 Dec;17(13):2668–77.
It has since been used in several other gastric physiology studies: