---
title: "Comparability of translated exam forms"
output: markdown::html_format
vignette: >
  %\VignetteIndexEntry{Comparability of translated exam forms}
  %\VignetteEngine{knitr::knitr}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

When an exam is offered in a second language, the question is whether a score
means the same thing in both. Standard DIF tools struggle when the translated
group is small, differs in ability, and many items shift in the same
direction. transDIF is built for that situation.

## Data

A source-language group of 2,000 and a translated-language group of 150 who
are 0.5 logits lower on average. Items with idioms, cultural referents,
measurement units or heavy vocabulary tend to shift in translation.

```{r}
library(transDIF)
sim <- td_simulate(n_ref = 2000, n_focal = 150, n_items = 40, seed = 5)
colSums(sim$features[-1])
```

## Calibrate each group, then link and detect DIF together

```{r}
cal <- td_calibrate(sim$responses, sim$group)
dif <- td_dif(cal)
dif
```

The linking shift (the ability difference) is the center of the densest
cluster of items, not the mean of all items, so it does not assume that DIF
cancels out. Compare with linking on
all items:

```{r}
c(true = -sim$truth$focal_mean, robust = dif$link[["c"]], all_items = dif$c_mean)
```

## Does DIF change pass rates?

```{r}
td_impact(dif, cut = 24, n_draws = 100, seed = 1)
```

## How much do the conclusions depend on the linking?

Linking assumes that DIF-free items form the largest cluster. When many
items show DIF, the data may not settle which items are DIF-free.
`td_sensitivity()` shows the results under each linking assumption, how
stable the mode is, and which items' DIF verdicts depend on the choice:

```{r}
sens <- td_sensitivity(dif, cut = 24, B = 100, n_draws = 50, seed = 1)
sens
```

The report has three parts:

- **Linkings.** The ability difference, the number of flagged items and the
  pass-rate impact under three assumptions: the densest cluster of items is
  DIF-free (mode), flagged items are removed until the rest agree
  (purification), and DIF cancels out over all items (mean).
- **Stability of the mode.** A parametric bootstrap redraws each item's
  between-language difference and re-estimates the mode. The share of
  bootstrap modes within the tolerance (0.15 logits by default) of the
  estimate shows whether the densest cluster is clear or whether a slightly
  different sample would have picked another cluster.
- **Linking-sensitive items.** Items whose DIF flag changes from one linking
  to another.

The verdict is "robust" only when the linkings agree within the tolerance
*and* the mode is stable (at least 80% of bootstrap modes within the
tolerance). Here the linkings agree closely, but with 150 candidates the mode
is not yet stable enough, so the verdict is "sensitive". Each part is
available for reporting:

```{r}
sens$linkings
sens$bootstrap
sens$items[sens$items$linking_sensitive, ]
```

### Two contrasting cases

With a larger translated-language group, the same 40-item design gives a
clear answer: the linkings agree and the mode barely moves under resampling.

```{r}
big <- td_simulate(n_ref = 1000, n_focal = 400, n_items = 40, seed = 1)
dif_big <- td_dif(td_calibrate(big$responses, big$group))
td_sensitivity(dif_big, cut = 24, B = 100, n_draws = 50, seed = 1)
```

Even a robust verdict can list a few linking-sensitive items: they sit near
the flagging boundary, so a small shift in the linking moves them across it.
They deserve review, but the overall conclusion does not depend on them.

A short test with pervasive DIF and a small group is the opposite case. No
cluster of DIF-free items dominates, the linkings disagree, and the mode
jumps between clusters under resampling:

```{r}
heavy <- c(idiom = 0.18, cultural = 0.18, units = 0.10, vocabulary = 0.2)
short <- td_simulate(n_ref = 1000, n_focal = 80, n_items = 16,
                     feature_prev = heavy, seed = 5)
dif_short <- suppressWarnings(td_dif(td_calibrate(short$responses, short$group)))
td_sensitivity(dif_short, cut = 10, B = 100, n_draws = 50, seed = 1)
```

Here the conclusion itself changes with the assumption: the estimated effect
of DIF on the pass rate is +3.6 points under the mode, +2.4 under
purification and essentially zero under mean linking, whose interval
includes zero.

### Reading the verdict

A "sensitive" verdict does not say which linking is right, and it does not
mean the mode is wrong. It says that a conclusion depends on an assumption
the data cannot check. In the package's simulations, "sensitive" verdicts
were concentrated in the hardest designs (short tests, small groups,
pervasive DIF), where linking errors are larger for every method; within a
given design, the verdict did not tell more accurate estimates from less
accurate ones. In practice:

1. When the verdict is robust, report the mode-based results.
2. When it is sensitive, report the results under all three linkings, say
   which conclusions change, and do not present a single pass-rate impact as
   settled.
3. In either case, send the linking-sensitive items to content and
   translation experts. Only their review can settle which items should
   anchor the scale.

The report can be included in the comparability report with
`td_report(dif, ..., sensitivity = sens)`.

## What should translators look at?

```{r}
td_features(dif, sim$features)
```

## A report to start from

```{r}
cat(td_report(dif, languages = c("English", "French")))
```

With very small translated-language groups (around 50 candidates), every
method has little power to detect DIF, so treat flags as candidates for
expert review rather than conclusions.
