Oft kommt es vor, dass verschiedene Datensätze aus mehreren Versuchen/Experimenten erhoben werden. In solche einem Fall ist es eventuell nötig

  1. die Daten aus den verschiedenen Dateien zusammenzutragen und/oder
  2. die verschiedenen Datensätze separat auszuwerten.

3 Datensätze - 3 Auswertungen

In diesem Beispiel liegen Daten von drei Feldversuchen vor, die sich prinzipiell recht ähnlich sind, aber an drei verschiedenen Orten (“L1”, “L2” und “L3”) stattgefunden haben. In den Versuchen wurden 89 verschiedene Sorten/Genotypen (“G1”-“G89”) in randomisierten, vollständigen Blockanlagen (RCBD) angebaut und schließlich ihr Ertrag gemessen.

Wir wollen hier nicht versuchen die Daten der 3 Orte gemeinsam auszuwerten, sondern separat. Prinzipiell soll also einfach die Auswertung einer einfaktoriellen RCBD, wie in diesem Beispiel durchgeführt werden, nur eben 3 mal hintereinander jeweils pro Ort.

Natürlich kann dies manuell erledigt werden, indem Beispielsweise ein R-Code pro Datensatz angelegt wird und lediglich an bestimmten Stellen Kleinigkeiten geändert werden, sodass sich der Code auf die richtige Excel-Datei bezieht und am Ende auch die Ergebnisse korrekt abspeichert. Diese Vorgehensweise hat aber folgende Probleme:

  1. Es wird natürlich mit steigender Anzahl Datensätze (also mehr als 3) schnell unübersichtlich und aufwendig und
  2. es muss bei einer nachträglichen Korrektur des Codes die Korrektur in allen R-Codes umgesetzt werden, was nur so nach Flüchtigkeitsfehlern schreit.

Deshalb bietet es sich manchmal an den R-Code so zu schreiben, dass man nur einen einzigen Code für die Auswertung aller Datensätze benötigt. Voraussetzung hierfür ist natürlich, dass die Datensätze und die Auswertungen sich alle sehr ähnlich sind.

Zur Umsetzung der Analyse

Sagen wir die 3 Dateien lägen alle im selben Ordner und hießen “Environment1.xlsx”, “Environment2.xlsx” und “Environment3.xlsx” und außerdem hieße das entsprechende Tabllenblatt innerhalb aller Excel-Dateien immer “Datenblatt”. Natürlich könnte man sie alle einzeln importieren:

library(readxl)

dat1 <- read_excel("Environment1.xlsx", sheet="Datenblatt")
dat2 <- read_excel("Environment2.xlsx", sheet="Datenblatt")
dat3 <- read_excel("Environment3.xlsx", sheet="Datenblatt")

Loops

Wie man sieht, ist aber der meiste Code hier redundant, da sich lediglich an zwei Stellen pro Zeile die Zahl von 1 bis 3 ändert. Für solche Fälle können sogenannte loops/Schleifen genutzt werden. Sie durchlaufen beispielsweise alle geforderten Ziffern nacheinander:

