rs_characterize(): the follow-up latent variance is now
freely estimated in the constrained (no response shift) and item-level
models, as in Phase 1 (longitudinal_grmtree()). Previously
it was fixed at 1, so a change in trait dispersion could be mistaken for
response shift. In the fully unconstrained model the follow-up mean and
variance are not identified and are now fixed at 0 and 1; the omnibus
likelihood ratio test therefore has n_constraints - 2
degrees of freedom (38 for eight five-category items), computed from the
numbers of estimated parameters. sigma2_T2 in the output
now reports the estimated variance.longitudinal_grmtree() and
rs_characterize(): when a response category was observed at
one occasion but not the other (e.g., nobody in a node chose the lowest
category at follow-up), mirt recoded the two columns
differently and the cross-occasion equality constraints tied together
thresholds of different categories, which inflated the response shift
tests. Such a category is now merged with its neighbouring category at
both occasions before fitting, so each item pair always has the same
categories.partykit (e.g., respMOS_Listen is now
MOS_Listen) in node models, extractor output and
plots.coef() returns a
node-by-parameter matrix and print() reports the correct
number of parameters per node.logLik() method for (longitudinal) GRM trees, which
also makes AIC() and BIC() available."holm",
"BH", …), the exploratory tree now keeps all other
mob_control() settings passed through
grmtree.control(...) (e.g., ordinal,
maxdepth); previously only the minimum node size and
mtry were kept.mtry argument of grmtree() and
longitudinal_grmtree() is now honoured (it was previously
ignored in favour of control$mtry).latentpar_longitudinal_grmtree() returns clean row
names.rs_threshold now describes how
flagged items are labelled (from the estimated parameter changes).rs_characterize() gains purify (default
FALSE) and max_iter. With
purify = TRUE, item-level tests are purified iteratively:
items flagged in one pass are freed in the next, so each item is tested
against non-flagged anchor items only. This guards against false flags
caused by response shift in other items.
rs_characterize() gains type_method.
The new default, "wald", decides the type of a flagged item
with two Wald tests in a model where the flagged items are free and the
other items are anchors: a slope test (reprioritization) and a location
test of the thresholds (recalibration), each with the other set of
parameters free. Items flagged by the joint test but significant on
neither component are labelled "Undetermined". No magnitude
cut-off is used; rs_threshold now applies only to
type_method = "threshold", which keeps the 0.3.0 rule. New
columns: W_slope, p_slope,
W_location, df_location,
p_location.
plot_rs_tree() and plot_rs_heatmap()
show "Undetermined" items in dark grey instead of hiding
them as "None".
purify = TRUE and the
corrected example output (omnibus df = 38; 43 parameters per node).grmforest() gains tree_fun and
tree_args arguments so one forest engine serves both trees.
The default (tree_fun = grmtree) grows a cross-sectional
GRM forest exactly as before;
tree_fun = longitudinal_grmtree with
tree_args = list(n_items = ...) grows a longitudinal GRM
forest for response-shift detection. varimp() detects the
tree type and scores out-of-bag respondents with the appropriate
marginal likelihood, the two-occasion correlated-factor likelihood for
longitudinal forests, so the same varimp() call ranks
covariates for either forest.longitudinal_grmtree() now freely estimates the
follow-up latent variance (previously fixed to 1), so the two-factor
model captures change in trait dispersion across occasions as well as in
the mean. The variance remains a structural parameter held out of the
split test.grmforest() now runs reliably in parallel
(n_cores > 1) on all platforms, including PSOCK/Windows
workers, and results are identical regardless of the number of cores.
Out-of-bag membership is stored as row indices, and forests can be grown
in chunks and combined with c().grmforest() coerces character partitioning variables to
factors, avoiding tree failures on such covariates.varimp() now scores out-of-bag respondents with the
marginal (latent- integrated) log-likelihood using the correct GRM
parameterization, and averages paired within-tree permutation
differences.grmfit() no longer computes standard errors during
fitting, which removes a chol() failure on hard resamples
and speeds up tree/forest construction; results are unchanged.longitudinal_grmtree() to
the ITEM parameters (measurement invariance) and not include the
structural parameters.rs_characterize().
Item-level tests now run only in nodes with omnibus response shift
detected after the across-node adjustment (RS_detected),
instead of the unadjusted p-value, so no item rows are produced for
nodes that are not significant family-wise. The shift type is now read
from the estimated item-parameter changes (occasion-general
discrimination and thresholds) rather than a magnitude cut-point: a
significant item is always classified as recalibration,
reprioritization, or both, the “Significant (small effect)” label is
removed, and the effect-size magnitude is reported separately in a new
RS_magnitude column.plot_rs_tree() and plot_rs_heatmap(). Both now
display p-values in fixed notation (e.g. “p < 0.001”) rather than
scientific notation, label the latent parameters as “Mean shift (T2)”
and “Cor(T1, T2)”, spell out the response-shift types in full, and
restrict the RS-type legend to Recalibration, Reprioritization, Both,
and None (the effect-size annotation is no longer shown as a shift
type).varimp() scores out-of-bag respondents with the
marginal (latent-integrated) log-likelihood and averages paired
within-tree permutation differences; out-of-bag membership is stored as
row indices and forests can be combined with c().longitudinal_grmtree() for response shift (RS)
detection in patient-reported outcome measures (PROMs) measured at two
time points. The method embeds a constrained two-factor longitudinal
graded response model within model-based recursive partitioning to
identify patient subgroups whose longitudinal measurement model
differs.rs_characterize(), with a print()
method, for Phase 2 response shift characterization. Within each
terminal node it performs an omnibus likelihood ratio test (constrained
vs unconstrained model) and, where significant, item-level tests that
classify each item as recalibration, reprioritization, or both. Supports
hierarchical p-value correction both across nodes
(global_p_adjust) and within nodes
(p_adjust).prepare_longitudinal_data() to construct the
wide-format response matrix required by
longitudinal_grmtree() from separate baseline and follow-up
item columns.threshpar_longitudinal_grmtree(),
discrpar_longitudinal_grmtree(),
itempar_longitudinal_grmtree(),
fscores_longitudinal_grmtree(), and
latentpar_longitudinal_grmtree().plot() method for
longitudinal_grmtree objects (threshold region plots
showing the unique items), and two response shift visualizations,
plot_rs_tree() and plot_rs_heatmap().generate_node_scores_dataset() now supports both
cross-sectional (grmtree) and longitudinal
(longitudinal_grmtree) trees, and merges node assignments
and factor scores back onto the original data frame.grmtree_long_data dataset
(longitudinal MOS-SS emotional domain, two time points) for examples,
tests, and the new vignette.grmtree.control() (Holm, Benjamini-Hochberg,
Benjamini-Yekutieli, Hochberg, and Hommel). The previous implementation
reduced each node to its minimum p-value before applying the adjustment,
which collapsed the within-node multiplicity across covariates. The
internal .adjust_and_prune_tree() now collects all
covariate-by-node p-values, applies the adjustment globally, and then
prunes non-significant nodes. This properly accounts for both
within-node (multiple covariates) and across-node (multiple splits)
multiplicity.This is the first official release of the grmtree
package, providing methods for fitting and analyzing graded response
model (GRM) trees and forests.
grmtree() for fitting tree-based graded
response models.grmforest() for building forests of GRM
trees.print() and plot()
methods for GRM tree/forest objects.