library(tidyverse)
library(tidycomm)
library(yardstick)
3 Reliabilität und Validierung
3.1 Intercoder-Reliabilität
Wir beginnen wieder mit dem Laden der benötigten Pakete. Für Reliabilitätsanalysen verwenden wir das Paket tidycomm
. Die Validierung führen wir mit dem Paket yardstick
durch.
Das Paket tidycomm
enthält auch bereits einen Datensatz mit Facebook-Posts, den wir für die Reliabilitätsanalyse verwenden können. Wir schauen uns zunächst die ersten Zeilen an.
fbposts
# A tibble: 270 × 7
post_id coder_id type n_pictures pop_elite pop_people pop_othering
<int> <int> <chr> <int> <int> <int> <int>
1 1 1 photo 1 0 0 0
2 1 2 photo 1 0 0 0
3 1 3 photo 1 0 0 0
4 1 4 photo 1 0 0 0
5 1 5 photo 1 0 0 0 # ℹ 265 more rows
Tidycomm bietet die Funktion test_icr()
, um Intercoder-Reliabilitätstests für mehrere Variablen (Kategorien/Codiereinheiten) gleichzeitig durchzuführen. Die Testdaten müssen im Long-Format vorliegen: eine Spalte für die Einheit (z. B. Artikel 1, Artikel 2), eine für den Codierer (als Name oder numerische ID) und jeweils eine Spalte pro zu testender Variable.
|>
fbposts ::test_icr(unit_var = post_id, coder_var = coder_id) tidycomm
# A tibble: 5 × 8
Variable n_Units n_Coders n_Categories Level Agreement Holstis_CR
* <chr> <int> <int> <int> <chr> <dbl> <dbl>
1 type 45 6 4 nominal 1 1
2 n_pictures 45 6 7 nominal 0.822 0.930
3 pop_elite 45 6 6 nominal 0.733 0.861
4 pop_people 45 6 2 nominal 0.778 0.916
5 pop_othering 45 6 4 nominal 0.867 0.945 # ℹ 1 more variable: Krippendorffs_Alpha <dbl>
Werden keine Skalenniveaus für die Variablen angegeben, dann nimmt die Funktion test_icr
an, dass es sich um nominale Variablen handelt. Mit dem Funktionsargument level()
können wir in der Form c(Variablenname = “Variablenlevel”) die Skalenniveaus der Variablen anpassen. Dies ändert die Berechnungsweise von Krippendorffs Alpha.
|>
fbposts ::test_icr(unit_var = post_id, coder_var = coder_id, levels = c(n_pictures = "ordinal")) tidycomm
# A tibble: 5 × 8
Variable n_Units n_Coders n_Categories Level Agreement Holstis_CR
* <chr> <int> <int> <int> <chr> <dbl> <dbl>
1 type 45 6 4 nominal 1 1
2 n_pictures 45 6 7 ordinal 0.822 0.930
3 pop_elite 45 6 6 nominal 0.733 0.861
4 pop_people 45 6 2 nominal 0.778 0.916
5 pop_othering 45 6 4 nominal 0.867 0.945 # ℹ 1 more variable: Krippendorffs_Alpha <dbl>
Wenn nicht alle Texte und Kategorien von allen Codierern bearbeitet wurden, d.h. fehlende Werte vorkommen, führt dies dazu, dass test_icr()
den Wert NA
für alle Variable, die einen fehlenden Wert enthlalten, zurückgibt. Wir simulieren hier den Fall, dass nicht alle Codierer alle Posts bearbeitet haben, indem wir den Datensatz auf 60% der Daten reduziere und führen den Test erneut durch.
<- fbposts |>
fb_incomplete sample_frac(.6)
|>
fb_incomplete ::test_icr(unit_var = post_id, coder_var = coder_id) tidycomm
# A tibble: 5 × 8
Variable n_Units n_Coders n_Categories Level Agreement Holstis_CR
* <chr> <int> <int> <int> <chr> <lgl> <lgl>
1 type 45 6 4 nominal NA NA
2 n_pictures 45 6 7 nominal NA NA
3 pop_elite 45 6 6 nominal NA NA
4 pop_people 45 6 2 nominal NA NA
5 pop_othering 45 6 3 nominal NA NA # ℹ 1 more variable: Krippendorffs_Alpha <dbl>
3.2 Validierung mit Goldstandard
Für die Validierung maschineller Codierungen benötigen wir einen Goldstandard, von dem wir annehmen, dass er die korrekte Codierung darstellt. Der nachfolgende Daten enthalten die maschniellen Codierungen (observed
) und den Goldstandard (gold
). Codiert wurde, ob der Text Sexismus enthält.
<- read_csv2("https://raw.githubusercontent.com/bachl/raw_data/refs/heads/main/task1_sample.csv") |>
d_sexist mutate(gold = factor(gold), observed = factor(observed))
d_sexist
# A tibble: 200 × 4
text gold observed reason_sexist
<chr> <fct> <fct> <chr>
1 My buddy and wife just stopped by to check on me… not … not sex… The comment …
2 You are obviously illiterate and stupid if you t… not … not sex… The comment …
3 Don't fred, you're a human, not a stonecold trp … not … not sex… The comment …
4 Has Huma been falling off any bridges recently o… not … sexist The comment …
5 [USER] & Anglin are on the same page. But yeah s… not … not sex… The comment … # ℹ 195 more rows
Mit der Funktion conf_mat()
können wir eine Konfusionsmatrix erstellen, um die Übereinstimmung zwischen Goldstandard und maschineller Codierung tabellarisch darzustellen. Diese Matrix ist die Grundlage für die Berechnung verschiedener Validierungsmetriken.
|>
d_sexist conf_mat(truth = gold, estimate = observed)
Truth
Prediction not sexist sexist
not sexist 72 14 sexist 28 86
Wir berechnen nun die Metriken Accuracy, Recall, Precision und das F-Maß, um die Qualität der maschinellen Codierung zu bewerten. Hierfür verwenden wir die Funktion metric_set()
aus dem Paket yardstick
. Wichtig ist, dass wir zuvor die Variablen in Faktoren umgewandelt haben. Das Funktionsargument event_level
gibt nämlich an, welche Ausprägung als positiv (Einordnung als “Sexismus”) oder negativ (Einordnung als “kein Sexismus”) betrachtet wird. Die Ergebnisse ändern sich, je nachdem, ob “Sexismus” oder “kein Sexismus” als positiv bewertet wird. In unserem Fall ist das zweite Level des Faktors (hier Sexismus) als positiv zu definieren.
<- metric_set(accuracy, recall, precision, f_meas)
vali_metrics |>
d_sexist vali_metrics(
truth = gold, estimate = observed,
event_level = "second"
)
# A tibble: 4 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 accuracy binary 0.79
2 recall binary 0.86
3 precision binary 0.754 4 f_meas binary 0.804
Alternativ zu den Metriken aus yardstick
können wir auch Krippendorffs Alpha berechnen, um die Übereinstimmung zwischen Goldstandard und maschineller Codierung zu bewerten. Hierfür müssen wir die Daten wieder in das Long-Format bringen, um anschließend test_icr()
anwenden zu können.
|>
d_sexist pivot_longer(gold:observed, names_to = "coder_id", values_to = "code") |>
test_icr(unit_var = text, coder_var = coder_id, code)
# A tibble: 1 × 8
Variable n_Units n_Coders n_Categories Level Agreement Holstis_CR
* <chr> <int> <int> <int> <chr> <dbl> <dbl>
1 code 200 2 2 nominal 0.79 0.79 # ℹ 1 more variable: Krippendorffs_Alpha <dbl>
3.3 Problematische Codiereinheiten finden
Das Auffinden problematischer Codiereinheiten ist bei zwei Codierern oder dem Vergleich zwischen Goldstandard und maschineller Codierung einfach. Es muss nur nach den Einheiten gesucht werden, bei denen die Codierungen voneinander abweichen. Dies ist ganz einfach mittels filter()
möglich.
|>
d_sexist filter(gold != observed)
# A tibble: 42 × 4
text gold observed reason_sexist
<chr> <fct> <fct> <chr>
1 "Has Huma been falling off any bridges recently … not … sexist "The comment…
2 "Yes, and men should not call women out for dres… not … sexist "The comment…
3 "I've never seen anything like it. At first, i w… not … sexist "The comment…
4 "They call it rape but I bet most of those poor … not … sexist "The comment…
5 "So now all the hens can peck each other [URL]" not … sexist "The comment… # ℹ 37 more rows
Für den Fall, dass es mehr als zwei Codierer gibt, können wir die Anzahl verschiedener Codes pro Variable/Codiereinheit zählen (Uneinigkeit) und die Einheiten nach Uneinigkeit sortieren. Die problematischen Codiereinheiten werden dann als erstes angezeigt.
|>
fbposts group_by(post_id) |>
summarise(pop_elite_codes = n_distinct(pop_elite)) |>
arrange(-pop_elite_codes)
# A tibble: 45 × 2
post_id pop_elite_codes
<int> <int>
1 33 4
2 17 3
3 18 2
4 27 2
5 28 2 # ℹ 40 more rows
Annschließend können einzelne Einheiten betrachtet werden.
|>
fbposts filter(post_id == 33)
# A tibble: 6 × 7
post_id coder_id type n_pictures pop_elite pop_people pop_othering
<int> <int> <chr> <int> <int> <int> <int>
1 33 1 photo 1 9 0 0
2 33 2 photo 1 0 0 0
3 33 3 photo 1 1 1 0
4 33 4 photo 1 1 0 0
5 33 5 photo 1 9 0 0 # ℹ 1 more row