Wide- & Long-Format

Wie man das Format von Tabellendaten ändern kann

Table of Contents


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 Person (oder einer anderen Untersuchungseinheit). So liegen beispielsweise messwiederholte Variablen in unterschiedlichen Spalten vor.

Beispiel Wide-Format:
Personen_ID Messung_t1 Messung_t2
1 4 3
2 5 2

Im Long-Format hingegen liegen Messung von einer Person (oder einer anderen Untersuchungseinheit) in mehreren Zeilen vor. Dabei sind die Werte einer Person, die in unterschiedlichen Modi (z.B. Messzeitpunkte, Rater) erhoben wurden, untereinander gelistet werden. So werden beispielsweise messwiederholte Variablen in einer Spalte dargestellt.

Beispiel Long-Format:
Personen_ID Zeitpunkt Messung
1 1 4
1 2 3
2 1 5
2 2 2

Wir schauen uns zwei Möglichkeiten an, mit denen man Datensätze umformatieren kann:

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. Ihr ladet 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 findet ihr hier.

  • weight: Körpergewicht eines Kükens in Gramm
  • Time: Tage seit der Geburt des Kükens
  • Chick: Identifikationsnummer des Kükens
  • Diet: Nummer der Futtergruppe (1, 2, 3, 4)
Von besonderem Interesse für uns ist die Zeitvariable 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.


Hinweis: Im Folgenden wird 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). In den Beispielen oben sind die messwiederholten Variablen Messung_t1 und Messung_t2 (Wide-Format) bzw. Messung (Long-Format). Hierbei muss es sich aber nicht zwangsläufig um längschnittlich erhobene Variablen handeln. Es kann analog z.B. eine Variable gemeint sein, die mit unterschiedlichen Ratern oder in sonstigen Gruppierungen gemessen wurde. Das gilt ebenso für den Begriff Zeitvariable, der die Messzeitpunkte der messwiederholten Variablen kodiert. Im Beispiel oben ist diese in die messwiederholten Variablen Messung_t1 und Messung_t2 integriert (Wide-Format) bzw. Zeitpunkt (Long-Format). Der Begriff kann sich auch auf andere Gruppierungsvariablen beziehen.


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.

Hinweis: 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 Personen-ID.

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:

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_wide <- reshape(data=ChickWeight, # Name des Datensatzes
                    v.names="weight", 
                      # messwiederholte Variable (in einer Spalte)
                    timevar="Time", 
                      # (bestehende) Zeitvariable
                    idvar="Chick", 
                      # (bestehende) ID-Variable
                    direction="wide") # vom Long- ins Wide-Format

Hinweis: Ganz links seht ihr die “alte” Zeilennummerierung von ChickWeight. Die Zeilennummerierung ändert sich in reshape_wide, weil alle Beobachtungen einer Untersuchungseinheit zu unterschiedlichen Messzeitpunkten in einer Zeile vorliegen. Das erkennt man auch daran, dass die Zeilennummerierung von ChickWeight (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 Datensatz reshape_wide.

tidyr: spread()

Hier sind nur drei Argumente wichtig:


library(tidyr) # Laden des Pakets

spread_wide <-  spread(data=ChickWeight, # Name des Datensatzes
                  key="Time", #  Zeitvariable
                  value="weight") # messwiederholte Variable

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).


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.

Hinweis: Manchmal werden nicht alle Untersuchungseinheiten zu jedem Zeitpunkt untersucht.

In unserem Datensatz ChickWeight ist das der Fall. Ihr könnt euch folgendermaßen anschauen, wie häufig jede Untersuchungseinheit beobachtet wurde: table(ChickWeight$Chick).

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.

stats: reshape()

Bei der Formatierung von Wide in Long sind folgende 6 Argumente wichtig:

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_long <- reshape(data=reshape_wide, # Name des Datensatzes
                    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

Hinweis: Ganz links seht ihr 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).

Warum wird hier varying=3:14 genutzt?

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.

Mit den Variablen, die man im Rahmen der Umformatierung erst neu erstellt (z.B. timevar), geht das aber nicht, da diese noch gar nicht existieren und von daher auch keine Indizes haben.


Wie kann man verschiedene messwiederholte Variablen jeweils zusammenfassen? bzw. Wie nutzt man varying=list() & 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:
reshape_dup <- cbind(reshape_wide, reshape_wide[3:14])

reshape_dup_long <- reshape(data=reshape_dup,
                    varying=list(3:14, 15:26), 
                      # zwei messwiederholte Variable (jeweils mehrere 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")


tidyr: gather()

Hierfür benötigt man 5 Argumente:


library(tidyr) # Laden des Pakets

gather_long <- gather(data=spread_wide, # Name des Datensatzes
                  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

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 (Messzeitpunkte) so benannt, wie vorher die einzelnen Spalten der messwiederholten Variablen hießen. Wenn ihr euch das anschauen wollt, bearbeitet einfach reshape_wide (anstatt spread_wide) mit gather(). 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.


3. Weiterführende Hilfe

Es gibt noch andere Funktionen, mit denen man Datensätze vom Long- ins Wide-Format oder umgekehrt umformatieren kann. Wenn ihr wissen wollt, wie man melt() (Wide zu Long) und dcast() (Long zu Wide) aus dem Paket reshape2 nutzt, könnt ihr dafür hier nachschauen.