
Asymmetric, GAM-based smoothed-association matrices for continuous
variables. Each off-diagonal cell shows a directional
mgcv::gam(y ~ s(x)) fit, so the upper and lower triangles
tell different stories whenever the relationship is genuinely asymmetric
— precisely where a scalar Pearson correlation loses information.
Two novelties, paired with the asymmetry story:
A = |EDF_yx − EDF_xy| / (EDF_yx + EDF_xy) ∈ [0, 1] — a
single-number summary of the directional disparity per pair.linear_up,
skewed_peak, bimodal, bi_wave,
rippled_monotone, …) via a (T, I) dispatch on
turning-point and inflection counts, with monotonicity/convexity indices
for disambiguation. The taxonomy rolls up through three broader tiers:
archetype (7), monotonic (3),
linear (2) — grounded in shape-constrained regression (Pya
& Wood 2015), dose-response pharmacology (Calabrese 2008), and Morse
critical- point classification (Milnor 1963).# development version from GitHub (fast; no vignettes)
pak::pak("max578/janusplot")
# or, with vignettes built locally (recommended — enables
# browseVignettes("janusplot") and vignette("janusplot")):
# install.packages("remotes")
remotes::install_github(
"max578/janusplot",
build_vignettes = TRUE,
dependencies = TRUE
)Note: neither pak::pak() nor
devtools::install_github() /
remotes::install_github() build vignettes by default when
installing from a source repository. The
build_vignettes = TRUE flag above is required for
browseVignettes() to find them. The CRAN release ships
prebuilt vignettes and needs no extra flag.
library(janusplot)
# Palmer penguins — four continuous traits
d <- na.omit(palmerpenguins::penguins[,
c("bill_length_mm", "bill_depth_mm",
"flipper_length_mm", "body_mass_g")])
janusplot(d)Default encoding:
RdBu palette symmetric around zero (override via
colour_by = "spearman" / "kendall" /
"edf" / "deviance_gap" /
"none").A = ... (asymmetry
index) stacked over EDF = ....· * ** ***).<name> (<code>).Opt into a per-cell shape marker via annotations:
# Two-letter shape code, top-left (ASCII — safe on any font / PDF):
janusplot(d, annotations = c("edf", "A", "code"))
# Unicode shape glyph, bottom-right:
janusplot(d, annotations = c("edf", "A", "shape"),
glyph_style = "unicode")| Archetype (7) | Categories | Example |
|---|---|---|
monotone_linear |
linear_up linear_down |
y = x |
monotone_curved |
convex_up concave_up
convex_down concave_down s_shape
rippled_monotone |
tanh, sqrt, exp(−x) |
unimodal |
u_shape inverted_u
skewed_peak broad_peak
rippled_peak |
(x−.5)², x·exp(−3x), plateau |
wave |
wave warped_wave rippled_wave
complex_wave |
sin(2πx) family |
multimodal |
bimodal bimodal_ripple
bi_wave bi_wave_ripple |
two-peak mix, sin(4πx) |
chaotic |
complex |
≥ 5 extrema / inflections |
degenerate |
flat indeterminate |
constant / fit failure |
Full table (with 2-letter codes + monotonic / linear rollups + glyph
+ gloss) via janusplot_shape_hierarchy().
The classifier’s recovery behaviour is characterised across a full factorial of sample sizes × noise levels × ground-truth shapes:
# Precomputed 2160-fit demo sweep — zero wait
data("shape_sensitivity_demo")
janusplot_shape_sensitivity_plot(shape_sensitivity_demo, "recovery_curves")
janusplot_shape_sensitivity_plot(shape_sensitivity_demo, "confusion_archetype")
# Run your own — full grid in parallel
future::plan(future::multisession, workers = 4L)
res <- janusplot_shape_sensitivity(parallel = TRUE)Four diagnostic plots ("confusion_fine" /
"confusion_archetype" / "accuracy_grid" /
"recovery_curves") + summary aggregations at fine and
archetype levels. Design, pre-registered hypotheses, and full
walk-through in the shape-recognition-sensitivity
vignette.
mgcv — EDF, F-test
p-values, confidence envelopes, random effects via
s(g, bs = "re").janusplot_data() output.adjust = formula — propagate
covariates and random effects into every cell’s smooth.janusplot_data() — per-pair raw fits,
correlations, EDFs, p-values, shape metrics + hierarchy columns, without
the plot.janusplot(..., with_data = TRUE) —
plot + tidy data frame in one call.future.apply for
large matrices and sensitivity sweeps.2026L pins the
shipped demo + vignette figures; user sweeps accept
seed =.A Pearson correlation discards both the shape of an
association and the direction information that a
non-linear data-generating process leaves in its residuals. Under the
additive-noise causal discovery setting (Hoyer et al. 2009; Peters et
al. 2014) the forward regression y ~ s(x) and its inverse
x ~ s(y) are generically asymmetric when the underlying DGP
is non-linear, and that asymmetry identifies the causal direction under
mild conditions. janusplot surfaces this asymmetry as a
visual pre-discovery diagnostic — not a causal
inference procedure. See the vignette and the accompanying paper for
scope and explicit non-claims.
vignette("janusplot") — quickstart + feature tourvignette("shape-recognition-sensitivity") — design,
hypotheses, every diagnostic plotR CMD check --as-cran clean (0 errors, 0 warnings, 3
cosmetic NOTEs — new submission / local env); 190 test expectations;
88.5 % coverage.
citation("janusplot")