Skip to contents

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")).

Example correlation wheel
Example correlation wheel

Installation

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.

Quick start — straight from your data

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)
)

Save to file

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.

Flexibility

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.

Example data

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.

How masking & statistics work

A link between variables i and j is drawn only if all hold:

  1. they are in different categories (when hide_within_group = TRUE), and never the same variable (self-correlations are always excluded);
  2. p_adjusted <= sig_level; and
  3. |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.

References

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:

  • Gharibans AA, Calder S, Varghese C, Waite S, Schamberg G, Daker C, et al. Gastric dysfunction in patients with chronic nausea and vomiting syndromes defined by a noninvasive gastric mapping device. Sci Transl Med. 2022 Sep 21;14(663):eabq3544.
  • Wang TH-H, Varghese C, Calder S, Gharibans A, Schamberg G, Bartlett A, et al. Long-term evaluation of gastric electrophysiology, symptoms and quality of life after pancreaticoduodenectomy. HPB (Oxford). 2025 Dec;27(12):1535–42.
  • Xu W, Wang T, Foong D, Schamberg G, Evennett N, Beban G, et al. Characterization of gastric dysfunction after fundoplication using body surface gastric mapping. J Gastrointest Surg. 2024 Mar;28(3):236–45.
  • Xu W, Gharibans AA, Calder S, Schamberg G, Walters A, Jang J, et al. Defining and phenotyping gastric abnormalities in long-term type 1 diabetes using a novel body surface gastric mapping device. Gastro Hep Adv. 2023 Aug 18;2(8):1120–32.