Asymmetric Smoothed-Association Matrices via GAM Fits
Overview
Render a pairwise, asymmetric smoothed-association matrix of continuous variables. Each cell shows the fitted spline from an 'mgcv' generalised additive model, with the upper triangle displaying 'gam(x_j ~ s(x_i))' and the lower triangle 'gam(x_i ~ s(x_j))'. Unlike Pearson's correlation matrix, the visualisation is intentionally asymmetric, revealing heteroscedasticity, leverage, and directional non-linearity that a single scalar correlation hides. An asymmetry index and a 24-category shape taxonomy quantify the directional difference and qualitative form of each fitted smooth.
v0.1.1 NUMERICAL-SHIFT NOTICE (engine default change)
**v0.1.1 changes the default GAM fitting backend from mgcv::gam
to mgcv::bam** (Feature 4 below). bam uses fREML estimation
instead of REML, which differs by ~1-3% in effective degrees of
freedom on identical data. **Existing v0.1.0 users will see
slightly different EDFs, asymmetry indices, and per-cell colour
fills when they upgrade.** This is the single non-byte-identical
change in v0.1.1 — every other v0.1.1 feature is strictly additive.
**Recovery.** Set engine = "gam" on janusplot() or
janusplot_data() to reproduce v0.1.0 numerical output verbatim.
The package's vdiffr visual-regression suite pins
engine = "gam" for exactly this purpose — every old snapshot
Show all changes in 0.1.1Hide the rest of the changes in 0.1.1
remains valid under the backward-compat escape.
v0.1.1 Feature 4 — Default fitting engine = bam (with gam escape)
- **New
engine = c("bam", "gam")argument**, default"bam". At janusplot's scale (k = 15-25 vars, 600+ fits per call)mgcv::bam's block-Lanczos solve + fREML estimation delivers ~3-10x wall-time speedup vsmgcv::gamwithout any new dependency (mgcvalready exports both). - **fREML vs REML.**
bamdefaults to fREML (fast REML);gamdefaults to REML. The two methods optimise the same penalty target via different paths — EDFs differ by ~1-3% on identical data. v0.1.1 surfaces this as the one numerical break with v0.1.0, documented prominently above. - **
methodargument default is nowNULL**, resolved per-engine:"fREML"for bam,"REML"for gam. Users who passedmethodexplicitly in v0.1.0 see no behaviour change. - **
discrete = FALSE(bam-only)** — opt-in to mgcv's covariate-discretisation optimisation. Further ~2-5x speedup at sub-pixel prediction shift cost. - **
nthreads = 1L(bam-only)** — intra-fit threading. Default 1 to avoid oversubscription when combined withparallel = TRUE(which fans out across pair-fits already). - **
engine+methodprovenance** — both columns now appear injanusplot(..., with_data = TRUE)$dataand on everyjanusplot_data()$pairs[[i]]entry. Useful for paper figures whose methodology section needs to document which backend produced the EDFs they report. - **
baminherits fromgam** —class(bam_fit)isc("bam", "gam", "glm", "lm"), somgcv::k.check(),predict.gam(), derivative LP-matrix arithmetic, and every shape-metric extractor work without modification. Engine is plumbing, not redesign. - **vdiffr suite pinned to
engine = "gam"** so every legacy visual snapshot continues to validate under the v0.1.0 backward-compat escape. Acts as a regression gate on theengine = "gam"recovery path.
v0.1.1 Feature 3 — Axes rendering modes + figure-to-file output
- **
axes** rendering knob with four modes:"original"(default),"standardised","centred","rank". Applied per variable from raw data; same transform propagates consistently to (a) raw scatter, (b) spline prediction grid, (c) CI ribbon, and (d) the matrix-border variable label ("mpg (z)","mpg (centred)","rank(mpg)"). - **Rendering-only invariant.** The underlying
mgcvfits are byte-identical across modes — verified intests/testthat/test-axes.Rvia direct column comparison ofwith_dataoutput across all four modes on a non-linear DGP. - **At-k coverage.** The new test suite renders every
(k, mode)combination acrossk ∈ {2, 5, 10, 15, 20, 26}andmode ∈ {original, standardised, centred, rank}— 24 panels — and asserts none emit warnings. Border-label suffixing is independently verified atk ∈ {3, 12, 20}by walking patchwork's plot list and matching against the expected suffix pattern. - **Tier-3 no-op.** At compact tier 3 (
n_var >= 25) the cells render only colour fill + shape-class glyph (no curve, no scatter), soaxesis a documented no-op there. The border labels still pick up the mode suffix so the user can tell which transform they're looking at. - **
save_as = NULL** — new file-output knob. When set to a path with extension, writes the assembled matrix to disk via [ggplot2::ggsave()]; the device is inferred from the extension. Supported:.png,.pdf,.svg,.jpg/.jpeg,.tif/.tiff,.eps,.ps,.bmp. The function still returns the ggplot — the file is a side-effect. - **
save_width/save_height/save_dpi** — overrides for the auto-resolved square (pmax(6, 0.65 * k_n)inches, 300 dpi for raster). Useful when targeting a fixed-size R Journal figure.
v0.1.1 Feature 2 — Scale-aware compact rendering
- **Progressive content tiers.** New
compactargument with values"auto"(default),"always", and"never". Under"auto", each matrix renders at a tier resolved from the pixel-budget ladder: - Tier 0 (
n_var < 12) — v0.1.0 behaviour: spline + CI + scatter - every annotation.
- Tier 1 (
12 <= n_var < 18) — drop raw scatter; keep spline, CI, colour fill, and at most one annotation (k_warnif it was requested). - Tier 2 (
18 <= n_var < 25) — spline + colour fill only. - Tier 3 (
n_var >= 25) — colour mini-tile + shape-class glyph at the cell centre; no spline. - **Configurable ladder.**
compact_threshold = 12Lsets where Tier 1 begins; the full ladder shifts with it (t2 = t1 + 6,t3 = t1 + 13). Power users can override the ladder directly viacompact_levels = list(t1 = ..., t2 = ..., t3 = ...). - **Backward-compat escape.**
compact = "never"forces Tier 0 on any matrix, reproducing v0.1.0 output regardless ofn_var.compact = "always"forces at least Tier 1 even at smalln_var(useful for very dense fixed-size renders). - **Focus filter (companion).** New
focus_byargument acceptsNA(default — no filter),"asymmetry","edf","non_linearity"(defined asedf - 1), or"k_flag". Cells whose chosen metric falls belowfocus_threshold(default"q90", a quantile string, or a numeric cutoff) are rendered ingrey85at alphafocus_dim_alpha(default0.25). The matrix shape is preserved; attention drains visually to high-metric cells. This is a **visual filter, not a statistical one** — the underlying fits and thewith_datatable are unchanged. - **Rendering-only knob.** Compact tier and focus mask are pure presentation layers. Fits, EDFs, asymmetry indices, shape classifications, k-check diagnostics, and the
with_datasummary table are byte-identical acrosscompactsettings — a regression-tested invariant.
v0.1.1 Feature 1 — Per-cell k-checking (Strategy A always-on + Strategy B opt-in)
- **Diagnostic always on.** Every cell's fit now carries five new diagnostic fields derived from
mgcv::k.check():k_prime,k_index,k_p,k_flag(Wood's trifecta:edf/k' > 0.9 & k-index < 1 & p < 0.05), andk_check_status("ok","flagged","unreliable"). Cells withn_unique(x) < 10are marked"unreliable"rather than flagged —k.check()'s simulation p-value is meaningless at very low unique-x. - **Opt-in auto-refit.** New
auto_refit_k = FALSE(default) andk_max_iter = 2Larguments onjanusplot()andjanusplot_data(). Withauto_refit_k = TRUE, every flagged cell is refit with a doubling-k loop until either the flag clears, the per-cell unique-x cap is reached, ork_max_iteriterations have passed. Refit trajectory persisted on each cell:k_initial,k_final,k_iterations,k_at_cap. - **Configurable thresholds.** New
k_check_thresholds = list(edf_ratio = 0.9, k_index = 1.0, p = 0.05)argument. Defaults trackmgcv::gam.check()and Wood (2017) §5.9. - **Console summary.** A 3-line
cli::cli_inform()summary fires at the end ofjanusplot()/janusplot_data()whenever at least one cell is flagged: total flagged, chance-expected count under α = 0.05, and a recovery hint pointing at eitherauto_refit_k = TRUEork_max_iter. Quiet on clean datasets. - **Cell annotation.** New
"k_warn"entry in theannotationsvocabulary onjanusplot(). When included, flagged cells render a red!(ASCII) or⚠(Unicode) in the top-left corner. Off by default — opt in withannotations = c("edf", "A", "k_warn"). - **Summary-table extension.**
janusplot(..., with_data = TRUE)$datagains 9 new columns:k_prime,k_index,k_p,k_flag,k_check_status,k_initial,k_final,k_iterations,k_at_cap. - **RNG isolation.**
mgcv::k.check()runs its own simulation (defaultn.rep = 400) for the basis-deficiency p-value. The diagnostic's RNG draw is now isolated via an internal seed-preservation helper, so it does not shift downstream Monte Carlo consumers —derivative_ci = "simultaneous"bands andfuture.seed = TRUEreproducibility are unaffected.
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-05
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-06-09
- ERROR12 OK, 1 ERROR2026-06-08
- OK5 OK2026-04-29
Documentation
- Help pages
- 11 (1 internal)includes a package overview page
- Examples that run
- 100%10 run in a plain check
- Exported functions with examples
- 100%
- Documented parameters
- 100%
- Return-value docs
- 100%
- Help pages citing references
- 9%
Downloads
Repository
- PRs merged (all time)
- 100%
- Median days to close, 50 most recently updated closed issues
- not measured
- Median days to close, 50 most recently updated closed PRs
- 31
Releases over time
PRs over time
Commits over time
24 development-tooling and community-health practices detected across 9 families in the upstream repository
Checks run against github.com/max578/janusplot on 2026-10-21.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2026-10-19 · The repository has a site deploy workflow
pkgdown site last built 2026-10-19
Show all practicesHide 5 practices
Dependencies
Code & Tests
Datasets
People & History
Pick two releases to compare their code metrics.
- 0.1.1Latest
- 0.1.0first seen 2026-04-28
- RR 4.6.0 released · 2026-04-24
Package metadata
- First seen
- 2026-04-28
- Total releases
- 2 / 1 yrs
- License
- GPL (>= 3) OSI
- Minimum R
- ≥ 4.3.0
- Bundled data
- 25 KB / 1 file
- Download size
- 2.6 MB
Cite
Cite this package
Run in R for the authors' entry:
citation("janusplot")The package provides a citation file, so citation() returns the authors' own entry.
The repository also keeps a CITATION.cff.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-24, which the citation names so these numbers can be found later. More on citing and the projects behind them.