# 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=