3. Validity en classificatie — sensitivity, specificity en hoe je verkeerd gaat

Het verhaal van dit thema

De vos kwam uit de hazelaars en zette zich naast de wezel. Hij rook iets, en hij wilde weten hoe goed hij dat rook.

“Ik heb een test,” zei de vos. “Een vragenlijst die zegt of een dier alarm-snel is of niet.”

“Werkt hij?” vroeg de wezel.

“Daar gaat dit thema over. Validiteit heet het. De vraag is niet of de score consistent is — dat hebben jullie thema 2 al uitgemest — maar of hij meet wat hij beweert te meten.”

“Hoe weet je dat?” vroeg de bunzing.

“Door hem te vergelijken met een andere maat van hetzelfde, met een andere maat van iets anders, en met een externe waarheid.” De vos rook nog eens. “En dan reken je een tabel met vier cellen die je sensitivity, specificity, PPV en NPV oplevert. Als je het goed doet.”

Tweehonderd bosdieren in dit thema. Een continue Bos-Alarm-Vragenlijst (BAV, 00-100100). Plus een ware diagnose (alarm-snel / alarm-traag) op basis van geobserveerd reactiegedrag, een convergente maat (alertheid-observatie), en een divergente maat (kalmte). Mini-dataset: 80 dassen op een voorspeller-uitkomst-koppel voor E5.

Drie hoofdvragen:

  1. Hangt de score samen met wat hij zou moeten meten? — convergent vs divergent (construct-validiteit).
  2. Voorspelt hij een externe uitkomst? — criterion-validiteit + classification table.
  3. Wat doe je bij meetfout? — correctie voor attenuatie, en waarom dat een gevaarlijk speeltje is.
OpmerkingA. Wat is validity?

Validity = de mate waarin een test meet wat hij beweert te meten. Verschillende soorten:

  • Content-validiteit content validity — dekken de items het construct? Theoretisch oordeel, geen statistiek. Bv. een depressie-schaal moet niet alleen stemming bevragen maar ook slaap, eetlust, concentratie.
  • Construct-validiteit construct validity — gedraagt de score zich zoals het construct dat zou moeten? Twee subtypes:
    • Convergent: hoge correlatie met andere maten van hetzelfde construct.
    • Divergent / discriminant: lage of negatieve correlatie met maten van andere constructen.
  • Criterion-validiteit criterion validity — voorspelt de score een externe uitkomst? Twee subtypes:
    • Concurrent: gelijktijdig gemeten externe uitkomst.
    • Predictive: latere uitkomst.

Reliability ≠\neq validity. Een onbetrouwbare schaal kan onmogelijk valide zijn (te veel ruis); een betrouwbare schaal hoeft nog niet valide te zijn (kan consistent het verkeerde meten — “perfect verkeerd”). Validiteit is altijd relatief aan een claim: deze test is valide voor het meten van X in populatie Y voor doel Z. Niet “valide” in absolute zin.

OpmerkingB. Classification table — vier cellen, vier verhoudingen die er echt toe doen

Bij criterion-validiteit met een binaire uitkomst gaat het om de classification table. De werkelijkheid staat in de rijen, wat de test zegt in de kolommen:

Voorspeld positief Voorspeld negatief
Werkelijk positief (Y=1Y=1) TP FN
Werkelijk negatief (Y=0Y=0) FP TN

De letters lees je van achter naar voren. De tweede zegt wat de test zei (Positief of Negatief), de eerste of dat klopte (True of False). FN is dus: de test zei negatief, en dat was fout. Echt positief, en gemist. FP is vals alarm.

Vier ratio’s die er toe doen:

  • Sensitivity (TPR, true positive rate): TP/(TP+FN)\text{TP} / (\text{TP} + \text{FN}) — gegeven dat iemand positief is, hoe vaak vindt de test hem? Je blijft in de rij werkelijk positief.
  • Specificity (TNR): TN/(TN+FP)\text{TN} / (\text{TN} + \text{FP}) — gegeven dat iemand negatief is, hoe vaak laat de test hem terecht gaan? Je blijft in de rij werkelijk negatief.
  • PPV (positive predictive value): TP/(TP+FP)\text{TP} / (\text{TP} + \text{FP}) — gegeven dat de test positief is, hoe vaak klopt dat? Je blijft in de kolom voorspeld positief.
  • NPV (negative predictive value): TN/(TN+FN)\text{TN} / (\text{TN} + \text{FN}) — gegeven dat de test negatief is, hoe vaak klopt dat? Je blijft in de kolom voorspeld negatief.

Sensitivity en PPV hebben dezelfde teller: TP. Alleen de noemer verschilt — bij sensitivity de hele rij, bij PPV de hele kolom. Precies daar gaat het het vaakst mis.

Tip“Gegeven dat” maakt de zaal kleiner

Gegeven dat iemand echt positief is betekent: alleen de echt positieven blijven zitten, de rest van de zaal gaat naar huis. Wie er blijft zitten, daar deel je door.

  • Sensitivity en specificity: alleen wie echt positief is (of echt negatief) blijft zitten. In de tabel is dat één rij.
  • PPV en NPV: alleen wie een positieve (of negatieve) uitslag kreeg, blijft zitten. In de tabel is dat één kolom. Ze dragen de voorspelling in hun naam — predictive value — en daarom is hier de test het gegeven.

Kom je de tabel ergens andersom tegen, met de voorspelling in de rijen, dan kloppen rij en kolom niet meer, maar de zaal nog wel: sensitivity deelt door alle echt positieven, waar die ook staan. Zoek dus eerst waar de werkelijkheid staat. Dat gebeurt echt; sommige R-functies willen de tabel zo.

Plus een vijfde maat:

  • PAC (proportion accurate classifications, ofwel accuracy): (TP+TN)/N(\text{TP} + \text{TN}) / N — welk percentage classificaties klopte überhaupt? Eenvoudig, maar misleidend bij scheve klassen (zie Don’t-callout verderop).
OpmerkingC. Vos-frame — wat onderzoeken we?

De vos heeft een test (BAV) en wil weten:

  • Meet de BAV alertheid? (convergent: correleert hij met een tweede maat van alertheid?)
  • Discrimineert hij van kalmte? (divergent: correleert hij negatief, of bijna nul, met een kalmte-maat?)
  • Voorspelt hij wie er echt snel reageert? (criterion + classification table — sensitivity, specificity, PPV, NPV).
  • En wat doe je als je weet dat de BAV niet perfect betrouwbaar is — corrigeer je dan voor attenuatie? (3.A — en daar gaat de vos zich verbazen).
  • Thema 1: norm-scoren — z-, T-scores, percentielen. Plus Ruw is Ruk.
  • Thema 2: Cronbach’s α\alpha als internal consistency. Plus een onbetrouwbare schaal kan onmogelijk valide zijn.

Validity bouwt voort op reliability. Stel je voor: een schaal met α=.60\alpha = .60 correleert r=.30r = .30 met een externe uitkomst. Klinkt magertjes — maar de echte correlatie tussen de constructs zou hoger zijn als de schaal betrouwbaar was. Correctie voor attenuatie (3.A) brengt die rekensom in beeld. Maar pas op: correctie zonder context is een eufemisme voor wensdenken.

Werkmaterialen — R-pakketten en functies

De vos pakt zijn gereedschap

