library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(kableExtra)

Attaching package: 'kableExtra'

The following object is masked from 'package:dplyr':

    group_rows
set.seed(12345)
theme_set(theme_classic(base_size = 16))

1 Learning objectives

  • Describe the logic of factorial ANOVA
    • Including main effects and interactions
  • Conduct and interpret the factorial ANOVA tests
  • Conduct post hoc tests for the factorial ANOVA

2 Data

  • birthwt dataset in the MASS package
    • race: mother’s race (1 = white, 2 = black, 3 = other).
    • smoke: smoking status during pregnancy.
    • bwt: birth weight in grams
  • Convert both race and smoke to factor variables using mutate() and as.factor()
library(MASS)

Attaching package: 'MASS'
The following object is masked from 'package:dplyr':

    select
data(birthwt)
birthwt <- birthwt %>%
  mutate(race = as.factor(race),
         smoke = as.factor(smoke))
head(birthwt)
   low age lwt race smoke ptl ht ui ftv  bwt
85   0  19 182    2     0   0  0  1   0 2523
86   0  33 155    3     0   0  0  0   3 2551
87   0  20 105    1     1   0  0  0   1 2557
88   0  21 108    1     1   0  0  1   2 2594
89   0  18 107    1     1   0  0  1   0 2600
91   0  21 124    3     0   0  0  0   0 2622

2.1 Research question

  • How are mother’s race and mother’s smoking status related to the birth weight of a baby?

2.2 Means

  • Means by race and smoke
bwt_sum <- birthwt %>% 
  group_by(race, smoke) %>%
  summarize(nbwt = length(bwt),
            meanbwt = mean(bwt),
            sdbwt = sd(bwt))
`summarise()` has grouped output by 'race'. You can override using the
`.groups` argument.
kable(bwt_sum)  
race smoke nbwt meanbwt sdbwt
1 0 44 3428.750 710.0989
1 1 52 2826.846 626.4725
2 0 16 2854.500 621.2543
2 1 10 2504.000 637.0568
3 0 55 2815.782 709.3493
3 1 12 2757.167 810.0446
  • Plot of means
ggplot(data = bwt_sum, 
       aes(x = race, 
           y = meanbwt, 
           group = smoke,
           color = smoke)) +
  geom_point() +
  geom_path(aes(group = smoke)) +
  geom_errorbar(aes(ymin = meanbwt - 1.96*sdbwt/sqrt(nbwt), 
                    ymax = meanbwt + 1.96*sdbwt/sqrt(nbwt)), 
                width = 0.2) +
  labs(x = "Race", 
       y = "Birth weight (g)") +
  scale_x_discrete(labels=c("1" = "White", 
                            "2" = "Black",
                            "3" = "Other")) +
  scale_color_discrete(name = "Smoker", 
                       labels = c("No", "Yes")) +
  geom_hline(yintercept = 2500, linetype = "dashed")

3 ANOVA

3.1 Assumptions: Equal variance

  • Do all the groups (6 groups defined by race and smoke) have equal variance on the outcome variable (bwt)?

3.1.1 Graphically

Code
ggplot(data = birthwt,
       aes(x = race, 
           y = bwt, 
           group = interaction(race, smoke))) +
  geom_boxplot(alpha = 0.5) +
  geom_jitter(width = 0.2, aes(color = smoke)) +
  geom_hline(yintercept = 2500, linetype = "dashed")

3.1.2 Levene’s Test

  • leveneTest() in car package
    • H_0: No differences in variances across groups
    • NS result = equal variances
Code
library(car)
Loading required package: carData

Attaching package: 'car'
The following object is masked from 'package:dplyr':

    recode
The following object is masked from 'package:purrr':

    some
Code
leveneTest(bwt ~ race:smoke, data = birthwt)
Levene's Test for Homogeneity of Variance (center = median)
       Df F value Pr(>F)
group   5   0.345  0.885
      183               
  • The assumption of equal variances is met

3.2 ANOVA

  • Step 1: lm() function for linear model
    • outcome ~ predictors format
    • Effects of race, smoke, and interaction (race*smoke)
    • Contrasts: Sum (contr.sum) in list() function to allow one for each factor
  • Step 2: Anova() function in car package
    • Provide model (m1 on last slide)
    • type = 3 for correct type III sums of squares
m1 <- lm(bwt ~ race + smoke + race*smoke,
         contrasts=list(race = contr.sum, 
                        smoke = contr.sum),
         data = birthwt)

