# Store the data in the variable my_data
my_data <- ToothGrowth
# Show a random sample
set.seed(1234)
dplyr::sample_n(my_data, 10)
# Check the structure
str(my_data)
'data.frame': 60 obs. of 3 variables:
$ len : num 4.2 11.5 7.3 5.8 6.4 10 11.2 11.2 5.2 7 ...
$ supp: Factor w/ 2 levels "OJ","VC": 2 2 2 2 2 2 2 2 2 2 ...
$ dose: num 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 ...
# Convert dose as a factor and recode the levels
# as "D0.5", "D1", "D2"
my_data$dose <- factor(my_data$dose,
levels = c(0.5, 1, 2),
labels = c("D0.5", "D1", "D2"))
head(my_data)
table(my_data$supp, my_data$dose)
D0.5 D1 D2
OJ 10 10 10
VC 10 10 10
# Box plot with multiple groups
# +++++++++++++++++++++
# Plot tooth length ("len") by groups ("dose")
# Color box plot by a second group: "supp"
library("ggpubr")
ggboxplot(my_data, x = "dose", y = "len", color = "supp",
palette = c("#00AFBB", "#E7B800"))

# Line plots with multiple groups
# +++++++++++++++++++++++
# Plot tooth length ("len") by groups ("dose")
# Color box plot by a second group: "supp"
# Add error bars: mean_se
# (other values include: mean_sd, mean_ci, median_iqr, ....)
library("ggpubr")
ggline(my_data, x = "dose", y = "len", color = "supp",
add = c("mean_se", "dotplot"),
palette = c("#00AFBB", "#E7B800"))

# Box plot with two factor variables
boxplot(len ~ supp * dose, data=my_data, frame = FALSE,
col = c("#00AFBB", "#E7B800"), ylab="Tooth Length")

# Two-way interaction plot
interaction.plot(x.factor = my_data$dose, trace.factor = my_data$supp,
response = my_data$len, fun = mean,
type = "b", legend = TRUE,
xlab = "Dose", ylab="Tooth Length",
pch=c(1,19), col = c("#00AFBB", "#E7B800"))

res.aov2 <- aov(len ~ supp + dose, data = my_data)
summary(res.aov2)
Df Sum Sq Mean Sq F value Pr(>F)
supp 1 205.4 205.4 14.02 0.000429 ***
dose 2 2426.4 1213.2 82.81 < 2e-16 ***
Residuals 56 820.4 14.7
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Two-way ANOVA with interaction effect
# These two calls are equivalent
res.aov3 <- aov(len ~ supp * dose, data = my_data)
res.aov3 <- aov(len ~ supp + dose + supp:dose, data = my_data)
summary(res.aov3)
Df Sum Sq Mean Sq F value Pr(>F)
supp 1 205.4 205.4 15.572 0.000231 ***
dose 2 2426.4 1213.2 92.000 < 2e-16 ***
supp:dose 2 108.3 54.2 4.107 0.021860 *
Residuals 54 712.1 13.2
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
require("dplyr")
group_by(my_data, supp, dose) %>%
summarise(
count = n(),
mean = mean(len, na.rm = TRUE),
sd = sd(len, na.rm = TRUE)
)
model.tables(res.aov3, type="means", se = TRUE)
Tables of means
Grand mean
18.81333
supp
supp
OJ VC
20.663 16.963
dose
dose
D0.5 D1 D2
10.605 19.735 26.100
supp:dose
dose
supp D0.5 D1 D2
OJ 13.23 22.70 26.06
VC 7.98 16.77 26.14
Standard errors for differences of means
supp dose supp:dose
0.9376 1.1484 1.6240
replic. 30 20 10
TukeyHSD(res.aov3, which = "dose")
Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = len ~ supp + dose + supp:dose, data = my_data)
$`dose`
diff lwr upr p adj
D1-D0.5 9.130 6.362488 11.897512 0.0e+00
D2-D0.5 15.495 12.727488 18.262512 0.0e+00
D2-D1 6.365 3.597488 9.132512 2.7e-06
library(multcomp)
summary(glht(res.aov2, linfct = mcp(dose = "Tukey")))
Simultaneous Tests for General Linear Hypotheses
Multiple Comparisons of Means: Tukey Contrasts
Fit: aov(formula = len ~ supp + dose, data = my_data)
Linear Hypotheses:
Estimate Std. Error t value Pr(>|t|)
D1 - D0.5 == 0 9.130 1.210 7.543 <1e-04 ***
D2 - D0.5 == 0 15.495 1.210 12.802 <1e-04 ***
D2 - D1 == 0 6.365 1.210 5.259 <1e-04 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Adjusted p values reported -- single-step method)
pairwise.t.test(my_data$len, my_data$dose,
p.adjust.method = "BH")
Pairwise comparisons using t tests with pooled SD
data: my_data$len and my_data$dose
D0.5 D1
D1 1.0e-08 -
D2 4.4e-16 1.4e-05
P value adjustment method: BH
# 1. Homogeneity of variances
plot(res.aov3, 1)