Gereedschap Waar het voor is Pakket
cor() Pearson-correlaties (convergent / divergent) base
table() Classification table base
prop.table() Percentages uit tabel base
cor.test() Correlatie + pp-waarde + CI base
glm(..., family = binomial) Logistische regressie als criterion-validatie base
psych::cor.smooth() Smoothing van correlatie-matrices (zelden nodig hier) psych
OpmerkingCode in dit hoofdstuk — wat moet je kunnen typen?

Code is standaard open als je hem zelf moet kunnen typen: cor(), cor.test(), table(), prop.table(), en de classificatie-formules met de hand. Code voor gt-tabellen of plot-decoratie staat ingeklapt. Staat een stap in twee tabbladen, dan typ je Base R; dplyr staat ernaast om te kijken, en overslaan mag.

Notatie — symbolen voor validity

OpmerkingSleutelsymbolen
  • rxyr_{xy} — geschatte (sample) correlatie tussen XX en YY.
  • rxxr_{xx} — reliability (Cronbach’s α\alpha of equivalent) van XX.
  • ryyr_{yy} — reliability van YY.
  • rxy*r_{xy}^* — voor attenuatie gecorrigeerde correlatie (zie 3.A).
  • TP, FP, FN, TN — true/false positive/negative.
  • PAC = proportion accurate classifications = (TP+TN)/N(TP + TN)/N.
  • Sensitiviteit, specificiteit, PPV, NPV — zie kader B.
  • Base rate — proportie positieven in de populatie. PPV en NPV hangen ervan af, sensitivity en specificity niet.
TipVuistregels zijn afspraken, geen wetten
  • Convergent: r≥.50r \geq .50 tussen maten van hetzelfde construct is een gangbare ondergrens. r≥.70r \geq .70 is sterk.
  • Divergent: |r|<.30|r| < .30 tussen maten van verschillende constructen. Hoger duidt op constructverschillen die niet duidelijk zijn.
  • Sensitivity / specificity bij screening: liever sensitivity hoog (≥.80\geq .80); false negatives missen is duurder dan false positives.
  • Sensitivity / specificity bij definitieve diagnose: liever specificity hoog (≥.90\geq .90); false positives kosten meer dan false negatives.
  • PPV en NPV: hangen sterk af van base rate. Bij zeldzame ziekte kan PPV laag zijn (<.30< .30) zelfs bij goede sensitivity en specificity.

Verschillende vakgroepen en handboeken hanteren iets andere drempels; werk je met ander materiaal, kijk dan welke afspraak dat volgt.

3.0 Project- en datavoorbereiding

install.packages(c("tidyverse"))
  1. Open de hoofd-dataset.
load("data/bos_alarm_test.RData")
str(bos_alarm_test)
'data.frame':   200 obs. of  7 variables:
 $ dier_id      : Factor w/ 200 levels "d001","d002",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ diersoort    : Factor w/ 4 levels "bunzing","egel",..: 1 1 3 4 3 2 2 1 3 4 ...
 $ leeftijd     : Factor w/ 3 levels "jong","volwassen",..: 1 3 2 1 2 1 3 1 2 2 ...
 $ alarm_score  : num  45.9 60.2 55.6 40.8 47.6 43.3 27.9 14.1 47.1 66.8 ...
 $ alertheid_obs: num  49 76.9 44.1 70 31.7 70.6 9.9 6.1 25.7 67.6 ...
 $ kalmte_obs   : num  35.1 51.2 45.4 46.9 52.8 53.1 86.2 75.6 63.6 59.7 ...
 $ ware_alarm   : Factor w/ 2 levels "traag","snel": 2 2 1 2 1 1 1 1 1 2 ...

Per dier: een dier_id, diersoort, leeftijd, een continue alarm_score (BAV, 00-100100), twee andere continue maten (alertheid_obs en kalmte_obs) voor convergente en divergente validatie, plus de ware binaire ware_alarm (snel/traag) als criterion.

3.1 Construct-validiteit — convergent en divergent

Hangt de test samen met wat hij zou moeten meten?

“Convergent moet hoog,” zei de vos. “Divergent moet laag, of negatief. Anders zegt de test niets over alertheid maar gewoon over iets wat met alles samenhangt.”

OpmerkingVoor je gaat rekenen — vijf vragen aan jezelf
  1. Wie of wat wordt er gemeten? De vos heeft 200 bosdieren genoteerd op vier maten.
  2. Wat wordt er gemeten? Drie continue scores plus één binaire diagnose.
  3. Onafhankelijk of afhankelijk? Voor construct-validatie geen voorspelmodel — we kijken naar correlatiepatronen. Voor criterion (3.2): wel een voorspelmodel.
  4. Meetniveau? Alle continue scores: interval. Ware-alarm: nominaal/binair.
  5. Welke techniek? Construct-validiteit: correlaties (convergent vs divergent). Criterion-validiteit: classification + sensitivity/specificity, eventueel logistische regressie: een regressie die een ja/nee-uitkomst voorspelt. Die hoort bij multivariate data-analyse, en is een onderwerp op zich.

T1 — Welke validity-soort?

OpmerkingVraag T1 — Pen-en-papier

Voor elk vignet: kies tussen content-validiteit, convergent-validiteit, divergent-validiteit, concurrent criterion-validiteit, predictive criterion-validiteit.

a) Een onderzoeker test of haar nieuwe burn-out-schaal sterker correleert met een gevestigde burn-out-meting (UBOS) dan met een depressie-meting.

b) Een commissie expert-psychologen evalueert of een autisme-vragenlijst alle relevante symptoom-domeinen dekt.

c) Een psychiater toetst of haar nieuwe ADHD-schaal vandaag samenhangt met een diagnose die op dezelfde dag is gesteld.

d) Een opleidingspsycholoog meet bij studenten in jaar 1 een werkethiek-vragenlijst, en kijkt of die scores hun afstudeer-cijfer 4 jaar later voorspellen.

e) Een psycholoog kijkt of een sociale-angst-schaal niet samenhangt met een tabel-tennis-vaardigheids-test (zou dat ook niet moeten).

a) Convergent + divergent. Convergent: BAS-burnout met UBOS-burnout (hoge correlatie verwacht). Divergent: BAS-burnout met depressie (lagere correlatie verwacht omdat het andere construct is).

b) Content-validiteit. Theoretisch oordeel of de items het construct dekken. Geen statistiek — wel expert-review en theoretisch kader.

c) Concurrent criterion-validiteit. Schaal en criterium gelijktijdig gemeten, vraag: voorspelt de schaal de criterium-uitkomst?

d) Predictive criterion-validiteit. Schaal eerst, criterium later in de tijd. Voorspelt de schaal de toekomstige uitkomst?

e) Divergent-validiteit. Een sociale-angst-schaal zou niet moeten samenhangen met tafeltennis-vaardigheid; correlatie nul-of-laag bevestigt divergent.

Vuistregel. Hetzelfde construct, andere maat = convergent. Ander construct = divergent. Externe binaire uitkomst = criterion (concurrent of predictive afhankelijk van timing). Theoretisch oordeel = content.

3.1.a Verkenning — correlatiematrix