library(car)
Anova(m1, type = 3)
Anova Table (Type III tests)

Response: bwt
               Sum Sq  Df   F value    Pr(>F)    
(Intercept) 965426089   1 2065.6367 < 2.2e-16 ***
race          5779165   2    6.1826  0.002522 ** 
smoke         3340683   1    7.1478  0.008185 ** 
race:smoke    2101808   2    2.2485  0.108463    
Residuals    85529548 183                        
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

3.3 Overall conclusions

  • We reject the null hypothesis that all race means are equal, F(2, 183) = 6.18, p = .0025. At least one race group mean is different from the others.
  • We reject the null hypothesis that all smoke means are equal, F(1, 183) = 7.15, p = .0082. At least one smoke group mean is different from the others.
  • We retain the null hypothesis that there is not an interaction, F(2, 183) = 2.25, p = .1085. There is no interaction of race and smoke.
    • The effect of race doesn’t depend on smoke
    • The effect of smoke doesn’t depend on race

3.4 Assumptions: Normality

3.4.1 Graphically

Code
m1resid <- data.frame(residuals(m1))
ggplot(data = m1resid, 
       aes(sample = residuals.m1.)) +
  geom_qq() +
  geom_qq_line()

Code
m1resid <- data.frame(residuals(m1))
ggplot(data = m1resid, 
       aes(x = residuals.m1.)) +
  geom_histogram()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

3.4.2 Shapiro-Wilk Test

  • shapiro.test() in stats
    • H_0: Observations come from normally distributed population
    • NS result = normally distributed residuals
Code
shapiro.test(resid(m1))

    Shapiro-Wilk normality test

data:  resid(m1)
W = 0.98836, p-value = 0.1246

3.5 Post hoc tests

  • emmeans() function in emmeans package
    • Provide model name (m1)
    • Type of contrast (spec = trt.vs.ctrl for 1st group as reference)
      • Other options: pairwise, trt.vs.ctrlk (last group as reference)
    • Factor you want contrasts for: here, only race
    • Adjustment for multiple comparisons: here, "none"
      • Other options: tukey, bonf, fdr
  • Note that these are not the raw means
    • Estimated marginal means (emmeans): Model based
library(emmeans)
Welcome to emmeans.
Caution: You lose important information if you filter this package's results.
See '? untidy'
emmeans(m1, spec = trt.vs.ctrl ~ race, adjust = "none")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 race emmean  SE  df lower.CL upper.CL
 1      3128  70 183     2990     3266
 2      2679 138 183     2407     2951
 3      2786 109 183     2572     3001

Results are averaged over the levels of: smoke 
Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 race2 - race1     -449 155 183  -2.902  0.0042
 race3 - race1     -341 129 183  -2.636  0.0091

Results are averaged over the levels of: smoke 
  • What if we used a Bonferroni correction?
emmeans(m1, spec = trt.vs.ctrl ~ race, adjust = "bonf")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 race emmean  SE  df lower.CL upper.CL
 1      3128  70 183     2990     3266
 2      2679 138 183     2407     2951
 3      2786 109 183     2572     3001

Results are averaged over the levels of: smoke 
Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 race2 - race1     -449 155 183  -2.902  0.0083
 race3 - race1     -341 129 183  -2.636  0.0182

Results are averaged over the levels of: smoke 
P value adjustment: bonferroni method for 2 tests 
  • What about using false discovery rate (FDR)?
emmeans(m1, spec = trt.vs.ctrl ~ race, adjust = "fdr")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 race emmean  SE  df lower.CL upper.CL
 1      3128  70 183     2990     3266
 2      2679 138 183     2407     2951
 3      2786 109 183     2572     3001

Results are averaged over the levels of: smoke 
Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 race2 - race1     -449 155 183  -2.902  0.0083
 race3 - race1     -341 129 183  -2.636  0.0091

Results are averaged over the levels of: smoke 
P value adjustment: fdr method for 2 tests 
  • What if we wanted all possible pairwise comparisons?
emmeans(m1, spec = pairwise ~ race, adjust = "none")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 race emmean  SE  df lower.CL upper.CL
 1      3128  70 183     2990     3266
 2      2679 138 183     2407     2951
 3      2786 109 183     2572     3001

Results are averaged over the levels of: smoke 
Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 race1 - race2      449 155 183   2.902  0.0042
 race1 - race3      341 129 183   2.636  0.0091
 race2 - race3     -107 176 183  -0.610  0.5423

