A silver powder process was run as a \(2^3\) factorial with two replicates. The response is powder density and the three factors are Ammonium (2% or 30%), Stir Rate (100 or 150 RPM) and Temperature (8 or 40 degrees C).
dat <- read.csv("https://raw.githubusercontent.com/tmatis12/datafiles/main/PowderProduction.csv",
fileEncoding = "UTF-8-BOM")
A <- factor(dat$Ammonium)
B <- factor(dat$StirRate)
C <- factor(dat$Temperature)
dat <- data.frame(A, B, C, Density = dat$Density)
head(dat)
## A B C Density
## 1 2 100 8 14.68
## 2 2 100 8 15.18
## 3 30 100 8 15.12
## 4 30 100 8 17.48
## 5 2 150 8 7.54
## 6 2 150 8 6.66
str(dat)
## 'data.frame': 16 obs. of 4 variables:
## $ A : Factor w/ 2 levels "2","30": 1 1 2 2 1 1 2 2 1 1 ...
## $ B : Factor w/ 2 levels "100","150": 1 1 1 1 2 2 2 2 1 1 ...
## $ C : Factor w/ 2 levels "8","40": 1 1 1 1 1 1 1 1 2 2 ...
## $ Density: num 14.68 15.18 15.12 17.48 7.54 ...
The three columns are turned into factors because each one only has two levels and we want R to treat them as groups, not as numbers.
Mean density at each level:
tapply(dat$Density, dat$A, mean)
## 2 30
## 11.19500 14.52625
tapply(dat$Density, dat$B, mean)
## 100 150
## 14.96250 10.75875
tapply(dat$Density, dat$C, mean)
## 8 40
## 12.71750 13.00375
The full \(2^3\) factorial model with two replicates is
\[ y_{ijkl} = \mu + \tau_i + \beta_j + \gamma_k + (\tau\beta)_{ij} + (\tau\gamma)_{ik} + (\beta\gamma)_{jk} + (\tau\beta\gamma)_{ijk} + \varepsilon_{ijkl} \]
where
All three factors are fixed.
A*B*C tells R to fit the three main effects and all the
interactions at once.
full_model <- aov(Density ~ A * B * C, data = dat)
summary(full_model)
## Df Sum Sq Mean Sq F value Pr(>F)
## A 1 44.39 44.39 11.180 0.01018 *
## B 1 70.69 70.69 17.804 0.00292 **
## C 1 0.33 0.33 0.083 0.78117
## A:B 1 28.12 28.12 7.082 0.02875 *
## A:C 1 0.02 0.02 0.005 0.94281
## B:C 1 10.13 10.13 2.551 0.14889
## A:B:C 1 1.52 1.52 0.383 0.55341
## Residuals 8 31.76 3.97
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Using alpha = 0.05, the terms that come out significant are:
| Term | p-value | Significant? |
|---|---|---|
| A (Ammonium) | 0.0102 | Yes |
| B (Stir Rate) | 0.0029 | Yes |
| C (Temperature) | 0.7812 | No |
| A:B | 0.0288 | Yes |
| A:C | 0.9428 | No |
| B:C | 0.1489 | No |
| A:B:C | 0.5534 | No |
interaction.plot(dat$A, dat$B, dat$Density,
main = "Ammonium by Stir Rate",
xlab = "Ammonium (%)",
ylab = "Mean Density",
trace.label = "Stir Rate")
interaction.plot(dat$A, dat$C, dat$Density,
main = "Ammonium by Temperature",
xlab = "Ammonium (%)",
ylab = "Mean Density",
trace.label = "Temperature")
interaction.plot(dat$B, dat$C, dat$Density,
main = "Stir Rate by Temperature",
xlab = "Stir Rate (RPM)",
ylab = "Mean Density",
trace.label = "Temperature")
In the first plot the two lines are far from parallel. At 100 RPM the line is almost flat, so changing the ammonium barely moves the density. At 150 RPM the line goes up steeply, from 7.77 to 13.75. That is the A:B interaction, and it is the one the table marks as significant.
In the second plot the two lines are almost parallel, so there is no interaction between ammonium and temperature.
In the third plot the lines cross a little, but the p-value is 0.1489, so that small crossing is not enough to call it an interaction.
We drop C, A:C, B:C and A:B:C because all of them have p-values above 0.05 (0.7812, 0.9428, 0.1489 and 0.5534). Temperature is the only main effect we drop. Its mean density is 12.72 at 8 degrees and 13.00 at 40 degrees, a difference of only 0.29, which is tiny next to the variation in the data.
We keep A, B and A:B. Note that even if a main effect came out weak, we would still have to keep A and B here, because A:B is significant and a model should not have an interaction without the main effects that form it.
reduced_model <- aov(Density ~ A * B, data = dat)
summary(reduced_model)
## Df Sum Sq Mean Sq F value Pr(>F)
## A 1 44.39 44.39 12.17 0.004472 **
## B 1 70.69 70.69 19.38 0.000861 ***
## A:B 1 28.12 28.12 7.71 0.016751 *
## Residuals 12 43.76 3.65
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
qqnorm(residuals(reduced_model), main = "Normal Q-Q Plot of the Residuals")
qqline(residuals(reduced_model))
plot(fitted(reduced_model), residuals(reduced_model),
main = "Residuals vs Fitted Values",
xlab = "Fitted Value",
ylab = "Residual",
pch = 19)
abline(h = 0, lty = 2)
The Q-Q plot is close to the line and the residuals vs fitted plot has no pattern and no funnel, so the reduced model is fine.
The significant terms and their final p-values from the reduced model are:
| Term | Final p-value |
|---|---|
| A (Ammonium) | 0.004472 |
| B (Stir Rate) | 0.000861 |
| A:B | 0.016751 |
Density goes up when the ammonium goes from 2% to 30% and goes down when the stir rate goes from 100 to 150 RPM. Because the A:B interaction is significant, these two cannot be read separately: how much the ammonium helps depends on the stir rate. Temperature does not affect the density in the range that was tested.
tapply(dat$Density, list(dat$A, dat$B), mean)
## 100 150
## 2 14.6225 7.7675
## 30 15.3025 13.7500
The best cell is 30% ammonium at 100 RPM, with a mean density of 15.30.
Here we only build the layout, we do not analyze any data. The experiment is a \(2^3\) factorial with three replications in a randomized complete block design.
The factors are:
| Factor | Description | Level 1 | Level 2 |
|---|---|---|---|
| A | Fertilizer Type | Organic compost | Chemical fertilizer |
| B | Irrigation Frequency | Once per day | Twice per day |
| C | Temperature Setting | 22 C | 28 C |
library(agricolae)
The function design.ab() builds the layout. The argument trt = c(2, 2, 2) says three factors with two levels each, r = 3 is the three replications, design = “rcbd” makes the replications into blocks, and the seed makes the randomization repeatable.
design <- design.ab(trt = c(2, 2, 2),
r = 3,
serie = 2,
design = "rcbd",
seed = 20261104)
design$parameters
## $design
## [1] "factorial"
##
## $trt
## [1] "1 1 1" "1 1 2" "1 2 1" "1 2 2" "2 1 1" "2 1 2" "2 2 1" "2 2 2"
##
## $r
## [1] 3
##
## $serie
## [1] 2
##
## $seed
## [1] 20261104
##
## $kinds
## [1] "Super-Duper"
##
## [[7]]
## [1] TRUE
##
## $applied
## [1] "rcbd"
layout <- design$book
layout
## plots block A B C
## 1 101 1 1 2 2
## 2 102 1 2 1 2
## 3 103 1 1 1 1
## 4 104 1 2 1 1
## 5 105 1 2 2 2
## 6 106 1 1 1 2
## 7 107 1 1 2 1
## 8 108 1 2 2 1
## 9 109 2 1 1 2
## 10 110 2 2 1 1
## 11 111 2 1 2 2
## 12 112 2 2 2 1
## 13 113 2 2 1 2
## 14 114 2 2 2 2
## 15 115 2 1 2 1
## 16 116 2 1 1 1
## 17 117 3 1 2 1
## 18 118 3 1 1 1
## 19 119 3 2 2 2
## 20 120 3 1 1 2
## 21 121 3 1 2 2
## 22 122 3 2 1 2
## 23 123 3 2 2 1
## 24 124 3 2 1 1
The table has 24 rows, which is the 8 treatment combinations times
the 3 blocks. plots is the plot number, block
is the greenhouse bench, and A, B and C are the levels of each factor.
Inside each block the 8 combinations appear once, in random order, which
is what a randomized complete block design means.
table(layout$block)
##
## 1 2 3
## 8 8 8
The same table is easier to read with the names from the problem instead of 1 and 2.
named <- layout
levels(named$A) <- c("Organic compost", "Chemical fertilizer")
levels(named$B) <- c("Once per day", "Twice per day")
levels(named$C) <- c("22 C", "28 C")
head(named, 8)
## plots block A B C
## 1 101 1 1 2 2
## 2 102 1 2 1 2
## 3 103 1 1 1 1
## 4 104 1 2 1 1
## 5 105 1 2 2 2
## 6 106 1 1 1 2
## 7 107 1 1 2 1
## 8 108 1 2 2 1
# ---- Problem 1 ----
dat <- read.csv("https://raw.githubusercontent.com/tmatis12/datafiles/main/PowderProduction.csv",
fileEncoding = "UTF-8-BOM")
A <- factor(dat$Ammonium)
B <- factor(dat$StirRate)
C <- factor(dat$Temperature)
dat <- data.frame(A, B, C, Density = dat$Density)
head(dat)
str(dat)
tapply(dat$Density, dat$A, mean)
tapply(dat$Density, dat$B, mean)
tapply(dat$Density, dat$C, mean)
# full model with all interactions
full_model <- aov(Density ~ A * B * C, data = dat)
summary(full_model)
# interaction plots
interaction.plot(dat$A, dat$B, dat$Density,
main = "Ammonium by Stir Rate",
xlab = "Ammonium (%)",
ylab = "Mean Density",
trace.label = "Stir Rate")
interaction.plot(dat$A, dat$C, dat$Density,
main = "Ammonium by Temperature",
xlab = "Ammonium (%)",
ylab = "Mean Density",
trace.label = "Temperature")
interaction.plot(dat$B, dat$C, dat$Density,
main = "Stir Rate by Temperature",
xlab = "Stir Rate (RPM)",
ylab = "Mean Density",
trace.label = "Temperature")
# reduced model
reduced_model <- aov(Density ~ A * B, data = dat)
summary(reduced_model)
qqnorm(residuals(reduced_model), main = "Normal Q-Q Plot of the Residuals")
qqline(residuals(reduced_model))
plot(fitted(reduced_model), residuals(reduced_model),
main = "Residuals vs Fitted Values",
xlab = "Fitted Value",
ylab = "Residual",
pch = 19)
abline(h = 0, lty = 2)
tapply(dat$Density, list(dat$A, dat$B), mean)
# ---- Problem 2 ----
library(agricolae)
design <- design.ab(trt = c(2, 2, 2),
r = 3,
serie = 2,
design = "rcbd",
seed = 20261104)
design$parameters
layout <- design$book
layout
table(layout$block)
named <- layout
levels(named$A) <- c("Organic compost", "Chemical fertilizer")
levels(named$B) <- c("Once per day", "Twice per day")
levels(named$C) <- c("22 C", "28 C")
head(named, 8)