Ook in dit thema staan een paar stappen in twee schrijfwijzen naast elkaar, in tabbladen. Net als in thema 1 en 2: base R staat vooraan en is het enige dat je zelf moet kunnen typen; dplyr staat ernaast om te kijken, en overslaan mag. Ze geven dezelfde getallen.

cor_validity <- cor(bos_alarm_test[, c("alarm_score", "alertheid_obs", "kalmte_obs")])
round(cor_validity, 3)
              alarm_score alertheid_obs kalmte_obs
alarm_score         1.000         0.720     -0.535
alertheid_obs       0.720         1.000     -0.486
kalmte_obs         -0.535        -0.486      1.000
library(dplyr)

# select() kiest kolommen bij hun kale naam: geen aanhalingstekens, geen c().
# Binnen dplyr kan dat; in base R blijven ze nodig.
# Wat eruit komt, gaat via het pijpje meteen cor() in.
cor_validity <- bos_alarm_test |>
  select(alarm_score, alertheid_obs, kalmte_obs) |>
  cor()
round(cor_validity, 3)
              alarm_score alertheid_obs kalmte_obs
alarm_score         1.000         0.720     -0.535
alertheid_obs       0.720         1.000     -0.486
kalmte_obs         -0.535        -0.486      1.000
OpmerkingVragen 3.1.a

a) Wat is de convergente correlatie (rr tussen alarm_score en alertheid_obs)?

b) Wat is de divergente correlatie (rr tussen alarm_score en kalmte_obs)?

c) Past dit patroon bij wat je zou verwachten als de BAV alertheid meet?

d) Wat zou je concluderen als de divergente correlatie r=−.05r = -.05 was geweest in plaats van duidelijk negatief?

a) rconvergent≈.72r_{\text{convergent}} \approx .72 — sterk positief. Past bij een test die alertheid meet.

b) rdivergent≈−.53r_{\text{divergent}} \approx -.53 — duidelijk negatief. Past bij een test die het tegenovergestelde van kalmte meet (alertheid en kalmte zijn conceptueel tegengesteld).

c) Ja. Convergent hoog (>.50> .50), divergent duidelijk negatief — sterke construct-validity-aanwijzingen.

d) Een correlatie van −.05-.05 tussen alarm en kalmte zou suggereren dat de BAV niet echt het tegenovergestelde van kalmte meet — misschien meet hij iets anders dan alertheid (algemene fysieke activatie? motor-reactie?). De divergent-validity zou dan zwak zijn — de constructen zijn niet duidelijk uitgesplitst.

APA-stijl.

Construct-validity van de Bos-Alarm-Vragenlijst (BAV) werd onderzocht via correlaties met twee andere maten (N=200N = 200). De convergente correlatie met een onafhankelijke alertheid-observatie was sterk, r=.72r = .72, p<.001p < .001. De divergente correlatie met een kalmte-maat was duidelijk negatief, r=−.53r = -.53, p<.001p < .001. Dit patroon ondersteunt de claim dat de BAV alertheid meet en niet alleen een algemene-activatie-construct.

T2 — Convergent én divergent — wanneer mag je tevreden zijn?

OpmerkingVraag T2 — Pen-en-papier

Een onderzoeker rapporteert voor een nieuwe stress-schaal:

  • rconvergent=.55r_{\text{convergent}} = .55 (met een gevestigde stress-meting)
  • rdivergent=.42r_{\text{divergent}} = .42 (met een depressie-meting)

a) Hoe interpreteer je dit patroon?

b) Welke twee verklaringen zijn mogelijk?

c) Wat zou je willen zien om beter overtuigd te raken?

d) Stel het patroon was: rconvergent=.55r_{\text{convergent}} = .55, rdivergent=.15r_{\text{divergent}} = .15. Wat is dan de conclusie?

a) Het convergent-getal is hoog, maar het divergent-getal is bijna even hoog. Onvoldoende discriminatie tussen stress en depressie — de schaal meet iets dat met beide samenhangt.

b) (1) De constructen zijn echt verwant — stress en depressie correleren in de natuur (vaak r≈.50r \approx .50). De schaal volgt die natuur. (2) De schaal is niet specifiek — hij meet algemene-distress-zonder-onderscheid. Welke van beide klopt: lastig zonder verdere data.

c) Multitrait-multimethod-matrix (Campbell & Fiske, 1959): meerdere constructen meten met meerdere methoden, en kijken of de constructen sterker samenhangen binnen dan tussen methoden. Plus: factor-analyse om dimensionaliteit expliciet te toetsen (komt in thema 5).

d) Wel-divergent. rconvergent=.55r_{\text{convergent}} = .55 is acceptabel, rdivergent=.15r_{\text{divergent}} = .15 is laag genoeg om te zeggen: schaal meet stress, niet algemeen-distress.

Inzicht. Convergent hoog op zichzelf is niet genoeg — moet altijd vergeleken met divergent. Het verschil tussen beide is de eigenlijke discriminatie-claim.

3.1.b Visuele inspectie — twee scatters

Toon code (twee scatterplots)
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))

plot(bos_alarm_test$alarm_score, bos_alarm_test$alertheid_obs,
     pch = 16, col = adjustcolor("#117733", alpha.f = 0.5),
     xlab = "BAV alarm-score", ylab = "Alertheid-observatie", ylim = c(0, 100),
     main = "Convergent — verwacht hoog positief")
abline(lm(alertheid_obs ~ alarm_score, data = bos_alarm_test),
       col = "#CC3311", lwd = 2)

plot(bos_alarm_test$alarm_score, bos_alarm_test$kalmte_obs,
     pch = 16, col = adjustcolor("#882255", alpha.f = 0.5),
     xlab = "BAV alarm-score", ylab = "Kalmte-observatie", ylim = c(0, 100),
     main = "Divergent — verwacht negatief")
abline(lm(kalmte_obs ~ alarm_score, data = bos_alarm_test),
       col = "#CC3311", lwd = 2)

Toon code (twee scatterplots)
par(mfrow = c(1, 1))

3.2 Criterion-validiteit en classification table

Voorspelt de test wie er echt snel reageert?

“Nu de echte test,” zei de vos. “Niet correlatie met een andere lijst — een echte classificatie. Dier reageert echt snel of niet. Vraag: voorspelt mijn vragenlijst dat?”

Positief betekent hier: alarm-snel. De test moet de snelle dieren vinden, dus snel staat in de tabel waar in kader B positief staat.

# Cutoff op alarm_score (mediaan, kan ook ergens anders).
cutoff <- median(bos_alarm_test$alarm_score)
cat("Cutoff (median):", round(cutoff, 2), "\n")
Cutoff (median): 50.25 
bos_alarm_test$voorspeld <- factor(
  ifelse(bos_alarm_test$alarm_score >= cutoff, "snel", "traag"),
  levels = c("snel", "traag")
)
# Positief eerst, ook bij de werkelijkheid -- dan ziet de tabel eruit als kader B.
bos_alarm_test$werkelijk <- factor(bos_alarm_test$ware_alarm,
                                   levels = c("snel", "traag"))

# Classification table. Wat je eerst noemt, komt in de rijen: dus werkelijk
# eerst, voorspeld daarna. Draai je ze om, dan draait de tabel mee.
ct <- table(Werkelijk = bos_alarm_test$werkelijk,
            Voorspeld = bos_alarm_test$voorspeld)
ct
         Voorspeld
Werkelijk snel traag
    snel    68    18
    traag   32    82