for (i in c(1,2,3)){
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
for (year in c(2019, 2020, 2021, 2022)){
  print(paste("The year is", year))
}
## [1] "The year is 2019"
## [1] "The year is 2020"
## [1] "The year is 2021"
## [1] "The year is 2022"

Wie man sieht wird für jedes Element eines Vektors (oben: i oder year) ausgeführt, was innerhalb der geschweiften Klammern steht. Es wird schnell klar, dass dies uns auch für den Fall mit Environment1 - Environment3 helfen kann. Wir wollen es uns allerdings noch ein wenig schwerer machen.

list.files

In Wirklichkeit kann es passieren, dass die Dateien nicht mal mit einheitlichen Namen vorliegen. So auch hier, denn es gibt zwar die Datei “Environment1.xlsx”, aber die anderen beiden heißen “environment2.xlsx” und “Location3.xlsx”. Anstatt die Dateien nun händisch umzubenennen, wollen wir stattdessen einfach mit den Dateinamen arbeiten, die vorliegen. Eine nützliche Funktion ist dabei list.files() - sie listet alle Dateien in einem Ordner auf:

DatPfad <- "D:/RKurse/MyDatasets/example_multiplefiles/threedatasets/"
DatNamen <- list.files(DatPfad)
DatNamen # Zeige DatNamen
## [1] "Environment1.xlsx" "environment2.xlsx" "Location3.xlsx"

Da wir die 3 Dateinamen nun im Vektor DatNamen abgespeichert haben, können wir sie auch einzeln mit einem Loop ansprechen:

for (i in DatNamen){
  print(paste0(DatPfad, i))
}
## [1] "D:/RKurse/MyDatasets/example_multiplefiles/threedatasets/Environment1.xlsx"
## [1] "D:/RKurse/MyDatasets/example_multiplefiles/threedatasets/environment2.xlsx"
## [1] "D:/RKurse/MyDatasets/example_multiplefiles/threedatasets/Location3.xlsx"

Listen in R

Um die 3 Dateien nun auch wirklich zu importieren und als Objekt abzuspeichern, bietet sich der R-Objekttyp Liste an. Eine Liste kann alle möglichen R Objekte in sich speichern. Ich selbst merke sie mir als einen Vektor voller R-Objekte. Man muss im vornherein auch nicht festlegen wie lange eine Liste wird - man muss lediglich einmalig die leere Liste erstellen. Im Gegensatz zu Vektoren o.ä. spricht man beispielsweise das erste Objekt einer Liste allerdings nicht mit Vektor[1] an, sondern mit einer doppelten eckigen Klammer Liste[[1]]:

library(data.table)
library(dplyr)
library(readxl)

DatListe <- list() # Erstelle leere Liste

for (i in DatNamen){
  DatListe[[i]] <- read_excel(path  = paste0(DatPfad, i),
                              sheet = "Datenblatt") %>% data.table
}

Nach dem Loop wurden die 3 Dateien als 3 Objekte im data.table Format in Datliste gespeichert (mehr zu data.table und dem %>% Befehl gibt’s hier):

summary(DatListe) # Zeige Infos zur DatListe
##                   Length Class      Mode
## Environment1.xlsx 4      data.table list
## environment2.xlsx 4      data.table list
## Location3.xlsx    4      data.table list

Hinweis: Length=4 meint hier die Anzahl Spalten des Datensatzes

DatListe[[1]] # Zeige nur erstes Objekt in DatListe
##      loc gen rep   y
##   1:  L1  G1  R1 2.0
##   2:  L1  G1  R2 2.5
##   3:  L1  G2  R1 1.5
##   4:  L1  G2  R2 2.0
##   5:  L1  G3  R1 1.0
##  ---                
## 168:  L1 G87  R2 2.5
## 169:  L1 G88  R1 3.5
## 170:  L1 G88  R2 2.0
## 171:  L1 G89  R1 2.5
## 172:  L1 G89  R2 1.0
DatListe # Zeige gesamte DatListe
## $Environment1.xlsx
##      loc gen rep   y
##   1:  L1  G1  R1 2.0
##   2:  L1  G1  R2 2.5
##   3:  L1  G2  R1 1.5
##   4:  L1  G2  R2 2.0
##   5:  L1  G3  R1 1.0
##  ---                
## 168:  L1 G87  R2 2.5
## 169:  L1 G88  R1 3.5
## 170:  L1 G88  R2 2.0
## 171:  L1 G89  R1 2.5
## 172:  L1 G89  R2 1.0
## 
## $environment2.xlsx
##      loc gen rep   y
##   1:  L2  G1  R1 2.0
##   2:  L2  G1  R2 1.5
##   3:  L2  G1  R3 2.0
##   4:  L2  G2  R1 2.0
##   5:  L2  G2  R2 2.0
##  ---                
## 263:  L2 G88  R2 2.0
## 264:  L2 G88  R3 2.0
## 265:  L2 G89  R1 1.5
## 266:  L2 G89  R2 2.0
## 267:  L2 G89  R3 1.5
## 
## $Location3.xlsx
##      loc gen rep   y
##   1:  L3  G1  R1 3.0
##   2:  L3  G1  R2 2.5
##   3:  L3  G1  R3 3.0
##   4:  L3  G2  R1 2.5
##   5:  L3  G2  R2 2.5
##  ---                
## 260:  L3 G88  R2 2.5
## 261:  L3 G88  R3 3.0
## 262:  L3 G89  R1 2.5
## 263:  L3 G89  R2 3.0
## 264:  L3 G89  R3 2.5

Wir haben jetzt also mit wenigen Zeilen Code dafür gesorgt, dass alle 3 Excel-Dateien aus einem Ordner importiert und in einer Liste abgespeichert werden. Übrigens würde derselbe Code auch funktionieren, wenn wir beispielsweise nachträglich noch eine vierte Excel-Datei hinzufügen würden.

Ergebnisliste

Da alle Versuche als einfaktorielle, randomisierte vollständige Blockanlage angelegt wurden, können wir sie mit dem gleichen Modell auswerten. Bei der Auswertung wollen wir uns hier der Einfachheit halber auf die Varianzanalyse beschränken. Auch die ANOVA Ergebnisse können wir direkt in einer Liste speichern:

ErgebnisListe <- list() # Erstelle leere Liste

for (i in DatNamen){
  mod <- lm(y ~ gen + rep, data=DatListe[[i]])
  ErgebnisListe[[i]] <- anova(mod)
}

ErgebnisListe # Zeige ErgebnisListe
## $Environment1.xlsx
## Analysis of Variance Table
## 
## Response: y
##           Df Sum Sq Mean Sq F value   Pr(>F)    
## gen       85 83.432  0.9815  2.9309  6.8e-07 ***
## rep        1  5.408  5.4084 16.1493 0.000126 ***
## Residuals 85 28.467  0.3349                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## $environment2.xlsx
## Analysis of Variance Table
## 
## Response: y
##            Df Sum Sq Mean Sq F value  Pr(>F)    
## gen        88 78.266 0.88939  5.8650 < 2e-16 ***
## rep         2  1.311 0.65543  4.3222 0.01471 *  
## Residuals 176 26.689 0.15164                    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## $Location3.xlsx
## Analysis of Variance Table
## 
## Response: y
##            Df Sum Sq Mean Sq F value    Pr(>F)    
## gen        87 36.503 0.41957  5.5155 < 2.2e-16 ***
## rep         2  1.249 0.62452  8.2097 0.0003932 ***
## Residuals 172 13.084 0.07607                      
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Wie man sieht ist der Sorteneffekt in allen 3 ANOVAs signifikant (p<0.05).

Mehrdimensionale Liste

Was ich jetzt hier beschreibe habe ich tatsächlich noch keinen meiner Kollegen jemals anweden sehen - ich selbst fand es aber in mehreren Situationen sehr praktisch. Eventuell deutet das darauf hin, dass es eine bessere Lösung gibt, von der ich nichts weiß, also gerne bescheid sagen.

Die Motivation dahinter ist, dass ich zu einem Datensatz/Modell oft mehr als nur ein Ergebnis (z.B. ANOVA Ergebnisse) speichern möchte. Ich möchte aber nicht immer auch für jeden Ergebnistyp eine Liste erstellen. Deshalb erstelle ich manchmal Liste, die selbst schon wieder wie Tabellen aussehen. Hier ein Beispiel:

# Gewünschte Ergebnisse
ErgebnisNamen <- c("n_obs","n_gen","n_rep","ANOVA")

# Erstelle leere Liste
result_list <- matrix(data=list(), 
                      nrow=length(DatNamen), 
                      ncol=length(ErgebnisNamen), 
                      dimnames=list(DatNamen, ErgebnisNamen))
result_list # Zeige leere Liste
##                   n_obs n_gen n_rep ANOVA
## Environment1.xlsx NULL  NULL  NULL  NULL 
## environment2.xlsx NULL  NULL  NULL  NULL 
## Location3.xlsx    NULL  NULL  NULL  NULL

Man sieht, dass die Liste von vornherein in ihren Dimensionen definiert sein muss. In diesem Fall würde ich gerne für jeden Datensatz die Anzahl Beobachtungen n_obs, Anzahl Sorten n_gen, Anzahl Blöcke n_rep und die ANOVA Ergebnisse ANOVA festhalten. Außerdem habe ich die Zeilen und Spalten mit dem dimnames= Befehl entsprechend benannt. Nun können wir die leere Liste befüllen:

for (i in DatNamen){
  result_list[[i, "n_obs" ]] <- nrow(DatListe[[i]]) 
  result_list[[i, "n_gen" ]] <- n_distinct(DatListe[[i]]$gen) 
  result_list[[i, "n_rep" ]] <- n_distinct(DatListe[[i]]$rep) 
    mod <- lm(y ~ gen + rep, data=DatListe[[i]])
  result_list[[i, "ANOVA"]] <- anova(mod)
}

result_list
##                   n_obs n_gen n_rep ANOVA 
## Environment1.xlsx 172   86    2     List,5
## environment2.xlsx 267   89    3     List,5
## Location3.xlsx    264   88    3     List,5

Die ersten drei Spalten werden praktischerweise ganz einfach wie eine Tabelle angezeigt, da sie je Datensatz auch nur eine Zahl beinhalten. Die letzten Spalte entählt jeweils die gesamte ANOVA Tabelle und kann somit nicht auf diese Weise dargestellt werden. Dennoch sind die Informationen vorhanden - man muss sie lediglich abfragen:

result_list[["Environment1.xlsx", "ANOVA"]] # Zeigt nur ANOVA Ergbnisse des Datensates Environment1.xlsx. Übrigens funktioniert result_list[[1, "ANOVA"]] auch!
## Analysis of Variance Table
## 
## Response: y
##           Df Sum Sq Mean Sq F value   Pr(>F)    
## gen       85 83.432  0.9815  2.9309  6.8e-07 ***
## rep        1  5.408  5.4084 16.1493 0.000126 ***
## Residuals 85 28.467  0.3349                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Man kann übrigens auch output von ggplot() in solchen Listen abspeichern. Zusammen mit der Export-Funktion in verschiedene Tabellenblätter derselben Excel-Datei lässt sich so der Arbeitsprozess in bestimmten Fällen also deutlich effizienter gestalten.

 

Bei Fragen kannst du mir gerne schreiben!

schmidtpaul@hotmail.de