---
title: "Getting started with iDIFr"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting started with iDIFr}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment  = "#>"
)
library(iDIFr)
```

## What is DIF?

Differential Item Functioning (DIF) occurs when test-takers from different
groups who have the same underlying ability have different probabilities of
answering a test item correctly. DIF threatens the validity of comparisons across
groups and is a central concern in fair assessment.

`iDIFr` makes DIF analysis accessible, with particular support for
**intersectional group designs** — where groups are defined by combinations of
demographic variables, such as gender × nationality × age band.

---

## A simple example

We'll use a small synthetic dataset with known DIF to illustrate the workflow.

### Step 1: Generate (or load) your data

Your data should be a data frame where:

- Item response columns contain only `0` and `1`
- Demographic columns identify group membership

```{r simulate}
set.seed(42)
dat <- simulate_dif(
  n_persons  = 600,
  n_items    = 20,
  n_groups   = 2,
  dif_items  = c(3, 7, 12),  # these items have DIF
  dif_effect = 0.9,
  dif_type   = "uniform"
)

head(dat[c(1:5, 21)])  # first 5 items + group column
```

### Step 2: Check your group structure

Before running the analysis, use `check_groups()` to inspect group cell sizes.
This is especially important in intersectional designs where small cells can
reduce statistical power.

```{r check-groups, eval = FALSE}
check_groups(dat, group = ~ group)
```

For an intersectional design with multiple demographic variables:

```{r check-intersectional, eval = FALSE}
# Add nationality variable for illustration
dat$nationality <- sample(c("UK", "DE", "FR"), 600, replace = TRUE)
dat$age_band    <- sample(c("18-30", "31-45", "46+"), 600, replace = TRUE)

check_groups(dat, group = ~ group * nationality * age_band)
```

If any cells are too small, `check_groups()` will tell you and point you to
`merge_groups()`.

### Step 3: Run the DIF analysis

Supply your data, the item columns, a group formula, and which method(s) to use.

```{r run-idifr, eval = FALSE}
result <- idifr(
  data   = dat,
  items  = 1:20,
  group  = ~ group,
  method = c("LR", "LRT")
)
```

`method` is required — you must choose. Options are:

| Method | What it does |
|--------|-------------|
| `"LR"` | Logistic Regression — flexible, non-IRT, effect size via Nagelkerke ΔR² |
| `"LRT"` | IRT Likelihood Ratio Test — model-based, effect size via standardised chi |
| `"MOB"` | Model-based recursive partitioning — non-parametric, detects intersectional instability |

### Step 4: Explore the results

```{r explore, eval = FALSE}
# Flagged items with effect sizes
print(result)

# Full breakdown by method
summary(result)

# Effect size heatmap
plot(result)

# Method concordance
plot(result, type = "concordance")

# Flat data frame for your own analysis
df <- tidy(result)
```

---

## Intersectional DIF

The key feature of `iDIFr` is first-class support for intersectional group
structures. Where conventional DIF analysis examines one demographic variable
at a time, intersectional analysis asks: *does DIF appear at the combination
of gender × nationality × age, even when no individual variable shows DIF?*

```{r intersectional, eval = FALSE}
result_intersectional <- idifr(
  data   = dat,
  items  = 1:20,
  group  = ~ group * nationality * age_band,  # crossing all three variables
  method = c("LR", "LRT")
)

print(result_intersectional)
```

### Handling small cells

Intersectional designs often produce small cells. `iDIFr` will warn you but
always run the analysis. To merge sparse cells:

```{r merge, eval = FALSE}
grp <- check_groups(dat, group = ~ group * nationality * age_band)

merged_dat <- merge_groups(
  grp,
  age_band = list("18-45" = c("18-30", "31-45"))  # combine two age bands
)

# Re-run with merged groups
result_merged <- idifr(merged_dat, 1:20,
                       group  = ~ group * nationality * age_band,
                       method = c("LR", "LRT"))
```

---

## Effect sizes

`iDIFr` leads with effect sizes, not just p-values. Flagging criteria require
*both* a significant p-value (after adjustment) *and* a meaningful effect size.

| Method | Effect size | Classification |
|--------|-------------|----------------|
| LR | Nagelkerke ΔR² | A: <.035 · B: .035–.070 · C: ≥.070 |
| LRT | Std. chi | Negligible: <.20 · Moderate: .20–.50 · Large: ≥.50 |
| MOB | Std. score difference | Negligible: <.20 · Moderate: .20–.50 · Large: ≥.50 |

---

## Intersectional Contrast Analysis (ICA)

Set `ica = TRUE` in `idifr()` to go one step further than a single intersectional
analysis. It runs one analysis per demographic variable (single-variable) *and*
one intersectional analysis, then classifies each item by comparing where it was
flagged.

```{r ica-example, eval = FALSE}
ica_res <- idifr(
  data   = dat,
  items  = 1:20,
  group  = ~ group * nationality * age_band,
  method = "LR",
  ica    = TRUE
)

print(ica_res)                 # includes the ICA classification section
tidy(ica_res, table = "ica")   # flat ICA classification table
```

Four item classifications are possible:

| Classification | Meaning |
|----------------|---------|
| `amplified` | Flagged in single-variable *and* intersectional runs |
| `pure_intersection` | Only flagged in the intersectional run |
| `obscured` | Flagged in a single-variable run but not intersectionally |
| `none` | Not flagged anywhere |

> **Note:** ICA runs multiple analyses without cross-analysis p-value
> correction. The `effect_threshold` argument (default 0.035) provides a
> de facto stricter criterion. Interpret `pure_intersection` and `obscured`
> findings with caution in small samples.

---

## Further reading

- Swaminathan & Rogers (1990) on logistic regression DIF
- Thissen, Steinberg & Wainer (1993) on IRT-LRT DIF
- Millsap & Everson (1993) on measurement bias methods
- Crenshaw (1989) on intersectionality as a conceptual framework