# De grens is één getal; daar komt geen dplyr aan te pas.
cutoff <- median(bos_alarm_test$alarm_score)
cat("Cutoff (median):", round(cutoff, 2), "\n")
Cutoff (median): 50.25 
# mutate() zet de twee kolommen erbij, en if_else() doet wat ifelse() bij
# base R doet.
bos_alarm_test <- bos_alarm_test |>
  mutate(
    voorspeld = factor(if_else(alarm_score >= cutoff, "snel", "traag"),
                       levels = c("snel", "traag")),
    werkelijk = factor(ware_alarm, levels = c("snel", "traag"))
  )

# ct wordt een gewone table(), net als bij base R: hieronder pakt de code er
# losse cellen uit met ct["snel", "snel"].
#
# select() kiest de twee kolommen, werkelijk eerst, en noemt ze meteen
# Werkelijk en Voorspeld.
ct <- bos_alarm_test |>
  select(Werkelijk = werkelijk, Voorspeld = voorspeld) |>
  table()
ct
         Voorspeld
Werkelijk snel traag
    snel    68    18
    traag   32    82

R zet de twee namen op een eigen plek: Werkelijk links, boven de rijnamen, en Voorspeld erboven, boven de kolomnamen. De regel Werkelijk snel traag betekent dus niet dat Werkelijk naar rechts loopt; snel en traag zijn daar de kolommen van Voorspeld.

# ct[werkelijk, voorspeld] -- eerst de rij, dan de kolom.
TP <- ct["snel",  "snel"]    # echt snel,  test zegt snel
FN <- ct["snel",  "traag"]   # echt snel,  test zegt traag: gemist
FP <- ct["traag", "snel"]    # echt traag, test zegt snel:  vals alarm
TN <- ct["traag", "traag"]   # echt traag, test zegt traag

sens <- TP / (TP + FN)
spec <- TN / (TN + FP)
PPV  <- TP / (TP + FP)
NPV  <- TN / (TN + FN)
PAC  <- (TP + TN) / sum(ct)

round(c(Sensitivity = sens, Specificity = spec,
        PPV = PPV, NPV = NPV, PAC = PAC), 3)
Sensitivity Specificity         PPV         NPV         PAC 
      0.791       0.719       0.680       0.820       0.750 
OpmerkingVragen 3.2

a) Wat is sensitivity? Specificity?

b) Wat is PPV? NPV?

c) Wat is PAC en wat is het verschil met sensitivity?

d) De cutoff staat nu op de mediaan. Wat verandert er als je hem verhoogt naar het 75ste percentiel?

a) Sensitivity =68/(68+18)=68/86≈.79= 68 / (68 + 18) = 68 / 86 \approx .79 — gegeven dat een dier echt snel-reagerend is, vindt de test dat in 79%79\% van de gevallen. Specificity =82/(82+32)=82/114≈.72= 82 / (82 + 32) = 82 / 114 \approx .72 — gegeven dat een dier echt traag-reagerend is, klassificeert de test dat correct in 72%72\% van de gevallen.

b) PPV =68/(68+32)=68/100=.68= 68 / (68 + 32) = 68 / 100 = .68 — gegeven dat de test “snel” zegt, klopt dat in 68%68\% van de gevallen. Zelfde teller als de sensitivity, andere noemer: de kolom in plaats van de rij. NPV =82/(82+18)=82/100=.82= 82 / (82 + 18) = 82 / 100 = .82 — gegeven dat de test “traag” zegt, klopt dat in 82%82\%.

c) PAC =(68+82)/200=.75= (68 + 82) / 200 = .75 — overall classificatie-accuraatheid. Verschil met sensitivity: PAC kijkt naar alle correcte classificaties (TP + TN) gedeeld door totaal. Sensitivity kijkt alleen naar de rij van de echt positieven. Bij scheve klassen (bv. 90%90\% negatief) kan PAC misleidend zijn — altijd “negatief” zeggen geeft 90%90\% PAC, maar sensitivity =0%= 0\%.

d) Cutoff verhogen → minder dieren als “snel” geklassificeerd → specificity stijgt (minder false positives), sensitivity daalt (meer echte snel-reagerenden missen). PPV stijgt waarschijnlijk (de dieren die je wel als snel classificeert zijn extremer); NPV daalt iets. PAC kan beide kanten op afhankelijk van base rate.

APA-stijl.

Met een cutoff op de mediaan (Mdn=50.25Mdn = 50.25) classificeerde de Bos-Alarm-Vragenlijst 200 dieren in snel-reagerend (n=100n = 100) versus traag-reagerend (n=100n = 100). Vergelijking met de geobserveerde ware-alarm-status leverde sensitivity =.79= .79, specificity =.72= .72, PPV =.68= .68, NPV =.82= .82 en PAC =.75= .75. De BAV vertoont matige criterion-validity bij deze cutoff; verschuiving naar een strenger criterium zou specificity verbeteren ten koste van sensitivity.

T3 — Sensitivity vs Specificity — vier scenario’s

OpmerkingVraag T3 — Pen-en-papier

Voor elk scenario: welke ratio is belangrijker?

a) Een psychiater screent 50005000 kinderen op autisme-symptomen. Doel: niemand mag worden gemist; vervolgconsult is laagdrempelig.

b) Een psychiater stelt een definitieve PTSS-diagnose. Gevolgen: medicatie, etiket, verzekerings-impact.

c) Een psycholoog meet bij 8080 cliënten een burn-out-risico-score, en gebruikt die als triage voor wachtlijst-prioriteit.

d) Een onderzoeker selecteert “extreem hoog scorers” voor een interventie-studie (n=30n = 30 per groep).

a) Sensitivity belangrijker. Missen is de fout — false negatives blijven onbehandeld. Iets meer false positives (vervolgconsult niet nodig) is laagdrempelig.

b) Specificity belangrijker. Onterechte diagnose is duur (medicatie, etiket, verzekerings-impact). Liever wat sensitivity opofferen.

c) Beide ongeveer even belangrijk; balans of F1F1-score relevant. Bij triage kun je niet iedereen op de wachtlijst zetten (capaciteit) maar ook niemand met echt risico missen.

d) Specificity belangrijker. Doel: alleen echte extremen in de groep. False positives verwateren de “extreme” studiegroep.

Vuistregel. Missen erger dan vals alarm = sensitivity. Vals alarm erger dan missen = specificity. Vrijwel alle real-world toepassingen vereisen een keuze; geen formule lost het op.

T4 — PPV en NPV — base rate effect

OpmerkingVraag T4 — Pen-en-papier

Een test heeft sensitivity =.90= .90 en specificity =.90= .90.

a) Bij een base rate van 50%50\% (helft positief): wat is PPV?

b) Bij een base rate van 1%1\% (zeldzame ziekte, 11 op 100100): wat is PPV?

c) Wat zegt dit verschil?

d) Sensitivity en specificity hangen niet af van base rate — waarom PPV en NPV wel?

Bayes-formule: PPV=sens⋅base ratesens⋅base rate+(1−spec)⋅(1−base rate)\text{PPV} = \frac{\text{sens} \cdot \text{base rate}}{\text{sens} \cdot \text{base rate} + (1 - \text{spec}) \cdot (1 - \text{base rate})}.

