Untersuchungseinheit | self | friend | parent |
---|---|---|---|
1 | 2 | 1 | 3 |
2 | 3 | 4 | 2 |
24 Wide- & Long-Format
Einleitung
Wenn man wissenschaftliche Fragestellungen untersuchen möchte, benötigt man ein geeignetes Format, in dem die Daten vorliegen. Manchmal stehen die Daten, die man auswerten möchte, aber nicht im “richtigen” Format zur Verfügung.
Im Wesentlichen gibt es zwei verschiedene Darstellungsformen von Tabellendaten (Datensätzen): das Wide- und das Long-Format.
Im Wide-Format entspricht jede Zeile einer Untersuchungseinheit. So liegen beispielsweise messwiederholte Variablen in unterschiedlichen Spalten vor.
Beispiel 1 Wide-Format: Messzeitpunkte
Beispiel 2 Wide-Format: Rater
Im Long-Format hingegen liegen Messung von einer Untersuchungseinheit in mehreren Zeilen vor. Dabei sind die Werte einer Untersuchungseinheit, die in unterschiedlichen Modi (z.B. Messzeitpunkte, Rater) erhoben wurden, untereinander gelistet. So werden beispielsweise messwiederholte Variablen in einer Spalte dargestellt.
Beispiel 1 Long-Format: Messzeitpunkte
Untersuchungseinheit | Zeitpunkt | Messung |
---|---|---|
1 | 1 | 4 |
1 | 2 | 3 |
1 | 3 | 1 |
2 | 1 | 5 |
2 | 2 | 2 |
2 | 3 | 3 |
Beispiel 2 Long-Format: Rater
Untersuchungseinheit | Rater | Messung |
---|---|---|
1 | self | 2 |
1 | friend | 1 |
1 | parent | 3 |
2 | self | 3 |
2 | friend | 4 |
2 | parent | 2 |
Wir schauen uns zwei Möglichkeiten an, mit denen man Datensätze umformatieren kann:
reshape()
aus dem Standardpaket statsgather()
bzw.spread()
aus dem Paket tidyr
Wir werden beide Pakete nutzen, um unsere Daten vom Long- ins Wide-Format und vom Wide- ins Long-Format zu überführen. Wir stellen beide Möglichkeiten zum Umformatieren vor, damit jeder ausprobieren kann, mit welcher Funktion sie bzw. er besser arbeiten kann. Während gather()
und spread()
manchen Nutzern intuitiver und einfacher erscheinen, werden die Daten mit reshape()
etwas übersichtlicher dargestellt (z.B. durch Zeilennummerierungen).
Falls ihr das Paket tidyr noch nicht installiert habt, könnt ihr das folgendermaßen tun: install.packages("tidyr", dependencies = TRUE)
Beispieldatensatz für dieses Kapitel
Im Zuge dieses Kapitels werden wir mit dem Datensatz ChickWeight
arbeiten, der standardmäßig in R enthalten ist. Wir laden ihn folgendermaßen herunter:
data(ChickWeight)
Die Daten stammen aus einem Längsschnitt-Experiment, in dem der Einfluss von verschiedenem Futter auf das Wachstum von Küken untersucht wurde.
Der Datensatz liegt im Long-Format vor und enthält vier Variablen. Mehr Informationen zu den Variablen finden wir hier.
weight
: Körpergewicht eines Kükens in GrammTime
: Tage seit der Geburt des KükensChick
: Identifikationsnummer des KükensDiet
: Nummer der Futtergruppe (1, 2, 3, 4)
Time
. Diese werden wir im Folgenden nutzen, um den Datensatz vom Long- ins Wide-Format, und dann wieder zurück ins Long-Format, zu bringen.
Achtung: Der Einfachheit halber wird im Folgenden der Begriff messwiederholte Variable verwendet, wenn von einer Variablen die Rede ist, die mehrfach erhoben wurde (d.h. im Wide-Format in mehreren Spalten und im Long-Format in einer Spalte vorliegt). Im Beispiel zum Wide-Format oben sind demnach die Variablen
t1
,t2
,t_3
sowieself
,friend
undparent
messwiederholt. In unserem Beispiel-Datensatz (der im Long-Format vorliegt) ist das die Variableweight
. Gleiches gilt für den Begriff Zeitvariable, die die verschiedenen Modi der messwiederholten Variablen kodiert. Im Beispiel zum Long-Format oben entspricht diese den VariablenZeitpunkt
undRater
. In unserem Beispiel-Datensatz (der im Long-Format vorliegt) ist das die VariableTime
.
24.1 Vom Long- ins Wide-Format
Hierbei wird eine messwiederholte Variable (weight
) in Abhängigkeit der Ausprägungen der Zeitvariable (Time
) in mehrere Spalten aufgeteilt.
Im Wide-Format gibt es mehr Spalten und weniger Zeilen (als im Long-Format). Die Anzahl der Zeilen entspricht der Anzahl der Untersuchungseinheiten.
Sowohl reshape()
als auch spread()
benötigen zur Formatierung von Long nach Wide eine ID-Variable im Datensatz (auch wenn man diese bei spread()
nicht an ein Argument übergeben muss). Eine ID-Variable bezeichnet die Untersuchungsobjekte. Im Eingangsbeispiel wäre dies die Variable Untersuchungseinheit
.
24.1.1 stats: reshape()
Mit dem reshape()
-Befehl kann man Daten sowohl vom Long- ins Wide-Format als auch vom Wide- ins Long-Format bringen.
Bei der Formatierung von Long in Wide sind folgende 5 Argumente wichtig:
data
: Name des Datensatzesv.names
: messwiederholte Variable, die im Long-Format in einer Spalte vorliegt und im Wide-Format auf mehrere Spalten ausgedehnt werden solltimevar
: Zeitvariable, die im Long-Format die Modi kodiertidvar
: ID-Variable, die unterschiedliche Untersuchungseinheiten kodiertdirection
: vom Long- ins Wide-Format
Zusätzlich kann man mit drop="Variable"
eine bzw. mit drop=c("Variable_1", "Variable_2", ..., "Variable_x")
mehrere Variablen aus dem umformatierten Datensatz entfernen.
<- reshape(data=ChickWeight, # Name des Datensatzes
reshape_wide v.names="weight",
# messwiederholte Variable (in einer Spalte)
timevar="Time",
# (bestehende) Zeitvariable
idvar="Chick",
# (bestehende) ID-Variable
direction="wide") # vom Long- ins Wide-Format
Achtung: Ganz links sehen wir die “alte” Zeilennummerierung von
ChickWeight
. Die Zeilennummerierung ändert sich inreshape_wide
, weil alle Beobachtungen einer Untersuchungseinheit zu unterschiedlichen Messzeitpunkten in einer Zeile vorliegen. Das erkennt man auch daran, dass die Zeilennummerierung vonChickWeight
(meistens) in 12-er Schritten vorliegt und es genau 12 Messzeitpunkte gibt. Da bei einigen Küken weniger als 12 Messzeitpunkte vorhanden sind, gilt das aber nicht für den ganzen Datensatzreshape_wide
.
24.1.2 tidyr: spread()
Hier sind nur drei Argumente wichtig:
data
: Name des Datensatzeskey
: Zeitvariable, die im Long-Format unterschiedliche Modi kodiertvalue
: messwiederholte Variable, die im Long-Format in einer Spalte vorliegt und im Wide-Format auf mehrere Spalten ausgedehnt werden soll
library(tidyr) # Laden des Pakets
<- spread(data=ChickWeight, # Name des Datensatzes
spread_wide key="Time", # Zeitvariable
value="weight") # messwiederholte Variable
24.1.3 Unterschied zwischen reshape()
und spread()
Bei reshape()
werden Zeilennummerierungen auf Basis des übergebenen Datensatzes erzeugt (siehe Hinweis im reshape()
-Abschnitt).
Wide-Format: Argumente von reshape()
und spread()
gegenübergestellt
reshape | spread |
---|---|
data | data |
v.names | value |
timevar | key |
idvar | *kein explizites Argument, aber benötigt* |
Die Benennung der Spalten der messwiederholten Variable unterscheidet sich in den beiden Ansätzen. Während bei reshape()
eine Kombination aus dem Namen der messwiederholten Variablen und der Ausprägung der Zeitvariablen genutzt wird (z.B. weight.0
), wird bei spread()
nur die Ausprägung der Zeitvariablen genutzt (z.B. 0
).
24.2 Vom Wide- ins Long-Format
Hierbei wird eine messwiederholte Variable (weight
), die in mehreren Spalten vorliegt, zu einer Spalte zusammengefasst und es wird eine dazugehörige Zeitvariable (Time
) erstellt.
Im Long-Format gibt es weniger Spalten und mehr Zeilen (als im Wide-Format). Die Anzahl der Zeilen entspricht nicht der Anzahl der Untersuchungseinheiten, sondern der Anzahl der Untersuchungseinheiten x Anzahl der (jeweiligen) Messwiederholungen. Deswegen ist es im Long-Format besonders wichtig, eine ID-Variable zu haben, um einzelne Untersuchungseinheiten differenzieren zu können.
Achtung: Manchmal werden nicht alle Untersuchungseinheiten zu jedem Zeitpunkt untersucht. In unserem Datensatz
ChickWeight
ist das der Fall.
Wir können uns folgendermaßen anschauen, wie häufig jede Untersuchungseinheit beobachtet wurde:
table(ChickWeight$Chick)
18 16 15 13 9 20 10 8 17 19 4 6 11 3 1 12 2 5 14 7 24 30 22 23 27 28
2 7 8 12 12 12 12 11 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12
26 25 29 21 33 37 36 31 39 38 32 40 34 35 44 45 43 41 47 49 46 50 42 48
12 12 12 12 12 12 12 12 12 12 12 12 12 12 10 12 12 12 12 12 12 12 12 12
Deswegen gibt es auch einen Unterschied zwischen der Anzahl der Zeilen im ursprünglichen Long-Format von ChickWeight
und den nachfolgend erstellten Long-Formaten des Datensatzes. Im ursprünglichen Long-Format ChickWeight
werden die Zeilen der Küken, die zu bestimmten Messzeitpunkten nicht beobachtet wurden, weggelassen. Mit reshape()
und gather()
werden diese Zeilen erstellt und die Beobachtung der messwiederholten Variable mit NA
(= fehlender Wert) versehen.
24.2.1 stats: reshape()
Bei der Formatierung von Wide in Long sind folgende 6 Argumente wichtig:
data
: Name des Datensatzesvarying
: messwiederholte Variable, die im Wide-Format auf mehrere Spalten ausgedehnt ist und im Long-Format in einer Spalte vorliegen solltimevar
: Zeitvariable, die unterschiedliche Modi kodiert, und die neu im Long-Format erstellt werden solltimes
: Ausprägungen der Zeitvariable, d.h. die Kodierung der Modiidvar
: ID-Variable, die unterschiedliche Untersuchungseinheiten kodiertdirection
: vom Wide- ins Long-Format
Mit v.names
kann man die messwiederholte Variable, welche im Wide-Format in mehreren Spalten vorliegt und nun im Long-Format zu einer Spalte zusammengefasst wird, umbenennen. Wenn alle Spaltennamen den gleichen Stamm haben (bei uns weight.0
, weight.2
, …), dann wird dieser als Name der zusammengefassten Variable genutzt. Wenn die Spalten unterschiedliche Namen haben, sollte man hier einen gemeinsamen Namen angeben.
Auch hier kann man einzelne Variablen mit drop="Variable"
bzw. mehrere Variablen mit drop=c("Variable_1", "Variable_2", ..., "Variable_x")
aus dem umformatierten Datensatz entfernen.
<- reshape(data=reshape_wide, # Name des Datensatzes
reshape_long varying=3:14,
# messwiederholte Variable (mehrere Spalten)
timevar="Time",
# (neu erstellte) Zeitvariable
times=c(seq(0, 20, 2), 21),
# Ausprägungen von timevar (Messzeitpunkte)
idvar="Chick",
# (bestehende) ID-Variable
direction="long") # vom Wide- ins Long-Format
Achtung: Ganz links sehen wir die neue Zeilennummerierung von
reshape_long
. Die Zeilennummerierung hat hier Nachkommastellen (z.B. 1.0), weil hier ID-Variable und Zeitvariable kombiniert wurden. Die Zahl vor dem Punkt steht für die Untersuchungseinheit (Chick
); die Zahl nach dem Komma für den Messzeitpunkt (Time
).
Was macht varying=3:14
?
Man kann bestehende Variablen auch mit ihren Indizes (d.h. hier: ihren Spaltennamen) ansprechen. Das ist häufig weniger schreibintensiv, als alle Spaltennamen in einem Vektor zu speichern z.B. varying=c("weight.0", "weight.2", ..., "weight.21")
. So könnte man z.B. auch idvar=1
anstatt idvar="Chick"
schreiben.
timevar
), geht das aber nicht, da diese noch gar nicht existieren und von daher auch keine Indizes haben.
Wie können wir verschiedene messwiederholte Variablen jeweils zusammenfassen? bzw. Wie nutzen wir varying=list()
und v.names
?
Wenn man mehrere Variablen zu den verschiedenen Messzeitpunkten erhoben hat, kann man diese mit varying=list()
vom Wide- ins Long-Format bringen. Dabei muss man darauf achten, dass alle messwiederholten Variablen die gleiche Anzahl an Spalten besitzen (d.h. alle zu jedem Messzeitpunkt erhoben wurden).
Für unseren Datensatz generieren wir ein fiktives Beispiel, in dem wir die 12 messwiederholten weight
-Spalten duplizieren und dann jeweils zu einer Variablen zusammenfügen. Deswegen schreiben wir varying=list(3:14, 15:26)
in reshape()
.
Zusätzlich kann man die messwiederholten Variablen mit v.names=c("...", "...")
umbenennen. Das funktioniert auch für einzelne messwiederholte Variablen: Dann benötigt man keinen Namensvektor c("...", "...")
mehr, sondern nur v.names="..."
.
Wir nennen die zwei fiktiven Variablen in unserem Beispiel AV_1
und AV_2
.
# messwiderholte Variablen duplizieren:
<- cbind(reshape_wide, reshape_wide[3:14])
reshape_dup
<- reshape(data=reshape_dup,
reshape_dup_long varying=list(3:14, 15:26),
# zwei messwiederholte Variable ...
# ... mit jeweils mehreren Spalten
v.names=c("AV_1", "AV_2"),
# Benennung der zwei messwiederholten Variablen
# optional
timevar="Time",
times=c(seq(0, 20, 2), 21),
# Ausprägungen von timevar (Messzeitpunkte)
idvar="Chick",
direction="long")
24.2.2 tidyr: gather()
Hierfür benötigt man 5 Argumente:
data
: Name des Datensatzeskey
: Zeitvariable, die unterschiedliche Modi kodiert und die neu im Long-Format erstellt werden sollvalue
: Benennung der neu zu erstellenden messwiederholten Variable...
: messwiederholte Variable, die im Wide-Format auf mehrere Spalten ausgedehnt ist und im Long-Format in einer Spalte vorliegen sollfactor_key
: ob die neu erstellte Zeitvariable als Faktor (TRUE
) oder als Character (FALSE
; Default) gehandhabt werden soll
library(tidyr) # Laden des Pakets
<- gather(data=spread_wide, # Name des Datensatzes
gather_long key="Time", # (neu erstellte) Zeitvariable
value="weight", # (neu erstellte) messwiederholte Variable
3:14, # messwiederholte Variable (mehrere Spalten)
factor_key=TRUE) # ob die Zeitvariable ein Faktor sein soll
24.2.3 Unterschied zwischen reshape()
und gather()
Bei reshape()
werden Zeilennummerierungen auf Basis des übergebenen Datensatzes erzeugt (siehe Hinweis im reshape()
-Abschnitt).
Long-Format: Argumente von reshape()
und gather()
gegenübergestellt
reshape | gather |
---|---|
data | data |
varying | ... |
timevar | key |
times | |
idvar | |
v.names | value |
drop | |
factor_key |
Bei gather()
werden die Ausprägungen der Zeitvariable (Modi) so benannt, wie vorher die einzelnen Spalten der messwiederholten Variablen hießen. Wenn wir uns das anschauen wollen, könnten wir einfach reshape_wide
(anstatt spread_wide
) mit gather()
bearbeiten. Im Gegensatz dazu kann man bei reshape()
mit times
eine eigene Benennung der Ausprägungen festlegen.
Bei reshape()
wird mit idvar
eine neue ID-Variable erstellt. Dazu muss ich dem Argument nur einen Namen geben. Wenn es bereits eine ID-Variable gibt, sollte man den Namen dieser angeben, weil ansonsten noch eine neue ID-Variable erstellt wird.
Mit drop
kann man zusätzlich bestimmte Variablen aus dem neu formatierten Datensatz entfernen.
Bei gather()
kann man mit mit factor_key
entscheiden, ob die neu erstellte Zeitvariable als Faktor oder als Character behandelt werden sollen. Mit reshape()
ist der Datentyp immer numerisch.
24.3 Andere Funktionen zum Umformatieren
Es gibt noch andere Funktionen, mit denen man Datensätze vom Long- ins Wide-Format oder umgekehrt umformatieren kann.
In gewisser Hinsicht sind pivot_longer()
und pivot_wider()
aus dem Paket tidyr die Nachfolger von gather()
und spread()
. Neben der intuitiveren Benennung enthalten sie einige Neuerungen wie z.B. die Möglichkeit, mit mehreren messwiederholten Variablen mit verschiedenen Datentypen zu arbeiten (dieses Feature wurde von melt()
und dcast()
übernommen). Außerdem wurde der Support für gather()
und spread()
eingestellt, d.h. bestehende Probleme mit diesen beiden Funktionen werden nicht mehr behoben werden. Hier finden wir eine Vignette, in der die Handhabung von pivot_longer()
und pivot_wider()
erklärt wird.
Wenn wir wissen wollen, wie man melt()
(Wide zu Long) und dcast()
(Long zu Wide) aus dem Paket reshape2 nutzt, können wir dafür hier nachschauen.
Um eine möglichst exakte Replikation der Funktionen zu gewährleisten gibt es im folgenden relevante Angaben zum System (R-Version, Betriebssystem, geladene Pakete mit Angaben zur Version), mit welchem diese Seite erstellt wurde.
sessionInfo()
R version 4.4.2 (2024-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.1 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Etc/UTC
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] kableExtra_1.4.0 dplyr_1.1.4 tidyr_1.3.1 rmarkdown_2.29
loaded via a namespace (and not attached):
[1] vctrs_0.6.5 svglite_2.1.3 cli_3.6.3 knitr_1.49
[5] rlang_1.1.4 xfun_0.49 stringi_1.8.4 purrr_1.0.2
[9] generics_0.1.3 jsonlite_1.8.9 glue_1.8.0 colorspace_2.1-1
[13] htmltools_0.5.8.1 scales_1.3.0 fansi_1.0.6 munsell_0.5.1
[17] evaluate_1.0.1 tibble_3.2.1 fastmap_1.2.0 yaml_2.3.10
[21] lifecycle_1.0.4 stringr_1.5.1 compiler_4.4.2 htmlwidgets_1.6.4
[25] pkgconfig_2.0.3 rstudioapi_0.17.1 systemfonts_1.1.0 digest_0.6.37
[29] viridisLite_0.4.2 R6_2.5.1 tidyselect_1.2.1 utf8_1.2.4
[33] pillar_1.9.0 magrittr_2.0.3 withr_3.0.2 tools_4.4.2
[37] xml2_1.3.6
Für Informationen zur Interpretation dieses Outputs schaut auch den Abschnitt Replizierbarkeit von Analysen des Kapitels zu Paketen an.