Question: Do light and fertilizer affect plant nutrient uptake?
IV: Light - 2 factors, fertilizer - 3 factors.
DV: Nutrient Uptake (continuous numerical)
Running a 2-factor ANOVA with 6 total levels.
light <- read.csv("Data/light_factorialanova.csv")
str(light) #turn characters to factors so tapply will work
## 'data.frame': 180 obs. of 4 variables:
## $ Light : chr "Low" "Low" "Low" "Low" ...
## $ Fertilizer : chr "None" "None" "None" "None" ...
## $ Growth : num 5.5 4.86 5.65 6.52 4.77 ...
## $ Nutrient_Uptake: num 3.17 3.44 4.75 4.61 3.98 ...
light$Light <- as.factor(light$Light)
light$Fertilizer <- as.factor(light$Fertilizer)
str(light) #great
## 'data.frame': 180 obs. of 4 variables:
## $ Light : Factor w/ 2 levels "High","Low": 2 2 2 2 2 2 2 2 2 2 ...
## $ Fertilizer : Factor w/ 3 levels "None","Organic",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Growth : num 5.5 4.86 5.65 6.52 4.77 ...
## $ Nutrient_Uptake: num 3.17 3.44 4.75 4.61 3.98 ...
nutrientm <- lm(Nutrient_Uptake~Light*Fertilizer, data=light)
#check assumptions for residuals
par(mfrow=c(2,2))
plot(nutrientm)
#normality of residuals
shapiro.test(residuals(nutrientm)) #normality assumption met
##
## Shapiro-Wilk normality test
##
## data: residuals(nutrientm)
## W = 0.99519, p-value = 0.8331
#equal variance across species-sex groups
leveneTest(Nutrient_Uptake ~ interaction(Light, Fertilizer),data = light)#variance assumption met
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 5 2.1714 0.05941 .
## 174
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Summary & Anovasummary(nutrientm) #R2adj: 91%
##
## Call:
## lm(formula = Nutrient_Uptake ~ Light * Fertilizer, data = light)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.3521 -0.6284 -0.0091 0.5495 3.0323
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 14.0288 0.1777 78.967 < 2e-16 ***
## LightLow -9.7469 0.2512 -38.795 < 2e-16 ***
## FertilizerOrganic -2.0861 0.2512 -8.303 2.73e-14 ***
## FertilizerSynthetic -4.9822 0.2512 -19.830 < 2e-16 ***
## LightLow:FertilizerOrganic 5.5977 0.3553 15.755 < 2e-16 ***
## LightLow:FertilizerSynthetic 11.9285 0.3553 33.572 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.973 on 174 degrees of freedom
## Multiple R-squared: 0.9155, Adjusted R-squared: 0.9131
## F-statistic: 377.2 on 5 and 174 DF, p-value: < 2.2e-16
Anova(nutrientm) #interaction term is significant
## Anova Table (Type II tests)
##
## Response: Nutrient_Uptake
## Sum Sq Df F value Pr(>F)
## Light 686.14 1 724.683 < 2.2e-16 ***
## Fertilizer 30.90 2 16.317 3.201e-07 ***
## Light:Fertilizer 1068.51 2 564.264 < 2.2e-16 ***
## Residuals 164.75 174
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#NOTE if interaction term is not significant in this step, go back and take it out of model and just test main effects
aov & TukeyHSDnutrientaov <- aov(Nutrient_Uptake~Light*Fertilizer, data=light)
tuk <- TukeyHSD(nutrientaov)
tuk
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Nutrient_Uptake ~ Light * Fertilizer, data = light)
##
## $Light
## diff lwr upr p adj
## Low-High -3.904821 -4.191111 -3.618531 0
##
## $Fertilizer
## diff lwr upr p adj
## Organic-None 0.7127126 0.2927510 1.1326743 0.0002625
## Synthetic-None 0.9820468 0.5620851 1.4020084 0.0000003
## Synthetic-Organic 0.2693341 -0.1506275 0.6892958 0.2858761
##
## $`Light:Fertilizer`
## diff lwr upr p adj
## Low:None-High:None -9.7468825 -10.4708896 -9.022875495 0.0000000
## High:Organic-High:None -2.0861369 -2.8101440 -1.362129892 0.0000000
## Low:Organic-High:None -6.2353203 -6.9593274 -5.511313252 0.0000000
## High:Synthetic-High:None -4.9821965 -5.7062036 -4.258189477 0.0000000
## Low:Synthetic-High:None -2.8005925 -3.5245996 -2.076585443 0.0000000
## High:Organic-Low:None 7.6607456 6.9367385 8.384752658 0.0000000
## Low:Organic-Low:None 3.5115622 2.7875552 4.235569298 0.0000000
## High:Synthetic-Low:None 4.7646860 4.0406790 5.488693073 0.0000000
## Low:Synthetic-Low:None 6.9462901 6.2222830 7.670297107 0.0000000
## Low:Organic-High:Organic -4.1491834 -4.8731904 -3.425176305 0.0000000
## High:Synthetic-High:Organic -2.8960596 -3.6200666 -2.172052530 0.0000000
## Low:Synthetic-High:Organic -0.7144556 -1.4384626 0.009551504 0.0553886
## High:Synthetic-Low:Organic 1.2531238 0.5291167 1.977130830 0.0000216
## Low:Synthetic-Low:Organic 3.4347278 2.7107208 4.158734864 0.0000000
## Low:Synthetic-High:Synthetic 2.1816040 1.4575970 2.905611089 0.0000000
letters <- multcompView::multcompLetters(tuk[["Light:Fertilizer"]][, "p adj"])
letters$Letters
## Low:None High:Organic Low:Organic High:Synthetic Low:Synthetic
## "a" "b" "c" "d" "b"
## High:None
## "e"
ggplot#add significance letter
letters <- data.frame(Fertilizer = rep(c("None", "Organic", "Synthetic"), each = 2), Light = rep(c("High", "Low"), times = 3), label = c("a", "e", "b", "d", "c", "b"), height = c(16.5, 7, 14.7, 10, 13, 14.2))
#plot that is not box
ggplot(light, aes(Fertilizer,Nutrient_Uptake , fill=Light))+
geom_violin(position = position_dodge(width = 0.8))+
geom_text(data = letters, aes(Fertilizer, height, label = label), position = position_dodge(width = 0.8), show.legend = F) +
labs(x = "Fertilizer type", y = "Nutrient Uptake (mg/plant)", fill = "Light Level") +
theme_bw()+
theme(legend.position=c(0.92, 0.12), legend.background = element_rect(color="black"))+
scale_fill_manual(values = c("#488073ff", "#ceb526ff"))
Figure 1. Nutrient uptake (mg/plant) by fertilizer type and light level. The effect of fertilizer on nutrient uptake depended on light level, \(F_{2, 174}\) = 564.26, p < 0.01. Under high light, nutrient uptake was highest without fertilizer, whereas under low light, uptake was highest with synthetic fertilizer. The model explained 91.6% of the variation (\(R^2\) = .916). Different letters indicate significant differences among fertilizer–light groups (Tukey’s HSD, \(\alpha\) = .05).
Used to help create figure caption:
summary(nutrientm)$r.squared #multiply by 100 for percent explained
## [1] 0.9155276
Anova(nutrientm) #F, numerator df, and p; denominator df comes from Residuals
## Anova Table (Type II tests)
##
## Response: Nutrient_Uptake
## Sum Sq Df F value Pr(>F)
## Light 686.14 1 724.683 < 2.2e-16 ***
## Fertilizer 30.90 2 16.317 3.201e-07 ***
## Light:Fertilizer 1068.51 2 564.264 < 2.2e-16 ***
## Residuals 164.75 174
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
This sentence is only here to practice pulling code from code chunck and putting it directly into my html paragraphs. The model explained 91.6% of the variation.
The first datapoint in the light dataframe has the following value for nutrient uptake: 3.171005 mg/plant.
Question: Do light and fertilizer affect plant growth? IV: Light - 2 factors, fertilizer - 3 factors. DV: Growth (continuous numerical) Running a 2-factor ANOVA with 6 total levels.
light <- read.csv("Data/light_factorialanova.csv")
str(light) #turn characters to factors so tapply will work
## 'data.frame': 180 obs. of 4 variables:
## $ Light : chr "Low" "Low" "Low" "Low" ...
## $ Fertilizer : chr "None" "None" "None" "None" ...
## $ Growth : num 5.5 4.86 5.65 6.52 4.77 ...
## $ Nutrient_Uptake: num 3.17 3.44 4.75 4.61 3.98 ...
light$Light <- as.factor(light$Light)
light$Fertilizer <- as.factor(light$Fertilizer)
str(light) #great
## 'data.frame': 180 obs. of 4 variables:
## $ Light : Factor w/ 2 levels "High","Low": 2 2 2 2 2 2 2 2 2 2 ...
## $ Fertilizer : Factor w/ 3 levels "None","Organic",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Growth : num 5.5 4.86 5.65 6.52 4.77 ...
## $ Nutrient_Uptake: num 3.17 3.44 4.75 4.61 3.98 ...
growthm <- lm(Growth~Light*Fertilizer, data=light)
#check assumptions for residuals
par(mfrow=c(2,2))
plot(growthm)
#normality of residuals
shapiro.test(residuals(growthm)) #normality assumption met
##
## Shapiro-Wilk normality test
##
## data: residuals(growthm)
## W = 0.99492, p-value = 0.8007
#equal variance across species-sex groups
leveneTest(Growth ~ interaction(Light, Fertilizer),data = light)#variance assumption met
## Levene's Test for Homogeneity of Variance (center = median)
## Df F value Pr(>F)
## group 5 0.5499 0.7383
## 174
Summary & Anovasummary(growthm) #R2adj: 96.1%
##
## Call:
## lm(formula = Growth ~ Light * Fertilizer, data = light)
##
## Residuals:
## Min 1Q Median 3Q Max
## -2.63263 -0.61351 -0.00418 0.55226 2.48349
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 14.9797 0.1737 86.220 < 2e-16 ***
## LightLow -10.1679 0.2457 -41.383 < 2e-16 ***
## FertilizerOrganic -3.0747 0.2457 -12.514 < 2e-16 ***
## FertilizerSynthetic 5.2990 0.2457 21.567 < 2e-16 ***
## LightLow:FertilizerOrganic 8.1417 0.3475 23.431 < 2e-16 ***
## LightLow:FertilizerSynthetic 1.9020 0.3475 5.474 1.52e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.9516 on 174 degrees of freedom
## Multiple R-squared: 0.962, Adjusted R-squared: 0.9609
## F-statistic: 881.3 on 5 and 174 DF, p-value: < 2.2e-16
Anova(growthm) #interaction term is significant
## Anova Table (Type II tests)
##
## Response: Growth
## Sum Sq Df F value Pr(>F)
## Light 2093.06 1 2311.34 < 2.2e-16 ***
## Fertilizer 1353.17 2 747.15 < 2.2e-16 ***
## Light:Fertilizer 544.19 2 300.47 < 2.2e-16 ***
## Residuals 157.57 174
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#NOTE if interaction term is not significant in this step, go back and take it out of model and just test main effects
aov & TukeyHSDgrowthaov <- aov(Growth~Light*Fertilizer, data=light)
TukeyHSD(growthaov)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Growth ~ Light * Fertilizer, data = light)
##
## $Light
## diff lwr upr p adj
## Low-High -6.82 -7.099983 -6.540018 0
##
## $Fertilizer
## diff lwr upr p adj
## Organic-None 0.996139 0.5854293 1.406849 1e-07
## Synthetic-None 6.250035 5.8393252 6.660745 0e+00
## Synthetic-Organic 5.253896 4.8431863 5.664606 0e+00
##
## $`Light:Fertilizer`
## diff lwr upr p adj
## Low:None-High:None -10.1678953 -10.875952 -9.4598384 0.000000
## High:Organic-High:None -3.0747065 -3.782763 -2.3666496 0.000000
## Low:Organic-High:None -5.1009108 -5.808968 -4.3928540 0.000000
## High:Synthetic-High:None 5.2990382 4.590981 6.0070951 0.000000
## Low:Synthetic-High:None -2.9668636 -3.674920 -2.2588067 0.000000
## High:Organic-Low:None 7.0931888 6.385132 7.8012457 0.000000
## Low:Organic-Low:None 5.0669844 4.358928 5.7750413 0.000000
## High:Synthetic-Low:None 15.4669335 14.758877 16.1749904 0.000000
## Low:Synthetic-Low:None 7.2010317 6.492975 7.9090886 0.000000
## Low:Organic-High:Organic -2.0262044 -2.734261 -1.3181475 0.000000
## High:Synthetic-High:Organic 8.3737447 7.665688 9.0818016 0.000000
## Low:Synthetic-High:Organic 0.1078429 -0.600214 0.8158998 0.997911
## High:Synthetic-Low:Organic 10.3999491 9.691892 11.1080060 0.000000
## Low:Synthetic-Low:Organic 2.1340472 1.425990 2.8421041 0.000000
## Low:Synthetic-High:Synthetic -8.2659018 -8.973959 -7.5578450 0.000000
ggplot#add significance letter
letters <- data.frame(Fertilizer = rep(c("None", "Organic", "Synthetic"), each = 2), Light = rep(c("High", "Low"), times = 3), label = c("b", "e", "c", "d", "a", "c"), height = c(18.5, 7.5, 15, 12.5, 23.5, 14.5))
#plot
ggplot(light, aes(Fertilizer,Growth , color=Light))+
geom_boxplot()+
geom_text(data = letters, aes(Fertilizer, height, label = label, color = Light), position = position_dodge(width = 0.8), show.legend = F) +
labs(x = "Fertilizer type", y = "Plant growth (cm)", color = "Light Level") +
theme_bw()+
theme(legend.position=c(0.92, 0.12), legend.background = element_rect(color="black"))
| Analysis | Predictors | Example R formula | Where I used it |
|---|---|---|---|
| One-way / one-factor ANOVA | 1 categorical factor | Y ~ A |
Lab 09: deer diets, soil types, and plant disturbance treatments |
| Two-way / two-factor factorial ANOVA | 2 categorical factors | Y ~ A * B |
HW 10, Part A: millipede species × sex |
| Three-way / three-factor factorial ANOVA | 3 categorical factors | Y ~ A * B * C |
— |
| Four-way / four-factor factorial ANOVA | 4 categorical factors | Y ~ A * B * C * D |
— |
| n-way / n-factor factorial ANOVA | n categorical factors | Includes n factors | — |
| ANCOVA with a common slope | Categorical factor + numerical covariate | Y ~ A + X |
HW 10, Part B: mole-rat treatment + temperature |
| ANCOVA with an interaction | Categorical factor + numerical covariate + their interaction | Y ~ A * X |
HW 10, Part C: burn status × water depth predicting fish density |
| ANOVA with a block | Treatment factor + categorical block | Y ~ A + Block |
HW 10, Part B: mole-rat treatment + experimental run |
Factors are categorical predictors; levels are the categories within a factor. Numeric labels do not make a factor numerical.
In ANCOVA, the covariate is numerical.
A + X assumes a common slope across groups;
A * X allows slopes to differ.
In R formulas, A * B expands to
A + B + A:B, including both main effects and their
interaction.
| Question | Fixed block: + Block |
Random intercept: + (1 \| Block) |
|---|---|---|
| Example model | lm(SOC ~ Treatment + Block, data = soil) |
lmer(SOC ~ Treatment + (1 \| Block), data = soil) |
| Basic idea | Account for differences among these particular blocks. | Account for block differences using a shared distribution of block deviations. |
| Can blocks have different baselines? | Yes. | Yes. |
| What is a baseline? | Predicted SOC under the reference treatment, such as Control. | Predicted SOC under the reference treatment, such as Control. |
| What does the intercept mean? | Baseline for the reference block under Control. | Shared baseline under Control; individual blocks deviate from it. |
| What is a block estimate? | A difference from the reference block. | A deviation from the shared baseline. |
| Example | Intercept = 20 and BlockB = +6 predicts Control SOC of 26 in Block B. | Shared intercept = 20 and Block B deviation = +4 predicts Control SOC of 24 in Block B. |
| Where are individual block estimates? | Coefficient rows in summary(model), such as
BlockB. |
ranef(model)$Block. |
What does summary() show about random blocks? |
Not applicable. | Estimated variance and SD among block intercepts under “Random effects.” |
| What is partial pooling? | Block coefficients are not shrunk toward a shared baseline. | Less certain block estimates are pulled more toward the shared baseline. |
| Does the treatment effect vary among blocks? | No, not in this formula. | No, not in this formula. That would require a random treatment slope. |
| Does this change the observed data? | No. | No. |
| Important detail | Keep Block categorical. | 1 means intercept; \| Block means it
varies among blocks. |
Examples are illustrative, not results from my data. With additional numerical predictors, the intercept also refers to those predictors being zero.