levels(bsp$Participant)
NULL
Und wieder: Daten auf Plausibilität prüfen
Zeitplanung:
Anzahl Bedingungen, Trials pro Bedingung etc. direkt zu Anfang prüfen
Plotten der Daten einzelner Vpn direkt zu Anfang programmieren
Anzahl Vpn, Gesamtzahl Trials etc. direkt am Ende der Erhebung prüfen!
Plausibilität haben wir doch schon geprüft?
Wie schon zu Beginn der Datenerhebung muss man bei jedem Auswertungsschritt prüfen, ob das, was man in den Daten hat, überhaupt Sinn macht. Es ist also kein Fehler, dass unser Dokument 2 Kapitel über Plausibilität hat.
Fokus zu Beginn: Sicherstellen dass das Experimentalprogramm richtig funktioniert, die Logfiles korrekt erstellt werden und wir alles richtig durchdacht und geplant haben.
Fokus jetzt: Prüfen, ob alle Daten vollständig erhoben, gespeichert, übertragen etc. wurden und die uns vorliegenden Daten so aussehen, wie wir es erwarten würden - ob also die Erhebung richtig geklappt hat.
Ganz zu Anfang schauen wir:
Außerdem sollte man sich die Daten der einzelnen Vpn mit ggplot plotten (visualisieren) und ansehen. Hierbei sollte man nach ungewöhnlich aussehenden Vpn suchen. Ungewöhnlich heißt z.B.:
Wie man die die Daten pro Versuchsperson plottet, behandeln wir in Chapter 7 .
Der “levels”-Befehl zegt an, welche Levels (also Faktorabstufungen) ein Faktor hat.
levels(bsp$Participant)
NULL
Wie viele Vpn habe ich im Datensatz?
length( levels( ds$participant ) ) # Variante 1: schauen, wie lang der eben Levels-Vektor ist
[1] 21
length( unique( ds$participant ) ) # Variante 2: anzeigen lassen, welche Faktorlevels es in allen Zeilen überhaupt gibt und davon die Länge ausgeben
[1] 21
Wir können so sehen, dass 21 Vpn enthalten sind.
Wenn man weiß, welche Faktoren manipuliert wurden, kann man berechnen, wie viele Zeilen in ds drin sein sollten. Hier war das:
21 * 2 * 2 * 2 * 8 * 6 * 4
[1] 32256
# 21 Vpn x 2 Posture x 2 Faktor A x 2 Faktor B x 8 SOA x 6 Stimulus-Stärken x 4 Wiederholungen
Nun können wir diese Anzahl mit der Anzahl an Zeilen in unserem Datensatz vergleichen. Die Funktion hierfür ist “nrow” (number of rows).
Mn kann auch schauen, was im Environment (in RStudio rechter oberer Bereich des Gesamtfensters) angezeigt wird, denn dort steht ebenfalls die Anzahl an Variablen und Zeilen von data.frames.
nrow( ds )
[1] 32455
Wie man sieht: wir haben mehr als berechnet. Seltsam. Also mal nach einzelnen Vpn aufschlüsseln.
<- ds %>%
sanCheck group_by( participant ) %>%
summarise(
n()
)
sanCheck
# A tibble: 21 × 2
participant `n()`
<fct> <int>
1 B006b-08 1536
2 B006b-09 1536
3 B006b-10 1536
4 B006b-11 1536
5 B006b-12 1536
6 B006b-13 1536
7 B006b-14 1536
8 B006b-15 1774
9 B006b-16 1536
10 B006b-17 1536
# ℹ 11 more rows
Einige Erläuterungen zu dem Code oben:
Die Funktionen “group_by” und “summarise” sind tidyverse/dplyr-Routinen; sie erstellen nicht einen data.frame, sondern einen “tibble”. Wenn man Tibbles ausgibt, werden immer nur einige Zeilen ausgegeben.
Wir verwenden in unseren Skripten oft die sog. “Pipe”. Hiervon gibt es mehrere Versionen, %>%, |> u.a.
Die Pipes sind sehr nützlich. Was es mit ihnen auf sich hat, lohnt sich sehr zu verstehen und ist in diesem Blogpost sehr gut beschrieben: https://towardsdatascience.com/understanding-the-native-r-pipe-98dea6d8b61b
#data.frame( sanCheck )
%>%
sanCheck print(Inf)
# A tibble: 21 × 2
participant `n()`
<fct> <int>
1 B006b-08 1536
2 B006b-09 1536
3 B006b-10 1536
4 B006b-11 1536
5 B006b-12 1536
6 B006b-13 1536
7 B006b-14 1536
8 B006b-15 1774
9 B006b-16 1536
10 B006b-17 1536
# ℹ 11 more rows
Man sieht, dass Vp15 mehr Zeilen hat als erwartet; Vp 28 weniger. Hier muss man also schauen, was los war: Muss man Trials entfernen? Muss man Vpn nacherheben?
Wir schauen uns Vp15 genauer an. Mit “filter” (ebenfalls tidyverse) kann man Zeilen auswählen. Wir wählen alle Zeilen der vp15.
%>% filter( participant == "B006b-15" ) %>%
ds group_by( factor_A, factor_B, posture ) %>%
summarize( n() ) %>%
print(Inf)
`summarise()` has grouped output by 'factor_A', 'factor_B'. You can override
using the `.groups` argument.
# A tibble: 8 × 4
# Groups: factor_A, factor_B [4]
factor_A factor_B posture `n()`
<fct> <fct> <fct> <int>
1 Level_1 Level_1 uncr 192
2 Level_1 Level_1 crossed 247
3 Level_1 Level_2 uncr 192
4 Level_1 Level_2 crossed 254
5 Level_2 Level_1 uncr 192
6 Level_2 Level_1 crossed 254
7 Level_2 Level_2 uncr 192
8 Level_2 Level_2 crossed 251
# wir verwenden wieder group_by, teilen den Datensatz jetzt aber in kleinere Stücke auf.
# Vorher hatten wir ja nur nach Versuchspersonen unterteilt. Jetzt unterteilen wir in alle Faktorstufen-Kombinationen.
# Wir erhalten so Statistiken für jede einzelne Bedingungskombination - mit nur sehr wenig Code.
Wir vergleichen mal mit einer Vp, die die erwartete Anzahl Zeilen hat:
%>% filter( participant == "B006b-16" ) %>%
ds group_by( factor_A, factor_B, posture ) %>%
summarize( n() ) %>%
print(Inf)
`summarise()` has grouped output by 'factor_A', 'factor_B'. You can override
using the `.groups` argument.
# A tibble: 8 × 4
# Groups: factor_A, factor_B [4]
factor_A factor_B posture `n()`
<fct> <fct> <fct> <int>
1 Level_1 Level_1 uncr 192
2 Level_1 Level_1 crossed 192
3 Level_1 Level_2 uncr 192
4 Level_1 Level_2 crossed 192
5 Level_2 Level_1 uncr 192
6 Level_2 Level_1 crossed 192
7 Level_2 Level_2 uncr 192
8 Level_2 Level_2 crossed 192
Es ist im Vergleich zu sehen, dass Vp16 in jeder Faktor-Kombination genau 192 Trials hat; VP15 dagegen hat bei allen “crossed” Bedingungen zu viele Trials. Und das ist noch nicht mal ganz einheitlich. Irgendetwas ist schiefgegangen. Vielleicht wurde der Versuch zwei mal gestartet und es gab einen ersten Durchgang mit crossed, der dann abgebrochen wurde und diese Trials wurden dennoch eingelesen?
Um das herauszufinden, müssen die Unterlagen aus der Testung (Testungsprotokoll/Testungstagebuch) geprüft werden. Lässt sich nicht klären, was schiefgegangen ist, muss die Vp ausgeschlossen werden. Lässt sich klären, was passiert ist, und kann man dies korrigieren, dann können die Daten entsprechend angepasst werden.
Im vorliegenden Fall könnte es sein, dass im Tagebuch vermerkt ist, dass das Experiment in der Mitte des ersten Blocks abgebrochen wurde, weil ein Stimulator nicht funktionierte. Nach Reparatur wurde von vorne begonnen. Allerdings wurde vergessen, den Logfile des ersten Starts zu löschen. Dies kann man nun im Nachhinein tun. Wenn Sie so eine Situation in den Daten entdecken, wäre das ein typischer Fall, bei dem Sie Ihre* Betreuer*in ansprechen, damit alle bescheid wissen und der Datensatz bzw. das Einlesen angeapsst werden kann.
Eine Alternative ist, die falschen Zeilen erst nach dem Einlesen der Daten zu löschen. Dies ist aber nicht immer einfach zu bewerkstelligen. Aus wissenschaftlicher Sicht ist die Problematik, dass wir Daten nicht manipulieren oder über unsere Daten lügen dürfen.
Ob das Löschen von Daten, wie hier besprochen, in Ordnung ist, hängt sehr von der konkreten Situation ab. Bei Vorliegen eines technischen Fehlers würde man aus unserer Sicht die fehlerhaften Daten zumeist so behandeln können, als seien sie nicht erhoben worden und entsprechend löschen.
Es sind aber Fälle denkbar, in denen dies nicht das korrekte Vorgehen wäre. Ein Bsp. ist, wenn es sich um ein Lernexperiment handelt, bei dem die Vp trotz des technischen Fehlers bereits etwas gelernt hat. Hier würde das Wegwerfen von Daten den Datensatz in Bezug auf die wissenschaftliche Frage verfälschen und wir würden die Daten eliminieren.
Wie Sie sehen, können solche Entscheidungen haarig sein. Seit der Diskussion über die Wissenschafts- und Replikationskrise sehen viele - so auch wir - es als die beste Vorgehensweise an, im Zweifelsfall Daten nicht zu löschen, aber von der Analyse auszuschließen und dies im Bericht/Paper explizit zu besprechen. Stellt man die nicht verwendeten Daten zur Verfügung, können andere Wissenschaftler bei Interesse die Analyse mit Inklusion der zweifelhaften Daten wiederholen.
Für unsere Beispieldaten nehmen wir an, dass die Vp nicht ausgewertet werden kann, weil wir nicht rekonstruieren konnten, was zur falschen Trialzahl geführt hat.
Man kann mit “filter” diese Vp aus ds entfernen; die Entfernung der Daten aus der Analyse muss im Bericht mit einer kurzen Begründung angegeben werden.
<- ds %>%
ds filter( participant != "B006b-15" ) # beachte, diesmal !=, d.h. wir wählen alles, was NICHT Vp15 ist
Als nächstes schauen wir uns Vp28 an:
%>% filter( participant == "B006b-28" ) %>%
ds group_by( factor_A, factor_B, posture ) %>%
summarize( n() ) %>%
print(Inf)
`summarise()` has grouped output by 'factor_A', 'factor_B'. You can override
using the `.groups` argument.
# A tibble: 8 × 4
# Groups: factor_A, factor_B [4]
factor_A factor_B posture `n()`
<fct> <fct> <fct> <int>
1 Level_1 Level_1 uncr 192
2 Level_1 Level_1 crossed 180
3 Level_1 Level_2 uncr 192
4 Level_1 Level_2 crossed 183
5 Level_2 Level_1 uncr 192
6 Level_2 Level_1 crossed 184
7 Level_2 Level_2 uncr 192
8 Level_2 Level_2 crossed 182
Wir sehen, dass bei Vp28 bei den “crossed” Bedingungen einige Trials fehlen. Da wir auch hier nicht rekonstruieren können, warum das so ist, entfernen wir auch diese Vp aus den Daten (analog zu oben). Wiederum muss diese Entscheidung im Bericht besprochen werden.
<- ds %>%
ds filter( participant != "B006b-28" )
Obwohl wir alle Daten der beiden Vpn 15 und 28 entfernt haben, führt R sie im Faktor participant noch weiter als Faktorlevel. R würde also weiterhin davon ausgehen, dass wir mit 21 Vpn rechnen, einige aber nur 0 Trials aufweisen. Dies ist meistens nicht das, was man sich für die Statistik wünscht - dort sollen nur die Vpn eingehen, die wirklich ausgewertet werden.
Die überschüssigen Levels im Faktor “participant” löscht man mit “droplevels”.
$participant <- droplevels(ds$participant)
ds
# hinterher prüfen: es dürfen jetzt nur noch 19 Vpn da sein
length( levels( ds$participant ) )
[1] 19
Die Einzeldaten sollen unbedingt mit den Betreuern durchgesehen und diskutiert werden. Dies sollte unmittelbar nach Ende der Erhebung stattfinden, falls Daten nacherhoben werden müssen!