Der größte Teil der Arbeit mit Befragungsdaten entfällt auf die Aufbereitung, nicht auf die Auswertung – häufig genannt wird ein Anteil von rund 70 Prozent. Die folgenden Schritte zeigen eine mögliche Abfolge für mehrwellige Befragungen. Je nach Fragebogenaufbau, Erhebungsweg und Auswertungsziel können Schritte entfallen, hinzukommen oder anders aussehen. Kontrollieren Sie nach jedem Schritt das Zwischenergebnis.
Ausgangslage der Beispielstudie und Datenstruktur
Der Datenexport der im ersten Teil beschriebenen Beispielstudie aus SoSci Survey enthält alle Fragebögen einer mehrwelligen Studie in einer gemeinsamen Datei. Die Variable QUESTNNR gibt für jede Zeile an, welcher Fragebogen beantwortet wurde. Eine Person, die an drei Befragungen teilgenommen hat, erscheint daher in drei Zeilen. Die Teilnehmenden-Kennung liegt in den Wellen, die über einen personalisierten Link durchgeführt wurden, als SERIAL vor. Da Registrierung über eine Opt-in-Frage erfolgte, steht dieselbe Kennung in der ersten Welle stattdessen in der Variable dieser Frage (beispielsweise R001) und muss vor der Zusammenführung nach SERIAL übertragen werden.
Da jeder Fragebogen eigene Rubrik-Kennungen verwendet, tragen inhaltlich identische Fragen in den verschiedenen Wellen unterschiedliche Variablennamen (etwa AD01 in t1 und BD01 in t2 für dieselbe Frage). Für eine Zeile eines bestimmten Messzeitpunkts sind deshalb alle Spalten der übrigen Wellen leer.
Ein Ausschnitt aus einem solchen Export könnte so aussehen:
| SERIAL | QUESTNNR | R001 | AD01_01 | BD01_01 |
| (leer) | t0 | abc123 | NA | NA |
| abc123 | t1 | NA | 3 | NA |
| xyz789 | t1 | NA | 2 | NA |
| abc123 | t2 | NA | NA | 5 |
| xyz789 | t2 | NA | NA | 3 |
Dieser Rohexport ist weder ein Wide- noch ein Long-Format. Im Wide-Format steht jede Person in genau einer Zeile. Im Long-Format hat jede Person je Messzeitpunkt eine eigene Zeile, jede messwiederholte Variable steht in genau einer Spalte, und eine zusätzliche Spalte gibt den Messzeitpunkt an. Beide Formate müssen aus dem Rohexport erst erzeugt werden.
Welche Datenwerte speichert SoSci Survey?
Neben der Struktur ist die Kodierung der Antworten zu beachten. Einige Eigenheiten wirken sich unmittelbar auf die Aufbereitung aus und führen sonst zu Ergebnissen, die plausibel aussehen, aber falsch sind. Gültige Antworten tragen stets einen positiven Wert. Diese Kodierung lässt sich nicht ändern, damit keine Überschneidung mit den Codes für fehlende Werte entsteht.
Bei Skalen erzeugt das Minimum den Wert 1, das Maximum den Wert n. Die angezeigte Beschriftung spielt keine Rolle: Wird die Skala von −2 bis +2 nummeriert oder das Minimum als Schulnote 6 beschriftet, wird trotzdem 1 gespeichert. Für das Umpolen ist deshalb die Zahl der Stufen maßgeblich, nicht die Beschriftung – bei sieben Stufen lautet die Formel 8 – x, bei fünf Stufen 6 – x.
Bei Einfachauswahl wird die Kennung der gewählten Option gespeichert, also 1 bis m. Bei Mehrfachauswahl wird jedes Item zu einer eigenen Variable mit den Werten 1 für nicht angekreuzt und 2 für angekreuzt. Daneben gibt es eine Sammelvariable ohne Item-Nummer, die die Anzahl der gewählten Optionen enthält – sie ist keine Antwortvariable und gehört nicht in die Umkodierung.
Für fehlende Werte sind vier Fälle zu unterscheiden, von denen nur die ersten beiden fehlende Werte im eigentlichen Sinn sind:
| Wert | Bedeutung |
| −9 | Die Frage wurde gestellt, aber nicht beantwortet |
| −8 | Eine ungültige Antwort wurde übermittelt |
| −1 bis −3 | Eine Ausweich- oder Residualoption wurde gewählt |
| leeres Feld | Die Frage wurde im Interview nicht gestellt |
Die Codes −1 bis −3 stehen für inhaltliche Antworten, etwa „Nichts davon“ oder „Darüber habe ich noch nie nachgedacht“. Wer pauschal alle negativen Werte in fehlende Werte umwandelt, löscht diese Angaben.
Hinzu kommen technische Variablen, die SoSci Survey in jedem Fragebogen mitliefert. Für die Prüfung der Datenqualität sind vor allem FINISHED, LASTPAGE, MISSING, MISSREL, TIME_SUM und TIME_RSI nützlich. Da sie in allen Wellen gleich heißen, führen sie beim Zusammenführen zu Namenskollisionen und werden vorher aus dem Arbeitsdatensatz genommen.
Codebuch und Variablenübersicht
Für die Aufbereitung braucht man beide Dokumente, denn sie beantworten unterschiedliche Fragen.
Das Codebuch führt je Variable und Antwortoption eine Zeile mit sechs Spalten: Kennung, Fragetitel, gespeicherter Zahlenwert, dessen Bedeutung, Skalenniveau und Fragetyp. Daraus lässt sich klären, welche Variablen zu einer Mehrfachauswahl gehören (Typ DICHOTOMOUS beziehungsweise CK), welche negativen Codes vorkommen und was sie bedeuten, und wie viele Stufen eine Skala hat. Ein Detail wird dabei leicht übersehen: Bei zweistufigen Fragen ist die Richtung der Kodierung nicht einheitlich. Mal gilt 1 = nein, 2 = ja, mal 1 = Ja, 2 = Nein. Eine pauschale Umkodierung mit x – 1 kehrt im zweiten Fall die Bedeutung um.
Das Codebuch gilt für das gesamte Projekt und enthält keine Spalte für den Fragebogen. Aus ihm ist deshalb nicht ersichtlich, welche Kennung zu welcher Erhebungswelle gehört.
Diese Information liefert die Variablenübersicht, die je Fragebogen abgerufen wird. Legt man die Übersichten mehrerer Wellen nebeneinander, lässt sich am identischen Fragetitel ablesen, welche Kennungen einander entsprechen. Genau diese Gegenüberstellung braucht man später für das Umbenennen der Spalten.
Welche Schritte sind erforderlich?
Der Umfang der Aufbereitung hängt davon ab, mit welchem Programm und mit welchem Verfahren ausgewertet werden soll. Hier wird die Aufbereitung bis zur Varianzanalyse mit Messwiederholung in R dargestellt.
Auswertung in SPSS. Deskriptive Statistik, t-Test und Varianzanalyse mit Messwiederholung benötigen in SPSS das Wide-Format. Da der Rohexport mehrere Zeilen je Person enthält, muss auch hier zunächst umstrukturiert werden. Grundlage dafür ist der gemeinsame Datensatz, den SoSci Survey für das gesamte Projekt exportiert; die Zeilen einer Person werden über SERIAL zusammengeführt, QUESTNNR. gibt die Welle an. Das Vorgehen dafür ist in der SPSS-Dokumentation unter „Daten umstrukturieren“ beschrieben.
Deskriptive Statistik und t-Test in R. siehe Teil 1.
Varianzanalyse mit Messwiederholung in R. Die in Luhmann Kapitel 14 verwendete Funktion aov_car() aus dem Paket afex setzt das Long-Format voraus.
Rücklauf oder Studienabbrüche berichten. Dafür ist ein eigener Zwischenschritt sinnvoll, der unabhängig von der eigentlichen Auswertung ist (siehe Teil 3).
Teil 1: Aufbereitung zum Wide-Format in R
Grundlage ist der CSV-Export aus SoSci Survey. Dieser ist tabgetrennt, weshalb als Trennzeichen "\t" angegeben wird. Anzupassen sind der Dateipfad und die Kennungen der eigenen Fragen; die Beispielkennungen AD03_01 und EM03_01 sind durch die eigenen zu ersetzen.
Die Anleitung verwendet die tidyverse-Pakete dplyr und tidyr. Sämtliche Schritte in Teil 1 lassen sich auch mit den R-Basisfunktionen umsetzen; für die Umstrukturierung ins Long-Format in Teil 2 ist tidyr erforderlich, weshalb hier einheitlich damit gearbeitet wird.
Zunächst werden die benötigten Pakete geladen. Die Installation ist nur einmal erforderlich, das Laden dagegen in jeder neuen R-Sitzung.
install.packages(c("readr", "dplyr", "tidyr"))
library(readr)
library(dplyr)
library(tidyr)
# Datensatz importieren
pfad <- "pfad/zur/datei.csv"
daten <- read_delim(pfad, delim = "\t", escape_double = FALSE,
na = "NA", trim_ws = TRUE)
# Erster Blick auf Umfang und Wertebereiche
nrow(daten)
summary(daten)
Fehlende Werte kennzeichnen
Beim CSV-Export bleiben die Codes für fehlende Werte als gewöhnliche Zahlen stehen. R erkennt sie nicht als fehlend und rechnet sie in alles ein: Aus -9 würde beim Umkodieren -10, beim Umpolen 15, und in den Skalenwert wird ein negativer Wert mitgezählt. Eine Fehlermeldung erscheint dabei nicht. Der Schritt gehört deshalb an den Anfang, vor alle weiteren Umformungen. Da in der Regel viele Variablen betroffen sind, werden deren Kennungen zunächst in einer Liste gesammelt und anschließend gemeinsam behandelt.
## Fehlende Werte kennzeichnen
# Kennungen der betroffenen Variablen sammeln
item_spalten <- c("AD03_01", "AD03_02", "EM03_01", "EM03_02")
daten <- daten %>%
mutate(across(all_of(item_spalten), ~ na_if(.x, -9)))
# Kontrolle: Anzahl fehlender Werte
sum(is.na(daten$EM03_01))
Ausweichoptionen mit den Codes -1 bis -3 bleiben dabei erhalten. Ob sie als fehlende Werte gelten sollen, ist eine inhaltliche Entscheidung und je Frage unterschiedlich zu beantworten. Z.B. „keine, ich bin staatenlos“, „darüber habe ich noch nie nachgedacht“. Sollen einzelne davon ebenfalls fehlend werden, geschieht das gezielt für die betroffene Variable: z.B.: daten$RW01_01 <- na_if(daten$RW01_01, -1).
Antwortwerte umkodieren
Mehrfachauswahl und zweistufige Fragen sind mit 1 und 2 kodiert. Für Anteilswerte ist eine Kodierung mit 0 und 1 sinnvoll, weil der Mittelwert dann unmittelbar dem Anteil der Zustimmung entspricht. Häufigkeitsauszählungen mit table() funktionieren unabhängig davon. Die umkodierten Werte kommen in neue Variablen mit dem Zusatz r, damit die ursprünglichen erhalten bleiben und nachvollziehbar ist, was bereits behandelt wurde. - 1 setzt voraus, dass der zweite Code die zutreffende Antwort ist. Gilt umgekehrt 1 = Ja, 2 = Nein, kehrt diese Rechnung die Bedeutung um. In dem Fall lautet der Befehl daten$LP01r <- 2 - daten$LP01. Welche Reihenfolge gilt, steht im Codebuch in der Spalte Response Label.
## Antworten umkodiederen
# Mehrfachauswahl: 1 = nicht gewaehlt, 2 = gewaehlt
daten$AD03_01r <- daten$AD03_01 - 1
# Zweistufige Frage: 1 = nein, 2 = ja
daten$EM03_01r <- daten$EM03_01 - 1
# Umkodierung prüfen
table(daten$EM03_01, daten$EM03_01r)
**Achtung:** `- 1` setzt voraus, dass der zweite Code die zutreffende Antwort ist. Gilt umgekehrt 1 = Ja, 2 = Nein, kehrt diese Rechnung die Bedeutung um. In dem Fall lautet der Befehl daten$LP01r <- 2 - daten$LP01. Welche Reihenfolge gilt, steht im Codebuch in der Spalte *Response Label*.
Nach Messzeitpunkt aufteilen
Die Variable QUESTNNR gibt an, aus welchem Fragebogen eine Zeile stammt. Da auch das Basispaket stats eine Funktion namens filter enthält, wird dplyr:: vorangestellt.
## Nach Messzeitpunkt aufteilen
daten_t0 <- daten %>% dplyr::filter(QUESTNNR == "t0")
daten_t1 <- daten %>% dplyr::filter(QUESTNNR == "t1")
daten_t2 <- daten %>% dplyr::filter(QUESTNNR == "t2")
ID-Variable angleichen
In der Beispielstudie diente t0 ausschließlich der Registrierung und enthält keine inhaltlichen Fragen. In t0 steht die Kennung in der Variable der Opt-in-Frage aus dem ersten Teil und wird nach SERIAL übertragen. Dieser Teildatensatz geht deshalb nicht in den Analysedatensatz ein. Er würde keine einzige Variable beitragen, aber alle Personen als Zeilen hinzufügen, die sich registriert und danach nie teilgenommen haben. Die Stichprobe wäre größer, als sie ist. Gebraucht wird t0 als Bezugsgröße für die Rücklaufquote (siehe Teil 3). Enthält die erste Welle auch inhaltliche Fragen, wird sie wie t1 und t2 weiterbehandelt. In t0 steht die Kennung in der Variable der Opt-in-Frage und wird nach SERIAL übertragen. Auch wenn t0 nicht in den Analysedatensatz eingeht, ist dieser Schritt nötig: Ohne ihn lassen sich die Registrierungen später nicht den Teilnahmen zuordnen, weil SERIAL in den t0-Zeilen leer ist. Anschließend werden die Kennungen auf denselben Datentyp gebracht und von Leerzeichen befreit, damit das Zusammenführen funktioniert.
## ID-Variable angleichen
daten_t0$SERIAL <- ifelse(is.na(daten_t0$SERIAL) | daten_t0$SERIAL == "",
daten_t0$R001, daten_t0$SERIAL)
daten_t0$SERIAL <- trimws(as.character(daten_t0$SERIAL))
daten_t1$SERIAL <- trimws(as.character(daten_t1$SERIAL))
daten_t2$SERIAL <- trimws(as.character(daten_t2$SERIAL))
Duplikate prüfen
Kommt eine Kennung innerhalb einer Welle mehrfach vor, vervielfacht das Zusammenführen die betroffenen Fälle ohne Warnung. Alle drei Befehle müssen den Wert 0 ergeben.
## Duplikate prüfen
sum(duplicated(daten_t0$SERIAL))
sum(duplicated(daten_t1$SERIAL))
sum(duplicated(daten_t2$SERIAL))
Technische Variablen sichern und entfernen
Die technischen Variablen heißen in allen Wellen gleich und führen beim Zusammenführen zu Namenskollisionen. Verworfen werden sollten sie trotzdem nicht, da sie für eine Qualitäts- oder Abbruchprüfung nützlich sind. Sie kommen deshalb zunächst in eigene Datensätze und werden danach aus den Arbeitsdatensätzen entfernt.
# Namen der technischen Variablen im Codebook erhalten
meta <- c("MODE", "MAILSENT", "LASTPAGE", "STARTED", "FINISHED",
"Q_VIEWER", "MAXPAGE", "MISSING", "MISSREL", "STATUS", "QUESTNNR")
# In eigene Datensaetze sichern
meta_t1 <- daten_t1 %>% select(SERIAL, starts_with("TIME"), any_of(meta))
meta_t2 <- daten_t2 %>% select(SERIAL, starts_with("TIME"), any_of(meta))
# Aus den Arbeitsdatensaetzen entfernen
daten_t1 <- daten_t1 %>% select(-starts_with("TIME"), -any_of(meta))
daten_t2 <- daten_t2 %>% select(-starts_with("TIME"), -any_of(meta))
Der Datensatz daten_t0 bleibt dabei wie gehabt. Da er nicht zusammengeführt wird, entstehen dort keine Namenskollisionen. Er enthält weiterhin alle technischen Variablen, darunter STARTED und FINISHED zur Registrierung, und dient in Teil 3 als Grundlage für die Rücklaufquote. Bleiben weitere gleichnamige Spalten übrig, zeigen sie sich nach dem Zusammenführen an den Endungen .x und .y. Vorab lassen sie sich so prüfen:
intersect(names(daten_t1), names(daten_t2))
# Außer `SERIAL` sollte hier nichts mehr erscheinen.
Zusammenführen zum Wide-Format
full_join() behält alle Personen aus beiden Datensätzen, auch solche, die nur an einer Welle teilgenommen haben. Sollen nur vollständige Fälle bleiben, wäre inner_join() zu verwenden.
## Zusammenführen zum Wide-Format
daten_wide <- daten_t1 %>%
full_join(daten_t2, by = "SERIAL")
# Stichprobengroesse pruefen
nrow(daten_t1)
nrow(daten_t2)
nrow(daten_wide)
Bei drei inhaltlichen Wellen wird eine weitere Zeile angefügt: full_join(daten_t0, by = "SERIAL". Damit liegen die Daten im Wide-Format vor: eine Zeile pro Person, der Messzeitpunkt ist in den Variablennamen enthalten.
Spalten einheitlich benennen
Die Spalten tragen an dieser Stelle noch die Kennungen aus SoSci Survey. Daran ist weder erkennbar, welches Konstrukt gemeint ist, noch aus welcher Welle der Wert stammt. Für die deskriptive Statistik und den t-Test wird der Datensatz durch sprechende Namen deutlich übersichtlicher, und für eine spätere Umstrukturierung ins Long-Format sind sie Voraussetzung.
Welche Kennung in welcher Welle zu welcher Frage gehört, ist der Variablenübersicht des jeweiligen Fragebogens zu entnehmen; das Codebuch enthält diese Zuordnung nicht.
Die neuen Namen folgen dem Muster kompetenz_01_t1, also Konstruktname, Itemnummer und Messzeitpunkt, jeweils durch einen Unterstrich getrennt. Entscheidend ist, dass der Messzeitpunkt am Ende steht.
Bei rename() steht links der neue Name, rechts der bisherige. Alle Variablen, die nicht aufgeführt werden, behalten ihren Namen. Wurden Items zuvor umkodiert, müssen die Variablen mit dem Zusatz r genutzt werden.
## Variablen/Spalten einheitlich benennen
daten_wide <- daten_wide %>%
rename(kompetenz_01_t1 = AD01_01,
kompetenz_02_t1 = AD01_02,
kompetenz_03_t1 = AD01_03,
kompetenz_01_t2 = BD01_01,
kompetenz_02_t2 = BD01_02,
kompetenz_03_t2 = BD01_03)
# Kontrolle
names(daten_wide)
Items umpolen
Negativ formulierte Items werden umgepolt, damit hohe Werte durchgehend eine hohe Merkmalsausprägung bedeuten. Dazu wird jeder Wert vom theoretischen Maximum plus 1 abgezogen – bei einer fünfstufigen Skala also von 6, bei einer siebenstufigen von 8. Gemeint ist der theoretisch mögliche Höchstwert, unabhängig davon, ob er in den Daten vorkommt.
Auch hier entsteht eine neue Variable mit dem Zusatz r. Im Wide-Format ist das je Messzeitpunkt einzeln vorzunehmen.
## Items umpolen
daten_wide$kompetenz_02r_t1 <- 6 - daten_wide$kompetenz_02_t1
daten_wide$kompetenz_02r_t2 <- 6 - daten_wide$kompetenz_02_t2
# Umpolung prüfen
table(daten_wide$kompetenz_02_t1, daten_wide$kompetenz_02r_t1)
#In der Tabelle muss aus 1 der Wert 5 werden, aus 2 der Wert 4 usw.
Skalenwerte berechnen
Der Skalenwert ist der zeilenweise Mittelwert über die Items eines Konstrukts. Dabei ist die umgepolte Variable einzusetzen, sonst bleibt die Umpolung wirkungslos. Der Skalenwert erhält denselben Namensaufbau wie die Items, endet also ebenfalls auf den Messzeitpunkt.
## Skalenwerte berechnen
daten_wide$kompetenz_t1 <- rowMeans(
daten_wide[, c("kompetenz_01_t1", "kompetenz_02r_t1", "kompetenz_03_t1")],
na.rm = TRUE)
daten_wide$kompetenz_t2 <- rowMeans(
daten_wide[, c("kompetenz_01_t2", "kompetenz_02r_t2", "kompetenz_03_t2")],
na.rm = TRUE)
Das Argument na.rm steuert den Umgang mit fehlenden Werten. Die Voreinstellung ist na.rm = FALSE; wird das Argument weggelassen, gilt sie. In diesem Fall erhält bereits eine Person, die ein einziges Item nicht beantwortet hat, keinen Skalenwert, sondern NA. Mit na.rm = TRUE wird der Mittelwert stattdessen über alle vorhandenen Werte gebildet. Welche Variante angemessen ist, ist inhaltlich zu entscheiden.
Damit ist der Datensatz für die deskriptive Statistik und den t-Test aufbereitet. Er sieht so aus (weitere Items ausgelassen):
| SERIAL | kompetenz_01_t1 | kompetenz_01_t2 | kompetenz_t1 | kompetenz_t2 |
| abc123 | 3 | 5 | 3.33 | 4.67 |
| xyz789 | 2 | 3 | 2.33 | 3.00 |
| def456 | 4 | NA | 4.33 | NA |
Wer eine Welle ausgelassen hat, erhält dort fehlende Werte. Dass diese Personen überhaupt im Datensatz verbleiben, liegt an der vorherigen Verwendung von full_join().
Teil 2: Vom Wide- ins Long-Format in R
Dieser Teil wird nur für die Varianzanalyse mit Messwiederholung benötigt. Für deskriptive Statistik und t-Test genügt der Datensatz aus Teil 1.
Der Grund für den zusätzlichen Schritt liegt in der Funktionsweise der Auswertungsverfahren: aov_car() aus dem Paket afex erwartet je Person und Messzeitpunkt eine eigene Zeile, eine gemeinsame Spalte je Konstrukt und eine weitere Spalte, die den Messzeitpunkt angibt. Der Datensatz aus Teil 1 ist anders aufgebaut — dort steht jede Person in einer Zeile, und die Messzeitpunkte sind in den Variablennamen.
Für die Umformung gibt es zwar auch eine Basis-R-Funktion (reshape), sie gilt jedoch als umständlich. Verwendet werden deshalb pivot_longer() und pivot_wider() aus dem Paket tidyr.
Umstrukturierung ins Long-Format
Die Umformung erfolgt in zwei Schritten. Zuerst zieht `pivot_longer()` alle Messwerte in eine einzige Spalte zusammen; die bisherigen Variablennamen wandern in eine zweite Spalte. Anschließend verteilt `pivot_wider()` die Merkmale wieder auf eigene Spalten, sodass je Konstrukt eine Spalte entsteht. Beide Schritte lassen sich mit der Pipe verketten.
Vier Angaben steuern den ersten Schritt: cols legt fest, welche Spalten umgeformt werden. Hier alle, die auf einen Messzeitpunkt enden. names_to benennt die beiden neuen Spalten für Merkmal und Messzeitpunkt. names_pattern beschreibt, wie der bisherige Name in diese beiden Teile zerlegt wird: Die erste Klammer steht für alles vor der Zeitpunktangabe, die zweite für den Zeitpunkt selbst. values_to benennt die Spalte, in der die Werte zwischengelagert werden.
## Umstrukturierung ins Long-Format
daten_long <- daten_wide %>%
pivot_longer(
cols = ends_with(c("_t1", "_t2")),
names_to = c("varname", "mzp"),
names_pattern = "(.+)_(t[0-9])$",
values_to = "wert") %>%
pivot_wider(
names_from = varname,
values_from = wert)
Das Ergebnis: eine Zeile je Person und Messzeitpunkt, SERIAL als Personenkennung, mzp als Messzeitpunkt.
| SERIAL | mzp | kompetenz_01 | kompetenz_02r | kompetenz_03 | kompetenz |
| abc123 | t1 | 3 | 4 | 3 | 3.33 |
| abc123 | t2 | 5 | 4 | 5 | 4.67 |
| xyz789 | t1 | 2 | 3 | 2 | 2.33 |
| xyz789 | t2 | 3 | 3 | 3 | 3.00 |
| def456 | t1 | 4 | 4 | 5 | 4.33 |
| def456 | t2 | NA | NA | NA | NA |
Die ersten drei Wertespalten enthalten die Einzelitems, kompetenz deren Mittelwert. Nur diese letzte Spalte geht als abhängige Variable in die Varianzanalyse ein. Da die Skalenwerte aus Teil 1 demselben Namensmuster folgen wie die Items, werden sie automatisch mit umgeformt.
Zwei Punkte sind dabei zu beachten. Erstens laufen alle erfassten Spalten vorübergehend in einer gemeinsamen Wertespalte zusammen. Sind darunter sowohl Zahlen als auch Text z.B. ein umbenanntes offenes Antwortfeld, bricht pivot_longer() mit einer Meldung über nicht vereinbare Variablentypen ab. Offene Antworten sollten deshalb gar nicht erst auf _t1 beziehungsweise _t2 enden oder vorher aus daten_wide entfernt werden. Zweitens werden nur Spalten erfasst, deren Name auf einen Messzeitpunkt endet. Alles andere SERIAL und weitere nicht messwiederholte Angaben bleiben als eigene Spalte stehen und wiederholen sich in jeder Zeile derselben Person. Das ist beabsichtigt und entspricht dem Aufbau, den die Auswertungsverfahren erwarten.
Vollständige Fälle prüfen
In die Varianzanalyse mit Messwiederholung gehen nur Personen ein, die zu allen betrachteten Messzeitpunkten einen Wert haben. aov_car() entfernt unvollständige Fälle selbstständig und meldet das, allerdings erst beim Rechnen. Wie groß die verbleibende Stichprobe ist, lässt sich vorher feststellen.
## Vollständige Fälle prüfen
# Wie viele Personen haben zu beiden Zeitpunkten einen Skalenwert?
sum(complete.cases(daten_wide[, c("kompetenz_t1", "kompetenz_t2")]))
# Zum Vergleich: Gesamtzahl der Personen
nrow(daten_wide)
Weichen beide Zahlen stark voneinander ab, lohnt sich ein Blick auf die Ausfälle, bevor gerechnet wird.
Varianzanalyse mit Messwiederholung
Die Formel liest sich so: Vor der Tilde steht die abhängige Variable, hier der Skalenwert. Im Error()-Term steht vor dem Schrägstrich die Personenkennung, dahinter die Variable mit den Messzeitpunkten. Erscheint beim Rechnen eine Warnmeldung über fehlende Werte, benennt sie die Kennungen der entfernten Fälle. Das ist kein Fehler, sondern der oben beschriebene Ausschluss unvollständiger Fälle.
## Varianzanalyse mit Messwiederholung
# install.packages("afex")
library(afex)
anova_1 <- aov_car(kompetenz ~ Error(SERIAL/mzp), data = daten_long)
anova_1
FAQ
SERIAL weitere Spalten, die in beiden Wellen identisch heißen. Meist sind es technische Variablen, die in der Liste meta noch fehlen. Finden lassen sie sich vorab:intersect(names(daten_t1), names(daten_t2))Außer
SERIAL sollte hier nichts erscheinen. Häufige Variablen neben den bereits berücksichtigten sind CASE, REF, LANGUAGE, ISOCODE, EXPIRED und LASTDATA.Ursache sind meist Testaufrufe während der Programmierung oder doppelte Registrierungen. Die Duplikatprüfung aus Teil 1 muss deshalb fehlerfrei durchlaufen, bevor zusammengeführt wird:
sum(duplicated(daten_t1$SERIAL))Ergibt der Befehl einen Wert größer 0, zeigt der folgende Aufruf die betroffenen Kennungen:
daten_t1$SERIAL[duplicated(daten_t1$SERIAL)]#alle registrierten Personen
teilnahme <- data.frame(SERIAL = daten_t0$SERIAL)
# Hat diese Person an t1 beziehungsweise t2 teilgenommen?
teilnahme$t1 <- teilnahme$SERIAL %in% daten_t1$SERIAL
teilnahme$t2 <- teilnahme$SERIAL %in% daten_t2$SERIAL
# Rücklaufquoten
mean(teilnahme$t1)
mean(teilnahme$t2)
# Teilnahmemuster: an beiden, nur einer oder keiner Welle
table(teilnahme$t1, teilnahme$t2)na.omit() und drop_na() alle Personen, die auf irgendeiner Variable einen fehlenden Wert haben. Sinnvoller ist es, gezielt die benötigten Variablen anzugeben.daten_ohne_na <- daten_long %>% drop_na(kompetenz)Zum Auffinden fehlender Werte dient
is.na(). Der folgende Befehl zählt sie je Variable:colSums(is.na(daten_wide))Literaturtipps
Luhmann, M. (2020). R für Einsteiger: Einführung in die Statistik-Software für die Sozialwissenschaften. Beltz.
Field, A. (2024). Discovering Statistics Using IBM SPSS Statistics. Sage.
Spickzettel zu den verwendeten R-Paketen:
Posit Software, PBC. Datentransformation mit dplyr (deutsche Übersetzung). https://rstudio.github.io/cheatsheets/translations/german/data-transformation_de.pdf
Posit Software, PBC (2023). Data tidying with tidyr. https://rstudio.github.io/cheatsheets/html/tidyr.html