Results are averaged over the levels of: smoke 
  • What if we wanted all possible pairwise comparisons and also wanted to include smoke in this?
emmeans(m1, spec = pairwise ~ race*smoke, adjust = "none")
$emmeans
 race smoke emmean    SE  df lower.CL upper.CL
 1    0       3429 103.1 183     3225     3632
 2    0       2854 170.9 183     2517     3192
 3    0       2816  92.2 183     2634     2998
 1    1       2827  94.8 183     2640     3014
 2    1       2504 216.2 183     2077     2931
 3    1       2757 197.4 183     2368     3147

Confidence level used: 0.95 

$contrasts
 contrast                    estimate  SE  df t.ratio p.value
 race1 smoke0 - race2 smoke0    574.2 200 183   2.877  0.0045
 race1 smoke0 - race3 smoke0    613.0 138 183   4.433  <.0001
 race1 smoke0 - race1 smoke1    601.9 140 183   4.298  <.0001
 race1 smoke0 - race2 smoke1    924.8 239 183   3.861  0.0002
 race1 smoke0 - race3 smoke1    671.6 223 183   3.016  0.0029
 race2 smoke0 - race3 smoke0     38.7 194 183   0.199  0.8422
 race2 smoke0 - race1 smoke1     27.7 195 183   0.141  0.8876
 race2 smoke0 - race2 smoke1    350.5 276 183   1.272  0.2050
 race2 smoke0 - race3 smoke1     97.3 261 183   0.373  0.7097
 race3 smoke0 - race1 smoke1    -11.1 132 183  -0.084  0.9334
 race3 smoke0 - race2 smoke1    311.8 235 183   1.327  0.1863
 race3 smoke0 - race3 smoke1     58.6 218 183   0.269  0.7882
 race1 smoke1 - race2 smoke1    322.8 236 183   1.368  0.1731
 race1 smoke1 - race3 smoke1     69.7 219 183   0.318  0.7507
 race2 smoke1 - race3 smoke1   -253.2 293 183  -0.865  0.3882
  • Wow, let’s throw a Bonferroni correction on that one…
emmeans(m1, spec = pairwise ~ race*smoke, adjust = "bonf")
$emmeans
 race smoke emmean    SE  df lower.CL upper.CL
 1    0       3429 103.1 183     3225     3632
 2    0       2854 170.9 183     2517     3192
 3    0       2816  92.2 183     2634     2998
 1    1       2827  94.8 183     2640     3014
 2    1       2504 216.2 183     2077     2931
 3    1       2757 197.4 183     2368     3147

Confidence level used: 0.95 

$contrasts
 contrast                    estimate  SE  df t.ratio p.value
 race1 smoke0 - race2 smoke0    574.2 200 183   2.877  0.0673
 race1 smoke0 - race3 smoke0    613.0 138 183   4.433  0.0002
 race1 smoke0 - race1 smoke1    601.9 140 183   4.298  0.0004
 race1 smoke0 - race2 smoke1    924.8 239 183   3.861  0.0023
 race1 smoke0 - race3 smoke1    671.6 223 183   3.016  0.0438
 race2 smoke0 - race3 smoke0     38.7 194 183   0.199  1.0000
 race2 smoke0 - race1 smoke1     27.7 195 183   0.141  1.0000
 race2 smoke0 - race2 smoke1    350.5 276 183   1.272  1.0000
 race2 smoke0 - race3 smoke1     97.3 261 183   0.373  1.0000
 race3 smoke0 - race1 smoke1    -11.1 132 183  -0.084  1.0000
 race3 smoke0 - race2 smoke1    311.8 235 183   1.327  1.0000
 race3 smoke0 - race3 smoke1     58.6 218 183   0.269  1.0000
 race1 smoke1 - race2 smoke1    322.8 236 183   1.368  1.0000
 race1 smoke1 - race3 smoke1     69.7 219 183   0.318  1.0000
 race2 smoke1 - race3 smoke1   -253.2 293 183  -0.865  1.0000

P value adjustment: bonferroni method for 15 tests 
  • What if we wanted to use the last group (“other”) as the reference?
emmeans(m1, spec = trt.vs.ctrlk ~ race, adjust = "none")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 race emmean  SE  df lower.CL upper.CL
 1      3128  70 183     2990     3266
 2      2679 138 183     2407     2951
 3      2786 109 183     2572     3001

Results are averaged over the levels of: smoke 
Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 race1 - race3      341 129 183   2.636  0.0091
 race2 - race3     -107 176 183  -0.610  0.5423

