MC³ LabCUHK Psychology

Lesson 16 · Statistics

Paired t-test

Test a change in the same people from one time to another.

In this lesson

  • Reshape data with pivot_longer()
  • Run a paired t_test()
  • Report a before-and-after change

You need

Datastudents.csvStudent Life Survey: 300 students, with their study habits, sleep, stress, wellbeing and exam results.
Scriptlesson-16.RAll the code in this lesson, ready to run.

Set up

R code
library(tidyverse)
library(rstatix)
students <- read_csv("data/students.csv", show_col_types = FALSE)

The question

Students who took the wellbeing workshop were measured twice: start (T1) and end (T2) of semester. Did their wellbeing change?

R code
workshop <- students %>% filter(Workshop == "Yes")
nrow(workshop)
Output
[1] 120

Wide to long

Right now each student has one row with two columns (wide). The paired test wants one row per measurement (long).

IDWellbeing_T1Wellbeing_T2
S0033.334.33
S0084.333.33
pivot_longer()→
IDTimeWellbeing
S003T13.33
S003T24.33
S008T14.33
S008T23.33
R code
workshop_long <- workshop %>%
  select(ID, Wellbeing_T1, Wellbeing_T2) %>%
  pivot_longer(cols = c(Wellbeing_T1, Wellbeing_T2),
               names_to = "Time",
               names_prefix = "Wellbeing_",
               values_to = "Wellbeing")
workshop_long
Output
# A tibble: 240 × 3
   ID    Time  Wellbeing
   <chr> <chr>     <dbl>
 1 S003  T1         3.33
 2 S003  T2         4.33
 3 S008  T1         4.33
 4 S008  T2         3.33
 5 S009  T1         3.67
 6 S009  T2         4.67
 7 S010  T1         5   
 8 S010  T2         5.67
 9 S012  T1         4   
10 S012  T2         3.67
# ℹ 230 more rows
  • cols: the columns to stack.
  • names_to: new column for the old column names; names_prefix trims them to T1, T2.
  • values_to: new column for the scores.

Run the test

R code
workshop_long %>%
  group_by(Time) %>%
  get_summary_stats(Wellbeing, type = "mean_sd")

t_ws <- workshop_long %>% t_test(Wellbeing ~ Time, paired = TRUE)
t_ws
workshop_long %>% cohens_d(Wellbeing ~ Time, paired = TRUE)
Output
# A tibble: 2 × 5
  Time  variable      n  mean    sd
  <chr> <fct>     <dbl> <dbl> <dbl>
1 T1    Wellbeing   120  4.29  1.07
2 T2    Wellbeing   120  4.78  1.30
# A tibble: 1 × 8
  .y.       group1 group2    n1    n2 statistic    df        p
* <chr>     <chr>  <chr>  <int> <int>     <dbl> <dbl>    <dbl>
1 Wellbeing T1     T2       120   120     -7.73   119 3.75e-12
# A tibble: 1 × 7
  .y.       group1 group2 effsize    n1    n2 magnitude
* <chr>     <chr>  <chr>    <dbl> <int> <int> <ord>    
1 Wellbeing T1     T2      -0.706   120   120 moderate 
  • t = −7.73: negative because the test does T1 − T2 and T1 is lower. Wellbeing went up.
  • df = 119 (pairs − 1). p < .001.
  • d = −0.71: a medium-to-large change (the sign is just the direction).
R code
ggplot(workshop_long, aes(x = Time, y = Wellbeing)) +
  geom_boxplot() +
  labs(subtitle = get_test_label(t_ws, detailed = TRUE), y = "Wellbeing (1 to 7)")
Boxplots of wellbeing at T1 and T2 for workshop students, with the test result as the subtitle; T2 is higher

The sign of t only shows which time came first, so it's fine to drop it and say the direction in words.

Exercises

Exercise 16.1 Dataset: students.csv

Without the workshop

Run the same paired test for students who did not take the workshop. What happened to their wellbeing?

Show answerHide answer
R code
no_ws_long <- students %>%
  filter(Workshop == "No") %>%
  select(ID, Wellbeing_T1, Wellbeing_T2) %>%
  pivot_longer(cols = c(Wellbeing_T1, Wellbeing_T2),
               names_to = "Time", names_prefix = "Wellbeing_",
               values_to = "Wellbeing")

no_ws_long %>% group_by(Time) %>% get_summary_stats(Wellbeing, type = "mean_sd")
no_ws_long %>% t_test(Wellbeing ~ Time, paired = TRUE)
no_ws_long %>% cohens_d(Wellbeing ~ Time, paired = TRUE)
Output
# A tibble: 2 × 5
  Time  variable      n  mean    sd
  <chr> <fct>     <dbl> <dbl> <dbl>
1 T1    Wellbeing   180  4.53  1.03
2 T2    Wellbeing   180  4.34  1.31
# A tibble: 1 × 8
  .y.       group1 group2    n1    n2 statistic    df        p
* <chr>     <chr>  <chr>  <int> <int>     <dbl> <dbl>    <dbl>
1 Wellbeing T1     T2       180   180      3.60   179 0.000407
# A tibble: 1 × 7
  .y.       group1 group2 effsize    n1    n2 magnitude
* <chr>     <chr>  <chr>    <dbl> <int> <int> <ord>    
1 Wellbeing T1     T2       0.269   180   180 small    

Their wellbeing dropped a little over the semester (t(179) = 3.60, p < .001, d = 0.27, small). Here t is positive because T1 was higher.

Exercise 16.2 Dataset: students.csv

Why compare groups too?

Exercise 16.1 and this lesson test each group on its own. Why is the two-group test in Exercise 15.1 (change with vs without workshop) the better test of whether the workshop works?

Show answerHide answer

Wellbeing can change over a semester for reasons that have nothing to do with the workshop (exams, workload). The no-workshop students show what happens anyway. Comparing the change in the two groups separates the workshop's effect from everything else.

Key points

  • Paired = the same people measured twice.
  • pivot_longer() reshapes wide data to long.
  • t_test(score ~ Time, paired = TRUE); keep the pairs in order.