a) PPV=(.90⋅.50)/(.90⋅.50+.10⋅.50)=.45/.50=.90\text{PPV} = (.90 \cdot .50) / (.90 \cdot .50 + .10 \cdot .50) = .45 / .50 = .90. Negen op tien positieve uitslagen kloppen.

b) PPV=(.90⋅.01)/(.90⋅.01+.10⋅.99)=.009/.108=.083\text{PPV} = (.90 \cdot .01) / (.90 \cdot .01 + .10 \cdot .99) = .009 / .108 = .083. Slechts 8%8\% van positieve uitslagen klopt — 92%92\% false positives.

c) Bij lage base rate is PPV laag, ook al is de test op zich goed (sens en spec hoog). De meerderheid van positieve uitslagen is false positive omdat er gewoon meer negatieven zijn die kunnen misclassificeren.

d) Sensitivity en specificity zijn conditional op de ware status: gegeven dat iemand positief is / gegeven dat iemand negatief is. Die kansen zijn eigenschappen van de test, niet van de populatie. PPV en NPV zijn conditional op de testuitslag: gegeven dat de test positief is / gegeven dat de test negatief is. Die kansen hangen af van hoeveel positieven er überhaupt in de populatie zijn.

Inzicht. “Goed-werkende test” (sensitivity en specificity hoog) is geen garantie voor “betrouwbare diagnose” (hoog PPV) als de ziekte zeldzaam is. Vandaar de waarschuwing tegen massaal screenen op zeldzame aandoeningen.

T5 — Cutoff verschuiven en de ROC-curve

OpmerkingVraag T5 — Pen-en-papier

Een test heeft een continue score. Door de cutoff te verschuiven verandert het sensitivity/specificity-paar.

a) Als je de cutoff verlaagt (meer dieren positief gelabeld): wat gebeurt met sensitivity? Specificity?

b) En als je hem verhoogt?

c) Wat is een ROC-curve?

d) Wat is AUC (area under the ROC curve)? Welk getal zou je willen?

a) Cutoff lager → meer “positieven” geklassificeerd → sensitivity stijgt (meer echte positieven gevangen), specificity daalt (meer false positives).

b) Cutoff hoger → omgekeerd. Sensitivity daalt, specificity stijgt.

c) ROC-curve (Receiver Operating Characteristic): plot van sensitivity (Y-as) tegen 1−1 - specificity (X-as) bij elke mogelijke cutoff. Toont de trade-off visueel: hoe hoger de curve naar de linkerbovenhoek, hoe beter de test.

d) AUC = oppervlakte onder de ROC-curve. AUC =1.0= 1.0 = perfecte test. AUC =0.5= 0.5 = puur toevallig (de ROC-curve loopt over de diagonaal). Vuistregels: AUC >.80> .80 = goed; .70.70-.80.80 = acceptabel; <.70< .70 = matig.

Inzicht. Een ROC-curve laat zien dat sensitivity en specificity samenhangen — geen vrije parameters. AUC is een cutoff-vrije maat: hij vat de hele ROC-curve samen. Vandaar de populariteit in machine learning en diagnostische test-validatie.

3.A Correctie voor attenuatie — handle with care

De vos roept iets, en het klinkt als wensdenken

“En als ik weet dat mijn schaal niet perfect betrouwbaar is?” vroeg de bunzing.

“Dan is je geobserveerde correlatie kleiner dan de werkelijke,” zei de vos. “Meetfout dempt elke samenhang.”

“Kan je dat corrigeren?”

“Technisch wel. Maar daar wil ik even over snauwen.”

Wat is correctie voor attenuatie?

Wanneer je een correlatie meet tussen XX en YY, en beide bevatten meetfout, dan onderschat je de “ware” correlatie tussen de onderliggende constructen. De geobserveerde correlatie is gedempt (afgenomen, attenuated) door meetfout. De Spearman-correctie:

rxy*=rxyrxx⋅ryy r^*_{xy} = \frac{r_{xy}}{\sqrt{r_{xx} \cdot r_{yy}}}

Met rxxr_{xx} en ryyr_{yy} de reliability van XX en YY (Cronbach’s α\alpha als proxy).

Voorbeeld: rxy=.50r_{xy} = .50, rxx=.80r_{xx} = .80, ryy=.85r_{yy} = .85.

rxy*=.50.80⋅.85=.50.68=.50.825≈.606 r^*_{xy} = \frac{.50}{\sqrt{.80 \cdot .85}} = \frac{.50}{\sqrt{.68}} = \frac{.50}{.825} \approx .606

Geobserveerd r=.50r = .50; gecorrigeerd r*=.61r^* = .61. Aha! Sterker.

WaarschuwingPas op — de correctie is een schatting onder zware aannames

Correctie voor attenuatie veronderstelt:

  1. Klassieke testtheorie geldt — meetfout is random, ongecorreleerd met ware score.
  2. Reliability-schatting is precies — bij α=.80\alpha = .80 veronderstelt de formule dat de ware reliability exact .80.80 is. Als α\alpha in werkelijkheid .70.70 is, krijg je een fout antwoord.
  3. Lineaire samenhang — geen niet-lineaire effecten, geen interactie.

Bij lage reliability (α<.60\alpha < .60) wordt de correctie-factor explosief instabiel. Een kleine fout in α\alpha leidt tot een grote fout in r*r^*. Plus: gecorrigeerde correlaties kunnen >1.0> 1.0 uitkomen — wiskundig onmogelijk, maar in praktijk niet ongebruikelijk bij kleine steekproeven met lage reliability.

BelangrijkDon’t: correctie voor attenuatie als algemene “boost” voor je correlaties

Hier wordt het tijd om recht uit te snauwen.

Correctie voor attenuatie is een rekentruc die zegt: “Stel je voor dat onze metingen perfect betrouwbaar waren — dan zou onze correlatie hoger zijn.” Dat klinkt als een uitspraak over de werkelijkheid. Het is geen uitspraak over de werkelijkheid. Het is een conditional gedachte-experiment: als de wereld anders was, dan…. De wereld is zoals hij is — de correlatie in jouw studie is wat je gemeten hebt.

In echte artikelen staat dan zoiets:

“De gecorrigeerde correlatie tussen werkstress en burn-out was r*=.87r^* = .87, wat duidt op een sterke onderliggende samenhang.”

Vraag aan jezelf: wat hadden ze écht gemeten? r=.50r = .50. Met αx=.60\alpha_x = .60, αy=.55\alpha_y = .55. Reken het uit: r*=.50/.33=.50/.574=.87r^* = .50 / \sqrt{.33} = .50 / .574 = .87. Klopt op de cijfers. Maar dat écht-gemeten getal — .50.50 — verdwijnt in de gecorrigeerde versie, en niemand vermeldt dat de reliability van zowel werkstress- als burn-out-meting twijfelachtig was. Wat lezers zien: “sterke onderliggende samenhang.” Wat de studie eigenlijk toont: “matige samenhang die we hebben opgepoetst door te delen door wortel-onbetrouwbaarheid.”

Dit is niet hetzelfde als wetenschappelijk frauduleus zijn — de correctie is wiskundig legitiem. Het is wel dichterbij: kennis-presentatie waarbij de zwakheid van de meting wordt verstopt onder een formule die “corrigeert”.