Results are averaged over the levels of: smoke 
  • Just for fun, let’s look at the smoke contrast, even though we don’t need to because there are only 2 levels
    • Remember that squaring the t statistic here will give you the F statistic from the ANOVA table
emmeans(m1, spec = pairwise ~ smoke, adjust = "none")
NOTE: Results may be misleading due to involvement in interactions
$emmeans
 smoke emmean    SE  df lower.CL upper.CL
 0       3033  73.3 183     2888     3178
 1       2696 102.6 183     2494     2898

Results are averaged over the levels of: race 
Confidence level used: 0.95 

$contrasts
 contrast        estimate  SE  df t.ratio p.value
 smoke0 - smoke1      337 126 183   2.674  0.0082

Results are averaged over the levels of: race 

4 How to do ANOVA wrong

  • There are a LOT of ways to do the ANOVA wrong
    • The most obvious one is to use the anova() function
    • This gives type I sums of squares instead of the correct type III
anova(m1)
Analysis of Variance Table

Response: bwt
            Df   Sum Sq Mean Sq F value   Pr(>F)    
race         2  5015725 2507863  5.3659 0.005438 ** 
smoke        1  7322575 7322575 15.6675 0.000108 ***
race:smoke   2  2101808 1050904  2.2485 0.108463    
Residuals  183 85529548  467375                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  • Compare to correct type II SSs
library(car)
Anova(m1, type = 3)
Anova Table (Type III tests)

Response: bwt
               Sum Sq  Df   F value    Pr(>F)    
(Intercept) 965426089   1 2065.6367 < 2.2e-16 ***
race          5779165   2    6.1826  0.002522 ** 
smoke         3340683   1    7.1478  0.008185 ** 
race:smoke    2101808   2    2.2485  0.108463    
Residuals    85529548 183                        
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  • What’s the same?
    • Degrees of freedom
    • Residual SS: That’s what’s left over after everything else
    • Sums of squares, etc. for interaction: More on that in a second
  • What’s different?
    • Everything else

4.1 Why are they different?

  • Type I SS partials out or holds constant all effects entered before this effect
    • Order you enter effects matters
    • Effect of race is partialling nothing
    • Effect of smoke is partialling race only
    • Effect of interaction is partialling both race and smoke
  • Type III SS partials out or holds constant all other effects
    • Order you enter effects doesn’t matter
    • Effect of race is partialling both smoke and the interaction
    • Effect of smoke is partialling both race and the interaction
    • Effect of interaction is partialling both race and smoke

4.2 Change the predictor order

  • Change the order of predictors: smoke then race then interaction
m2 <- lm(bwt ~ smoke + race + race*smoke,
         contrasts=list(race = contr.sum, 
                        smoke = contr.sum),
         data = birthwt)
  • Type I (wrong) model
    • Different results from original predictor order (m1)
anova(m2)
Analysis of Variance Table

Response: bwt
            Df   Sum Sq Mean Sq F value    Pr(>F)    
smoke        1  3625946 3625946  7.7581 0.0059101 ** 
race         2  8712354 4356177  9.3205 0.0001397 ***
smoke:race   2  2101808 1050904  2.2485 0.1084634    
Residuals  183 85529548  467375                      
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  • Type III (correct) model
    • Same results as original predictor order (m1)
    • (Note different order of predictors)
library(car)
Anova(m2, type = 3)
Anova Table (Type III tests)

Response: bwt
               Sum Sq  Df   F value    Pr(>F)    
(Intercept) 965426089   1 2065.6367 < 2.2e-16 ***
smoke         3340683   1    7.1478  0.008185 ** 
race          5779165   2    6.1826  0.002522 ** 
smoke:race    2101808   2    2.2485  0.108463    
Residuals    85529548 183                        
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5 How would I actually run this?

  • Research question: How are mother’s race and mother’s smoking status related to the birth weight of a baby?
    • I rarely use an actual ANOVA
    • Linear regression (for continuous outcomes) is more flexible

5.1 Recode grouping variables

  • We already recoded the group variables into factors, but I’m going to explicitly create dummy codes (also called “indicator variables”) for them
    • Each dummy code has a value of 1 for the group it’s about
    • e.g., white dummy code has a 1 when race = 1 and a 0 otherwise
    • e.g., black dummy code has a 1 when race = 2 and a 0 otherwise
    • e.g., other dummy code has a 1 when race = 3 and a 0 otherwise
    • I use mutate() with ifelse() to create these
      • ifelse() has 3 arguments:
        • Logical argument (e.g., race == 1)
        • What to do if the logical argument is true
        • What to do otherwise
      • For the new variable white:
        • If race = 1, then white = 1
        • Otherwise, white = 0
    • Note that I’m naming each variable after its group
