| Title: | Differential Item Functioning for AI-Scored Assessments |
|---|---|
| Description: | Detects and quantifies differential item functioning (DIF) in AI-scored educational and psychological assessments. Provides a fully self-contained robust DIF engine (M-estimation via iteratively re-weighted least squares with the bi-square loss) alongside the novel Differential AI Scoring Bias (DASB) test, which detects item-level scoring shifts that differ across subgroups when comparing human and AI scoring conditions. Includes simulation utilities, anchor weight diagnostics, and an AI-effect classification framework. |
| Authors: | Subir Hait [aut, cre] (ORCID: <https://orcid.org/0009-0004-9871-9677>) |
| Maintainer: | Subir Hait <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.1.0 |
| Built: | 2026-05-22 18:52:25 UTC |
| Source: | https://github.com/causalfragility-lab/aidif |
Compares the DIF flagging patterns from human and AI scoring conditions
and classifies each item as: "stable_clean" (not flagged in
either), "stable_dif" (flagged in both), "introduced"
(flagged only under AI), "masked" (flagged only under human), or
"new_direction" (flagged in both but bias reverses sign).
ai_effect_summary(dif_human, dif_ai, alpha = 0.05)ai_effect_summary(dif_human, dif_ai, alpha = 0.05)
dif_human |
A |
dif_ai |
A |
alpha |
Significance threshold for flagging. Default: |
A data.frame with one row per item/threshold and columns:
human_deltaEstimated DIF effect under human scoring.
ai_deltaEstimated DIF effect under AI scoring.
human_flagLogical: flagged under human scoring?
ai_flagLogical: flagged under AI scoring?
statusClassification (see Description).
eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) ai_effect_summary(mod$dif_human, mod$dif_ai)eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) ai_effect_summary(mod$dif_human, mod$dif_ai)
Returns the bi-square weights assigned to each item under each scoring condition. Items with weight near zero are effectively excluded from the robust scaling estimate, indicating likely DIF contamination.
anchor_weights(object)anchor_weights(object)
object |
An |
A data.frame with columns human_weight and
(if AI data were provided) ai_weight. Higher weight means
the item is contributing more to the robust scale estimate.
eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) anchor_weights(mod)eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) anchor_weights(mod)
Estimates a robust location parameter for the vector of IRT scaling functions using iteratively re-weighted least squares (IRLS) with the bi-square loss. This is the core estimation engine of aiDIF.
estimate_robust_scale( mle, alpha = 0.05, scale_by = "pooled", tol = 1e-07, maxit = 100L )estimate_robust_scale( mle, alpha = 0.05, scale_by = "pooled", tol = 1e-07, maxit = 100L )
mle |
A validated mle list. |
alpha |
Significance level controlling the bi-square
tuning parameter |
scale_by |
Scaling denominator; passed to
|
tol |
Convergence tolerance. Default |
maxit |
Maximum IRLS iterations. Default |
A list of class rdif_fit with elements:
estEstimated robust scale parameter.
weightsBi-square item weights.
rho_valueValue of objective at solution.
n_iterNumber of iterations used.
kTuning parameter used.
yRaw scaling function values.
vcov_estCovariance matrix of y at solution.
dif_testWald item-level DIF test (data.frame).
dtf_testWald test of differential test functioning.
dat <- simulate_aidif_data(n_items = 5, seed = 1) fit <- estimate_robust_scale(dat$human) print(fit$est)dat <- simulate_aidif_data(n_items = 5, seed = 1) fit <- estimate_robust_scale(dat$human) print(fit$est)
The primary estimation function of aiDIF. Runs the robust DIF
procedure under both human and AI scoring using the built-in IRLS engine
(estimate_robust_scale), then tests for Differential AI
Scoring Bias (DASB).
fit_aidif( human_mle, ai_mle = NULL, alpha = 0.05, scale_by = "pooled", tol = 1e-07, maxit = 100L )fit_aidif( human_mle, ai_mle = NULL, alpha = 0.05, scale_by = "pooled", tol = 1e-07, maxit = 100L )
human_mle |
A validated mle list for human-scored data. |
ai_mle |
A validated mle list for AI-scored data, or |
alpha |
Significance level. Default |
scale_by |
Denominator for standardising intercept differences:
|
tol |
IRLS convergence tolerance. Default |
maxit |
Maximum IRLS iterations. Default |
An object of class "aidif".
estimate_robust_scale,
scoring_bias_test, simulate_aidif_data
dat <- simulate_aidif_data(n_items = 6, seed = 1) mod <- fit_aidif(dat$human, dat$ai) print(mod) summary(mod)dat <- simulate_aidif_data(n_items = 6, seed = 1) mod <- fit_aidif(dat$human, dat$ai) print(mod) summary(mod)
Constructs and returns the built-in example dataset: paired human and AI item parameter estimates for 6 items in two groups, with known DIF and DASB planted at specific items.
make_aidif_eg()make_aidif_eg()
The data-generating model includes:
Item 1: DIF under human scoring (intercept +0.5 in focal group).
Item 3: Differential AI Scoring Bias (DASB) — AI scoring adds +0.4 to the focal-group intercept only.
Impact: 0.5 SD (focal group higher on latent trait).
AI drift: uniform +0.1 calibration offset on all items in both groups.
A list with elements human and ai, each a
validated mle list (see simulate_aidif_data for
format details).
simulate_aidif_data, fit_aidif
eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) summary(mod)eg <- make_aidif_eg() mod <- fit_aidif(eg$human, eg$ai) summary(mod)
"aidif".Produces one of several diagnostic plots depending on type.
## S3 method for class 'aidif' plot(x, type = "dif_forest", ...)## S3 method for class 'aidif' plot(x, type = "dif_forest", ...)
x |
An object of class |
type |
Character. One of:
|
... |
Additional graphical parameters passed to low-level plot functions. |
x, invisibly.
"aidif".Prints a compact summary of the estimated robust scaling parameters and, when available, the number of items flagged for DIF and DASB.
## S3 method for class 'aidif' print(x, ...)## S3 method for class 'aidif' print(x, ...)
x |
An object of class |
... |
Further arguments (currently ignored). |
x, invisibly.
Takes two mle lists (one per scoring condition) and returns a validated
aidif_data object for use in fit_aidif.
read_ai_scored(human_mle, ai_mle)read_ai_scored(human_mle, ai_mle)
human_mle |
An mle list for human-scored data. Must contain
|
ai_mle |
An mle list for AI-scored data in the same format. |
A list of class "aidif_data" with elements
human and ai.
fit_aidif, make_aidif_eg,
simulate_aidif_data
For each item, computes the change in item intercept from human to AI scoring within each group, then tests whether this scoring shift differs significantly across groups. A significant result indicates the AI scoring engine introduces a group-dependent parameter distortion — i.e., the AI does not merely re-scale all items uniformly but disfavours (or favours) one group at specific items.
scoring_bias_test(human_mle, ai_mle, fun = "d_fun3")scoring_bias_test(human_mle, ai_mle, fun = "d_fun3")
human_mle |
Output of |
ai_mle |
Output of |
fun |
Scaling function (passed to
the internal scaling function) to use when normalising shifts.
Default: |
Estimand. Define the scoring shift in group for item
threshold as:
The DASB is . Under
and independence across scoring
conditions and groups,
where each is the diagonal element of the corresponding
group-specific covariance matrix.
A data.frame with one row per item (per threshold for
polytomous items) and columns:
shift_g1Scoring shift .
shift_g2Scoring shift .
DASBDifferential AI Scoring Bias: .
seStandard error of DASB under the delta method.
zWald z-statistic.
p_valTwo-tailed p-value.
eg <- make_aidif_eg() scoring_bias_test(eg$human, eg$ai)eg <- make_aidif_eg() scoring_bias_test(eg$human, eg$ai)
Generates a synthetic aidif_data-compatible list suitable for
benchmarking and method evaluation. The data-generating model contains:
classical DIF in the human scoring condition (controlled via
dif_items and dif_mag), differential AI scoring bias
(controlled via dasb_items and dasb_mag), and a latent
group mean difference (impact).
simulate_aidif_data( n_items = 10L, n_obs = 500L, impact = 0.5, dif_items = 1L, dif_mag = 0.5, dasb_items = 3L, dasb_mag = 0.4, ai_drift = 0.1, seed = 42L )simulate_aidif_data( n_items = 10L, n_obs = 500L, impact = 0.5, dif_items = 1L, dif_mag = 0.5, dasb_items = 3L, dasb_mag = 0.4, ai_drift = 0.1, seed = 42L )
n_items |
Integer. Number of items. Default: |
n_obs |
Integer. Approximate number of observations per group,
used to scale the covariance matrices. Default: |
impact |
Numeric. Latent mean difference (group 2 minus group 1)
in SD units. Default: |
dif_items |
Integer vector. Indices of items with DIF in the human
scoring condition (intercept shift added to group 2). Default:
|
dif_mag |
Numeric. Magnitude of the intercept DIF effect (in
IRT metric). Default: |
dasb_items |
Integer vector. Indices of items where AI scoring
introduces differential bias (intercept shift added to group 2 in the
AI condition only). Default: |
dasb_mag |
Numeric. Magnitude of the DASB effect. Default:
|
ai_drift |
Numeric. Uniform intercept shift applied to ALL items in
BOTH groups under AI scoring (simulates AI calibration offset).
Default: |
seed |
Integer seed for reproducibility, or |
Rather than simulating item responses and refitting IRT models (which
requires additional dependencies), this function directly simulates
maximum-likelihood estimates and their asymptotic covariance matrices,
consistent with a 2PL model fitted to n_obs observations per
group.
A list with elements human and ai, each formatted
identically to the output of
read_ai_scored. Can be passed directly to
fit_aidif.
dat <- simulate_aidif_data( n_items = 8, n_obs = 600, dif_items = c(1, 2), dasb_items = 5 ) mod <- fit_aidif(dat$human, dat$ai) summary(mod)dat <- simulate_aidif_data( n_items = 8, n_obs = 600, dif_items = c(1, 2), dasb_items = 5 ) mod <- fit_aidif(dat$human, dat$ai) summary(mod)
"aidif".Prints a detailed report including DIF test tables for each scoring condition, the DASB table, and the AI-effect classification.
## S3 method for class 'aidif' summary(object, ...)## S3 method for class 'aidif' summary(object, ...)
object |
An object of class |
... |
Further arguments (currently ignored). |
NULL, invisibly.