Wie een gecorrigeerde correlatie rapporteert moet altijd:

  1. De rauwe rr erbij melden (“rauw” en “gecorrigeerd” naast elkaar).
  2. De gebruikte reliabilities rxxr_{xx} en ryyr_{yy} expliciet noemen.
  3. De standaardfout of CI rond de gecorrigeerde schatting rapporteren — die is bij lage reliability enorm.
  4. Een interpretatie geven van de gecorrigeerde correlatie als theoretische schatting, niet als de echte studie-uitkomst.

Wie dat niet doet, en met “r*=.87r^* = .87” zwaait alsof het een echte uitkomst is, doet aan statistisch theater. Leg zo iemand een kopie van Cronbach (1951) op het bureau, opengeslagen op pagina 309309, en vraag of daar staat dat correctie voor attenuatie de echte correlatie oplevert.

Hint: dat staat er niet.

# Voorbeeld: alarm_score (BAV) en alertheid_obs.
# Stel: BAV alpha = .85, alertheid_obs alpha = .75 (hypothetisch).
r_xy <- cor(bos_alarm_test$alarm_score, bos_alarm_test$alertheid_obs)
r_xx <- 0.85   # hypothetische reliability BAV.
r_yy <- 0.75   # hypothetische reliability alertheid_obs.

r_corrected <- r_xy / sqrt(r_xx * r_yy)
round(c(r_observed = r_xy, r_corrected = r_corrected), 3)
 r_observed r_corrected 
      0.720       0.902 
WaarschuwingDie twee alpha’s zijn verzonnen

rxyr_{xy} komt uit de data — 200 dieren, de BAV tegen de alertheid-observatie. De twee reliabilities eronder niet: rxx=.85r_{xx} = .85 en ryy=.75r_{yy} = .75 zijn hypothetische waarden. Deze dataset heeft geen losse items, en zonder items valt er geen Cronbach’s α\alpha te berekenen.

Dat maakt het voorbeeld niet minder bruikbaar — het laat zien wát de correctie doet — maar het gecorrigeerde getal dat er zo meteen uit komt is dus een rekensom op een aanname, niet een uitkomst van deze studie. Dat is precies het onderscheid waar de rest van deze sectie over gaat.

OpmerkingVraag 3.A

a) Wat is robservedr_{\text{observed}} in dit voorbeeld? Wat is r*r^*?

b) Bereken r*r^* als rxx=.60r_{xx} = .60 en ryy=.60r_{yy} = .60 (lage reliability).

c) Wat is je conclusie over wat je in een paper rapporteert?

d) Schrijf één APA-zin die eerlijk rapporteert, met rauw + gecorrigeerd + reliabilities erbij.

a) robserved≈.72r_{\text{observed}} \approx .72, r*=.72/.85⋅.75=.72/.798≈.90r^* = .72 / \sqrt{.85 \cdot .75} = .72 / .798 \approx .90. Een aanzienlijke “boost”, gerekend met de twee hypothetische reliabilities uit het codeblok hierboven. Een lezer die alleen r*=.90r^* = .90 ziet, denkt: “vrijwel perfect samenhangend”. De rauwe data zegt: “sterk samenhangend, maar niet vrijwel perfect.”

b) r*=.72/.60⋅.60=.72/.60=1.20r^* = .72 / \sqrt{.60 \cdot .60} = .72 / .60 = 1.20. Wiskundig onmogelijk — een correlatie kan niet groter dan 11 zijn. Bij lage reliability krachtigere overshoot. Dit is precies waarom de Don’t-callout boos doet.

c) Altijd rauw én gecorrigeerd rapporteren, met reliability-bronnen erbij. Een gecorrigeerde correlatie zonder context is misleidend.

d) Voorbeeldzin:

De geobserveerde correlatie tussen BAV en alertheid-observatie was r=.72r = .72, p<.001p < .001 (N=200N = 200). Onder correctie voor attenuatie (rxx=.85r_{xx} = .85 voor BAV en ryy=.75r_{yy} = .75 voor alertheid-observatie, beide Cronbach’s α) bedraagt de gecorrigeerde correlatie r*=.90r^* = .90. We rapporteren de gecorrigeerde schatting als hypothetische bovengrens van de werkelijke construct-correlatie; de geobserveerde r=.72r = .72 blijft het betrouwbare studie-resultaat.

Inzicht. Het VPRO-principe: een eerlijke rapportage laat de truc zien, niet alleen het resultaat. Wie alleen het gecorrigeerde getal rapporteert verbergt de bron.

Examen-stijl vragen

Vier kortere oefeningen plus één mini-practical

OpmerkingVraag E1 — Classification table interpreteren

Een classification table:

Voorspeld positief Voorspeld negatief
Werkelijk positief 4040 1010
Werkelijk negatief 2020 130130

Welke conclusie?

  1. Sensitivity =.80= .80, specificity ≈.87\approx .87.
  2. Sensitivity =.67= .67, specificity ≈.87\approx .87.
  3. PPV =.80= .80, NPV ≈.87\approx .87.
  4. De test is onbruikbaar omdat PAC onder .90.90 ligt.

a) Sensitivity =40/(40+10)=.80= 40 / (40 + 10) = .80. Specificity =130/(130+20)≈.87= 130 / (130 + 20) \approx .87.

Optie b: sensitivity is .80.80, niet .67.67. Die .67.67 krijg je als je in de kolom voorspeld positief blijft in plaats van in de rij werkelijk positief: 40/6040/60, en dat is de PPV. Optie c zet de getallen van optie a onder de verkeerde naam. De echte PPV is 40/60≈.6740/60 \approx .67 en de echte NPV 130/140≈.93130/140 \approx .93, niet .80.80 en .87.87. Optie d: PAC =(40+130)/200=.85= (40+130)/200 = .85 — niet onbruikbaar, gewoon niet uitstekend.

Dezelfde tabel andersom. Zo kun je hem ook tegenkomen, met de voorspelling in de rijen:

Werkelijk positief Werkelijk negatief
Voorspeld positief 4040 2020
Voorspeld negatief 1010 130130

De 1010 en de 2020 zijn van plaats gewisseld, de som niet. Sensitivity deelt nog steeds door alle echt positieven, en die staan nu in de eerste kolom: 40/(40+10)=.8040/(40 + 10) = .80.

OpmerkingVraag E2 — Convergent vs divergent

Een nieuwe sociale-fobie-vragenlijst (SFV) wordt getest op construct-validiteit:

  • rr(SFV, andere SF-vragenlijst) =.68= .68
  • rr(SFV, depressie-meting) =.42= .42
  • rr(SFV, IQ-test) =.03= .03

Welke conclusie?

  1. Sterk convergent en duidelijk divergent (alleen IQ).
  2. Onvoldoende discriminatie van depressie.
  3. Test heeft alleen content-validiteit.
  4. Meting is statistisch ongeldig.

b) Convergent r=.68r = .68 is sterk. Divergent r=.42r = .42 met depressie is te hoog — sociale fobie en depressie verschillen, maar als je nieuwe meting bijna even hoog correleert met depressie als met andere sociale-fobie-meting, kun je de constructen niet onderscheiden. Wel duidelijke discriminatie van IQ.