birthwt <- birthwt %>%
  mutate(white = ifelse(race == 1, 1, 0),
         black = ifelse(race == 2, 1, 0),
         other = ifelse(race == 3, 1, 0))
kable(head(birthwt))
low age lwt race smoke ptl ht ui ftv bwt white black other
85 0 19 182 2 0 0 0 1 0 2523 0 1 0
86 0 33 155 3 0 0 0 0 3 2551 0 0 1
87 0 20 105 1 1 0 0 0 1 2557 1 0 0
88 0 21 108 1 1 0 0 1 2 2594 1 0 0
89 0 18 107 1 1 0 0 1 0 2600 1 0 0
91 0 21 124 3 0 0 0 0 0 2622 0 0 1
  • smoke is already coded 0 and 1, but I’ll do the same there
    • I’m calling this variable smoker: a value of 1 means they’re a smoker
birthwt <- birthwt %>%
  mutate(smoker = ifelse(smoke == 1, 1, 0))
kable(head(birthwt))
low age lwt race smoke ptl ht ui ftv bwt white black other smoker
85 0 19 182 2 0 0 0 1 0 2523 0 1 0 0
86 0 33 155 3 0 0 0 0 3 2551 0 0 1 0
87 0 20 105 1 1 0 0 0 1 2557 1 0 0 1
88 0 21 108 1 1 0 0 1 2 2594 1 0 0 1
89 0 18 107 1 1 0 0 1 0 2600 1 0 0 1
91 0 21 124 3 0 0 0 0 0 2622 0 0 1 0

5.2 Use dummy codes as predictors

  • You already know everyone’s group from any 2 of the 3 dummy codes
    • In general, for a groups, you use a - 1 of the variables
    • If you tried to use all three variables, you’d get an error because you have redundant information
      • It’s like if you used weight in pounds and weight in kg in the same model
  • The dummy code you leave out is the reference group
    • Use black and other: “White” is the reference group
    • Use white and other: “Black” is the reference group
    • Use white and black: “Other” is the reference group
    • You get to just pick your reference group

5.3 Reference: “white” and “nonsmoker”

m1_white <- lm(bwt ~ black + other + smoker + black*smoker + other*smoker,
               data = birthwt)
summary(m1_white)

Call:
lm(formula = bwt ~ black + other + smoker + black * smoker + 
    other * smoker, data = birthwt)

Residuals:
     Min       1Q   Median       3Q      Max 
-2407.75  -416.50    31.25   462.50  1561.25 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    3428.7      103.1  33.268  < 2e-16 ***
black          -574.2      199.6  -2.877  0.00449 ** 
other          -613.0      138.3  -4.433 1.60e-05 ***
smoker         -601.9      140.0  -4.298 2.79e-05 ***
black:smoker    251.4      309.1   0.813  0.41712    
other:smoker    543.3      259.0   2.098  0.03728 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 683.6 on 183 degrees of freedom
Multiple R-squared:  0.1444,    Adjusted R-squared:  0.1211 
F-statistic: 6.179 on 5 and 183 DF,  p-value: 2.562e-05
  • Intercept: Mean when all predictors = 0 (reference on both)
    • 3428.7 = mean birth weight for white moms (black = 0 and other = 0) who don’t smoke (smoker = 0)
    • Significant t-test: This value is significantly different from 0 (which is likely not of interest, but could be)
  • black: Difference between black moms and white moms (black = 0 and other = 0) for nonsmokers (smoker = 0)
    • -574.2 = Difference between white and black moms who don’t smoke
      • 3428.7 - 574.2 = 2854.5 = mean birth weight for black moms who don’t smoke
      • Significant t-test: These two means are significantly different from one another
  • other: Difference between other race moms and white moms (black = 0 and other = 0) for nonsmokers (smoker = 0)
    • -613.0 = Difference between white and other race moms who don’t smoke
      • 3428.7 - 613.0 = 2815.7 = mean birth weight for other race moms who don’t smoke
      • Significant t-test: These two means are significantly different from one another
  • smoker: Difference between nonsmokers (smoker = 0) and smokers (smoker = 1) for white moms (black = 0 and other = 0)
    • -601.9 = Difference between white smokers and white nonsmokers
      • From above: 3428.7 = mean birth weight for white moms (black = 0 and other = 0) who don’t smoke (smoker = 0)
      • 3428.7 - 601.9 = 2826.8 = mean birth weight for white smoker moms
      • Significant t-test: These two means are significantly different from one another
  • black:smoker: Difference in difference between white smokers and white nonsmokers vs black smokers and black nonsmokers
    • From above: -601.9 = Difference between white smokers and white nonsmokers
    • Difference between black smokers and black nonsmokers is 251.4 points higher than that, or -601.9 + 251.4 = -350.5
    • Non-significant t-test: These two differences are not significantly different from one another
  • other:smoker: Difference in difference between white smokers and white nonsmokers vs other race smokers and other race nonsmokers
    • From above: -601.9 = Difference between white smokers and white nonsmokers
    • Difference between other race smokers and other race nonsmokers is 543.3 points higher than that, or -601.9 + 543.3 = -58.6
    • Significant t-test: These two differences are significantly different from one another

