library(data.table) # bessere Datenmanipulation
library(ggplot2) # bessere Plots
library(emmeans) # adjustierte Mittelwerte
library(lme4); library(lmerTest) # gemischtes Modell
library(RColorBrewer) # bessere Farben
In diesem Experiment wurden die Auswirkungen von 6 Stickstoffdüngern (N) auf den Ertrag von 4 Reissorten (G) untersucht. Demnach gab es zwei Behandlungsfaktoren mit insgesamt 6x4=24 Behandlungsstufenkombinationen. Diese 24 Kombinationen wurden 3 mal in vollständigen Blöcken wiederholt - wir haben also ein RCBD. Hier mehr Infos zu Versuchsdesigns
## Warning in if (class(data) == "formula") {: the condition has length > 1 and only
## the first element will be used
print(riceRCBD, nrows=10)
## N G rep yield
## 1: N1 A rep1 4520
## 2: N2 A rep1 5598
## 3: N4 A rep1 6192
## 4: N6 A rep1 8542
## 5: N3 A rep1 5806
## ---
## 68: N6 D rep3 1744
## 69: N3 D rep3 4236
## 70: N1 D rep3 5016
## 71: N2 D rep3 4382
## 72: N5 D rep3 2856
str(riceRCBD)
## Classes 'data.table' and 'data.frame': 72 obs. of 4 variables:
## $ N : Factor w/ 6 levels "N1","N2"..
## $ G : Factor w/ 4 levels "A","B","..
## $ rep : Factor w/ 3 levels "rep1",""..
## $ yield: int 4520 5598 6192 8542 580..
## - attr(*, ".internal.selfref")=<exter..
Erst wollen wir ein Gefühl für den Datensatz bekommen und betrachten einen Boxplot für die Stufenkombinationen der zwei Behandlungsfaktoren. Mittels des col=
statements der boxplot()
Funktion können wir angeben welche Füllfarben die Boxen haben sollen. Wir wählen dieselben Farben wie im Feldplan - mehr Infos dazu hier.
boxplot(yield ~ N*G, col=brewer.pal(6, "Dark2"), data=riceRCBD, las=2) # las=2 dreht die Achsenbeschriftung
Es fällt auf, dass sich das Muster der Werte der verschiedenen N-Stufen für Sorte D deutlich von denen der anderen 3 Sorten unterscheiden. Dies deutet auf Wechselwirkungen zwischen den beiden Behandlungsfaktoren hin ( mehr Infos dazu hier).
Wir können uns nun entschließen die Daten mittels eines linearen Modells zu analysieren. Das Modell ist dem aus dem vorangegangen Beispiel mit RCBD ähnlich, muss jedoch aufgrund des zweiten Behandlungsfaktors erweitert werden. Wir beginnen mit dem vollen Modell um die backward elimination anzuwenden ( mehr Infos dazu hier).
mod <- lm(yield ~ N + G + N:G + rep, data=riceRCBD)
Zunächst sollten nun die Residuenplots (z.b. mit autoplot(mod)
) evaluiert werden, was hier aber übersprungen wird. Erst dann ist eine Varianzanalyse zulässig.
library(car)
Anova(mod, test.statistic="F", type="III")
## Anova Table (Type III tests)
##
## Response: yield
## Sum Sq Df F value Pr(>F)
## (Intercept) 47548894 1 120.6463 1.893e-14 ***
## N 35269882 5 17.8981 8.064e-10 ***
## G 3162391 3 2.6747 0.05821 .
## rep 1084820 2 1.3763 0.26272
## N:G 69378044 15 11.7356 4.472e-11 ***
## Residuals 18129432 46
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Da dies Ergebnisse einer mehrfaktorielle Varianzanalyse sind, betrachten zualleerst den F-Test für die Wechselwirkung. Der signifikante p-Wert von G:N
deutet darauf hin, dass die beiden Behandlungsfaktoren nicht rein additiv wirken, sondern wechselwirken. Dies bestätigt die Vermutung aus dem Boxplot der Rohdaten oben. Das bedeutet außerdem, dass wir die adjustierten Mittelwerte der Wechselwirkungseffekte berechnen wollen und nicht die der beiden Haupteffekte.
Wäre der Wechselwirkungseffekt hier nicht signifikant gewesen, würden wir ihn im Rahmen der Backwards Elimination aus dem Modell nehmen und erneut eine ANOVA für das reduzierte Modell durchführen. Mehr dazu hier.
Mit emmeans()
können wir auch adjustierte Mittelwerte für Wechselwirkungseffekte berechnen. Das geht entweder mit pairwise ~ N : G
oder pairwise ~ N | G
. Im beiden Fällen werden wie erwartet Mittelwerte für alle Kombinationen errechnet. Der Unterschied besteht in der Anzahl der Differenzen zwischen den Mittelwerten, die berechnet werden. Im ersten Fall werden wie gewohnt alle Mittelwerte miteinander verglichen. Im zweiten Fall, werden nur N-Stufen innerhalb einer Sorte miteinander verglichen. Natürlich können ‘N’ und ‘G’ hier auch ausgetauscht werden. Es kommt auf die Versuchsfrage an, welche Mittelwertvergleiche von größerem Interesse sind. Wir wollen uns hier auf die Vergleiche der N-Mittelwerte getrennt pro Sorte fokussieren.
means <- emmeans(mod, pairwise ~ N | G, adjust="tukey") # adjust="none" für t-test
means <- CLD(means$emmeans, Letters=letters)
## Warning: 'CLD' will be deprecated. Its use is discouraged.
## See '?cld.emmGrid' for an explanation. Use 'pwpp' or 'multcomp::cld' instead.
means
## G = A:
## N emmean SE df lower.CL upper.CL .group
## N1 4252.667 362.4537 46 3256.266 5249.067 a
## N2 5672.000 362.4537 46 4675.599 6668.401 ab
## N3 6400.000 362.4537 46 5403.599 7396.401 bc
## N4 6732.667 362.4537 46 5736.266 7729.067 bc
## N5 7563.333 362.4537 46 6566.933 8559.734 cd
## N6 8700.667 362.4537 46 7704.266 9697.067 d
##
## G = B:
## N emmean SE df lower.CL upper.CL .group
## N1 4306.000 362.4537 46 3309.599 5302.401 a
## N2 5982.000 362.4537 46 4985.599 6978.401 b
## N3 6259.000 362.4537 46 5262.599 7255.401 b
## N6 6540.333 362.4537 46 5543.933 7536.734 b
## N4 6895.000 362.4537 46 5898.599 7891.401 b
## N5 6950.667 362.4537 46 5954.266 7947.067 b
##
## G = C:
## N emmean SE df lower.CL upper.CL .group
## N1 3177.333 362.4537 46 2180.933 4173.734 a
## N2 5442.667 362.4537 46 4446.266 6439.067 b
## N3 5994.000 362.4537 46 4997.599 6990.401 b
## N4 6014.000 362.4537 46 5017.599 7010.401 b
## N6 6065.333 362.4537 46 5068.933 7061.734 b
## N5 6687.333 362.4537 46 5690.933 7683.734 b
##
## G = D:
## N emmean SE df lower.CL upper.CL .group
## N6 1880.667 362.4537 46 884.266 2877.067 a
## N5 2046.667 362.4537 46 1050.266 3043.067 a
## N4 3816.000 362.4537 46 2819.599 4812.401 b
## N1 4481.333 362.4537 46 3484.933 5477.734 b
## N3 4812.000 362.4537 46 3815.599 5808.401 b
## N2 4816.000 362.4537 46 3819.599 5812.401 b
##
## Results are averaged over the levels of: rep
## Confidence level used: 0.95
## Conf-level adjustment: sidak method for 6 estimates
## P value adjustment: tukey method for comparing a family of 6 estimates
## significance level used: alpha = 0.05
Erneut wollen wir die Ergebnisse abschließend in einem Balkendiagramm darstellen. Weil wir unser aber die N-Vergleiche isoliert pro Sorte konzentrieren wollen, können wir uns entscheiden ein Balkendiagramm pro Sorte zu erstellen. In solch einer Situation kann in der ggplot()
Funktion das facet_wrap()
statement genutzt werden.
Außerdem wollen wir die Balken wieder mit denselben Farben versehen, die die N-Stufen bereits im Feldplan und dem Boxplot hatten. Dazu fügen wir zuerst dem geom_bar()
statement fill=N
hinzu. Nun bekommt jede N-Stufe eine andere Farbe. Um nicht die default Farben zu nehmen, sondern selbst zu bestimmen welche Farben es sein sollen, schreiben wir zusätzlich noch scale_fill_manual(values=...)
in die Funktion. Um die dann automatisch generierte Legende zu unterdrücken (weil sie in diesem Fall keine zusätzlichen Informationen bringt), fügen wir außerdem guides(fill=FALSE)
hinzu.
means.plot <- as.data.table(means) # korrektes Format für ggplot
means.plot$.group <- gsub(" ", "", means.plot$.group, fixed = TRUE) # Entferne Leerzeichen
print(means.plot, nrows=2)
## N G emmean SE df lower.CL upper.CL .group
## 1: N1 A 4252.667 362.4537 46 3256.266 5249.067 a
## 2: N2 A 5672.000 362.4537 46 4675.599 6668.401 ab
## 3: N3 A 6400.000 362.4537 46 5403.599 7396.401 bc
## 4: N4 A 6732.667 362.4537 46 5736.266 7729.067 bc
## 5: N5 A 7563.333 362.4537 46 6566.933 8559.734 cd
## ---
## 20: N5 D 2046.667 362.4537 46 1050.266 3043.067 a
## 21: N4 D 3816.000 362.4537 46 2819.599 4812.401 b
## 22: N1 D 4481.333 362.4537 46 3484.933 5477.734 b
## 23: N3 D 4812.000 362.4537 46 3815.599 5808.401 b
## 24: N2 D 4816.000 362.4537 46 3819.599 5812.401 b
ggplot(data=means.plot, aes(x=N)) +
geom_bar(aes(y=emmean, fill=N), stat="identity", width=0.8) +
scale_fill_manual(values=brewer.pal(6, "Dark2")) + guides(fill=FALSE) +
geom_errorbar(aes(ymin=lower.CL, ymax=upper.CL), width=0.4) +
geom_text(aes(y=emmean+1500, label=.group)) +
facet_wrap(~G) + # ein plot pro Sorte
theme_bw() +
labs(main="Adj. N-Mittelwerte pro Sorte",
y="Adjustierter Ertragsmittelwert ± 95% Konfidenzintervall", x="N-Dünger",
caption="Getrennt für jede Sorte gilt: Mittelwerte, die mit einem gemeinsamen Buchstaben versehen sind,\n sind laut Tukey-test nicht signifikant voneinander verschieden.")
oder alternativ
ggplot() + theme_bw() +
# Rohdaten (crd)
geom_boxplot(data=riceRCBD, aes(x=N, y=yield), outlier.shape=NA, width=0.6) +
geom_jitter(data=riceRCBD, aes(x=N, y=yield), width=0.25, height=0, shape=1) +
# Ergebnisse (means)
geom_point(data=means.plot, aes(x=as.numeric(N)+0.4, y=emmean), col="red", shape=16, size=2) +
geom_errorbar(data=means.plot, aes(x=as.numeric(N)+0.4, ymin=lower.CL, ymax=upper.CL), col="red", width=0.1) +
geom_text(data=means.plot, aes(x=N, y=9600, label=.group), col="red") +
facet_wrap(~G) + # ein plot pro Sorte
ylim(0, 10000) +
labs(main="Adj. N-Mittelwerte pro Sorte",
y="Adjustierter Ertragsmittelwert ± 95% Konfidenzintervall", x="N-Dünger",
caption="Getrennt für jede Sorte gilt: Mittelwerte, die mit einem gemeinsamen Buchstaben versehen sind,\n sind laut Tukey-test nicht signifikant voneinander verschieden.")
Bei Fragen kannst du mir gerne schreiben!
schmidtpaul@hotmail.de