Optie a: r=.42r = .42 is geen “duidelijke discriminatie”. Optie c: er is wel construct-data, niet alleen content. Optie d: “statistisch ongeldig” is geen betekenisvol predikaat — wel: convergent OK, divergent zwak voor de specifieke vergelijking met depressie.

OpmerkingVraag E3 — Sensitivity, specificity en cutoff-verschuiving

Een test op een continue score heeft bij cutoff =50= 50: sensitivity =.90= .90, specificity =.60= .60. Bij cutoff =70= 70: sensitivity =.55= .55, specificity =.95= .95.

Welke cutoff is geschikt voor screening op zeldzame aandoeningen waar missen kostbaar is?

  1. Cutoff =50= 50 (hoge sensitivity).
  2. Cutoff =70= 70 (hoge specificity).
  3. Cutoff doet er niet toe.
  4. Beide gelijkwaardig — kies op basis van PAC.

a) Bij screening op zeldzame aandoening: missen (FNFN) kostbaar — false negatives zijn de gevreesde fout. Hogere sensitivity vereist; cutoff =50= 50 (.90.90 sensitivity) is geschikt. Lagere specificity (.60.60) accepteer je: false positives gaan door naar definitief onderzoek, daar wordt het verder uitgezocht.

OpmerkingVraag E4 — Correctie voor attenuatie

Een onderzoeker rapporteert:

“De gecorrigeerde correlatie tussen werkstress en burn-out was r*=.91r^* = .91.”

Reliabilities: rxxr_{xx} (werkstress) =.65= .65, ryyr_{yy} (burn-out) =.60= .60.

a) Bereken de rauwe correlatie rxyr_{xy} die hieraan voorafging.

b) Wat is je oordeel over deze rapportage?

c) Welke twee elementen ontbreken in de zin?

a) rxy=r*⋅rxx⋅ryy=.91⋅.65⋅.60=.91⋅.39=.91⋅.624≈.57r_{xy} = r^* \cdot \sqrt{r_{xx} \cdot r_{yy}} = .91 \cdot \sqrt{.65 \cdot .60} = .91 \cdot \sqrt{.39} = .91 \cdot .624 \approx .57. Een matige correlatie. De r*r^*-presentatie maakt er een vrijwel perfecte samenhang van.

b) Misleidend. Een rauwe correlatie van .57.57 is een ander verhaal dan een gecorrigeerde van .91.91. Zonder beide getallen + reliabilities erbij krijgt de lezer een vertekend beeld.

c) Ontbreken: (1) de rauwe rr, (2) de reliability-bronnen (α\alpha-waarden) gebruikt voor correctie. Plus eventueel: NN, pp-waarde, betrouwbaarheids-interval rond r*r^*.

R-practical opdrachtje

De das en de voorspelling

OpmerkingVraag E5 — Mini-classificatie bij de das

Een ecoloog meet bij 80 dassen een continue voorspelscore en een binaire uitkomst (ja/nee). De dataset staat in data/das_voorspelling.RData met das_id, voorspelscore, en uitkomst (factor: nee/ja).

a) Bereken de mediaan van voorspelscore als cutoff. Maak de classification table.

b) Bereken sensitivity, specificity, PPV, NPV en PAC.

c) Verschuif de cutoff naar het 75ste percentiel. Wat verandert er?

d) Schrijf één APA-zin met je conclusie.

load("data/das_voorspelling.RData")
str(das_voorspelling)
'data.frame':   80 obs. of  3 variables:
 $ das_id       : Factor w/ 80 levels "b01","b02","b03",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ voorspelscore: num  28.8 41.1 45.1 46.8 11.4 61.4 33.3 53.2 63.5 46.5 ...
 $ uitkomst     : Factor w/ 2 levels "nee","ja": 1 1 1 2 1 2 1 2 2 1 ...
# (a) Cutoff op mediaan, classification table. Ja is positief -- ja is wat de
# test moet vinden -- dus ja eerst, ook al staat in het bestand nee eerst.
cutoff_med <- median(das_voorspelling$voorspelscore)
das_voorspelling$voorspeld_med <- factor(
  ifelse(das_voorspelling$voorspelscore >= cutoff_med, "ja", "nee"),
  levels = c("ja", "nee")
)
das_voorspelling$werkelijk <- factor(das_voorspelling$uitkomst,
                                     levels = c("ja", "nee"))
ct_med <- table(Werkelijk = das_voorspelling$werkelijk,
                Voorspeld = das_voorspelling$voorspeld_med)
ct_med
         Voorspeld
Werkelijk ja nee
      ja  29   9
      nee 11  31
# (a) Cutoff op mediaan, classification table. Zelfde route als bij 3.2, ja eerst.
cutoff_med <- median(das_voorspelling$voorspelscore)
das_voorspelling <- das_voorspelling |>
  mutate(
    voorspeld_med = factor(if_else(voorspelscore >= cutoff_med, "ja", "nee"),
                           levels = c("ja", "nee")),
    werkelijk = factor(uitkomst, levels = c("ja", "nee"))
  )
ct_med <- das_voorspelling |>
  select(Werkelijk = werkelijk, Voorspeld = voorspeld_med) |>
  table()
ct_med
         Voorspeld
Werkelijk ja nee
      ja  29   9
      nee 11  31
# (b) Zelfde route als bij 3.2.
# ct_med[werkelijk, voorspeld] -- eerst de rij, dan de kolom.
TP <- ct_med["ja",  "ja"]    # echt ja,  test zegt ja
FN <- ct_med["ja",  "nee"]   # echt ja,  test zegt nee: gemist
FP <- ct_med["nee", "ja"]    # echt nee, test zegt ja:  vals alarm
TN <- ct_med["nee", "nee"]   # echt nee, test zegt nee

sens <- TP / (TP + FN)
spec <- TN / (TN + FP)
PPV  <- TP / (TP + FP)
NPV  <- TN / (TN + FN)
PAC  <- (TP + TN) / sum(ct_med)

round(c(Sensitivity = sens, Specificity = spec,
        PPV = PPV, NPV = NPV, PAC = PAC), 3)
Sensitivity Specificity         PPV         NPV         PAC 
      0.763       0.738       0.725       0.775       0.750 
# (c) Cutoff op 75ste percentiel.
cutoff_75 <- quantile(das_voorspelling$voorspelscore, 0.75)
das_voorspelling$voorspeld_75 <- factor(
  ifelse(das_voorspelling$voorspelscore >= cutoff_75, "ja", "nee"),
  levels = c("ja", "nee")
)
ct_75 <- table(Werkelijk = das_voorspelling$werkelijk,
               Voorspeld = das_voorspelling$voorspeld_75)
ct_75
         Voorspeld
Werkelijk ja nee
      ja  17  21
      nee  3  39
# Dezelfde vijf als bij (b), nu uit ct_75.
TP <- ct_75["ja",  "ja"]
FN <- ct_75["ja",  "nee"]
FP <- ct_75["nee", "ja"]
TN <- ct_75["nee", "nee"]

sens <- TP / (TP + FN)
spec <- TN / (TN + FP)
PPV  <- TP / (TP + FP)
NPV  <- TN / (TN + FN)
PAC  <- (TP + TN) / sum(ct_75)

round(c(Sensitivity = sens, Specificity = spec,
        PPV = PPV, NPV = NPV, PAC = PAC), 3)
Sensitivity Specificity         PPV         NPV         PAC 
      0.447       0.929       0.850       0.650       0.700 

