estimation_plot(), and group_comparison_plot(differences = TRUE) which
calls it, failed in a fresh R session with "Can't find method for generic
/(e1, e2)", or with "non-numeric argument to binary operator" under
ggplot2 3.5. The two panels were stacked with patchwork's / operator, which
exists only once patchwork has been loaded, and nothing in depictr loaded it.
The panels are now composed with patchwork::wrap_plots(), and depictr
imports patchwork, so library(depictr) loads it.
estimation_plot() drew the differences out of line with the groups they
belong to. The lower panel had one column fewer than the upper and spread its
columns across the full width, so with reference North, the East difference
sat beneath North, and with two groups, the single difference sat midway
between them. Every group now keeps its column in both panels, and the
reference's column in the lower panel is left empty.
group_comparison_plot(differences = TRUE) is fixed with it. The function is
also described for what it draws, a Cumming estimation plot with the
differences stacked beneath the data. The claim that zero lines up with the
reference group's mean has been removed, since the stacked panels have
independent axes. The caption now writes a round number of resamples in full,
as 100,000 where it printed 1e+05.
coefficient_plot() attached an unnamed labels vector to the wrong
coefficients. The labels were assigned after the terms had been reversed or
sorted for display, so in lm(yield ~ rainfall + fertiliser + soil_ph) soil pH
was labelled 'Rainfall'. An unnamed vector is now bound to the terms in model
order before any reordering. compare_models() and
frequentist_bayesian_plot() counted one row per term and source, or one per
posterior draw, and so refused a vector with one label per term. Both now
accept one, read with the terms in the order they first appear. Named labels
are unchanged and remain the safer form. The documentation of order now says
which end the largest estimate lands at.
model_fit_table() and the model_report() subtitle misreported R² for glm
models. McFadden's ratio was applied to every family, so a Gaussian glm
could report a negative value and a quasi-likelihood family reported NA.
The null model was refitted with update(). That used more rows than the
model when a predictor had missing values, dropped an offset written in the
formula, put an intercept in the null of a model fitted without one and gave
NA for a model fitted inside a function. Every glm, including
quasi-likelihood and negative binomial fits, now reports the deviance R²,
1 - D/D0, computed from the deviances the fit already holds. It equals the
ordinary R² for a Gaussian model with the identity link and McFadden's R²
for binary logistic regression, so a binary logistic value changes only
where the old refit went wrong. Values for Poisson, grouped binomial, Gamma
and other models change by definition. The model_report() subtitle now
calls it the deviance R², where it said pseudo-R².
For mixed models fitted by REML with lme4::lmer(), model_fit_table() now
takes AIC, BIC and the log-likelihood from a maximum-likelihood refit and
says so in a message. REML criteria cannot be compared between models with
different fixed effects, which is the comparison the model-estimates
vignette makes. If a refit fails, a warning says so and that model's
criteria are NA. The R2 column stays NA for mixed models, as the help
page now documents.
A glm fitted with na.action = na.exclude broke the classification and
diagnostic plots. Its fitted values are padded with NA to the length of the
data while its response is not, and the plots paired the two. The ROC,
precision-recall, threshold, gain and lift plots and the confusion matrix
failed, and calibration_plot() silently dropped bins and drew the rest with
other rows' outcomes. In the Q-Q panel of residual_diagnostics_plot(), the
quantile residuals set each response against another row's fitted value, and
for a Gaussian model they were all NA, so the panel was empty. All of them
now use the model's own unpadded components. residual_diagnostics_plot() and
influence_plot() also leave the excluded rows out of their frames, for an
lm as for a glm. They no longer warn about removing those rows, and the
2p/n leverage line of influence_plot() no longer counts them.
The quantile residuals in the Q-Q panel of residual_diagnostics_plot()
ignored prior weights in Gaussian and Gamma models. Every observation was given
the same scale, so a correctly specified weighted model showed spurious
departures from the line. The weights now scale each observation's variance,
as they do in the fit.
A brms posterior passed as a posterior draws object was read as a plain
table. frequentist_bayesian_plot() silently pooled b_Intercept with
Intercept, the intercept brms reports for centred predictors, into one
slab. It also drew the sampler's log density and every random effect as
parameters. posterior_plot() stopped with an error about duplicated factor
levels. Draws objects now keep their population-level (b_) parameters,
named without the prefix as for a brms fit, with a message naming what was
dropped. A labels key for one of them is written without the prefix too.
In both functions, two parameters whose draws would share a label are now an
error, and lp__ and lprior are never drawn as parameters, whatever form
the draws take. For a stanreg fit, the group-level b[...] and
Sigma[...] draws are no longer read as fixed effects either.
When bayesian holds draws, frequentist_bayesian_plot() now draws only the
terms both inputs share, and a message names any it dropped. A sigma in the
posterior, or a frequentist term with no posterior, no longer takes a row of
its own. Inputs that share no terms are an error.
A brms::fixef() matrix was read as draws, so it came out as four
'parameters' named after its columns. frequentist_bayesian_plot() now takes
the summary path for it, and posterior_plot(), which needs draws, refuses
it. The summary path also no longer warns about label keys you never
supplied.
conf_level in frequentist_bayesian_plot() set the level of the
frequentist intervals only. With draws, the half-eyes stayed at 66% and 95%,
so conf_level = 0.8 drew 80% confidence intervals beside 95% credible ones,
and nothing on the figure named either level. The level now sets the outer
posterior interval too. The interval drawn when ggdist is not installed
follows it, and so does the span that decides which faceted panels draw the
reference line. The 66% inner interval is kept while conf_level
exceeds 0.66, so that it stays the narrower of the two. A caption now names
the level of each source's intervals on either path, and the new caption
argument replaces it or, with NA, omits it. A table of estimates keeps the
intervals it holds, and the caption says that they are as supplied.
The two paths of frequentist_bayesian_plot() no longer swap the sources'
colours. With draws, the Bayesian half-eyes were brand blue and the
frequentist overlay orange, the reverse of the forest plot drawn from
summaries and of the Python package. The frequentist estimates are now brand
blue and the Bayesian ones orange on both paths.
survival_plot() read the status coding separately in each group. Under the
survival::Surv() 1/2 convention, a group whose subjects were all censored
(all 1) fits the 0/1 convention as well, so it was read as a group in which
every subject had the event, and its curve fell to zero. Codings that mixed
the two conventions across groups were accepted silently. The coding is now
read once, over all observations.
survival_plot() silently misread a factor status. The factor was coerced
to its level codes and read under the 1/2 convention, so whichever level sorted
first was taken as censored, whatever it meant. A character status became
NA throughout, with only a coercion warning, and drew a flat curve at one.
Both are now refused with an error that says to recode the indicator first.
survival_plot() ignored its group and status arguments when time was a
data frame. It read both from conventionally named columns only (group,
strata or arm, and status or event), so
survival_plot(df, group = "treatment") drew a single pooled curve with no
legend, log-rank test or risk-table rows. A column name or a vector with one
entry per row is now honoured for both, and takes precedence over the
conventionally named column. A name that matches no column is an error.
The number-at-risk table of survival_plot() could miscount at its default
breaks. pretty() returns multiples of a binary floating-point step, so with
follow-up up to 1.2 the break labelled 0.6 was 0.6000000000000001, and a
subject followed up to exactly 0.6 was left out of the count there. A last
break equal to the longest follow-up was lost the same way. The breaks are
now rounded to their step before the counts are taken.
The median-survival guides of survival_plot() mislabelled some medians.
The survival estimate was compared with 0.5 without a tolerance, so a step
that the arithmetic left a rounding error above 0.5 was taken to stay above
it. With times c(2, 3, 4, 8, 8, 10, 10, 11) and status
c(1, 1, 1, 1, 0, 1, 0, 1), the guide read 'median 10' where
survival::quantile.survfit() gives 9. A curve that ended at 0.5 drew no
guide. For a survfit object, the midpoint of a curve that sat at 0.5 was
taken with the next row, which can be a censoring time at which the curve
does not move. The medians now follow the rule of quantile.survfit(),
including its tolerance and its midpoint for a curve that ends at 0.5,
whatever the input and whether or not survival is installed.
save_plot() wrote files that fail the checks journals run on submitted
figures. An EPS left out every translucent element, including all the points
and the confidence band of the first scatter_trend() example, with only a
terse device warning to show for it, and it named its fonts without embedding
them. A PDF did not embed its fonts either. A TIFF was uncompressed: 8.1 MiB
at the default 7 by 4.5 inches, and more than the 10 MB PLOS allows at the
largest figure size it accepts. A transparent background, such as
theme_void() leaves, could also give the TIFF the alpha channel PLOS
refuses. PDF and EPS files now go through the cairo devices, which embed the
fonts. An EPS rasterises its translucent layers, and whatever they overlap,
at 600 dpi or more, with a message when it does. A TIFF is now LZW-compressed
and drawn on white unless bg is given. On a Mac without ragg, it is drawn
by the cairo TIFF device, because the default quartz one ignores compression.
A device or device argument passed to save_plot() still wins. Without
cairo, the old devices are used, with a message or a warning saying what the
file lacks. R for macOS from CRAN loads cairo only where XQuartz is
installed, so on a Mac without it a PDF or EPS is written this way, and a
TIFF is left uncompressed unless ragg is installed.
effects_plot(), interaction_plot() and model_report() failed on any
model with a transformed term (log(), poly(), ns(), scale(),
factor(), I()) or an offset. Where a transformed term sat beside its raw
variable, they drew a flat effect line or coincident interaction lines
without complaint. The prediction grid is now built from the raw variables,
so predict() re-applies every transformation, and offsets are held at their
sample mean on the link scale, as the y-axis label now says. A model-frame
name such as "log(rainfall)" stands for the variable it transforms, and the
x-axis shows that variable.
The raw values behind a transformed term come from the data named in the
model's call. A model fitted to workspace vectors without data = is
therefore refused, where it could silently plot its fitted values against the
grid, and so is a data frame that has changed since the fit or a formula that
reads one with $. A term computed from statistics of its rows, such as
I(x - mean(x)), is refused as well, since predict() would compute it
afresh over the grid. Beside its raw variable, such a term used to shift the
line without complaint.
model_report() judged every glm by normal-theory panels. A correctly
specified logistic model, simulated with 400 observations, put about a third
of its standardised deviance residuals outside the Q-Q band, the picture of
a broken model. A glm now
gets the binned-residual and quantile-residual panels that
residual_diagnostics_plot() already used, and model_report() gains the
same glm_panels and seed arguments. glm_panels = FALSE restores the
old panels. The help page of qq_plot() now says that the standardised
deviance residuals of a non-Gaussian glm are not expected to be normal.
ts_forecast(), and the forecast overlay of timeseries_plot(), drew
prediction intervals far too narrow. In rolling-origin backtests on eight
classic series, the nominal 95% band covered about 60% of one-step-ahead
outcomes. The documentation credited its square-root widening to seasonal
naive forecasting, whose variance does not grow that way. Forecasts now come
from a random walk with drift on the STL-seasonally-adjusted series, with the
last seasonal cycle added back, which reproduces
forecast::stlf(method = "rwdrift", s.window = "periodic"). A new lambda
argument in both functions applies a Box-Cox (or log) transformation for a
seasonal swing that grows with the level, and a caption on the overlay names
the interval and the method. A series of exactly two cycles now gets the
package's own message, and decompose_plot() gains s_window. The
vignette's claim that STL lets the seasonal component evolve held only for a
window this release exposes for the first time.
timeseries_plot() drew the forecast of a numeric x in the wrong place.
The forecaster counted time in cycles from 1, while the history sits at the
observation index or at the supplied time. The overlay therefore jumped
back towards the start of the axis, and on a Date axis it landed in the
year 4. The forecast now continues the history's own axis, one typical step
at a time. An unsorted time is also put in order before forecasting, as it
already was for the moving average.
pca_plot() now fixes the aspect ratio, so the angles drawn between the
loading arrows are their angles in loading space whatever the size of the
device. The panel's height and width follow the ranges of its axes, so a
figure of another shape gains blank space around the panel. The arrows and
their labels are drawn in a neutral dark grey (grey20) in place of the
accent, which is also the palette's fourth colour and so tied the variables
to the fourth group. A misspelt group column, a column name passed with a
prcomp object and a group vector of the wrong length are now errors. The
first put one point in a level named after the misspelling and the rest in
NA. The second put every point in one level named after the column, and
the third was padded with NA or recycled in silence. group also takes an
unquoted column name, as elsewhere in the package. A prcomp object fitted
with retx = FALSE has no scores to draw, and it is now refused with a
message that says so.
silhouette_plot() recoded the cluster labels it was given to 1, 2, 3 and
so on, so a clustering labelled "low", "mid" and "high", or coded 2, 5
and 9, came back with other names in the band labels and in the attached
"silhouette" table. The labels passed in are now kept, and the table's
neighbor column is a factor of them.
random_effects_plot() failed for any model with more than one
random-effect term per grouping factor fitted without a correlation,
(1 + x || g) for instance. The same error, "incorrect number of
dimensions", stopped split terms such as (1 | g) + (0 + x | g) and their
glmer() equivalents. It now reads lme4's conditional standard deviations
for every model shape. For a correlated model, they equal the square roots of
the diagonal it used before, so those intervals are unchanged. lme4 is now
suggested at version 1.1-19 or later. Conditional variances for several
terms per factor arrived in lme4 1.1-18, and 1.1-19 revised the data-frame
method that reads them.
power_curve_plot() reads power tables with several curves, one per effect
or design, and draws one line each, told apart by colour and line type. It
joined them into a single zig-zag before. An effect, param or term
column with more than one level separates the curves, with a message naming
the columns used, and rows that share a sample size with no such column are
now an error. It recognises n_subject and <power>_lo/<power>_hi columns
such as pilotr's, so the output of pilotr's power_curve_mixed() plots as it
is, with its band. It gains n, power, group, lower, upper and
y_lab, appended after the existing arguments so that positional calls keep
working. The y-axis title is "Power" for the usual power columns and the
column's name otherwise, so a decision probability such as pilotr's
p_meaningful is not labelled as power.
The grouped plots each treated a grouping variable in their own way. A
numeric code, such as a block numbered 1 and 2, failed in scatter_trend()
with "Continuous value supplied to a discrete scale", and
explore_distribution() and ecdf_plot() drew it as one ungrouped curve. A
level with no rows, as subset() leaves in a factor, took a colour in the
correlation panel of explore_pairs() but not in its scatter panels, so the
levels after it changed colour from one panel to the next.
group_comparison_plot() and raincloud_plot() warned that such a level had
too few observations and kept an empty place for it on the axis, which
group_comparison_plot() moved to the end. Missing groups made
raincloud_plot() fail, were drawn as a grey group of their own by
scatter_trend(), outlier_plot() and explore_pairs(), and vanished
without a word elsewhere. scatter_trend(), explore_distribution(),
ecdf_plot(), raincloud_plot(), group_comparison_plot(),
explore_categorical(), explore_pairs(), ridgeline_plot() and
outlier_plot() now share one rule. The group becomes a factor, so a numeric
code is a group, but a numeric column with more than 20 distinct values is
refused as continuous, as explore_categorical() already refused such an
x. Rows with a missing group are dropped with a message, as
survival_plot() drops them, and levels left without rows take no colour,
legend key or place on an axis. Colours are matched to the levels by name, so
a level keeps its colour in every layer and panel, and a group too sparse for
a ridge no longer shifts the colours of the ridges after it. An unnamed
palette is assigned to the levels in use in their order, and a named one
still pins a colour to each level. explore_categorical() now orders its
groups by level, where it sorted them alphabetically, so they take the same
colours as in the Python package. explore_pairs() leaves a numeric group
out of its default columns and stops with a clear error when cols names
it. With the default palette, the warning that more than eight groups lose
the colour-vision guarantee now comes once, when the plot is made, where it
came each time the plot was drawn.
palette_preview(), confusion_matrix_plot() and correlation_heatmap()
drew some labels below the 4.5:1 contrast that WCAG asks of text. The first
two switched from white to dark grey text at a relative luminance of 0.4,
although the two colours tie near 0.2. Four of the eight default swatches
therefore had white labels at 2.85 to 3.87:1, and counts on mid-blue cells of
a confusion matrix fell as low as 2.3:1. correlation_heatmap() drew white
labels whenever |r| exceeded 0.6, which left the r = -0.70 of
wellbeing_survey at 3.6:1, and its n/a sat in grey40 on a grey85 cell at
4.1:1. Each label is now black or white, whichever has the higher contrast
against its tile as drawn, which is at least 4.58:1 on any colour. The tile
colours are read as the fill scale draws them, interpolated in the Lab colour
space, where the confusion matrix used an RGB approximation.
vif_plot() computed the VIFs of a weighted lm or a glm from the
correlations of the design matrix, which leave out the weights of the fit.
The generalised VIF of Fox and Monette (1992) compares the confidence region
of a term's coefficients with the one they would have without collinearity,
so it depends on those weights. The VIFs now come from the covariance of the
coefficients, as car::vif() computes them, and agree with it. For
glm(adverse_event ~ biomarker + age + arm, family = binomial) fitted to
clinical_trial, the VIF of age moves from 1.0073 to 1.0046. An unweighted
lm with an intercept gives the values it gave before. A model fitted
without an intercept now gives VIFs computed from columns that are not
centred, with a warning, as car::vif() does.
A multivariate ts, such as cbind(mdeaths, fdeaths), or a numeric matrix
was read as one series, its columns laid end to end. timeseries_plot() drew
a single line. For a ts it zigzagged between the columns at every time, and
for a matrix it ran on from one column into the next. acf_plot() returned
the autocorrelation of the joined columns. seasonal_plot() and the
classical decompose_plot() recycled the seasons and times of one column
over all of them, so the columns were drawn over one another, and
ts_forecast() forecast the joined columns at missing times.
timeseries_plot() now draws one line per column, named after the columns.
acf_plot(), decompose_plot(), seasonal_plot() and ts_forecast()
refuse more than one column with a message that gives the count. They read a
ts with a single column as that series. ts() makes one from a one-column
data frame, and the STL decomposition stopped on it with "only univariate
series are allowed".
decompose_plot() and ts_forecast() now trim missing values at either end
of a series, as the Python package does, so a forecast, the overlay of
timeseries_plot() included, starts after the last observation.
ts_forecast() and the STL decomposition stopped with the error "missing
values in object" from stl(). A missing value inside the series is refused
with a message that names x and asks for the value to be dropped or
imputed, where stl() and decompose() stopped with errors of their own.
acf_plot() still computes the correlations across such a gap from the pairs
of observations present, with a warning, while the Python package refuses the
series. The help pages of both packages now say so.
timeseries_plot() keeps the level order of a factor group, which sets the
legend and the colours, and leaves out levels that no row uses. It ordered
the groups by first appearance.
timeseries_plot() drew a moving average over an even window, such as
rolling = 12 for monthly data, half an observation early. Equal weights
over twelve observations are centred between two of them, and the mean of
observations 1 to 12 was drawn at observation 6, where its centre is 6.5. An
even window now gives the 2×12 moving average (Hyndman & Athanasopoulos,
2021, Section 3.3), a 12-point average followed by a 2-point one, which is
centred on each observation and is the trend that decompose() estimates.
The caption of a grouped overlay names it, and the window needs a series one
observation longer than itself. The Python package drew the same average
half an observation late, and the two packages now draw the same values. An
odd window is unchanged.
survival_plot() truncated a non-integer status code, so c(0, 0.5, 1) was
read as two censored observations and an event. Non-integer codes are now
refused with the existing coding message.
survival_plot() kept an arm whose every status was missing when group
was a factor. The arm stayed in the legend with no curve and a row of zeros
in the risk table, and logrank = TRUE failed with an error that did not say
why. The arm is now dropped with the missing statuses, as it already was for
a character group.
vif_plot() refuses a model with aliased coefficients, for which no VIF
exists, and names the terms they belong to. It failed with an unrelated
base-R error, "missing value where TRUE/FALSE needed".
seasonal_plot() refused every frequency that is not a whole number, such
as the 365.25 / 7 of weekly data, with a message about season_labels that
no labels could satisfy, while decompose_plot() drew the same series. Such
a frequency is now rounded down to whole seasons, counted from the first
observation as stl() counts them, and a message says so.
check_figure() measured all text against the plot background, so text on a
coloured facet strip or legend box was judged against a background it is not
drawn on: white strip text on a dark strip failed and unreadable grey strip text
passed. Each text is now measured against its own background, with a
translucent fill taken as the colour it shows over what lies beneath it. Large
text (18 pt, or 14 pt bold, as printed) is held to the 3:1 that WCAG asks of it,
where it used to be held to 4.5:1. The text_contrast row reports the text that
falls furthest short of its own threshold and gives that threshold, and its
detail names the text's printed size and weight. A translucent plot or panel
background is also measured as the colour it shows. Its alpha used to be
dropped, so geometry_contrast moves for such a figure. The
relative-luminance code now cites the current WCAG 2.2 definition and its
0.04045 breakpoint, which changes no ratio.check_figure() measured translucent marks and text as if they were opaque,
so a figure could pass contrast, colour-vision and greyscale checks that its
drawn colours fail. The 'mended' example in the Get started vignette was one:
its points, drawn at alpha 0.7, passed the geometry check at 3.87:1, and the
vermillion a reader sees reaches 2.56:1. Colours are now composited over
their background with the alpha each geom applies, outlines drawn opaque are
credited, and the colours of each aesthetic are compared among themselves. A
translucent colour or text is named in the detail with its alpha and the
colour it is drawn as, and the separability rows say whether their closest
pair are colours or fill colours. Verdicts on translucent figures can change
in either direction. The vignette's example now draws its points opaque.check_figure() now treats a colour or fill mapped in several layers, one
value per layer, as the encoding it is. Such figures were reported as
encoding nothing by colour. With red and green drawn one group per layer,
every colour row read "not applicable", whereas the same figure drawn in one
layer fails the deuteranopia check at 0.37. frequentist_bayesian_plot()
with draws, whose half-eyes and frequentist overlay are separate layers, was
never audited for colour. It now is, and the orange of its half-eyes fails
geometry_contrast at 2.25:1, as that colour does on white in any figure.
Shape and line type split across layers count towards redundant_encoding in
the same way. A colour set as a constant, as in geom_point(colour = "red"),
is still left out, as the help page now says.check_figure() gains an annotation_text_size row for the text drawn
inside the panel: annotations, the labels of layers such as geom_text() and
geom_label(), and the cell values and labels of depictr's own plots. The
text-size check never saw that text, so a 5.7 pt annotation passed a 6 pt
floor while the report gave 8.80 pt as the smallest text. The new row holds
it to the same floor at its printed size. It is appended last, so the report
now has ten rows. The contrast of that text is not measured, and nor is text
a layer builds only when it is drawn, such as ggrepel's labels. When all of a
figure's text is inside the panel, the two rows for the text around it no
longer say that the figure draws no text.check_figure() accepts a multi-panel composite and audits each panel on
its own, in the order patchwork tags them, with a leading panel column. It
used to refuse one, which left the package's own composites without an
audit. Among them are the figures of arrange_plots(),
residual_diagnostics_plot(), model_report(), estimation_plot() and
survival_plot(risk_table = TRUE). Each panel's legend is measured with the
panel. A spacer or a guide area takes no panel number, and a wrapped element,
such as a grob or a table, is left out with a warning. Composite-level titles
and tags are not measured.check_figure(max_text_pt =) checks a ceiling as well as a floor, and
save_plot(check = TRUE) audits the figure at the width it is saved at, so
the text sizes are the printed ones. The ceiling adds a last row,
text_size_max, for the largest text at its printed size, around the panel
and inside it, and that row passes at or below its threshold. Nature, for
one, allowed no text above 7 pt but the panel labels. Without max_text_pt,
the report keeps its ten rows. save_plot(check = TRUE) gives the saved
width, converted to centimetres from inches, millimetres or pixels, as both
width_cm and render_width_cm. It warns with each check the figure fails
and attaches the report to the file name it returns, as attr(, "check").The declared ggplot2 floor rises from 3.5.0 to 3.5.2. The current patchwork
cannot load against ggplot2 3.5.0 or 3.5.1, so every function that composes
panels failed on the floor the package declared: arrange_plots(),
model_report(), residual_diagnostics_plot(), estimation_plot(),
explore_pairs(), decompose_plot() and survival_plot(risk_table = TRUE).
On ggplot2 3.5.x, pca_plot() and silhouette_plot() also warned about an
unknown linewidth and drew borders round their labels. They now name the
border argument each ggplot2 version understands and draw no border on either.
The reference lines of coefficient_plot(), frequentist_bayesian_plot()
and posterior_plot() no longer warn about an unknown inherit.aes on
ggplot2 3.5.x. The floors job in continuous integration now also tests the
floor against the newest patchwork, and runs every example with warnings
treated as errors.
ragg is listed in Suggests. save_plot() asks whether it is installed when
it chooses a device for a TIFF, and R CMD check warns about a package
looked up that way unless the package declares it.
citation("depictr"), the website and CITATION.cff give the DOI that CRAN
assigned to the package, https://doi.org/10.32614/CRAN.package.depictr, in
place of the Zenodo concept DOI. It names the R package across all its
versions, and it is the DOI that the CRAN page and the citation R generates
for a CRAN package already gave, so the package no longer circulates under
two. Zenodo still archives every GitHub release, and CITATION.cff lists both
identifiers. Its release date is now the day CRAN published 0.3.0, 2026-09-01,
where it gave a date that matched neither CRAN nor the GitHub release. The
CRAN DOI and the Zenodo DOI are shown as full links on the website and in the
README, and the sidebar of the home page shows the CRAN DOI once: the DOI
badge in the README no longer appears there a second time.
posterior_plot() and frequentist_bayesian_plot() formatted a label for
every posterior draw, which made a ten-parameter figure take seconds and a
full brms draws object minutes. Labels are formatted once per parameter.
format_terms() now strips the b_ and bs_ prefixes from a whole vector
with one regular expression. It split each term on its colons and took about
15 seconds for 40,000 labels, a job that now takes a fraction of a second. An
unnamed labels vector, one label per parameter in the order of draws,
now works in posterior_plot(). It failed with a message that counted the
draws as terms. The two bootstrap tests that took most of the test time are
skipped on CRAN and still run in continuous integration.
The gap statistic's default search in k_diagnostic() and
cluster_plot(suggest_k = "gap") could not return k = 1, so it reported
clusters in pure noise (k = 2 for 20 of 20 seeded Gaussian data sets).
k_range now defaults to NULL, which searches 1:8 for the gap and elbow
criteria and 2:8 for the silhouette, as before. A suggestion made with the
old default can change, so pass k_range = 2:8 to reproduce it. The help
pages now say that the elbow never chooses the first or last candidate.
standardise = TRUE in coefficient_plot(), and so the default
model_report(), multiplied every model-matrix column by its standard
deviation. That made a factor contrast depend on how the groups happened to
be balanced: one true effect of 1 came out as 0.52, 0.43 or 0.23 with 50%,
20% or 5% of observations in the group. It also scaled an interaction by the
spread of a product column, 265 for rainfall:treatment in the crop-yield
model. Continuous inputs are now scaled by their standard deviation, and
factor, logical and two-valued inputs such as 0/1 are left as they are
(Gelman, 2008). An
interaction is scaled through its continuous inputs, so
rainfall:treatment becomes the change in the rainfall slope per SD of
rainfall. The new sd_multiple = 2 gives Gelman's two-SD convention.
poly(), ns() and other matrix-valued terms are left unscaled with a
message. An interaction of continuous inputs draws a warning unless they are
all centred, because each main effect is a slope at zero on the other input.
The axis label adds "(contrasts unscaled)" when a contrast is drawn, and the
model_report() panel is titled "Coefficients (per SD of continuous
predictors)". The help page and the model-estimates vignette no longer claim
that the standardised magnitudes are comparable.
binned_residual_plot() draws its band from the model, as twice the
standard error of a bin's mean residual that the fitted model implies. The
old band, twice the standard deviation of the bin's residuals over the square
root of its count, collapsed wherever a bin held no events. On the package's
own example, a correctly specified model, it flagged five of seventeen bins.
When the adverse events were redrawn 200 times from the fitted model, so that
each refit was correct by construction, the old band flagged 31% of the bins
and the new one 3%. The binned-residual panel that
residual_diagnostics_plot() and model_report() draw for a glm changes
with it. band = "empirical" reproduces the earlier figure, whose band is
the one arm::binnedplot() draws. Observations with zero prior weight, which
take no part in the fit, are now left out of the bins.
survival_plot() draws log-log confidence limits by default. They stay
between 0 and 1 and cover better in small samples than the untransformed
Greenwood limits it drew before (Borgan & Liestøl, 1990). The new
conf_type argument restores those with "plain" and gives the default of
survival::survfit() with "log". The limits of a survfit object were
copied from the object, at the level and on the scale it was made with, so
conf_level was ignored and a 90% band showed the object's 95% limits. They
are now recomputed from the object's standard errors at the requested
conf_level and conf_type, so they equal those that survfit() gives with
the same conf.int and conf.type. An object made with
conf.type = "none" keeps its standard errors, so it now gets limits as
well, which conf_level = NA leaves out. An object without standard errors,
or one whose lower limits follow conf.lower = "peto" or "modified", keeps
its own limits, with a warning when they differ from those requested. Where
a curve falls to zero, the limits keep the values of the step before, so a
band drawn from follow-up times no longer stops one event early. A
conf_level outside (0, 1), such as 95 meant as a percentage, is now an
error.
The cycle lines of seasonal_plot(style = "season") start from a colour
that clears 3:1 against white. They took the sequential ramp of
depictr_palette(), which starts at #e6eff5, 1.16:1 against white, so the
earliest cycles were close to invisible. With twelve cycles, four of the
lines fell below the 3:1 that WCAG 2.2 asks of a graphical object. The ramp
now runs from #4a8fc0, at 3.51:1, through the brand blue to #08315a, and
palette still replaces it. The sequential palette keeps its pale end for
fills. Past about six cycles, neighbouring lines take colours too close to
tell apart, as the help page now says, and style = "subseries" keeps each
cycle in its own place on the axis.
Colour separability in palette_safety() and check_figure() is now
measured with CIEDE2000 (Sharma, Wu & Dalal, 2005). The 1976 Euclidean
distance it replaces overstates differences between saturated colours:
ggplot2's default five-colour palette was certified safe under
deuteranopia, though two of its colours become almost identical, 2.74 apart
in CIEDE2000. The threshold stays at 5, and the Okabe-Ito palette's closest
pair now measures 6.43, where it measured 7.4. depictr_palette(9) already
warned that interpolated colours lose the guarantee, and its nine colours
now fail the check too. metric = "cie76" restores the old measure for this
release, and the greyscale check is the lightness gap under either.
simulate_cvd() at partial severity now uses Machado et al.'s published
matrices, interpolated between the tabulated severities. Interpolating
towards normal vision missed them on the default palette by up to 11 units
of the 1976 Delta-E, or 6.2 in CIEDE2000. Full-severity results, and so
palette_safety(), palette_preview() and check_figure(), are unchanged.
The authors tabulate a matrix for each tenth of severity, and a severity
between two tenths now takes the linear interpolation of the two, as the
colorspace package does, whose output it matches. The old transform drew the
full-severity matrix towards the identity. Between severities 0.3 and 0.7, it
moved the palette's colours too little under protan and deutan and too far
under tritan, so under a red-green deficiency they looked further apart than
the model predicts.
Confidence intervals for a glm whose dispersion is estimated (the
Gaussian, Gamma, inverse Gaussian and quasi families) now use the t
distribution on the residual degrees of freedom, matching the t tests of
summary.glm() and the lm path. They used z and were about 10% too narrow
at 12 residual degrees of freedom, so a Gaussian glm disagreed with lm()
on the same model. At 100 degrees of freedom, the two differ by about 1%.
The choice is read from the model's own summary, so binomial, Poisson and
MASS::glm.nb() fits keep z, as do mixed models. tidy_estimates(),
coefficient_plot(), compare_models(), frequentist_bayesian_plot() and
model_report() change with it, and so do the bands of effects_plot()
and interaction_plot(). In the Python twin the intervals follow the
fitted result's use_t, which gives the same t intervals.
The coefficient of a two-level factor is now labelled with its level,
treatment: enhanced, as the coefficients of factors with more levels
already were, so the row says which contrast it shows. It was labelled
treatment, which hid the direction of the contrast. Interaction rows name
their levels the same way, rainfall × treatment: enhanced where they showed
rainfall × treatmentenhanced. The labels change in coefficient_plot(),
compare_models(), frequentist_bayesian_plot() and
optimizer_fixef_plot(). compare_models() now takes them from every
fitted model it is given, so a factor term that only a later model holds is
labelled too. A named labels vector still sets any label, and
labels = c(treatmentenhanced = "treatment") restores the old one. The
Python twin now labels its terms by the same rules.
depictr_palette(order = "contrast"), also accepted by
scale_colour_depictr() and scale_fill_depictr(), reorders the built-in
palette so that up to three groups pass every colour check of
check_figure(), contrast against white included, and up to four keep 3:1.
It leads with the brand blue, the vermillion and the reddish purple, then the
bluish green. It warns past four colours. The ramps refuse it, and a palette
set with depictr_options(palette = ) is returned as it was set, with a
warning. depictr_options(palette = depictr_palette(order = "contrast"))
makes the order the default for every later plot. The default order is
unchanged, and the Python twin gives the same order.
simulate_figure() shows a finished figure, legend and colourbar included,
as a reader with a colour-vision deficiency sees it. It builds the grob
table of a ggplot or a patchwork and passes every colour in it through
simulate_cvd(). That covers the line and fill colours of each grob and the
cells of each raster, and each colour keeps its alpha. It returns a gtable,
which grid::grid.draw() draws and save_plot() saves. Recolouring the data
before the plot is built would have left the legend keys and colourbars in
their original colours. The Python twin's simulate_figure() uses the same
matrices on the pixels of the rendered figure.
exponentiate = TRUE in tidy_estimates(), coefficient_plot() and
compare_models() gives odds, rate or risk ratios, and the two plots draw
them on a log axis with the reference line at 1. The axis is labelled "Odds
ratio" for a logit link, "Rate ratio" for a log link in a Poisson,
quasi-Poisson or negative binomial model, "Risk ratio" for a binomial model
with a log link and "exp(Estimate)" otherwise. tidy_estimates()
exponentiates the estimates and interval limits and leaves std.error on
the link scale, as broom::tidy() does. Its glm method used to swallow
the argument and return log-odds. coefficient_plot() exponentiates after
standardise, so a standardised slope becomes a ratio per SD of its
predictor. A data frame of estimates is read on the link scale.
reference_line now defaults to 1 when exponentiating and to 0 otherwise.
The Python twin gains the same arguments.
check_figure(), an accessibility and honesty audit of the figure you are
about to submit. Until now the package could vouch for its palette and say
nothing about a finished plot, which is the thing a reader sees. Give it
anything a depictr function returns, including a plot extended afterwards with
+, and it introspects the build and returns a tidy table. The rows cover the
separability of the encoding colours under each dichromacy and in greyscale, the
smallest text size against a stated physical output width, the WCAG contrast of
the text and of the geometry against their backgrounds, and whether any
distinction is carried by colour alone. Every row carries the value it measured
beside the threshold it was measured against, so a verdict can be argued with.simulate_cvd() and palette_safety()
were internal here while the Python twin exported both, so the advertised parity
did not hold. They now match in name, arguments, return shape and refusal
wording. simulate_cvd() gains a severity argument and returns lower-case hex,
as the Python twin does. palette_safety() returns the full report, naming the
worst condition, the closest pair and the verdict. The old return was a bare
vector of distances.#e69f00) and sky blue (#56b4e9) differ by 0.79 in CIE
lightness, so a black-and-white printer renders them as the same grey. The
Okabe-Ito guarantee is about hue confusion and was never a claim about greyscale.
The threshold stays where it is, and vignette("depictr") now states the
limitation where the claim is made, so the package's own defaults are held to the
same standard as anybody else's.quantile_residuals() produced nonsense for a cbind(successes, failures)
binomial model. The two-column response matrix was flattened to a vector and
the raw success counts were then multiplied by the trial totals a second time,
so the residuals of a well-specified model centred far from zero and the Q-Q
diagnostic looked catastrophically misspecified. The matrix response now
supplies its counts and trial totals directly, and the residuals are standard
normal again where they should be.ridgeline_plot() stacked its overlaps upside down. The row sort meant to
draw the top ridge first is a no-op, because ggplot2 draws ribbon groups in
factor-level order, so each upper ridge painted over the one below it, the
opposite of the conventional ridgeline overlap. The draw order is now carried
by the group aesthetic, with the colour assignment unchanged.random_effects_plot(sort = TRUE) froze every facet in the first facet's
order. With more than one term the level factor was shared across panels, so
only the first panel came out sorted and the rest zig-zagged. Each facet now
orders its own levels, with the plain level names kept on the axis.title in power_curve_plot() is no longer run through
format_terms(), which turned a colon into a multiplication sign and blanked
underscores. Only a title recovered from the power-curve object, which is a raw
term name, is tidied.survival_plot() drew a phantom arm for a group that does not exist. An NA in
group became a level of its own, matching no observation, so the plot gained an
all-censored curve for a group nobody was in, and under logrank = TRUE it failed
outright. Missing groups are now dropped with a message saying how many.survival_plot() silently discarded non-finite follow-up times. Dropping a case
from a Kaplan-Meier fit changes the denominator, and so every step of the curve and
every cell of the number-at-risk table, while the figure carries no trace of it. It
now refuses them, with the same message as the Python twin, since whether to drop
or impute is the analyst's decision to make. This replaces a silent drop, so it is
a behaviour change for anyone who relied on the old handling.depictr_palette() interpolated past its accessibility guarantee without saying
so. Beyond the eight Okabe-Ito base colours the palette is a ramp, and the
colour-vision-deficiency guarantee that is this package's reason for existing stops
holding, so the interpolated palette fails the package's own safety check. It now
warns at the point of interpolation, and only for the built-in palette, since a
user-supplied one carries no such claim. The documentation is qualified to match.summary_table() counted missing-group records in Overall and in no group
column, so the per-group sizes silently fell short of the headline N. They now get a
Missing column of their own.compare_models(facet = TRUE) now honours depictr_options(reference = ) for its
per-panel reference line. seasonal_plot() no longer labels a frequency-7 series
Mon..Sun, an alignment a plain ts cannot know. And a mistyped labels key now
warns, so the raw parameter name no longer sits on the plot.survival_plot() now says when it drops observations with a missing status,
in the wording of the missing-group message. The drop was previously silent.
A status that is missing for every observation is an error. This brings the
third kind of incomplete survival input into line with the other two, a missing
group being announced and a non-finite time refused.silhouette_plot() checks that clusters has one entry per row of data
before dropping incomplete rows. A vector sized to the complete rows used to
slip past the late check, because subsetting it with the logical index padded
it with NA, and then died in the distance computation. It is now refused with
the same message cluster_plot() uses.tidy_estimates() no longer fails on a rank-deficient lm with a raw
"differing number of rows" error. confint() keeps aliased terms as NA rows
while coef(summary()) drops them, so the intervals are now cut to the estimated
terms, with a message naming the aliased terms that were left out.cluster_plot() and k_diagnostic() drop zero-variance columns with a message
when scale = TRUE, as correlation_heatmap() already did, so a raw k-means
error no longer escapes. k_diagnostic() now names the values of k_range it
cannot evaluate instead of dropping them from the search without a word, and
explore_pairs() labels an undefined correlation n/a where it once printed
r = NA beside a raw stats::cor() warning.model_report() at all, because
patchwork::free(type =, side =) arrived in 1.3.0.gain_plot() documents that the perfect-model reference line is drawn for a
single model only, which is what the code has always done, and a test now pins
it. The line bends at the prevalence of the outcome, and overlaid models need
not share a prevalence.model_fit_table() documents that a single model is enough, which is what the
code always accepted, and raincloud_plot() no longer claims to be built from
base graphics primitives: like ridgeline_plot(), it is base R and ggplot2
alone.?depictr again lists every exported function (scale_fill_depictr() and the
scale_color_depictr() alias were missing), the README no longer describes the Python
package as a feature-parity twin (its own README says otherwise), CONTRIBUTING no
longer claims the maintenance workflows close their own issues, and the
standardise = TRUE axis label now names the x-only convention the figure uses.vif_plot() restricts its scale to the severity levels present, which removes
an empty key entry from the rendered figure.depictr_palette() returning the palette's
hex colours directly, ready for scale_fill_manual() or a base-graphics
col = argument, and shows depictr_options() setting defaults for every
later plot and returning the previous values, so the earlier look can be put
back afterwards.vignette("diagnostics-and-uncertainty") saves an arranged panel with
save_plot(), which writes at a print-ready 300 dpi by default and creates
any missing directories.vignette("multivariate-and-survival") adds k_diagnostic(method = "gap"),
which compares within-cluster dispersion against a null reference and so,
unlike the other two criteria, can support k = 1.vignette("time-series") adds classical decomposition, which holds the
seasonal component fixed across the series where STL lets it evolve from year
to year.optimizer_fixef_plot() and power_curve_plot() reference pages
describe what each plot shows. Both pages previously described the prototype
gists the plots grew from.roc_curve_plot() rejects a ci that resolves to fewer than one bootstrap
resample, with a clear error. Until then it drew an all-NA band and an
[NA, NA] AUC annotation, saying nothing.wellbeing_survey, region now shifts stress and income, which flow through
to life satisfaction. The region-grouped plots (faceted densities, ridgelines,
the region dendrogram) therefore compare four distinct distributions where
earlier they compared four samples of one. The bundled datasets are regenerated
by data-raw/generate_datasets.R as before.inst/CITATION and CITATION.cff) carries the Zenodo
concept DOI, and the citation title uses sentence case.?depictr) lists every exported function and all five
bundled datasets. Previously several functions and two datasets were missing.vif_plot() example fits deliberately collinear predictors, so the plot
shows inflated VIFs sitting above the threshold line. The earlier example
produced near-identical bars around 1, with the line off the axis.depictr_options() describes what brand and accent actually drive, and
depictr_palette() notes that qualitative colours interpolated beyond the
base set are not guaranteed to stay distinguishable under colour-vision
deficiency.lift_plot() documents its own top-right inside-legend corner rather than
inheriting gain_plot()'s bottom-right wording, and DESCRIPTION notes
that composite panels return 'patchwork' objects.LICENSE.md carries the full MIT text so the site's
licence page renders in full, as in the sibling packages.posterior_plot(), residual_diagnostics_plot(), decompose_plot()) are
wrapped in \donttest{} so each example file stays within CRAN's
five-second budget. The first call of every example still runs.CITATION.cff and the test artefact Rplots.pdf are excluded from the
build tarball, and the test that produced Rplots.pdf draws to a null
device instead.legend_inside gates. The legend moves inside the panel
when a plot's gate is satisfied, and the theme is left alone when it is not.LICENSE file, and added community and contribution
files.First release. depictr is a unified, consistent toolkit of publication-ready
plots spanning the whole analysis workflow. It grew out of, and generalises,
three earlier plotting functions (frequentist_bayesian_plot,
plot.fixef.allFit and powercurvePlot).
explore_distribution(), explore_categorical(), explore_bivariate(),
explore_pairs(), correlation_heatmap(), missingness_map(),
outlier_plot(), raincloud_plot(), group_comparison_plot(),
scatter_trend() and summary_table().estimation_plot() for estimation statistics: group effect sizes
(mean differences, Cohen's d / Hedges' g) with bootstrap confidence
intervals, in the spirit of the 'new statistics'.ecdf_plot() (empirical cumulative distribution, optionally by group),
ridgeline_plot() (overlapping per-group densities) and dumbbell_plot()
(a connected two-group comparison across categories).explore_distribution() gains facet to draw one panel per group instead of
overlaying them (much clearer beyond a few groups), and
correlation_heatmap() gains reorder to cluster correlated variables
together.pca_plot() and scree_plot() (principal component analysis),
cluster_plot() (k-means on principal-component axes) and
dendrogram_plot() (hierarchical clustering), and survival_plot()
(Kaplan-Meier curves with a number-at-risk table, median survival and an
optional log-rank test, all computed in base R).silhouette_plot() and k_diagnostic() help choose and validate the number
of clusters (silhouette widths, plus elbow and average-silhouette
diagnostics).timeseries_plot() (one or more series with an optional moving average),
acf_plot() (autocorrelation / partial autocorrelation) and
decompose_plot() (trend / seasonal / remainder decomposition).seasonal_plot() (seasonal subseries) and ts_forecast() (a simple,
dependency-free forecast with prediction intervals).tidy_estimates() provides the shared tidy estimate table, with methods for
lm, glm, merMod and data frames and a fallback to broom::tidy().coefficient_plot(), compare_models(), frequentist_bayesian_plot(),
effects_plot(), interaction_plot(), random_effects_plot(),
optimizer_fixef_plot() and model_fit_table().frequentist_bayesian_plot() now draws the full Bayesian posterior for each
term as a half-eye density and overlays the matching frequentist point
estimate and confidence interval, so the two inferential frameworks can be
compared directly. It reads posterior draws from brmsfit, stanreg,
draws/matrix objects or a data frame.residual_diagnostics_plot(), influence_plot(), qq_plot(),
vif_plot(), roc_curve_plot(), pr_curve_plot(), gain_plot(),
lift_plot(), calibration_plot() and confusion_matrix_plot().binned_residual_plot() (binned residuals for logistic and other GLMs, with
approximate error bounds) and threshold_plot() (classification metrics
across decision thresholds, highlighting Youden's J and the maximum-F1
cut-off).posterior_plot() summarises posterior draws with a choice of styles
("halfeye", "interval", "gradient" or "dots") and can annotate a
region of practical equivalence (ROPE) and the probability of direction.power_curve_plot() for power-analysis curves (e.g. from simr).theme_depictr(), depictr_palette(), scale_colour_depictr() (and
scale_color_depictr(), scale_fill_depictr()), palette_preview(),
format_terms(), model_report() (a one-figure model overview),
arrange_plots() and save_plot().depictr_options() sets package-wide defaults once, covering the brand and
accent colours, qualitative palette, base font size and family, and the colour
used for missing values. Every plot and scale then honours them.coefficient_plot(), compare_models(), posterior_plot() and
frequentist_bayesian_plot() gain a facet/scales option that lays each
term out in its own free-scaled panel, so terms on very different scales (a
large intercept alongside small slopes) stay legible instead of being squished
onto the zero line. frequentist_bayesian_plot() uses this layout by default.silhouette_plot() cluster labels
no longer clip. raincloud_plot() uses one colour per group across all
layers. dendrogram_plot() hides leaf labels for large trees.
confusion_matrix_plot() picks each label's colour from the tile luminance.
gain_plot() and lift_plot() label their reference lines.
timeseries_plot() shows a single legend, and k_diagnostic() now returns
the diagnostic curve as a plot.coefficient_plot() gains standardise, scaling each coefficient by its
predictor's standard deviation so magnitudes are comparable. model_report()
uses it by default, removing the empty band in its coefficient panel.vif_plot() shows the ordinary VIF (not its square root) for single-df terms,
scales the axis to the data, and draws a single clearly-labelled threshold
line (reported in the caption when it is off-axis), leaving no wide empty band
or hard-to-read guides.seasonal_plot(style = "season") reverses its sequential legend so the
darkest, most-recent cycle sits at the top, matching the plotted order.coefficient_plot(), compare_models() and
frequentist_bayesian_plot(), where they are read from the model, so
conditionunrelated becomes condition and word_frequency becomes
word frequency. optimizer_fixef_plot()
and posterior_plot() gain a labels argument for the same. Any user-supplied
labels take precedence. pca_plot() likewise shows underscores in its
loading-arrow labels as spaces (soil_ph -> soil ph).silhouette_plot() (the bands are
labelled in place) and cluster_plot() when the centroids are labelled.survival_plot() has been tidied in several ways. The log-rank annotation
renders a proper chi-squared and an italic p, formatted APA style (no
leading zero, p < .001 below that threshold). The median guide is labelled
median <value>. The y-axis title margin is tighter, and the colour legend
and the number-at-risk table list the groups in the same order, following the
group factor's levels.legend_inside argument (off by default) draws the legend inside the panel,
over a semi-transparent background, in a corner the plot usually leaves empty,
which reclaims the right-hand margin. It is offered by roc_curve_plot(),
gain_plot(), lift_plot() (bottom-right / top-right of the curve),
ecdf_plot(), survival_plot(), explore_distribution(), dumbbell_plot()
and missingness_map(). For any other plot the same is one theme() call,
and vignette("exploring-data") shows how, alongside tidying legend titles.theme_depictr() now centres legend titles over their keys, which reads more
tidily than ggplot2's default left alignment, especially for an inside or a
top/bottom legend.estimation_plot() reserves more headroom above the lower panel so the
effect-size annotation (Hedges' g / Cohen's d) is never clipped.scree_plot() colour-matches and names its dual axes, 'Variance explained
(bars)' on the left and 'Cumulative (line)' on the right.model_report()'s n and R, and estimation_plot()'s g / d.crop_yield column is now
fertiliser, coefficient_plot()/model_report() take standardise, and
confusion_matrix_plot() takes normalise.lexical_decision (counterbalanced
priming experiment), wellbeing_survey (with realistic missingness),
crop_yield (a fertiliser-by-treatment field trial), clinical_trial
(right-censored survival with a rare adverse event) and monthly_sales
(two seasonal retail series).depictr_palette() provides
sequential and diverging variants. palette_preview() can show any one,
or all three, and can simulate deuteranopia, protanopia or tritanopia so a
palette's legibility can be checked directly.lme4, broom, simr, survival, brms,
posterior, ggdist, cluster, boot) are in Suggests and used only
when available, so the package installs and checks without them. Vignettes
draw on small precomputed model fits shipped in inst/extdata/, so they
knit without a Bayesian or mixed-model toolchain.seed (cluster_plot(), qq_plot() and
residual_diagnostics_plot()) restore the caller's random number generator
state afterward, so passing one for reproducibility has no side effect on
your own subsequent random draws.