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 rowsTidycomm 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 |>
tidycomm::test_icr(unit_var = post_id, coder_var = coder_id)# 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 |>
tidycomm::test_icr(unit_var = post_id, coder_var = coder_id, levels = c(n_pictures = "ordinal"))# 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.
fb_incomplete <- fbposts |>
sample_frac(.6)
fb_incomplete |>
tidycomm::test_icr(unit_var = post_id, coder_var = coder_id)# 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.
d_sexist <- read_csv2("https://raw.githubusercontent.com/bachl/raw_data/refs/heads/main/task1_sample.csv") |>
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 rowsMit 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 86Wir 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.
vali_metrics <- metric_set(accuracy, recall, precision, f_meas)
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.804Alternativ 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 rowsFü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 rowsAnnschließ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