Oft kommt es vor, dass verschiedene Datensätze aus mehreren Versuchen/Experimenten erhoben werden. In solche einem Fall ist es eventuell nötig
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:
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.
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")
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.
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"
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.
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).
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