% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/dif_tree.R
\name{RMdifTree}
\alias{RMdifTree}
\title{Tree-based DIF analysis with effect-size classification}
\usage{
RMdifTree(
  data,
  covariates,
  model = c("auto", "PCM", "RM"),
  effect_size = c("auto", "MH", "pgamma"),
  purification = c("none", "iterative"),
  p_adj = c("none", "fdr", "bonferroni"),
  thresholds = c(0.21, 0.31),
  alpha = 0.05,
  prune_negligible = FALSE,
  stability = FALSE,
  stability_B = 100L,
  stability_sampler = c("subsampling", "bootstrap"),
  min_n_per_level = 20L,
  on_rescale = c("message", "warning", "stop"),
  output = c("kable", "dataframe", "tree", "plot", "list"),
  ...
)
}
\arguments{
\item{data}{A data.frame or matrix of item responses (non-negative
integers, 0-based). One column per item. Items only -- pass
covariates separately via \code{covariates}.}

\item{covariates}{A data.frame with \code{nrow(data)} rows giving the
DIF covariates. Columns may be numeric (continuous), factor, ordered
factor, or logical. At least one column is required.}

\item{model}{One of \code{"auto"} (default), \code{"PCM"}, or
\code{"RM"}. \code{"auto"} picks RM when the data are dichotomous
(max response = 1) and PCM otherwise.}

\item{effect_size}{One of \code{"auto"} (default), \code{"MH"}
(Mantel-Haenszel), or \code{"pgamma"} (partial gamma). \code{"auto"}
uses MH for RM and partial gamma for PCM.}

\item{purification}{\code{"none"} (default) or \code{"iterative"}.
Iterative purification recomputes the effect size while excluding
items already classified as DIF from the matching score (Bjorner et
al., 1998).}

\item{p_adj}{Multiple-testing adjustment for the partial-gamma
classification: \code{"none"} (default), \code{"fdr"} (Benjamini &
Hochberg), or \code{"bonferroni"}. Ignored for MH (which uses the
ETS test directly).}

\item{thresholds}{Numeric length-2 vector with the partial-gamma
B/C boundaries (default \code{c(0.21, 0.31)}). Ignored for MH (the
ETS Delta-scale boundaries 1.0 / 1.5 are used).}

\item{alpha}{Significance level for the A/C tests. Default
\code{0.05}.}

\item{prune_negligible}{Logical. If \code{TRUE}, splits whose every
item is classified as A are pruned from the tree. Default
\code{FALSE}.}

\item{stability}{Logical. If \code{TRUE}, runs
\code{stablelearner::stabletree()} on the fitted tree to assess
variable-selection and cutpoint stability across resamples.
Default \code{FALSE}.}

\item{stability_B}{Integer. Number of resamples for stability
assessment. Default \code{100}.}

\item{stability_sampler}{One of \code{"subsampling"} (default) or
\code{"bootstrap"}. Subsampling is recommended for tree stability
because bootstrap resamples can drop levels of categorical
covariates, breaking the model fit on small subsamples.}

\item{min_n_per_level}{Integer. Minimum count required for any factor
level in \code{covariates}. Default \code{20}. Set to \code{0} to
disable.}

\item{on_rescale}{One of \code{"message"} (default), \code{"warning"},
or \code{"stop"}. Controls how the function reports the situation
in which one or more items have no responses in their lowest
category within a terminal node of the tree (psychotree silently
rescales those items per node, which leaves the per-node item
parameters on a different metric). \emph{The MH and partial-gamma
effect sizes reported here are computed on the raw responses and
are not affected.} The diagnostic concerns only the per-node item
parameters that are visible via \code{plot(tree)}.}

\item{output}{One of \code{"kable"} (default), \code{"dataframe"},
\code{"tree"}, \code{"plot"}, or \code{"list"}. \code{"kable"}
renders the
per-split per-item effect-size table as a \code{knitr::kable()};
\code{"dataframe"} returns the underlying tidy data.frame;
\code{"tree"} returns the augmented partykit tree object;
\code{"plot"} returns the partykit tree plot with item names on
the terminal-node x-axis (\code{tp_args = list(names = TRUE)});
\code{"list"} returns both views from a single fit, as
\code{list(table = <data.frame>, tree = <tree object>)}.
For full control over the plot, use \code{output = "tree"} and
call \code{plot()} on the result with your own \code{tp_args}.
When
\code{stability = TRUE}, the stability summary is attached as
\code{attr(result, "stability")} (a small data.frame) and
\code{attr(result, "stability_kable")} (pre-rendered kable).}

\item{...}{Additional arguments forwarded to
\code{psychotree::raschtree()} or \code{psychotree::pctree()}, e.g.
\code{minsize}, \code{alpha} for the parameter-instability test.
Note: this \code{alpha} (the tree-fitting argument) is independent
of the \code{alpha} for ETS classification above; pass it via
\code{...}.}
}
\value{
Depending on \code{output}:
\describe{
\item{\code{"kable"}}{A \code{knitr_kable} object with one row per
(split node x item), grouped by node via \code{pack_rows}-style
section headers. The caption summarises model, effect-size
measure, purification, and (if requested) stability.}
\item{\code{"dataframe"}}{A data.frame with one row per (split node
x item): columns \code{NodeID}, \code{Split} (human-readable
description of the split), \code{Variable}, \code{Direction},
\code{Item}, \code{EffectSize}, \code{SE}, \code{Class} (A/B/C),
\code{Flagged} (TRUE for B or C), \code{n_left}, \code{n_right}.}
\item{\code{"tree"}}{The fitted partykit tree object with class
\code{c("RMdifTree", ...)}, with effect-size results stored at
\code{tree$info$effectsize}.}
\item{\code{"plot"}}{A plotted partykit tree.}
\item{\code{"list"}}{A list with both views from a single fit:
\code{table} (the \code{"dataframe"} result, with its attributes)
and \code{tree} (the \code{"tree"} result).}
}

Stability results (when \code{stability = TRUE}) are attached to the
return value as \code{attr(result, "stability")} (data.frame) and
\code{attr(result, "stability_kable")} (pre-rendered kable).
}
\description{
Detects Differential Item Functioning (DIF) by recursively splitting
the sample on one or more covariates using \code{psychotree::raschtree()}
(dichotomous data) or \code{psychotree::pctree()} (polytomous data),
then computes per-split effect-size measures for every item -- the
Mantel-Haenszel odds-ratio (Holland & Thayer, 1986), on the Delta scale
developed at the Educational Testing Service (ETS; Zwick, 2012), for
dichotomous data, or the partial gamma coefficient for polytomous data -- and
classifies them into ETS A/B/C categories (Bjorner et al., 1998). Optionally,
an iterative purification step (Henninger et al., 2025) is applied, and tree
stability across resamples can be assessed via
\code{stablelearner::stabletree()} (Philipp et al., 2018).
}
\details{
Continuous covariates (e.g., age in years) and interactions among
multiple covariates are handled natively by the model-based
recursive-partitioning machinery of \code{partykit} / \code{psychotree}.

\strong{ETS Delta-scale Mantel-Haenszel.} The MH common odds ratio
\eqn{\hat{\alpha}_{MH}} is mapped to the ETS Delta scale via
\eqn{\Delta_{MH} = -2.35 \log \hat{\alpha}_{MH}}. The classification
rules (Holland & Thayer, 1988; Zwick, 2012) are:
\itemize{
\item Class \strong{A}: \eqn{|\Delta_{MH}| < 1} or test of
\eqn{\Delta_{MH} = 0} not rejected at \code{alpha}.
\item Class \strong{C}: \eqn{|\Delta_{MH}| \ge 1.5} and test of
\eqn{|\Delta_{MH}| \le 1} rejected at \code{alpha}.
\item Class \strong{B}: otherwise.
}
easyRasch2 uses the sign convention of \code{effecttree}: positive
\eqn{\Delta} indicates that the item is more difficult for the second
(reference) group.

\strong{Partial gamma.} \code{iarm::partgam_DIF()} provides the gamma
estimate and SE per item. ETS-style classification follows Bjorner et
al. (1998): A if \eqn{|\gamma| < 0.21} or the test of \eqn{\gamma = 0}
is not rejected at \code{alpha}; C if \eqn{|\gamma| > 0.31} and the
test of \eqn{|\gamma| \le 0.21} is rejected at \code{alpha}; B
otherwise.

\strong{Rule-of-thumb caveat.} The A/B/C boundaries (Delta 1.0 / 1.5;
gamma 0.21 / 0.31) are conventions carried over from large-scale
educational testing, not values calibrated to the sample and items at
hand -- in contrast to the simulation-based cutoffs used elsewhere in
this package -- so the classification is best read as a rough
magnitude guide rather than a calibrated test. For a sample-calibrated
partial-gamma DIF test, see \code{\link[=RMdifGamma]{RMdifGamma()}} with an
\code{\link[=RMdifGammaCutoff]{RMdifGammaCutoff()}} object (optionally with \code{p_value = TRUE}).

\strong{Continuous and interaction effects.} The recursive
partitioning step automatically handles continuous covariates (the
parameter-instability test searches for the optimal cutpoint) and
multivariate interactions (later splits are conditional on earlier
ones). To assess sensitivity of variable selection and cutpoints to
the particular sample, set \code{stability = TRUE}.

\strong{Stability assessment.} When \code{stability = TRUE}, the same
tree-fitting call is replayed on \code{stability_B} resamples of the
data. The returned \code{stabletree} object reports, per covariate,
the proportion of resamples in which it was selected at any split,
and (for continuous covariates) the empirical distribution of
cutpoints. Stability is independent of effect-size classification --
a stable split with a negligible effect is still negligible, and a
large effect on an unstable split should be interpreted with care.
Cost is roughly \code{stability_B} times the original fitting time.

\strong{Acknowledgement.} The effect-size machinery -- MH and partial
gamma per node, iterative purification, ETS A/B/C classification --
follows the implementations by Henninger & Radek in the GitHub
packages \emph{raschtreeMH} and \emph{effecttree}. The relevant code
has been adapted here under the MIT licence (see file header).
}
\examples{
\donttest{
if (requireNamespace("psychotree", quietly = TRUE) &&
    requireNamespace("partykit", quietly = TRUE) &&
    requireNamespace("difR", quietly = TRUE) &&
    requireNamespace("iarm", quietly = TRUE)) {
  data("DIFSimPC", package = "psychotree")

  items <- as.data.frame(as.matrix(DIFSimPC$resp))
  covs  <- DIFSimPC[, c("age", "gender", "motivation")]

  # Default: kable of per-split effect sizes
  RMdifTree(items, covariates = covs)

  # Tidy data.frame -- one row per (split node x item)
  df <- RMdifTree(items, covariates = covs, output = "dataframe")
  df[df$Flagged, ]

  # Tree object (for plotting via partykit::plot)
  tree <- RMdifTree(items, covariates = covs, output = "tree")
  plot(tree)

  # Stability assessment refits the tree on B resamples.
  # (use more resamples, e.g. 100+, in real analyses)
  if (requireNamespace("stablelearner", quietly = TRUE)) {
    kbl <- RMdifTree(items, covariates = covs,
                     purification = "iterative", p_adj = "fdr",
                     stability = TRUE, stability_B = 25)
    kbl                          # main effect-size kable
    attr(kbl, "stability_kable") # pre-rendered stability kable
    attr(kbl, "stability")       # raw stability data.frame
  }
}
}

}
\references{
Bjorner, J. B., Kreiner, S., Ware, J. E., Damsgaard, M. T., & Bech,
P. (1998). Differential item functioning in the Danish translation
of the SF-36. \emph{Journal of Clinical Epidemiology, 51}(11),
1189-1202. \doi{10.1016/S0895-4356(98)00111-5}

Henninger, M., Debelak, R., & Strobl, C. (2023). A new stopping
criterion for Rasch trees based on the Mantel-Haenszel effect size
measure for DIF. \emph{Educational and Psychological Measurement,
83}, 181-212. \doi{10.1177/00131644221077135}

Henninger, M., Radek, J., Debelak, R., & Strobl, C. (2025). Partial
credit trees meet the partial gamma coefficient for quantifying DIF
and DSF in polytomous items. \emph{Behaviormetrika, 52}, 221-257.
\doi{10.1007/s41237-024-00252-3}

Holland, P. W., & Thayer, D. T. (1986). Differential item performance
and the Mantel-Haenszel procedure.
\emph{ETS Research Report Series}(2).
\doi{10.1002/j.2330-8516.1986.tb00186.x}

Philipp, M., Rusch, T., Hornik, K., & Strobl, C. (2018). Measuring
the stability of results from supervised statistical learning.
\emph{Journal of Computational and Graphical Statistics, 27},
685-700. \doi{10.1080/10618600.2018.1473779}

Asamoah, N. A. B., Turner, R. C., Lo, W.-J., Crawford, B. L., & Jozkowski,
K. N. (2025). Impacts of DIF Item Balance and Effect Size Incorporation With
the Rasch Tree. \emph{Educational and Psychological Measurement}.
\doi{10.1177/00131644251370605}

Zwick, R. (2012). A review of ETS differential item functioning
assessment procedures: Flagging rules, minimum sample size
requirements, and criterion refinement. \emph{ETS Research Report
Series}(1). \doi{10.1002/j.2333-8504.2012.tb02290.x}
}
\seealso{
\code{\link{RMdifLR}}, \code{\link{RMdifGamma}}
}