a) Cutoff op mediaan (48.8548.85); de classification table verdeelt de dassen over 4040 keer voorspeld ja en 4040 keer voorspeld nee.

b) Sens, spec, PPV, NPV, PAC liggen tussen .73.73 en .78.78 — matige tot redelijke voorspelling.

c) Cutoff op 75ste percentiel: minder dieren als “ja” geklassificeerd → specificity stijgt (minder false positives), sensitivity daalt (meer echte ja’s gemist). PPV stijgt vaak; PAC kan beide kanten op.

Bij N=80N = 80 dassen voorspelde de voorspelscore de binaire uitkomst met matige criterion-validity. Op de mediane cutoff: sensitivity =.76= .76, specificity =.74= .74, PPV =.73= .73, NPV =.78= .78, PAC =.75= .75. Bij verschuiving naar het 75ste percentiel verbeterde specificity ten koste van sensitivity, conform de cutoff-trade-off.

R-spiekblad — validity in zes stappen

# 1. Pakketten + data laden -------------------------------------------
library(tidyverse)
load("data/bos_alarm_test.RData")

# 2. Construct-validiteit — convergent en divergent ------------------
cor(bos_alarm_test[, c("alarm_score", "alertheid_obs", "kalmte_obs")])

# Met p-waarden + CI:
cor.test(bos_alarm_test$alarm_score, bos_alarm_test$alertheid_obs)
cor.test(bos_alarm_test$alarm_score, bos_alarm_test$kalmte_obs)

# 3. Cutoff op continue score -----------------------------------------
cutoff <- median(bos_alarm_test$alarm_score)

bos_alarm_test$voorspeld <- factor(
  ifelse(bos_alarm_test$alarm_score >= cutoff, "snel", "traag"),
  levels = c("snel", "traag")
)
bos_alarm_test$werkelijk <- factor(bos_alarm_test$ware_alarm,
                                   levels = c("snel", "traag"))

# 4. Classification table: werkelijk in de rijen ----------------------
ct <- table(Werkelijk = bos_alarm_test$werkelijk,
            Voorspeld = bos_alarm_test$voorspeld)
ct

# 5. Sensitivity, specificity, PPV, NPV, PAC --------------------------
# ct[werkelijk, voorspeld]
TP <- ct["snel",  "snel"]; FN <- ct["snel",  "traag"]
FP <- ct["traag", "snel"]; TN <- ct["traag", "traag"]

sensitivity <- TP / (TP + FN)
specificity <- TN / (TN + FP)
PPV         <- TP / (TP + FP)
NPV         <- TN / (TN + FN)
PAC         <- (TP + TN) / sum(ct)

round(c(Sensitivity = sensitivity, Specificity = specificity,
        PPV = PPV, NPV = NPV, PAC = PAC), 3)

# 6. Correctie voor attenuatie (gebruik met VOORZICHT) -----------------
r_xy        <- cor(bos_alarm_test$alarm_score, bos_alarm_test$alertheid_obs)
r_xx        <- 0.85   # HYPOTHETISCHE reliability BAV -- deze data heeft geen items
r_yy        <- 0.75   # HYPOTHETISCHE reliability alertheid_obs
r_corrected <- r_xy / sqrt(r_xx * r_yy)
c(r_obs = r_xy, r_corrected = r_corrected)

Wat blijft liggen

Onderwerpen die buiten dit thema blijven

Construct + criterion validity met classification table en correctie voor attenuatie is de hoofdingang. Voor verdere studie:

  • Multitrait-multimethod-matrix (Campbell & Fiske 1959) — meerdere constructen × meerdere methoden om convergent en divergent expliciet te toetsen.
  • ROC-curves en AUC — pROC::roc(). Cutoff-vrije evaluatie van diagnostische tests.
  • Likelihood ratios — alternatief voor sensitivity/specificity dat base rate apart houdt.
  • Bayesian updating — bij bekende prior-prevalence en testkenmerken: update naar posterieure waarschijnlijkheid van de aandoening.
  • Net Reclassification Improvement (NRI) — als je twee modellen vergelijkt op classification.
  • Calibratie — niet alleen welke uitkomst voorspeld, maar ook of de voorspelde kansen kloppen.

Voor verdieping: Pepe (2003, The Statistical Evaluation of Medical Tests); Sackett et al. (2000, Evidence-Based Medicine); Streiner et al. (2015).

Aan het eind van de dag

De vos zat tegen de schemer en wachtte. Hij had niet veel meer te zeggen.

“Sensitivity en specificity,” zei hij eindelijk. “PPV en NPV. Vier getallen, twee richtingen door de tabel.”

“En correctie voor attenuatie?” vroeg de bunzing.

“Een goocheltruc,” zei de vos. “Mooi om te kennen, gevaarlijk om te gebruiken. Wie hem inzet zonder zijn rauwe correlatie erbij: ik vind hem.”

“En morgen?”

“PCA,” zei de vos. “Komt de rat tevoorschijn. Veel items, weinig dimensies — hij rangschikt ze.” Hij keek even naar de boom waar de rat zat. “Doe je best.”

De rat blikte even op, en zijn ogen glinsterden. “Eigenwaarden. Eindelijk.”

De bunzing kroop naar zijn hol. Boven het bos werd het donker, en de vos sloop weg, geruisloos zoals altijd.

Verantwoording

Dit oefenboek is geschreven voor studenten die psychometrie leren via R. Het is materiaal van CountCamp, geen officieel onderdeel van een opleiding. De voorbeelden, datasets, vragen en formuleringen zijn origineel; de didactische volgorde is dat bewust niet — die volgt de gangbare opbouw van Nederlandse universitaire psychometrie-cursussen. De gebruikte drempels en formules zijn standaard psychometrische conventies; verwijzingen naar Cronbach (1951), Campbell & Fiske (1959) en Streiner, Norman & Cairney (2015) volgen de gebruikelijke citaatpraktijk in dit veld.

Waar dit thema een eigen weg gaat. De classification table staat hier met de werkelijkheid in de rijen en de voorspelling in de kolommen, en dus sensitiviteit en specificiteit per rij, PPV en NPV per kolom; andere boeken draaien dat om. Eigen is hoe kader B dat laat onthouden — gegeven dat maakt de zaal kleiner, en wat erna komt is waar je door deelt — omdat die regel ook werkt als je de tabel een keer andersom tegenkomt. Eigen keuze is ook hoe streng dit thema doet over correctie voor attenuatie: je leert hier niet alleen de formule, maar ook dat je een gecorrigeerde correlatie nooit in je eentje rapporteert — de rauwe rr ernaast, beide betrouwbaarheden erbij, en een interval eromheen. En dat de twee betrouwbaarheden in 3.A verzonnen zijn, staat er met zoveel woorden bij: deze dataset heeft geen losse items, en dan valt er niets aan interne consistentie te berekenen. De indeling van kader A — content, construct met convergent en divergent, criterion met concurrent en predictive — is de klassieke; die indeling en die namen zijn niet van ons.

Zegt ander materiaal dat je gebruikt iets anders dan dit oefenboek, reken dan met wat daar staat. Meestal is het verschil een afspraak — een drempel, een letter, de naam van een model — en geen rekenfout.

Versie: oktober 2026 — CountCamp Lab

Terug naar boven