library(car)
leveneTest(len ~ supp*dose, data = my_data)
Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 5 1.7086 0.1484
54
# 2. Normality
plot(res.aov3, 2)

# Extract the residuals
aov_residuals <- residuals(object = res.aov3)
# Run Shapiro-Wilk test
shapiro.test(x = aov_residuals )
Shapiro-Wilk normality test
data: aov_residuals
W = 0.98499, p-value = 0.6694
#Compute two-way ANOVA test in R for unbalanced designs
library(car)
my_anova <- aov(len ~ supp * dose, data = my_data)
Anova(my_anova, type = "III")
Anova Table (Type III tests)
Response: len
Sum Sq Df F value Pr(>F)
(Intercept) 1750.33 1 132.730 3.603e-16 ***
supp 137.81 1 10.450 0.002092 **
dose 885.26 2 33.565 3.363e-10 ***
supp:dose 108.32 2 4.107 0.021860 *
Residuals 712.11 54
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
DQotLS0NCnRpdGxlOiAiVHdvLVdheSBBTk9WQSBUZXN0IGluIFIgKEFOT1ZBIE5PIFIpIHBhcmEgbyBtZXUgYW1pZ28gTklMTyBkYSBVRVJKIC0gMjAxOSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KYGBge3J9DQojIFN0b3JlIHRoZSBkYXRhIGluIHRoZSB2YXJpYWJsZSBteV9kYXRhDQpteV9kYXRhIDwtIFRvb3RoR3Jvd3RoDQpgYGANCg0KYGBge3J9DQojIFNob3cgYSByYW5kb20gc2FtcGxlDQpzZXQuc2VlZCgxMjM0KQ0KZHBseXI6OnNhbXBsZV9uKG15X2RhdGEsIDEwKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDaGVjayB0aGUgc3RydWN0dXJlDQpzdHIobXlfZGF0YSkNCmBgYA0KYGBge3J9DQojIENvbnZlcnQgZG9zZSBhcyBhIGZhY3RvciBhbmQgcmVjb2RlIHRoZSBsZXZlbHMNCiMgYXMgIkQwLjUiLCAiRDEiLCAiRDIiDQpteV9kYXRhJGRvc2UgPC0gZmFjdG9yKG15X2RhdGEkZG9zZSwgDQogICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKDAuNSwgMSwgMiksDQogICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJEMC41IiwgIkQxIiwgIkQyIikpDQpoZWFkKG15X2RhdGEpDQpgYGANCg0KDQpgYGB7cn0NCnRhYmxlKG15X2RhdGEkc3VwcCwgbXlfZGF0YSRkb3NlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBCb3ggcGxvdCB3aXRoIG11bHRpcGxlIGdyb3Vwcw0KIyArKysrKysrKysrKysrKysrKysrKysNCiMgUGxvdCB0b290aCBsZW5ndGggKCJsZW4iKSBieSBncm91cHMgKCJkb3NlIikNCiMgQ29sb3IgYm94IHBsb3QgYnkgYSBzZWNvbmQgZ3JvdXA6ICJzdXBwIg0KbGlicmFyeSgiZ2dwdWJyIikNCmdnYm94cGxvdChteV9kYXRhLCB4ID0gImRvc2UiLCB5ID0gImxlbiIsIGNvbG9yID0gInN1cHAiLA0KICAgICAgICAgIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBMaW5lIHBsb3RzIHdpdGggbXVsdGlwbGUgZ3JvdXBzDQojICsrKysrKysrKysrKysrKysrKysrKysrDQojIFBsb3QgdG9vdGggbGVuZ3RoICgibGVuIikgYnkgZ3JvdXBzICgiZG9zZSIpDQojIENvbG9yIGJveCBwbG90IGJ5IGEgc2Vjb25kIGdyb3VwOiAic3VwcCINCiMgQWRkIGVycm9yIGJhcnM6IG1lYW5fc2UNCiMgKG90aGVyIHZhbHVlcyBpbmNsdWRlOiBtZWFuX3NkLCBtZWFuX2NpLCBtZWRpYW5faXFyLCAuLi4uKQ0KbGlicmFyeSgiZ2dwdWJyIikNCmdnbGluZShteV9kYXRhLCB4ID0gImRvc2UiLCB5ID0gImxlbiIsIGNvbG9yID0gInN1cHAiLA0KICAgICAgIGFkZCA9IGMoIm1lYW5fc2UiLCAiZG90cGxvdCIpLA0KICAgICAgIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiKSkNCmBgYA0KDQpgYGB7cn0NCiMgQm94IHBsb3Qgd2l0aCB0d28gZmFjdG9yIHZhcmlhYmxlcw0KYm94cGxvdChsZW4gfiBzdXBwICogZG9zZSwgZGF0YT1teV9kYXRhLCBmcmFtZSA9IEZBTFNFLCANCiAgICAgICAgY29sID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiksIHlsYWI9IlRvb3RoIExlbmd0aCIpDQojIFR3by13YXkgaW50ZXJhY3Rpb24gcGxvdA0KaW50ZXJhY3Rpb24ucGxvdCh4LmZhY3RvciA9IG15X2RhdGEkZG9zZSwgdHJhY2UuZmFjdG9yID0gbXlfZGF0YSRzdXBwLCANCiAgICAgICAgICAgICAgICAgcmVzcG9uc2UgPSBteV9kYXRhJGxlbiwgZnVuID0gbWVhbiwgDQogICAgICAgICAgICAgICAgIHR5cGUgPSAiYiIsIGxlZ2VuZCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICB4bGFiID0gIkRvc2UiLCB5bGFiPSJUb290aCBMZW5ndGgiLA0KICAgICAgICAgICAgICAgICBwY2g9YygxLDE5KSwgY29sID0gYygiIzAwQUZCQiIsICIjRTdCODAwIikpDQpgYGANCmBgYHtyfQ0KcmVzLmFvdjIgPC0gYW92KGxlbiB+IHN1cHAgKyBkb3NlLCBkYXRhID0gbXlfZGF0YSkNCnN1bW1hcnkocmVzLmFvdjIpDQpgYGANCg0KDQpgYGB7cn0NCiMgVHdvLXdheSBBTk9WQSB3aXRoIGludGVyYWN0aW9uIGVmZmVjdA0KIyBUaGVzZSB0d28gY2FsbHMgYXJlIGVxdWl2YWxlbnQNCnJlcy5hb3YzIDwtIGFvdihsZW4gfiBzdXBwICogZG9zZSwgZGF0YSA9IG15X2RhdGEpDQpyZXMuYW92MyA8LSBhb3YobGVuIH4gc3VwcCArIGRvc2UgKyBzdXBwOmRvc2UsIGRhdGEgPSBteV9kYXRhKQ0Kc3VtbWFyeShyZXMuYW92MykNCmBgYA0KDQpgYGB7cn0NCnJlcXVpcmUoImRwbHlyIikNCmdyb3VwX2J5KG15X2RhdGEsIHN1cHAsIGRvc2UpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY291bnQgPSBuKCksDQogICAgbWVhbiA9IG1lYW4obGVuLCBuYS5ybSA9IFRSVUUpLA0KICAgIHNkID0gc2QobGVuLCBuYS5ybSA9IFRSVUUpDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KbW9kZWwudGFibGVzKHJlcy5hb3YzLCB0eXBlPSJtZWFucyIsIHNlID0gVFJVRSkNCmBgYA0KYGBge3J9DQpUdWtleUhTRChyZXMuYW92Mywgd2hpY2ggPSAiZG9zZSIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KG11bHRjb21wKQ0Kc3VtbWFyeShnbGh0KHJlcy5hb3YyLCBsaW5mY3QgPSBtY3AoZG9zZSA9ICJUdWtleSIpKSkNCmBgYA0KDQpgYGB7cn0NCnBhaXJ3aXNlLnQudGVzdChteV9kYXRhJGxlbiwgbXlfZGF0YSRkb3NlLA0KICAgICAgICAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIpDQpgYGANCg0KDQpgYGB7cn0NCg0KIyAxLiBIb21vZ2VuZWl0eSBvZiB2YXJpYW5jZXMNCnBsb3QocmVzLmFvdjMsIDEpDQpgYGANCg0KDQpgYGB7cn0NCg0KbGlicmFyeShjYXIpDQpsZXZlbmVUZXN0KGxlbiB+IHN1cHAqZG9zZSwgZGF0YSA9IG15X2RhdGEpDQpgYGANCg0KYGBge3J9DQojIDIuIE5vcm1hbGl0eQ0KcGxvdChyZXMuYW92MywgMikNCmBgYA0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgcmVzaWR1YWxzDQphb3ZfcmVzaWR1YWxzIDwtIHJlc2lkdWFscyhvYmplY3QgPSByZXMuYW92MykNCiMgUnVuIFNoYXBpcm8tV2lsayB0ZXN0DQpzaGFwaXJvLnRlc3QoeCA9IGFvdl9yZXNpZHVhbHMgKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiNDb21wdXRlIHR3by13YXkgQU5PVkEgdGVzdCBpbiBSIGZvciB1bmJhbGFuY2VkIGRlc2lnbnMNCg0KbGlicmFyeShjYXIpDQpteV9hbm92YSA8LSBhb3YobGVuIH4gc3VwcCAqIGRvc2UsIGRhdGEgPSBteV9kYXRhKQ0KQW5vdmEobXlfYW5vdmEsIHR5cGUgPSAiSUlJIikNCmBgYA0KDQo=