% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/brms_dif.R
\name{dif_statistic}
\alias{dif_statistic}
\title{Differential Item Functioning (DIF) Analysis for Bayesian IRT Models}
\usage{
dif_statistic(
  model,
  group_var,
  item_var = item,
  person_var = id,
  data = NULL,
  dif_type = c("uniform", "non-uniform"),
  prob = 0.95,
  rope = 0.5,
  refit = TRUE,
  ...
)
}
\arguments{
\item{model}{A fitted \code{\link[brms]{brmsfit}} object from
the baseline (no-DIF) model.}

\item{group_var}{An unquoted variable name identifying the
grouping variable for DIF testing (e.g., \code{gender}).
Must be a factor or character variable with exactly 2 levels
in the current implementation.}

\item{item_var}{An unquoted variable name identifying the item
grouping variable. Default is \code{item}.}

\item{person_var}{An unquoted variable name identifying the
person grouping variable. Default is \code{id}.}

\item{data}{An optional data frame containing all variables
needed for the DIF model, including the group variable. If
\code{NULL} (the default), the function attempts to use
\code{model$data}. Since the baseline model formula typically
does not include the group variable, brms will have dropped
it from the stored model data. In that case, you must supply
the original data frame here.}

\item{dif_type}{Character. For polytomous ordinal models only.
\code{"uniform"} (the default) tests for a uniform location
shift per item via a \code{group:item} fixed-effect
interaction. \code{"non-uniform"} fits group-specific
thresholds per item and computes per-threshold DIF effects
as the difference between groups. Ignored for dichotomous
models.}

\item{prob}{Numeric in \eqn{(0, 1)}. Width of the credible
intervals. Default is 0.95.}

\item{rope}{Numeric. Half-width of the Region of Practical
Equivalence (ROPE) around zero for DIF effects, on the logit
scale. Default is 0.5, corresponding to a practically
negligible DIF effect. Set to 0 to skip ROPE analysis.}

\item{refit}{Logical. If \code{TRUE} (the default), the DIF
model is fitted automatically by updating the baseline model
via \code{\link[stats]{update}}, which reuses the compiled
Stan code for faster sampling. If \code{FALSE}, only the DIF
model formula is returned (useful for manual fitting with
custom settings).}

\item{...}{Additional arguments passed to
\code{\link[brms]{update.brmsfit}} when refitting the DIF
model (e.g., \code{cores}, \code{control}).}
}
\value{
A list with the following elements:
\describe{
\item{summary}{A \code{\link[tibble]{tibble}} with one row per
item (for uniform DIF) or per item × threshold (for
non-uniform DIF) containing: \code{item}, optionally
\code{threshold}, \code{dif_estimate} (posterior mean),
\code{dif_lower}, \code{dif_upper} (credible interval),
\code{dif_sd} (posterior SD), \code{pd} (probability of
direction), \code{rope_percentage} (proportion inside ROPE),
and \code{flag} (classification).}
\item{dif_draws}{A matrix of posterior draws for the DIF
effects (draws × effects), for further analysis.}
\item{dif_model}{The fitted DIF \code{brmsfit} object (if
\code{refit = TRUE}), or \code{NULL}.}
\item{dif_formula}{The \code{brmsformula} used for the DIF
model.}
\item{baseline_model}{The original baseline model.}
\item{plot}{A \code{\link[ggplot2]{ggplot}} forest plot of
DIF effects with credible intervals and ROPE.}
}
}
\description{
Tests for differential item functioning (DIF) in Bayesian
Rasch-family models fitted with \pkg{brms} by comparing item
parameters across subgroups defined by an exogenous variable.
The function fits a DIF model that includes group-by-item
interactions and summarizes the posterior distribution of the
DIF effects.
}
\details{
For polytomous models, two types of DIF can be tested:
\describe{
\item{Uniform DIF (\code{dif_type = "uniform"}, default)}{
A single location shift per item across groups, modelled as
a \code{group:item} fixed-effect interaction. This tests
whether the average item difficulty differs between groups.}
\item{Non-uniform / threshold-level DIF
(\code{dif_type = "non-uniform"})}{Each item receives
group-specific thresholds via
\code{thres(gr = interaction(item, group))}. DIF effects
are computed as the difference in each threshold between
groups, revealing whether DIF affects specific response
categories.}
}

The function constructs a DIF model by adding a group-by-item
interaction to the baseline model:

\itemize{
\item \strong{Dichotomous models}
(\code{family = bernoulli()}): The baseline
\code{response ~ 1 + (1 | item) + (1 | id)} becomes
\code{response ~ 1 + group + (1 + group | item) + (1 | id)},
where the group slope varying by item captures item-specific
DIF.
\item \strong{Polytomous uniform DIF}
(\code{dif_type = "uniform"}): The baseline
\code{response | thres(gr = item) ~ 1 + (1 | id)} becomes
\code{response | thres(gr = item) ~ 1 + group:item + (1 | id)}.
\item \strong{Polytomous non-uniform DIF}
(\code{dif_type = "non-uniform"}): The baseline becomes
\code{response | thres(gr = item_group) ~ 1 + (1 | id)},
where \code{item_group = interaction(item, group)}. Each
item × group combination gets its own thresholds. DIF
effects are the differences between group-specific
thresholds for each item, computed draw-by-draw from the
posterior.
}

DIF effects are summarized using:
\describe{
\item{Probability of Direction (pd)}{The proportion of the
posterior on the dominant side of zero. Values > 0.975
indicate strong directional evidence.}
\item{ROPE}{The Region of Practical Equivalence (Kruschke,
2018). If > 95\\% of the posterior falls inside the ROPE,
the DIF effect is practically negligible. If > 95\\% falls
outside, the effect is practically significant.}
\item{Credible Interval}{If the CI excludes zero, there is
evidence of DIF at the specified credibility level.}
}
}
\examples{
\donttest{
library(brms)
library(dplyr)
library(tidyr)
library(tibble)

# --- Dichotomous Rasch with DIF testing ---

set.seed(123)
df <- expand.grid(id = 1:200, item = paste0("I", 1:10)) \%>\%
  mutate(
    gender = rep(sample(c("M", "F"), 200, TRUE), each = 10),
    theta  = rep(rnorm(200), each = 10),
    delta  = rep(seq(-2, 2, length.out = 10), 200),
    dif    = ifelse(item == "I3" & gender == "F", 1.0,
             ifelse(item == "I7" & gender == "F", -0.8, 0)),
    p      = plogis(theta - delta - dif),
    response = rbinom(n(), 1, p)
  )

fit_base <- brm(
  response ~ 1 + (1 | item) + (1 | id),
  data   = df,
  family = bernoulli(),
  chains = 4, 
  cores  = 2, # use more cores if you have
  iter   = 1000 # use at least 2000 
)

dif_result <- dif_statistic(
  model     = fit_base,
  group_var = gender,
  data      = df
)

dif_result$summary
dif_result$plot

# --- Partial Credit Model: uniform DIF ---

df_pcm <- eRm::pcmdat2 \%>\%
  mutate(across(everything(), ~ .x + 1)) \%>\%
  rownames_to_column("id") \%>\%
  mutate(gender = sample(c("M", "F"), n(), TRUE)) \%>\%
  pivot_longer(!c(id, gender),
               names_to = "item", values_to = "response")

fit_pcm <- brm(
  response | thres(gr = item) ~ 1 + (1 | id),
  data   = df_pcm,
  family = acat,
  chains = 4, 
  cores  = 2, # use more cores if you have
  iter   = 1000 # use at least 2000 
)

# Uniform DIF (default): one shift per item
dif_uni <- dif_statistic(fit_pcm, group_var = gender, data = df_pcm)
dif_uni$plot

# Non-uniform DIF: threshold-level effects
dif_nu <- dif_statistic(fit_pcm, group_var = gender, data = df_pcm,
                         dif_type = "non-uniform")
dif_nu$summary
dif_nu$plot
}

}
\references{
Kruschke, J. K. (2018). Rejecting or accepting parameter values
in Bayesian estimation. \emph{Advances in Methods and Practices
in Psychological Science}, \emph{1}(2), 270--280.

Bürkner, P.-C. (2021). Bayesian Item Response Modeling in R with
brms and Stan. \emph{Journal of Statistical Software}, \emph{100},
1--54. \doi{10.18637/jss.v100.i05}
}
\seealso{
\code{\link{infit_statistic}} for item fit,
\code{\link{q3_statistic}} for local dependence,
\code{\link[brms]{brm}},
\code{\link[brms]{hypothesis}}.
}
