Nothing
This release implements the measurement protocol recommended by the simulation study "Estimand or estimator? Comparing vowel overlap measures against a known ground truth" (Berry, under review, Sec. VII.A), so that its three steps and their conditions can be run and reported without hand-assembling them.
rank_contrasts(data, features, category_col, group_col) computes, per
speaker, Jensen-Shannon distance (sqrt_jsd) and shared probability mass
from one shared kernel density estimate and the Pillai trace on the same
tokens (step 1); ranks the speakers on the percentile-rank scale
(average rank - 1/2) / k, ties averaged, after setting measurements at the
sqrt(JSD) ceiling (default 0.99) apart (step 2); and flags speakers whose
Pillai and sqrt(JSD) percentile ranks differ by margin (default 0.25 of
the ordering) or more (step 3).rank_licensed, flag_licensed, rank_basis; ranking licensed from 50
tokens per category at d = 2, 200 at d = 3-4, 500 at d = 5-8; the flag
readable from 100 at d = 2 and 200 at d = 3-4, undefined when d >= 2n,
withheld from d = 5), a warning below eight speakers, and the bandwidth
check (sqrt(JSD) recomputed at half and twice the diagonal Scott
bandwidth; bw_shift, sign_change, set_aside).phontrast_ranking tibble with a print() method that
summarizes the protocol, the estimator, the floors, the flagged and
set-aside speakers, and the ceiling. The cleaned tokens travel with it in
attr(x, "protocol").plot_rank_agreement() (also plot() / ggplot2::autoplot() on the
ranking) draws Pillai percentile rank against sqrt(JSD) percentile rank
with the identity line and the +/- margin inspection band; flagged speakers
are coloured and labelled, set-aside speakers crossed, unreadable flags
hollow, and ceiling speakers listed in the caption.inspect_contrast(ranking, group) redraws one speaker with
plot_contrast()'s distribution-aware layers at half, the selected, and
twice the diagonal Scott bandwidth, annotating each panel with sqrt(JSD)
and shared mass at that bandwidth plus the speaker's Pillai, and restating
the reported ranks and the bandwidth-check outcome in the subtitle.percentile_rank() (the protocol's rank scale, with an exclude
mask), protocol_floors(d) (the licensing floors by dimensionality), and
recommended_estimator(d) (the kernel estimator settings the study used at
each dimensionality: plug-in / ks at d <= 4, diagonal Scott / fast_diag
with 200 evaluation tokens from d = 5, no leave-one-out from d = 14).vowel_cohort, with a planted
Pillai / sqrt(JSD) disagreement, a ceiling speaker, and two under-sampled
speakers; and a vignette, "Ranking speakers by Jensen-Shannon distance and
checking Pillai agreement", that walks through the protocol on it.bw_scale argument on the kernel path (jsd_kde_nd(),
percent_overlap_kde(), estimate_jsd(), estimate_overlap(),
phontrast(), plot_contrast()): a positive multiplier on the selected
bandwidth on the standard-deviation scale (h * bw_scale; bandwidth
matrices H * bw_scale^2). 0.5 and 2 give the halved and doubled
bandwidths of the smoothing-sensitivity check. plot_contrast() draws its
regions, overlap shading, and annotations at the scaled bandwidth and
records it in the caption. Ignored under density = "mvnorm".phontrast() now reads Jensen-Shannon divergence and distance, proportional
overlap, total variation, and the matched-kernel Bhattacharyya affinity /
distance and Hellinger distance off a single density estimate per
comparison, instead of separate kernel passes for JSD and overlap. Values of
the existing columns are unchanged; bootstraps do half the kernel work.phontrast(metrics = ...): "tv" (total variation,
total_variation = 1 - percent_overlap); "bhattacharyya_kde"
(bhatt_kde_dist, bhatt_kde_affinity, hellinger), the same quantity as
the closed-form Gaussian "bhattacharyya" columns under a matched kernel
estimator, with the same partial leave-one-out self-density as JSD, so
measure and estimator can be told apart; and "euclidean"
(euclidean_dist), the distance between the category means after dividing
each feature by the pooled two-category standard deviation (bounded by
2 * sqrt(d) for equal category sizes). The default metric set and the
column order of earlier releases are unchanged; long output and bootstraps
cover the new columns.citation("phontrast") now lists the study behind the protocol as a second
entry (manuscript under review, with the OSF replication package), after the
software citation.chol() of the within-class error SSCP threw an
error, which is BLAS-dependent on (near-)singular input: reference LAPACK
errors where Intel MKL can return a tiny positive pivot, letting degenerate
designs slip past the guard and fail later inside summary.manova() with a
less specific message. The guard now decides rank by R's tolerance-based QR
of the residual matrix -- the same criterion summary.manova() applies --
so degenerate designs raise the documented "nonsingular" error on every
BLAS build. Estimates on well-posed designs are unchanged.DESCRIPTION to 2.4.0. The original
release tags contained the 2.4.0 changes but retained the 2.3.1 package
metadata.CITATION.cff
so GitHub and future Zenodo deposits use the package's declared authorship.proportion_standardized = TRUE mode to
pillai_overlap(). It appends the plug-in and unbiased squared Mahalanobis
separation estimates and Becker's proportion-standardized Pillai score while
leaving the default two-field return and raw pillai value unchanged.pillai_eq is NA, pillai_eq_fallback is TRUE, and the
multiplicatively corrected first term appears only as d2_fallback beside
its closed-form upward-bias term.install.packages("phontrast").proportion_standardized = TRUE mode to
pillai_overlap(). It appends the plug-in and unbiased squared Mahalanobis
separation estimates and Becker's proportion-standardized Pillai score while
leaving the default two-field return and raw pillai value unchanged.pillai_eq is NA, pillai_eq_fallback is TRUE, and the
multiplicatively corrected first term appears only as d2_fallback beside
its closed-form upward-bias term.extract_mfcc() example with a fast, executable
example that creates and removes a short WAV file in the R session's
temporary directory. The optional tuneR dependency is guarded with
requireNamespace() as recommended for packages in Suggests.phontrast() and hier_boot_jsd_model() bootstrap
examples after confirming that they run well under five seconds. Reduced the
illustrative estimate_jsd() bootstrap count so that example is also under
five seconds and can be unwrapped. No examples now use \dontrun{} or
\donttest{}..GlobalEnv. Seeded KDE subsampling and
multivariate-normal Monte Carlo now use a private deterministic generator,
retaining reproducibility without reading, writing, or replacing the user's
.Random.seed. Unseeded stochastic calls retain their previous behavior.plot_contrast(). Draws the same density
model the distributional metrics are computed from: under
density = "kde" it shows highest-density regions of each category's kernel
density estimate (same bandwidth selection and ks evaluation as the
metrics); under density = "mvnorm" it shows coverage ellipses of the
fitted Gaussians used by the parametric backend. The pointwise minimum of
the two densities -- the mass the proportional-overlap metric integrates --
is shaded directly (a ribbon in 1-D, a soft raster in 2-D, normalized across
panels so fainter panels genuinely overlap less).phontrast() under the
plotted density model (optionally with bootstrap intervals via n_boot);
the caption records the estimator configuration (backend, bandwidth,
mc_n, seed, n); and the full annotation table is attached to the plot as
attr(p, "contrast_metrics").phontrast() output now carries class
"phontrast_contrast", so plot() and ggplot2::autoplot() draw the
metric comparison directly via plot_overlap_metrics().theme_phontrast(),
scale_colour_phontrast() / scale_color_phontrast() /
scale_fill_phontrast(), and phontrast_palette() (a colorblind-safe
Okabe-Ito palette ordered so the leading pair maximizes contrast for
two-category plots). All plotting functions -- including the existing
plot_overlap_metrics(), plot_category_space(), and
plot_category_pca(), whose arguments and behavior are otherwise
unchanged -- now share this theme and palette.ggplot2 stays in Suggests.density argument to decouple the density estimator from the
metric. The distributional metrics (Jensen-Shannon divergence and
proportional overlap) previously always used kernel density estimation. They
now accept density = "kde" (the default, unchanged) or density = "mvnorm",
which fits one multivariate normal per category and estimates the metric
between the two Gaussians. This lets the estimator behind JSD and overlap be
matched to the same multivariate-normal assumptions the Pillai, Bhattacharyya,
and Mahalanobis metrics already make, and makes the metric x estimator
interaction a controlled choice rather than hard-wired to KDE."mvnorm" backend estimates it by
fresh-sample Monte-Carlo: it draws mc_n points (default 10000) from
each fitted Gaussian and averages the log density ratio. The estimand is the
JSD / overlap between the fitted Gaussians; eval_seed makes the draw
reproducible without disturbing the caller's random-number stream. The
Gaussian fit has no self-kernel, so the KDE-specific leave-one-out correction
does not apply.density (and mc_n) are threaded through phontrast(),
compare_overlap_metrics(), estimate_jsd(), estimate_overlap(),
jsd_summary(), global_boot_jsd(), jsd_kde_nd(), and
percent_overlap_kde(). In phontrast() the argument affects only the
Jensen-Shannon and overlap columns; the Pillai, Bhattacharyya, and
Mahalanobis columns are parametric by construction and are unchanged.density = "kde" and all
existing results are unchanged.method = "mc" (the default KDE JSD estimator) flooring small but
real divergences to exactly 0. The full leave-one-out correction could
collapse a category's self-density at isolated points, driving the raw
plug-in mean negative, which the final clamp then floored to 0 -- while
method = "legacy" still reported a nonzero contrast. The estimator now uses
a partial leave-one-out correction that removes a sample-size-scaled
fraction n / (n + 20) of each point's own kernel: half at 20 tokens per
category (the min_tokens default), approaching the full correction as the
category grows. The corrected density stays strictly positive, so
near-merged categories yield small positive estimates instead of exact 0.method = "legacy", and still passes the package's
grid-calibration test at n = 200 within the original tolerance.method = "mc" results relative to 2.0.x, most visibly for
small divergences and small samples (estimates that were floored at 0 become
small positive values; others typically shift upward slightly).
method = "legacy" is unchanged, and loo = FALSE is unchanged.ROADMAP.md link in the README with a plain reference,
since ROADMAP.md is excluded from the built package; this resolves the
"invalid file URI" flagged by the CRAN incoming checks.OSF/ reproducibility-data directory and the top-level ROADMAP.md are now
listed in .Rbuildignore, resolving the non-portable-paths and
non-standard-top-level-files NOTEs from R CMD check. Added "mel" to
inst/WORDLIST.estimate_jsd() example to a fast point estimate and moved the
bootstrap demonstrations into \donttest{}, keeping every example under
CRAN's execution-time limit.Description,
normalized non-ASCII characters in the R sources, added inst/WORDLIST and
cran-comments.md, and removed the AI assistant from Authors@R (the AI-use
acknowledgment remains in the README). No user-facing code changes.phonJSD to phontrast and reoriented
around comparing multiple category contrast and separation metrics rather
than Jensen-Shannon divergence alone. Update your code from library(phonJSD)
to library(phontrast). Function names are unchanged except as noted below,
and no metric estimates change relative to 1.2.0 -- this release is a rename
and API reframe, not a numerical change.phontrast()phontrast(), the package's headline function: compute and compare
any subset of the contrast metrics -- Jensen-Shannon divergence and distance,
Pillai-Bartlett trace, Bhattacharyya distance and affinity, Mahalanobis
distance, and proportional overlap -- for a two-category contrast in one call,
globally or by group, wide or tidy long, with optional bootstrap intervals.metrics argument selects which metrics to compute (default: all),
e.g. phontrast(data, features, "vowel", metrics = c("jsd", "pillai")).compare_overlap_metrics() is deprecated in favor of phontrast(). It
still works (it calls phontrast() with output = "wide") and emits a
deprecation message; it will be removed in a future release.ROADMAP.md.method = "mc"). Each category's KDE is evaluated at
that category's own observations and the true log density ratio against the
mixture is averaged (with a leave-one-out bias correction, loo = TRUE). This
estimates the continuous Jensen-Shannon divergence in any dimension, replacing
the previous self-normalized sample-point index, which was a bounded relative
separation measure rather than the JSD integral and depended on eval_on.method = "legacy" to jsd_kde_nd(),
percent_overlap_kde(), estimate_jsd(), estimate_overlap(),
jsd_summary(), global_boot_jsd(), or compare_overlap_metrics(). The
eval_on control applies to method = "legacy" only.fast_diag engine now evaluates true (normalized) densities, matching
ks::kde() to machine precision for diagonal bandwidths.min_tokens = 20 and
n_boot = 1000 everywhere (previously estimate_jsd() defaulted to
min_tokens = 5, and jsd_summary()/boot_jsd()/hier_boot_jsd_model() to
min_tokens = 30 / n_boot = 300).estimate_*/global_* wrappers to return tibbles uniformly.pillai_p_value (wide) and p_value (long) columns returned
by compare_overlap_metrics().bw = "scott.diag",
evaluation-point subsampling via eval_n/eval_seed, and
engine = "fast_diag" for chunked diagonal-Gaussian KDE evaluation.engine = "fast_diagonal" as an alias for engine = "fast_diag".compare_overlap_metrics() so separation and overlap estimates use aligned
density-estimation settings.speaker_jsd(), boot_jsd(), jsd_summary(), and global_boot_jsd().group_col = c("Sex", "Style"); grouped outputs retain a single labeled
group column.extract_mfcc() to tuneR::melfcc() and removed the stale
seewave::mfcc() reference.jsd() output to the mathematical range [0, 1], so floating-point
rounding on near-identical categories can no longer yield NaN
Jensen-Shannon distances (est_distance = TRUE) or abort
hier_boot_jsd_model() through prepare_jsd_beta().sample() edge case in hier_boot_jsd_model() that could
misdraw a single numeric group identifier.NA when its metric cannot be
computed and emit a single summarizing warning, instead of the previous
inconsistent behavior where speaker_pillai()/speaker_bhatt() silently
dropped failed groups (so estimate_pillai() and estimate_jsd() could
return different rows for the same data) while other metrics returned a silent
NA.data must be a data frame,
category_col a single column name, features a non-empty character vector,
and features may not overlap with category_col/group_col.LazyData field (no data/ directory), the unused lme4
suggestion, and dead dplyr imports (filter, n, ungroup); added URL
and BugReports; standardized the author name to "Grant M. Berry"; and scoped
the CI workflow to the existing main branch.plot_overlap_metrics() and plot_category_space()
helpers for visualizing metric comparisons and one- or two-dimensional
phonological category spaces.plot_category_pca() for two-dimensional PCA diagnostics of arbitrary
multidimensional feature spaces.compare_overlap_metrics() to compute Pillai trace, Bhattacharyya
distance and affinity, Jensen-Shannon divergence and distance, Mahalanobis
distance, and percent overlap in one global or grouped comparison table.conf_level.I/i contrasts work without manually
calling droplevels().conf_level, ci_lower, and
ci_upper columns while retaining jsd_low and jsd_high as aliases.compare_overlap_metrics() with progress
messages and metric-specific confidence intervals.labphon_2026..Rbuildignore entries so local analysis, load-test, and manuscript
artifacts are excluded from package builds.replicate() control-flow edge cases with explicit
vapply() iteration.Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.