MixedModelsMakie.jl API
Coefficient Plots
MixedModelsMakie.coefplot — Function
coefplot(xs::Union{MixedModel,MixedModelBootstrap}...; kwargs...)::Figure
coefplot!(fig::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, xs::Union{MixedModel,MixedModelBootstrap}...;
show_legend=length(xs) > 1, legend_attributes=(;), kwargs...)
coefplot!(ax::Axis, xs::Union{MixedModel,MixedModelBootstrap}...;
conf_level=0.95, vline_at_zero=true, show_intercept=true,
ptype=nothing,
scatter_attributes=(;),
errorbars_attributes=(;),
show_legend=length(xs) > 1,
legend_attributes=(;),
labels=string.(1:length(xs)),
attributes...)Create a coefficient plot of the requested parameters and associated confidence intervals. When multiple models are supplied, they are overlaid on the same axes for comparison; all models must share the same coefficient names.
For a MixedModelBootstrap, ptype selects which parameters to plot: :β (fixed effects, default), :σ, :ρ, or :θ (see ridgeplot for details; the ASCII aliases :sigma, :rho, :theta are also accepted). ptype is not supported for a plain MixedModel.
group restricts ptype ∈ (:σ, :ρ) to a single grouping factor, e.g. group=:subj. It is not supported for :β/:θ or for a plain MixedModel.
attributes are passed onto both scatter! and errorbars!, while scatter_attributes and errorbars_attributes are passed only onto scatter! and errorbars!, respectively. (Starting with Makie 0.21, unsupported attributes for a given plottype are no longer silently ignored, so it's necessary to separate out the attributes that are only valid for a single plot type.)
labels controls the legend entry for each model (defaults to "1", "2", ...).
show_legend controls placement of the legend. Accepted values:
false: no legendtrueor:bottom: horizontal legend below the axis (default for multi-model figures):top: horizontal legend above the axis:left: vertical legend to the left of the axis:right: vertical legend to the right of the axis:axis: legend embedded inside the axis viaaxislegend
legend_attributes is a named tuple of keyword arguments forwarded to the Makie Legend (or axislegend) constructor.
The mutating methods return the original object.
using CairoMakie
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
using Random
verbagg = MixedModels.dataset(:verbagg)
gm1 = fit(MixedModel,
@formula(r2 ~ 1 + anger + gender + btype + situ + (1|subj) + (1|item)),
verbagg,
Bernoulli();
progress=false)
coefplot(gm1)sleepstudy = MixedModels.dataset(:sleepstudy)
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress=false)
boot = parametricbootstrap(MersenneTwister(42), 1000, fm1)
coefplot(boot; conf_level=0.999, title="Custom Title")Ridge Plots
MixedModelsMakie.ridgeplot — Function
ridgeplot(xs::MixedModelBootstrap...; kwargs...)::Figure
ridgeplot!(fig::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, xs::MixedModelBootstrap...;
show_legend=length(xs) > 1, legend_attributes=(;), kwargs...)
ridgeplot!(ax::Axis, xs::MixedModelBootstrap...;
conf_level=0.95, vline_at_zero=true, show_intercept=true,
ptype=:β,
scatter_attributes=(;),
errorbars_attributes=(;),
band_attributes=(;),
lines_attributes=(;),
show_legend=length(xs) > 1,
legend_attributes=(;),
labels=string.(1:length(xs)),
attributes...)Create a ridge plot for the bootstrap samples of the requested parameters. When multiple bootstrap objects are supplied, they are overlaid on the same axes for comparison; all inputs must share the same coefficient names.
ptype selects which bootstrap parameters to plot: :β (fixed effects, default), :σ (random-effect standard deviations, including the residual), :ρ (random-effect correlations), or :θ (the unconstrained Cholesky parameterization, labeled positionally as θ01, θ02, ... since these lack a natural per-group name). The ASCII aliases :sigma, :rho, :theta are also accepted. show_intercept only applies to :β; it is a no-op for :σ/:ρ/:θ.
group restricts ptype ∈ (:σ, :ρ) to a single grouping factor, e.g. group=:subj. It is not supported for :β/:θ.
Densities are normalized so that the maximum density is always 1. For bounded parameters (:σ ≥ 0, :ρ ∈ [-1, 1], :θ per-element via lowerbd), the density curve is truncated to the parameter's valid range, since kernel smoothing can otherwise leak density past a hard boundary.
Setting histogram=true plots a histogram instead of a KDE for each row (counts normalized the same way). A histogram respects a parameter's bounds by construction, and can show a genuine spike where many bootstrap draws land on a boundary (e.g. a singular fit) — something a smoothed KDE cannot represent. bins is forwarded to StatsBase.fit(Histogram, ...; nbins=bins) when histogram=true; otherwise StatsBase's automatic bin selection is used.
The highest density interval corresponding to conf_level is marked with a bar at the bottom of each density. Setting conf_level=missing removes the markings for the highest density interval.
attributes are passed onto coefplot, band! and lines!. scatter_attributes and errorbars_attributes are passed only onto coefplot. band_attributes and lines_attributes are passed only onto band! and lines!, respectively. (Starting with Makie 0.21, unsupported attributes for a given plottype are no longer silently ignored, so it's necessary to separate out the attributes that are only valid for a single plot type.)
labels controls the legend entry for each model (defaults to "1", "2", ...).
show_legend controls placement of the legend. Accepted values:
false: no legendtrueor:bottom: horizontal legend below the axis (default for multi-model figures):top: horizontal legend above the axis:left: vertical legend to the left of the axis:right: vertical legend to the right of the axis:axis: legend embedded inside the axis viaaxislegend
legend_attributes is a named tuple of keyword arguments forwarded to the Makie Legend (or axislegend) constructor.
The mutating methods return the original object.
ridgeplot(boot)Ridge 2D Plots
MixedModelsMakie.ridge2d — Function
ridge2d(bs::MixedModelBootstrap; ptype=:β, kwargs...)
ridge2d!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, bs::MixedModelBootstrap; ptype=:β)Plot pairwise bivariate scatter plots with overlain densities for a bootstrap sample.
ptype specifies the set of parameters to examine, e.g. :β, :σ, :ρ, :θ (the ASCII aliases :sigma, :rho, :theta are also accepted).
The mutating methods return the original object.
ridge2d(boot)Random effects and group-level predictions
Caterpillar Plots
MixedModelsMakie.RanefInfo — Type
RanefInfoInformation on random effects conditional modes/means, variances, etc.
Used for creating caterpillar plots.
Fields
cnames::Vector{String}: Column names, i.e. predictor coefficient nameslevels::Vector: Levels of the random effect, i.e. group namesranef::Matrix{T} where T<:AbstractFloat: Conditional modes (or means) of the random effectsstddev::Matrix{T} where T<:AbstractFloat: Conditional standard deviations of the random effects
MixedModelsMakie.ranefinfo — Function
ranefinfo(m::MixedModel)Return a NamedTuple{fnames(m), NTuple{k, RanefInfo}} from model m
ranefinfo(m::LinearMixedModel, gf::Symbol, re=ranef(m))Return a RanefInfo corresponding to the grouping variable gf in model m.
The optional re argument is a precomputed ranef(m) result, used to avoid recomputation.
MixedModelsMakie.ranefinfotable — Function
ranefinfotable(ri::RanefInfo)
ranefinfotable(ris::NamedTuple)
ranefinfotable(m::MixedModel, args...; kwargs...)Return the information in ri (or derived from m) as a column table (NamedTuple of Vectors)
The columns are
name: name of the random effectlevel: level of the grouping factorcmode: conditional mode of the random effectcstddev: conditional standard deviation of the random effect
When called with a NamedTuple (as returned by ranefinfo(m::MixedModel)), a group column is prepended containing the grouping factor name for each row.
The MixedModel method is a convenience wrapper equivalent to ranefinfotable(ranefinfo(m, args...; kwargs...)).
MixedModelsMakie.caterpillar — Function
caterpillar(m::MixedModel, gf::Symbol=first(fnames(m)); kwargs...)::Figure
caterpillar!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::MixedModel,
gf::Symbol=first(fnames(m)); kwargs...)
caterpillar!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, r::RanefInfo;
orderby=1, cols::Union{Nothing,AbstractVector}=nothing,
dotcolor=(:red, 0.2), barcolor=:black,
vline_at_zero::Bool=false)Create a "caterpillar plot" of the random-effects conditional means and prediction intervals.
A "caterpillar plot" is a horizontal error-bar plot of conditional means and standard deviations of the random effects.
When passing a MixedModel, gf specifies which grouping variable is displayed. Alternatively, ranefinfo may be used to construct the RanefInfo object directly. Constructing RanefInfo directly can be used to avoid re-computing the conditional variances.
The order of the levels on the vertical axes is increasing orderby column of r.ranef, usually the (Intercept) random effects. orderby can be an integer column index, a column name (as a Symbol or String), or nothing to disable sorting. Setting orderby=nothing returns the levels in the order they are stored in.
The display can be restricted to a subset of random effects associated with a grouping variable by specifying cols, either by indices or term names.
The mutating methods return the original object.
using CairoMakie
CairoMakie.activate!(type = "svg")
using MixedModels
using MixedModelsMakie
sleepstudy = MixedModels.dataset(:sleepstudy)
verbagg = MixedModels.dataset(:verbagg)
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress=false)
gm0 = fit(MixedModel,
@formula(r2 ~ 1 + anger + gender + btype + situ + (1|subj) + (1|item)),
verbagg,
Bernoulli();
progress=false)
subjre = ranefinfo(fm1)[:subj]
caterpillar!(Figure(; size=(800,600)), subjre)caterpillar!(Figure(; size=(800,600)), subjre; orderby=2)caterpillar!(Figure(; size=(800,600)), subjre; orderby=nothing)caterpillar(gm0, :item)MixedModelsMakie.qqcaterpillar — Function
qqcaterpillar(m::MixedModel, gf::Symbol=first(fnames(m)); kwargs...)::Figure
qqcaterpillar!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::MixedModel,
gf::Symbol=first(fnames(m)); kwargs...)
qqcaterpillar!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, r::RanefInfo;
cols::Union{Nothing,AbstractVector}=nothing,
dotcolor=(:red, 0.2), barcolor=:black,
vline_at_zero::Bool=false)Create a caterpillar plot with the vertical axis on the Normal() quantile scale.
When passing a MixedModel, gf specifies which grouping variable is displayed. Alternatively, ranefinfo may be used to construct the RanefInfo object directly. Constructing RanefInfo directly can be used to avoid re-computing the conditional variances.
The display can be restricted to a subset of random effects associated with a grouping variable by specifying cols, either by indices or term names.
The order of the levels on the vertical axes is increasing orderby column of r.ranef, usually the (Intercept) random effects. Setting orderby=nothing will disable sorting, i.e. return the levels in the order they are stored in.
The mutating methods return the original object.
qqcaterpillar(fm1)qqcaterpillar(gm0, :item)qqcaterpillar!(Figure(; size=(400,300)), subjre; cols=[1])qqcaterpillar!(Figure(; size=(400,300)), subjre; cols=[:days])Shrinkage Plots
MixedModelsMakie.ShrinkageInfo — Type
ShrinkageInfoInformation on random effects compared to pseudo-OLS estimates.
Used for creating shrinkage caterpillar plots.
Fields
cnames::Vector{String}: Column names, i.e. predictor coefficient nameslevels::Vector: Levels of the random effect, i.e. group namesblups::Matrix{T} where T<:AbstractFloat: The conditional modes of the random effectsblimps::Matrix{T} where T<:AbstractFloat: The 'reference' mode of the random effect, corresponding to the conditional mode evaluated at the reference value of θ. Typically, this approximates the value you would get without any shrinkage, e.g. from classical within-groups (non-mixed) regression
λ::Matrix{T} where T<:AbstractFloat: The (lower-triangular) relative covariance factor of the random effects for this grouping factor, such that the covariance matrix is proportional to λλ'. Shared across all levels. Used for constructing correlation ellipses in shrinkage plots.
MixedModelsMakie.shrinkageinfo — Function
shrinkageinfo(m::MixedModel, θref::Vector{<:AbstractFloat}=_ref_theta(m))Return a NamedTuple{fnames(m), NTuple{k, ShrinkageInfo}} from model m
The optional θref argument is the θ vector at which to compute the reference (unshrunken) estimates; it defaults to _ref_theta(m).
shrinkageinfo(m::MixedModel, gf::Symbol, θref::Vector{<:AbstractFloat}=_ref_theta(m))Return a ShrinkageInfo corresponding to the grouping variable gf in model m.
The optional θref argument is the θ vector at which to compute the reference (unshrunken) estimates; it defaults to _ref_theta(m).
MixedModelsMakie.shrinkageinfotable — Function
shrinkageinfotable(si::ShrinkageInfo)
shrinkageinfotable(sis::NamedTuple)
shrinkageinfotable(m::MixedModel, args...; kwargs...)Return the information in si (or derived from m) as a column table (NamedTuple of Vectors)
The columns are
name: name of the random effectlevel: level of the grouping factorcmode: conditional mode of the random effectrmode: reference mode of the random effect, corresponding to the conditional mode evaluated at the reference value of θ. Typically, this approximates the value you would get without any shrinkage, e.g. from classical within-groups (non-mixed) regression
When called with a NamedTuple (as returned by shrinkageinfo(m::MixedModel)), a group column is prepended containing the grouping factor name for each row.
The MixedModel method is a convenience wrapper equivalent to shrinkageinfotable(shrinkageinfo(m, args...; kwargs...)).
MixedModelsMakie.shrinkagedot — Function
No documentation found for public binding MixedModelsMakie.shrinkagedot.
MixedModelsMakie.shrinkagedot is a Function.
# 2 methods for generic function "shrinkagedot" from MixedModelsMakie:
[1] shrinkagedot(m::MixedModels.MixedModel, gf::Symbol; kwargs...)
@ ~/work/MixedModelsMakie.jl/MixedModelsMakie.jl/src/shrinkage.jl:419
[2] shrinkagedot(m::MixedModels.MixedModel; ...)
@ ~/work/MixedModelsMakie.jl/MixedModelsMakie.jl/src/shrinkage.jl:419MixedModelsMakie.shrinkageplot — Function
shrinkageplot(m::MixedModel, gf::Symbol=first(fnames(m)), θref=_ref_theta(m); kwargs...)::Figure
shrinkageplot!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::MixedModel,
gf::Symbol=first(fnames(m)), θref=_ref_theta(m); kwargs...)
shrinkageplot!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, si::ShrinkageInfo;
ellipse=false, ellipse_scale=1, n_ellipse=5,
cols::Union{Nothing,AbstractVector}=nothing,
shrunk_dotcolor=(:blue, 0.25), ref_dotcolor=(:red, 0.25),
ellipse_color=:green, ellipse_linestyle=:dash,
labels::Union{Bool,Symbol,AbstractVector}=false,
labelcolor=:black, labelsize=10, n_labels::Integer=5)Create a scatter-plot matrix of the conditional means, b, of the random effects for grouping factor gf.
Two sets of conditional means are plotted: those at the estimated parameter values and those at θref. The default θref results in Λ being a very large multiple of the identity. The corresponding conditional means can be regarded as unpenalized.
The display can be restricted to a subset of random effects associated with a grouping variable by specifying cols, either by indices or term names.
The reference (unshrunk) points can be labeled with the levels of gf by passing labels=true (label every level), a vector of level names/indices (label only that subset), or labels=:auto (automatically pick the n_labels "most interesting" levels). labelcolor and labelsize control the appearance of the labels; n_labels only applies to labels=:auto and defaults to a small number so the plot doesn't get overcrowded.
Correlation ellipses can be added with ellipse=true, with the number of ellipses controlled by n_ellipse. The ellipses are equally spaced between the outer ellipse and the origin (center). The scaling of the ellipses can be adjusted with the multiplicative ellipse_scale. If you are unable to see the ellipses, try increasing ellipse_scale.
The mutating methods return the original object.
See also shrinkagedot.
using CairoMakie
# The SVG sometimes renders incorrectly,
# so you may want to use PNG here
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
sleepstudy = MixedModels.dataset(:sleepstudy)
verbagg = MixedModels.dataset(:verbagg)
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress=false)
shrinkageplot(fm1; labels=true)shrinkageplot(fm1; ellipse=true)shrinkageplot!(Figure(; size=(400,400)), fm1; labels=:auto)shrinkagedot(fm1; ordertype=:shrunk)shrinkagedot(fm1; ordertype=:ref)gm1 = fit(MixedModel,
@formula(r2 ~ 1 + anger + gender + btype + situ + (1|subj) + (1+gender|item)),
verbagg,
Bernoulli();
progress=false)
shrinkageplot(gm1, :item)Diagnostics
We have also provided a few useful plot recipes for common plot types applied to mixed models. These are especially useful for diagnostics and model checking.
QQ Plots
The methods for qqnorm and qqplot are implemented using Makie recipes. In other words, these are convenience wrappers for calling the relevant plotting methods on residuals(model).
Specify the type of line on the QQ plots with the qqline keyword-argument. The default for qqnorm is :fitrobust, which delivers an R-style line connecting the first and third quartiles. The default for qqplot is :identity, which plots the line with slope = 1 and intercept = 0. The final possibility is :fit, which plots the line of best fit (i.e. regressing the quantiles of the residuals onto the quantiles of the reference distribution).
The reference distribution for qqnorm is the standard normal, which differs from the behavior in previous versions of Makie.
The options and associated names for the qqline keyword argument changed in Makie 0.16.3 (and were broken in Makie 0.16.0-0.16.2). The equivalent to qqline=:R is qqline=:fitrobust. qqline=:R will be supported for backwards compatibility only until the next breaking release.
using CairoMakie
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
sleepstudy = MixedModels.dataset(:sleepstudy)
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress=false)
qqnorm(fm1; qqline=:fitrobust)# the residuals should have mean 0
# and standard deviation equal to the residual standard deviation
qqplot(Normal(0, fm1.σ), fm1)Profile Plots
Requires MixedModels 4.14 or above.
using CairoMakie
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
sleepstudy = MixedModels.dataset(:sleepstudy)
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days|subj)), sleepstudy; progress=false)
pr1 = profile(fm1)
zetaplot(pr1)# show zeta on the absolute value scale with coverage intervals
zetaplot(pr1; absv=true)# show zeta for the variance components
zetaplot(pr1; absv=true, ptyp='θ')profiledensity(pr1)profiledensity(pr1; share_y_scale=false)profiledensity(pr1; ptyp='σ')UpSet Plots
UpSet plots[upset] visualize the intersection structure of categorical conditions, showing which combinations of condition levels co-occur in the data and how many observations (or grouping-factor levels) fall in each combination.
The visual design of UpSet plots (e.g. colors, markers, and how marginal/collapsed cells are distinguished from true intersections) is still being refined. Such visual changes may land in minor releases without being treated as breaking.
Sets are the individual levels of each categorical predictor (e.g., "gender: M", "gender: F", "btype: curse"). Columns of the combination matrix are the full factorial cells (every combination of each level per predictor) plus marginal cells where all levels of a single predictor are simultaneously active (showing whether that predictor is within-subjects or between-subjects). A filled circle means that condition level is active in that column; a connecting line spans the active conditions within each column. The top bars show counts per column; the left bars show counts per individual condition level.
A column in which only within-subjects predictors are collapsed will have non-zero counts; a column where a between-subjects predictor is collapsed will be empty because no single unit can appear in all levels of a between-subjects factor.
MixedModelsMakie.upsetplot! — Method
upsetplot(m::MixedModel,
gf::Union{Symbol,Nothing}=first(fnames(m)); kwargs...)::Figure
upsetplot!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::MixedModel,
gf::Union{Symbol,Nothing}=first(fnames(m));
sortby::Symbol=:count,
show_empty::Bool=true,
orientation::Symbol=:horizontal,
show_setsize::Bool=true,
intersection_pos::Symbol=...,
setsize_pos::Symbol=...,
filled_color=:black,
empty_color=:lightgray,
bar_color=:steelblue,
union_color=:gray30,
dot_size=12)Create an UpSet plot showing which levels of grouping factor gf appear in which categorical fixed-effect conditions.
Pass gf=nothing to count observations instead of grouping-factor levels.
Predictor names, levels, and per-observation values are recovered from the model's formula, design matrix, and contrast coding — no original data frame is needed.
The visual design of UpSet plots (e.g. colors, markers, and how marginal/collapsed cells are distinguished from true intersections) is still being refined. Such visual changes may land in minor releases without being treated as breaking.
Layout keywords:
orientation::horizontal(default — combinations as columns, sets as rows) or:vertical(combinations as rows, sets as columns).show_setsize: show the set-size bar chart (trueby default).intersection_pos: where the intersection-size bars go relative to the incidence matrix —:top/:bottom(horizontal) or:left/:right(vertical).setsize_pos: where the set-size bars go —:left/:right(horizontal) or:top/:bottom(vertical).
Marginal cells (which collapse one predictor, leaving all of its levels active) get an "or" bracket drawn around that predictor's sibling dots, since lighting up every level of a predictor represents "not constrained on this predictor" (a union), not an intersection like the solid line connecting the rest of the dots.
The mutating method returns the original object.
MixedModelsMakie.upsetplot! — Method
upsetplot(data; cols=All(), gf::Union{Symbol,Nothing}=nothing, kwargs...)::Figure
upsetplot!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, data;
cols=All(),
gf::Union{Symbol,Nothing}=nothing,
sortby::Symbol=:count,
show_empty::Bool=true,
orientation::Symbol=:horizontal,
show_setsize::Bool=true,
intersection_pos::Symbol=...,
setsize_pos::Symbol=...,
filled_color=:black,
empty_color=:lightgray,
bar_color=:steelblue,
union_color=:gray30,
dot_size=12)Create an UpSet plot directly from a Tables.jl-compatible table.
Non-numeric columns (optionally restricted by cols) become the sets. Pass gf=:col to count unique values of that column per cell instead of observations.
The visual design of UpSet plots (e.g. colors, markers, and how marginal/collapsed cells are distinguished from true intersections) is still being refined. Such visual changes may land in minor releases without being treated as breaking.
See upsetplot!(::Indexable, ::MixedModel) for layout keyword details.
The mutating method returns the original object.
using CairoMakie
CairoMakie.activate!(; type="svg")
using DataFrames: Not
using MixedModels
using MixedModelsMakie
verbagg = MixedModels.dataset(:verbagg)
gm1 = fit(MixedModel,
@formula(r2 ~ 1 + anger + gender + btype + situ + (1|subj) + (1+gender|item)),
verbagg, Bernoulli(); progress=false)
# gender is between-subjects; btype and situ are within-subjects.
# Marginal cells that collapse gender are empty (no subject has both genders),
# while marginals that collapse btype or situ are non-empty.
upsetplot(gm1, :subj, show_empty=false)# Observation counts instead of subject counts
upsetplot(gm1, nothing, show_empty=false)# Table-based: no model required — non-numeric columns are detected automatically.
# Numeric columns (anger) and explicit exclusions (subj, item, r2) are dropped.
upsetplot(verbagg; cols=Not([:subj, :item, :r2]), gf=:subj, show_empty=false)# Sort by degree (full factorial cells first, then marginals) rather than by count
upsetplot(gm1, :subj, sortby=:degree, show_empty=false)The same incidence data drawn by upsetplot can be pulled out as a table with upsettable, for cases where you want to filter, sort, or re-analyze it directly rather than (or in addition to) plotting it:
MixedModelsMakie.upsettable — Function
upsettable(m::MixedModel, gf::Union{Symbol,Nothing}=first(fnames(m)))Return the incidence table underlying upsetplot!: one row per combination cell (every full factorial cell of the categorical fixed-effect predictors, plus marginal cells that collapse a single predictor), with:
cell: a label for the combinationdegree: the number of predictors involved (one fewer for marginal cells)count: the number of observations (or grouping-factor levels, ifgfis given) falling in the cell- one
Boolcolumn per set (an individual condition level, e.g."gender: M") indicating whether that set is active in the cell
Pass gf=nothing to count observations instead of grouping-factor levels, matching upsetplot!.
upsettable(data; cols=All(), gf::Union{Symbol,Nothing}=nothing)Return the incidence table underlying upsetplot!, built directly from a Tables.jl-compatible table. See upsetplot! for the meaning of cols and gf.
first(upsettable(gm1, :subj), 10)| Row | cell | degree | count | gender: F | gender: M | btype: curse | btype: scold | btype: shout | situ: other | situ: self |
|---|---|---|---|---|---|---|---|---|---|---|
| String | Int64 | Int64 | Bool | Bool | Bool | Bool | Bool | Bool | Bool | |
| 1 | gender: F & btype: curse & situ: other | 3 | 243 | true | false | true | false | false | true | false |
| 2 | gender: M & btype: curse & situ: other | 3 | 73 | false | true | true | false | false | true | false |
| 3 | gender: F & btype: scold & situ: other | 3 | 243 | true | false | false | true | false | true | false |
| 4 | gender: M & btype: scold & situ: other | 3 | 73 | false | true | false | true | false | true | false |
| 5 | gender: F & btype: shout & situ: other | 3 | 243 | true | false | false | false | true | true | false |
| 6 | gender: M & btype: shout & situ: other | 3 | 73 | false | true | false | false | true | true | false |
| 7 | gender: F & btype: curse & situ: self | 3 | 243 | true | false | true | false | false | false | true |
| 8 | gender: M & btype: curse & situ: self | 3 | 73 | false | true | true | false | false | false | true |
| 9 | gender: F & btype: scold & situ: self | 3 | 243 | true | false | false | true | false | false | true |
| 10 | gender: M & btype: scold & situ: self | 3 | 73 | false | true | false | true | false | false | true |
Nesting/Crossing Plots
Grouping factors in a mixed model (e.g. subj, item, school, class) can be nested (every level of one occurs with exactly one level of another, as with students nested within classrooms in a single year) or crossed (levels of both factors co-occur relatively freely, as with subjects and items in a repeated-measures design). nestingplot shows this structure for every pair of grouping factors in a model.
The plot is laid out like a correlation matrix:
- diagonal: each grouping factor's name and number of levels.
- lower triangle: a heatmap of the co-occurrence contingency table between the row and column factor. Levels are reordered (for display only) to group each level with its most common partner, so nested structure appears as a block-diagonal pattern and crossed structure appears as a dense or scattered rectangle.
- upper triangle: a text badge classifying the same pair — one factor nested in the other (
A ⊂ B),identical(the two names partition observations the same way, e.g. two labels for one grouping factor), or crossed, further split intocomplete(every combination of levels is observed) andpartial(only some combinations are, with the observed density reported). This split matters in practice: a completely-crossed subject × item design supports estimating both by-subject and by-item slopes for every condition, while a partially crossed (e.g. Latin-square) design may not.
Pass swap_triangles=true to swap which triangle shows which — heatmaps in the upper triangle and text badges in the lower.
MixedModelsMakie.nestingplot — Function
nestingplot(m::MixedModel, gfs::Union{Symbol,AbstractString,Integer}...;
colormap=:Blues, fontsize::Real=20, swap_triangles::Bool=false)::Figure
nestingplot!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::MixedModel,
gfs::Union{Symbol,AbstractString,Integer}...;
colormap=:Blues, fontsize::Real=20, swap_triangles::Bool=false)Create a nesting/crossing matrix for the grouping factors of m.
Each distinct grouping factor (e.g. subj, item) becomes one row/column of a matrix, laid out like a correlation-matrix plot:
- diagonal: factor name and number of levels
- lower triangle (upper if
swap_triangles=true): a heatmap of the co-occurrence contingency table between the row and column factor. Rows and columns are reordered (for display only) to group each level with its most common partner, so that nested structure appears as a block-diagonal pattern and crossed structure appears as a dense or scattered rectangle. - upper triangle (lower if
swap_triangles=true): a text badge classifying the same pair as one factor nested in the other (A ⊂ B),identical(the two names partition observations the same way), orcrossed—completeif every combination of levels is observed,partial(with the observed density) otherwise.
gfs, if given, restricts and orders which grouping factors to show (by name or index); otherwise all distinct grouping factors are shown, in the order they first appear in the model.
The classification only depends on the grouping factors' level assignments (ReMat.refs), not on the original data, matching the model-based approach used by upsetplot!.
The mutating method returns the original object.
using CairoMakie
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
kb07 = MixedModels.dataset(:kb07)
gm2 = fit(MixedModel,
@formula(rt_trunc ~ 1 + spkr * prec * load +
(1 + spkr + prec + load | subj) +
(1 + spkr | item)),
kb07; progress=false)
# subj and item are nearly (but not perfectly) crossed in this design
nestingplot(gm2)As with upsetplot, the data underlying nestingplot is available as tables. nestingtable gives the raw co-occurrence counts in long format, with one row per pair of levels — including zero-count rows for combinations that never co-occur, since those absences are exactly what reveal nesting or partial crossing:
MixedModelsMakie.nestingtable — Function
nestingtable(m::MixedModel, gfs::Union{Symbol,AbstractString,Integer}...)Return the co-occurrence table underlying nestingplot, in long format: one row per combination of a level of grouping factor A and a level of grouping factor B, for every pair of distinct grouping factors in m (restricted/ordered by gfs, as in nestingplot). Columns:
factor_a,level_a,factor_b,level_b: the pair of levelscount: the number of observations sharing that pair of levels (0for combinations that never co-occur — these absences are what reveal nesting or partial crossing)
See nestingstructure for the pairwise nested/crossed classification instead of the raw counts.
first(nestingtable(gm2), 10)| Row | factor_a | level_a | factor_b | level_b | count |
|---|---|---|---|---|---|
| String | String | String | String | Int64 | |
| 1 | item | I01 | subj | S030 | 1 |
| 2 | item | I01 | subj | S031 | 1 |
| 3 | item | I01 | subj | S034 | 1 |
| 4 | item | I01 | subj | S035 | 1 |
| 5 | item | I01 | subj | S036 | 1 |
| 6 | item | I01 | subj | S037 | 1 |
| 7 | item | I01 | subj | S038 | 1 |
| 8 | item | I01 | subj | S039 | 1 |
| 9 | item | I01 | subj | S041 | 1 |
| 10 | item | I01 | subj | S042 | 1 |
Filtering to count == 0 finds specific missing combinations, e.g. the subjects who never saw a particular item:
filter(:count => iszero, nestingtable(gm2))| Row | factor_a | level_a | factor_b | level_b | count |
|---|---|---|---|---|---|
| String | String | String | String | Int64 | |
| 1 | item | I05 | subj | S086 | 0 |
| 2 | item | I23 | subj | S082 | 0 |
| 3 | item | I24 | subj | S102 | 0 |
nestingstructure gives the pairwise nested/crossed classification and density shown in nestingplot's upper-triangle badges. This results in one row per pair of grouping factors, rather than one row per pair of levels:
MixedModelsMakie.nestingstructure — Function
nestingstructure(m::MixedModel, gfs::Union{Symbol,AbstractString,Integer}...)Return the pairwise nested/crossed classification underlying nestingplot's upper-triangle badges: one row per pair of distinct grouping factors in m (restricted/ordered by gfs, as in nestingplot), with:
factor_a,factor_b: the pair of grouping factorsn_levels_a,n_levels_b: their numbers of levelsrelationship: one of:identical,:a_nested_in_b,:b_nested_in_a,:complete_crossing, or:partial_crossingdensity: the fraction of then_levels_a × n_levels_bgrid of combinations that is actually observed (1.0for:complete_crossing)
See nestingtable for the raw per-level co-occurrence counts instead of this per-pair summary.
nestingstructure(gm2)| Row | factor_a | factor_b | n_levels_a | n_levels_b | relationship | density |
|---|---|---|---|---|---|---|
| String | String | Int64 | Int64 | Symbol | Float64 | |
| 1 | item | subj | 32 | 56 | partial_crossing | 0.998326 |
Facet Regression Plots
facetregression draws a Cleveland-trellis-style small-multiples display: one panel per level of a grouping column, each showing a scatter of a response against a predictor plus that group's own OLS regression line. Panels share linked axes so slopes remain visually comparable across groups; by default the shared y-range is chosen so that the mean of the per-group slopes appears at 45° on screen ("banking to 45°"), which is where slope differences are easiest to compare visually (Cleveland, W. S. (1993), Visualizing Data, Hobart Press).
MixedModelsMakie.FacetRegressionInfo — Type
FacetRegressionInfoPer-group data and within-unit OLS fits underlying facetregression!.
Fields
group::Symbol: Name of the grouping variable/factorpredictor::Symbol: Name of the predictor (x) variableresponse::Symbol: Name of the response (y) variablelabels::Vector: Group levels, in encounter orderx::Array{Vector{T}, 1} where T<:AbstractFloat: Per-group predictor valuesy::Array{Vector{T}, 1} where T<:AbstractFloat: Per-group response valuesn::Vector{Int64}: Per-group observation countsintercept::Vector{T} where T<:AbstractFloat: Per-group within-unit OLS interceptslope::Vector{T} where T<:AbstractFloat: Per-group within-unit OLS slopexrange::Tuple{T, T} where T<:AbstractFloat:(min, max)of the predictor across all groupsyrange::Tuple{T, T} where T<:AbstractFloat:(min, max)of the response across all groupsfixef::Union{Nothing, Tuple{T, T}} where T<:AbstractFloat: Population fixed-effects(intercept, slope);nothingfor the table-based constructorshrunken_intercept::Union{Nothing, Vector{T}} where T<:AbstractFloat: Per-group shrunken/BLUP intercept;nothingfor the table-based constructorshrunken_slope::Union{Nothing, Vector{T}} where T<:AbstractFloat: Per-group shrunken/BLUP slope;nothingfor the table-based constructor
MixedModelsMakie.facetregressioninfo — Function
facetregressioninfo(data, response, predictor, group)::FacetRegressionInfoCompute the per-group data and within-unit OLS fits underlying facetregression! from a Tables.jl-compatible data source.
response, predictor, and group are Symbol or AbstractString column names. fixef, shrunken_intercept, and shrunken_slope are nothing for this table-based method (there is no model to derive them from).
facetregressioninfo(m::LinearMixedModel, predictor, group=first(fnames(m)))::FacetRegressionInfo
facetregressioninfo(m::LinearMixedModel; group=first(fnames(m)))::FacetRegressionInfoCompute the per-group data and within-unit OLS fits underlying facetregression! from a fitted LinearMixedModel, sourced entirely from the model's own matrices (m.X, m.y, m.reterms) rather than a raw data table – see ranefinfo (caterpillar.jl) and the co-occurrence extraction in nesting.jl for the precedent of using only ReMat.refs/.levels this way. Also populates fixef (the population intercept/slope) and, per group, shrunken_intercept/shrunken_slope (fixed effects + that group's conditional modes from ranef(m)).
predictor may be omitted, in which case it defaults to the model's only non-intercept fixed-effect term – this throws an ArgumentError if the model has more than one such term.
MixedModelsMakie.facetregression! — Function
facetregression(data, response, predictor, group; kwargs...)::Figure
facetregression!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, data, response, predictor, group; kwargs...)
facetregression(m::LinearMixedModel, predictor, group=first(fnames(m)); kwargs...)::Figure
facetregression!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::LinearMixedModel, predictor,
group=first(fnames(m)); kwargs...)
facetregression(m::LinearMixedModel; group=first(fnames(m)), kwargs...)::Figure
facetregression!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, m::LinearMixedModel;
group=first(fnames(m)), kwargs...)
facetregression(info::FacetRegressionInfo; kwargs...)::Figure
facetregression!(f::Union{GridLayoutBase.GridLayout, GridLayoutBase.GridPosition, GridLayoutBase.GridSubposition, Makie.Figure}, info::FacetRegressionInfo; kwargs...)Create a Cleveland-trellis-style small-multiples display: one panel per level of group, each showing a scatter of response against predictor plus that group's own OLS regression line.
The table-based methods take response, predictor, and group as Symbol or AbstractString column names into any Tables.jl-compatible data.
The LinearMixedModel-based methods instead extract everything from the model's own matrices — no data table is needed. predictor must be the name of a continuous fixed-effect term (e.g. :days); group names a grouping factor and defaults to the first one (first(fnames(m))). Two extra reference lines are drawn in every panel: the population-level fixed-effects fit (show_fixef, the same line in every panel) and that group's own shrunken/BLUP fit (show_shrunken`).
predictor may be omitted entirely (note group then becomes keyword-only, to avoid ambiguity with the predictor positional argument), in which case it defaults to the model's only non-intercept fixed-effect term — this throws an ArgumentError if the model has more than one such term.
Alternatively, facetregressioninfo may be used to construct the FacetRegressionInfo object directly. Constructing it directly can be used to avoid re-computing the per-group OLS fits (and, for a model source, fixef/ ranef) when generating multiple plots (e.g. with different layout or bank45 settings) from the same data.
All panels share linked x/y axes (Cleveland-trellis convention), so slopes and scatter remain visually comparable across panels.
facetregression is still experimental. The keyword set (e.g. the LinearMixedModel overlay colors/lines, banking behavior) may change in minor releases without being treated as breaking.
Keywords
orderby::Union{Symbol,Nothing}=nothingpanel order —nothingpreserves the order groups are first encountered (not alphabetical),:intercept/:slopesort by the group's own within-unit OLS fit.rev::Bool=false: reverse the panel order produced byorderby(applied after sorting, so it also reversesnothing's first-encountered order).layout::Union{Nothing,Tuple{Union{Nothing,Int},Union{Nothing,Int}}}=nothing:(nrow, ncol)grid shape.nothingauto-computes a roughly square grid. Either element may benothingto auto-compute just that one from the other (e.g.layout=(2, nothing)fixes 2 rows and picks enough columns to fit every group). If both are given and their product is smaller than the number of groups, the grid is used as given (never enlarged) and a warning reports how many trailing groups are left undisplayed.bank45::Bool=true: shape the grid columns (viacolsize!with aGridLayoutBase.Aspectsize) so that a line with the mean of the per-group within-unit OLS slopes appears at 45° on screen ("banking", after Cleveland's trellis displays), easing visual comparison of slopes across panels. Full data is always shown — banking reshapes the panels, it never clips data.bank45=falseleaves panels at their natural (roughly square) shape.xlabel/ylabel: shared axis titles spanning the whole grid (default to thepredictor/responsenames).scattercolor,linecolor: per-panel scatter and within-unit regression-line colors.show_fixef::Bool=true,fixefcolor=:black,fixeflinestyle=:dash: whether to draw the population fixed-effects line (LinearMixedModelsource only).show_shrunken::Bool=true,shrunkencolor=:green: whether to draw the per-group shrunken/BLUP line (LinearMixedModelsource only).labelcolor,labelsize: styling for the per-panel group-level corner label.
The mutating methods return the original object.
using CairoMakie
CairoMakie.activate!(; type="svg")
using MixedModels
using MixedModelsMakie
sleepstudy = MixedModels.dataset(:sleepstudy)
facetregression(sleepstudy, :reaction, :days, :subj)# order panels by slope instead of table order
facetregression(sleepstudy, :reaction, :days, :subj; orderby=:slope)# disable banking to compare against the natural data-range aspect
facetregression(sleepstudy, :reaction, :days, :subj; bank45=false)facetregression can also be built directly from a fitted LinearMixedModel, with no data table needed. This additionally overlays two reference lines in every panel: the population-level fixed-effects fit (black, dashed) and that group's shrunken/BLUP fit (green), alongside the within-unit OLS fit (red):
fm1 = fit(MixedModel, @formula(reaction ~ 1 + days + (1 + days | subj)), sleepstudy;
progress=false)
facetregression(fm1, :days)predictor may be omitted when the model has exactly one non-intercept fixed effect:
facetregression(fm1)Computing the per-group OLS fits (and, for a model, fixef/ranef) can be expensive to repeat, so facetregressioninfo can be called once and the resulting FacetRegressionInfo reused across multiple facetregression calls:
info = facetregressioninfo(fm1, :days)
facetregression(info)facetregression(info; bank45=false)The per-group fits are also available as a table via facetregressioninfotable:
MixedModelsMakie.facetregressioninfotable — Function
facetregressioninfotable(info::FacetRegressionInfo; orderby::Union{Symbol,Nothing}=nothing, rev::Bool=false)
facetregressioninfotable(data, response, predictor, group; orderby::Union{Symbol,Nothing}=nothing, rev::Bool=false)
facetregressioninfotable(m::LinearMixedModel, predictor, group=first(fnames(m));
orderby::SUnion{Symbol,Nothing}=nothing, rev::Bool=false)
facetregressioninfotable(m::LinearMixedModel; group=first(fnames(m)),
orderby::Union{Symbol,Nothing}=nothing, rev::Bool=false)Return the per-group OLS fits in info (or derived from data/m) underlying facetregression! as a column table, one row per level of group, with columns:
group: the group leveln: number of observations in that groupintercept,slope: the group's within-unit OLS fit ofresponseonpredictor
As with facetregression!, predictor may be omitted for the LinearMixedModel methods (group then becomes keyword-only), defaulting to the model's only non-intercept fixed-effect term.
The LinearMixedModel-derived info additionally has:
fixef_intercept,fixef_slope: the population fixed-effects fit (same for every row)shrunken_intercept,shrunken_slope: that group's shrunken/BLUP fit (fixed effects plus that group's conditional modes fromranef(m))
orderby and rev have the same meaning as in facetregression!.
The data/m-based methods are convenience wrappers equivalent to facetregressioninfotable(facetregressioninfo(data_or_m, args...); orderby, rev).
facetregression/facetregressioninfotable are still experimental and their design (e.g. the LinearMixedModel columns) may change in minor releases without being treated as breaking. Relatedly, the binding facetregressiontable is deprecated and may be removed in a future release without being considered breaking.
facetregressioninfotable(sleepstudy, :reaction, :days, :subj; orderby=:slope)(group = ["S335", "S309", "S330", "S331", "S310", "S351", "S333", "S371", "S332", "S372", "S369", "S334", "S349", "S352", "S370", "S337", "S350", "S308"], n = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], intercept = [263.03469737659805, 205.05494440252136, 289.68509798916904, 285.7389703924006, 203.48422352183954, 261.14700899991124, 275.01910344904115, 253.6360368208452, 264.25161299272025, 267.04480535333806, 254.96814353249292, 240.16292197487567, 215.1117673006925, 276.3720753062856, 210.4490952925249, 290.1041281960228, 225.83460471413352, 244.19267134232956], slope = [-2.8810344349254366, 2.261785426284321, 3.0080716219815313, 5.26601710464015, 6.1148997913707275, 6.433497850822675, 9.142044529770361, 9.188445675011828, 9.56676700476443, 11.298072768702644, 11.348109759706436, 12.253140073834047, 13.493933845288817, 13.566547555634457, 18.056149939334745, 19.025972678444585, 19.504016945578833, 21.764700964725375])facetregressioninfotable(fm1, :days; orderby=:slope)(group = ["S335", "S309", "S330", "S331", "S310", "S351", "S333", "S371", "S332", "S372", "S369", "S334", "S349", "S352", "S370", "S337", "S350", "S308"], n = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10], intercept = [263.03469737659805, 205.05494440252136, 289.68509798916904, 285.7389703924006, 203.48422352183954, 261.14700899991124, 275.01910344904115, 253.6360368208452, 264.25161299272025, 267.04480535333806, 254.96814353249292, 240.16292197487567, 215.1117673006925, 276.3720753062856, 210.4490952925249, 290.1041281960228, 225.83460471413352, 244.19267134232956], slope = [-2.8810344349254366, 2.261785426284321, 3.0080716219815313, 5.26601710464015, 6.1148997913707275, 6.433497850822675, 9.142044529770361, 9.188445675011828, 9.56676700476443, 11.298072768702644, 11.348109759706436, 12.253140073834047, 13.493933845288817, 13.566547555634457, 18.056149939334745, 19.025972678444585, 19.504016945578833, 21.764700964725375], fixef_intercept = [251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093, 251.4051060532093], fixef_slope = [10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485, 10.467285505609485], shrunken_intercept = [250.3674418963411, 211.35668964980192, 274.2371315591448, 272.954875422857, 212.97208811143793, 255.67906978575976, 267.84697165685594, 252.1283579763354, 260.2206227446339, 263.5239987305756, 254.66363623139867, 244.40846372149852, 226.8471416910777, 272.02726978239303, 226.69505976457546, 286.0713847623575, 239.07071702004563, 254.22098845065835], shrunken_slope = [-0.13212675748280844, 1.8231868995316258, 5.808582434814334, 7.522805054654685, 4.953868891044062, 7.511957723499471, 10.308485653239533, 9.49623460531637, 10.232089424740735, 11.777990014697734, 11.338998486208961, 11.500008033385004, 11.531648462975104, 14.02900794190443, 15.126970362404837, 19.099685773373505, 16.938951487110348, 19.542794609551635])General plots
We also provide a splom or scatter-plot matrix plot for data frames with numeric columns (i.e. a matrix of all pairwise plots). These plots can be used to visualize the joint distribution of, say, the parameter estimates from a simulation.
MixedModelsMakie.splom! — Function
splom!(f::Indexable, df::DataFrame)Create a scatter-plot matrix in f from the columns of df.
Non-numeric columns are ignored.
using CairoMakie
CairoMakie.activate!(; type="svg")
using DataFrames
using LinearAlgebra
using MixedModelsMakie
data = rmul!(randn(100, 3), LowerTriangular([+1 +0 +0;
+1 +1 +0;
-1 -1 +1]))
df = DataFrame(data, [:x, :y, :z])
splom!(Figure(; size=(800, 800)), df)Meanwhile, splomaxes! provides a lower-level backend for splom!
MixedModelsMakie.splomaxes! — Function
splomaxes!(f::Indexable, labels::AbstractVector{<:AbstractString},
panel!::Function, args...;
extraticks::Bool=false, kwargs...)Populate f with a set of (k*(k-1))/2 axes in a lower triangle for all pairs of labels, where k is the length of labels. The panel! function should have the signature panel!(ax::Axis, i::Integer, j::Integer, args...; kwargs...) and should draw the [i,j] panel in ax.
using Statistics
mat = Array(df)
function pfunc(ax, i, j)
# note that this references mat from the outer scope!
# [j, i] because:
# - i is the row in the figure, which corresponds to the y var in each panel
# - j is the col in the figure, which corresponds to the x var in each panel
v = view(mat, :, [j, i])
scatter!(ax, v; color=(:blue, 0.2))
cc = cor(eachcol(v)...)
cc = round(cc; digits=2)
text!(ax, "r=$(cc)")
return ax
end
splomaxes!(Figure(; size=(800, 800)),
names(df), pfunc)- upsetLex, A., Gehlenborg, N., Strobelt, H., Vuillemot, R., & Pfister, H. (2014). UpSet: Visualization of Intersecting Sets. IEEE Transactions on Visualization and Computer Graphics, 20(12), 1983–1992. https://doi.org/10.1109/TVCG.2014.2346248