5.3.1 Intercept and main effects

library(grid)
ggplot(data = bwt_sum, 
       aes(x = race, 
           y = meanbwt, 
           group = smoke,
           color = smoke)) +
  geom_point(size = 8) +
  labs(x = "Race", 
       y = "Birth weight (g)") +
  scale_x_discrete(labels=c("1" = "White", 
                            "2" = "Black",
                            "3" = "Other")) +
  scale_color_discrete(name = "Smoker", 
                       labels = c("No", "Yes")) +
  annotate("text", x = 1, y = 3500, label = "3428.7 *") +
  annotate("segment", x = 1, xend = 2, y = 3428, yend = 2854,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 1.5, y = 3125, label = "-574.2 *") +
  annotate("segment", x = 1, xend = 3, y = 3428, yend = 2815,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 2.5, y = 3125, label = "-613.0 *") +
  annotate("segment", x = 1, xend = 1, y = 2826, yend = 3428,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = .8, y = 3125, label = "-601.9 *")

5.3.2 Interaction effects

library(grid)
ggplot(data = bwt_sum, 
       aes(x = race, 
           y = meanbwt, 
           group = smoke,
           color = smoke)) +
  geom_point(size = 8) +
  labs(x = "Race", 
       y = "Birth weight (g)") +
  scale_x_discrete(labels=c("1" = "White", 
                            "2" = "Black",
                            "3" = "Other")) +
  scale_color_discrete(name = "Smoker", 
                       labels = c("No", "Yes")) +
  annotate("segment", x = 1, xend = 1, y = 2826, yend = 3428,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = .8, y = 3125, label = "-601.9 *") +
  
  annotate("segment", x = 2, xend = 2, y = 2854, yend = 2504,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 2, y = 3000, label = "-350.5 = -601.9 + 251.4") +
  
  annotate("segment", x = 3, xend = 3, y = 2815, yend = 2757,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 3, y = 2900, label = "-58.6 = -601.9 + 543.3") +
  
  annotate("segment", x = 1, xend = 1.95, y = 3500, yend = 3500,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 1.5, y = 3450, label = "251.4 NS") +
  
  annotate("segment", x = 2.05, xend = 3, y = 3500, yend = 3500,
           arrow = arrow(ends = "both", angle = 90, length = unit(.2,"cm"))) +
  annotate("text", x = 2.5, y = 3450, label = "543.3 *")

5.4 Reference: “black” and “nonsmoker”

m1_black <- lm(bwt ~ white + other + smoker + white*smoker + other*smoker,
               data = birthwt)
summary(m1_black)

Call:
lm(formula = bwt ~ white + other + smoker + white * smoker + 
    other * smoker, data = birthwt)

Residuals:
     Min       1Q   Median       3Q      Max 
-2407.75  -416.50    31.25   462.50  1561.25 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   2854.50     170.91  16.702  < 2e-16 ***
white          574.25     199.58   2.877  0.00449 ** 
other          -38.72     194.19  -0.199  0.84218    
smoker        -350.50     275.59  -1.272  0.20505    
white:smoker  -251.40     309.13  -0.813  0.41712    
other:smoker   291.88     351.27   0.831  0.40710    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 683.6 on 183 degrees of freedom
Multiple R-squared:  0.1444,    Adjusted R-squared:  0.1211 
F-statistic: 6.179 on 5 and 183 DF,  p-value: 2.562e-05
  • Which means do each of the effects (coefficients) refer to?
    • Use the table of raw means as guide