% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/api-advanced.R
\name{dif_interaction_table}
\alias{dif_interaction_table}
\title{Compute interaction table between a facet and a grouping variable}
\usage{
dif_interaction_table(
  fit,
  diagnostics,
  facet,
  group,
  data = NULL,
  min_obs = 10,
  p_adjust = "holm",
  abs_t_warn = 2,
  abs_bias_warn = 0.5
)
}
\arguments{
\item{fit}{Output from \code{\link[=fit_mfrm]{fit_mfrm()}}.}

\item{diagnostics}{Output from \code{\link[=diagnose_mfrm]{diagnose_mfrm()}}.}

\item{facet}{Character scalar naming the facet.}

\item{group}{Character scalar naming the grouping column.}

\item{data}{Optional data frame with the group column. If \code{NULL}
(default), the data stored in \code{fit$prep$data} is used, but it
must contain the \code{group} column.}

\item{min_obs}{Minimum observations per cell. Cells with fewer than
this many observations are flagged as sparse and their test
statistics set to \code{NA}. Default \code{10}.}

\item{p_adjust}{P-value adjustment method, passed to
\code{\link[stats:p.adjust]{stats::p.adjust()}}. Default \code{"holm"}.}

\item{abs_t_warn}{Threshold for flagging cells by absolute t-value.
Default \code{2}.}

\item{abs_bias_warn}{Threshold for flagging cells by absolute
Obs-Exp average (in logits). Default \code{0.5}.}
}
\value{
Object of class \code{mfrm_dif_interaction} with:
\itemize{
\item \code{table}: tibble with per-cell statistics and flags.
\item \code{summary}: tibble summarizing flagged and sparse cell counts.
\item \code{gpcm_boundary}: for bounded \code{GPCM} fits, a capability-boundary table.
\item \code{config}: list of analysis parameters.
}
}
\description{
Produces a cell-level interaction table showing Obs-Exp differences,
standardized residuals, and screening statistics for each
facet-level x group-value cell.
}
\details{
This function uses the fitted model's observation-level residuals
(from the internal \code{compute_obs_table()} function) rather than
re-estimating the model. For each facet-level x group-value cell,
it computes:
\itemize{
\item N: number of observations in the cell
\item ObsScore: sum of observed scores
\item ExpScore: sum of expected scores
\item ObsExpAvg: mean observed-minus-expected difference
\item Var_sum: sum of model variances
\item StdResidual: (ObsScore - ExpScore) / sqrt(Var_sum)
\item t: approximate t-statistic (equal to StdResidual)
\item df: N - 1
\item p_value: two-tailed p-value from the t-distribution
}
}
\section{When to use this instead of analyze_dff()}{

Use \code{dif_interaction_table()} when you want cell-level screening for a
single facet-by-group table. Use \code{\link[=analyze_dff]{analyze_dff()}} when you want group-pair
contrasts summarized into differential-functioning effect sizes and
method-appropriate classifications.
}

\section{Further guidance}{

For plot selection and follow-up diagnostics, see
\link{mfrmr_visual_diagnostics}.
}

\section{Interpreting output}{

\itemize{
\item \verb{$table}: the full interaction table with one row per cell.
\item \verb{$summary}: overview counts of flagged and sparse cells.
\item \verb{$config}: analysis configuration parameters.
\item \verb{$gpcm_boundary}: for bounded \code{GPCM} fits, a capability-boundary table
marking the table as caveated DFF screening evidence.
\item Cells with \verb{|t| > abs_t_warn} or \verb{|ObsExpAvg| > abs_bias_warn}
are flagged in the \code{flag_t} and \code{flag_bias} columns.
\item Sparse cells (N < min_obs) have \code{sparse = TRUE} and NA statistics.
}
}

\section{GPCM boundary}{

For bounded \code{GPCM}, the interaction table uses the fitted slope-aware
expected-score/residual scale and should be reported as screening evidence,
not as a standalone fairness, invariance, or operational subgroup decision.
}

\section{Typical workflow}{

\enumerate{
\item Fit a model with \code{\link[=fit_mfrm]{fit_mfrm()}}.
\item Run \code{dif_interaction_table(fit, diag, facet = "Rater", group = "Gender", data = df)}.
\item Inspect \verb{$table} for flagged cells.
\item Visualize with \code{\link[=plot_dif_heatmap]{plot_dif_heatmap()}}.
}
}

\examples{
toy <- load_mfrmr_data("example_bias")

fit <- fit_mfrm(toy, "Person", c("Rater", "Criterion"), "Score",
                 method = "JML", model = "RSM", maxit = 30)
diag <- diagnose_mfrm(fit, residual_pca = "none")
int <- dif_interaction_table(fit, diag, facet = "Rater",
                             group = "Group", data = toy, min_obs = 2)
int$summary
head(int$table[, c("Level", "GroupValue", "ObsExpAvg", "flag_bias")])
}
\seealso{
\code{\link[=analyze_dff]{analyze_dff()}}, \code{\link[=analyze_dif]{analyze_dif()}}, \code{\link[=plot_dif_heatmap]{plot_dif_heatmap()}}, \code{\link[=dif_report]{dif_report()}},
\code{\link[=estimate_bias]{estimate_bias()}}
}
