Setup

library(tidyverse)
library(rstatix)
library(emmeans)
library(effectsize)
library(afex)
library(car)

options(scipen = 999)
afex::set_sum_contrasts()

Data

# Data file is in parent of knit/ (eohi3/eohi3.csv)
df <- read.csv(
  "/home/ladmin/Documents/DND/EOHI/eohi3/eohi3.csv",
  stringsAsFactors = FALSE,
  check.names = FALSE,
  na.strings = "NA"
)

between_vars <- c("perspective", "temporalDO")
within_vars <- c(
  "past_pref_DGEN", "past_pers_DGEN", "past_val_DGEN",
  "fut_pref_DGEN",  "fut_pers_DGEN",  "fut_val_DGEN"
)

missing_vars <- setdiff(c(between_vars, within_vars, "pID"), names(df))
if (length(missing_vars) > 0) {
  stop(paste("Missing required variables:", paste(missing_vars, collapse = ", ")))
}

anova_data <- df %>%
  select(pID, all_of(between_vars), all_of(within_vars)) %>%
  filter(
    !is.na(perspective), perspective != "",
    !is.na(temporalDO),  temporalDO  != ""
  )

Long format

long_data <- anova_data %>%
  pivot_longer(
    cols = all_of(within_vars),
    names_to = "variable",
    values_to = "DGEN_SCORE"
  ) %>%
  mutate(
    time = case_when(
      grepl("^past_", variable) ~ "past",
      grepl("^fut_",  variable) ~ "fut",
      TRUE ~ NA_character_
    ),
    domain = case_when(
      grepl("_pref_DGEN$", variable) ~ "pref",
      grepl("_pers_DGEN$", variable) ~ "pers",
      grepl("_val_DGEN$",  variable) ~ "val",
      TRUE ~ NA_character_
    )
  ) %>%
  mutate(
    TIME       = factor(time,   levels = c("past", "fut")),
    DOMAIN     = factor(domain, levels = c("pref", "pers", "val")),
    perspective = factor(perspective),
    temporalDO  = factor(temporalDO),
    pID         = factor(pID)
  ) %>%
  select(pID, perspective, temporalDO, TIME, DOMAIN, DGEN_SCORE) %>%
  filter(!is.na(DGEN_SCORE))

Descriptive statistics

desc_stats <- long_data %>%
  group_by(perspective, temporalDO, TIME, DOMAIN) %>%
  summarise(
    n        = n(),
    mean     = round(mean(DGEN_SCORE), 5),
    variance = round(var(DGEN_SCORE), 5),
    sd       = round(sd(DGEN_SCORE), 5),
    median   = round(median(DGEN_SCORE), 5),
    q1       = round(quantile(DGEN_SCORE, 0.25), 5),
    q3       = round(quantile(DGEN_SCORE, 0.75), 5),
    min      = round(min(DGEN_SCORE), 5),
    max      = round(max(DGEN_SCORE), 5),
    .groups  = "drop"
  )

# Show all rows and columns (no truncation)
options(tibble.width = Inf)
print(desc_stats, n = Inf)
## # A tibble: 24 × 13
##    perspective temporalDO TIME  DOMAIN     n  mean variance    sd median    q1
##    <fct>       <fct>      <fct> <fct>  <int> <dbl>    <dbl> <dbl>  <dbl> <dbl>
##  1 other       future     past  pref     122  3.74     7.19  2.68    4       1
##  2 other       future     past  pers     122  3.49     7.56  2.75    3       1
##  3 other       future     past  val      122  3.72     7.64  2.76    3       1
##  4 other       future     fut   pref     122  3.69     6.65  2.58    3       2
##  5 other       future     fut   pers     122  3.29     7.03  2.65    3       1
##  6 other       future     fut   val      122  3.43     7.59  2.75    3       1
##  7 other       past       past  pref     139  3.93     7.56  2.75    4       2
##  8 other       past       past  pers     139  3.79     8.12  2.85    3       1
##  9 other       past       past  val      139  3.30     7.26  2.69    3       1
## 10 other       past       fut   pref     139  3.60     7.53  2.74    3       1
## 11 other       past       fut   pers     139  3.50     8.31  2.88    3       1
## 12 other       past       fut   val      139  3.42     9.24  3.04    3       1
## 13 self        future     past  pref     138  3.88     7.39  2.72    3.5     2
## 14 self        future     past  pers     138  3.73     7.48  2.74    3       1
## 15 self        future     past  val      138  3.65     7.75  2.78    3       1
## 16 self        future     fut   pref     138  3.92     6.86  2.62    4       2
## 17 self        future     fut   pers     138  3.72     7.66  2.77    3       1
## 18 self        future     fut   val      138  3.73     8.04  2.83    4       1
## 19 self        past       past  pref     123  3.96     7.51  2.74    4       2
## 20 self        past       past  pers     123  4.24     8.30  2.88    4       2
## 21 self        past       past  val      123  3.88     9.32  3.05    4       1
## 22 self        past       fut   pref     123  3.77     7.87  2.80    4       1
## 23 self        past       fut   pers     123  3.71     8.55  2.92    3       1
## 24 self        past       fut   val      123  3.34     8.19  2.86    2       1
##       q3   min   max
##    <dbl> <dbl> <dbl>
##  1  6        0    10
##  2  5.75     0    10
##  3  5.75     0    10
##  4  5        0    10
##  5  5        0    10
##  6  5        0    10
##  7  6        0    10
##  8  6        0    10
##  9  5        0    10
## 10  5.5      0    10
## 11  5        0    10
## 12  5        0    10
## 13  6        0    10
## 14  6        0    10
## 15  6        0    10
## 16  6        0    10
## 17  6        0    10
## 18  6        0    10
## 19  6        0    10
## 20  6        0    10
## 21  6        0    10
## 22  6        0    10
## 23  6        0    10
## 24  6        0    10

Interpretation:
1. Mean and median values are similar w/ slightly more variation than in the domain specific anova.
2. Highest to lowest group n size ratio is 1.14 (139/122). Acceptable ratio for ANOVA (under 1.5).
3. Highest to lowest overall group variance ratio is 1.40 (9.32/6.65). Acceptable ratio for ANOVA (under 4).
For the sake of consistency w/ the other EHI studies, I also calculated Hartley’s F-max ratio.
The conservative F-max critical value is 1.60 (same as DS anova since number of groups and n sizes doesn’t change), which is still higher than the highest observed F-max ratio of 1.28.

Assumption checks

Missing values

missing_summary <- long_data %>%
  group_by(perspective, temporalDO, TIME, DOMAIN) %>%
  summarise(
    n_total     = n(),
    n_missing   = sum(is.na(DGEN_SCORE)),
    pct_missing = round(100 * n_missing / n_total, 2),
    .groups     = "drop"
  )

print(missing_summary, n = Inf)
## # A tibble: 24 × 7
##    perspective temporalDO TIME  DOMAIN n_total n_missing pct_missing
##    <fct>       <fct>      <fct> <fct>    <int>     <int>       <dbl>
##  1 other       future     past  pref       122         0           0
##  2 other       future     past  pers       122         0           0
##  3 other       future     past  val        122         0           0
##  4 other       future     fut   pref       122         0           0
##  5 other       future     fut   pers       122         0           0
##  6 other       future     fut   val        122         0           0
##  7 other       past       past  pref       139         0           0
##  8 other       past       past  pers       139         0           0
##  9 other       past       past  val        139         0           0
## 10 other       past       fut   pref       139         0           0
## 11 other       past       fut   pers       139         0           0
## 12 other       past       fut   val        139         0           0
## 13 self        future     past  pref       138         0           0
## 14 self        future     past  pers       138         0           0
## 15 self        future     past  val        138         0           0
## 16 self        future     fut   pref       138         0           0
## 17 self        future     fut   pers       138         0           0
## 18 self        future     fut   val        138         0           0
## 19 self        past       past  pref       123         0           0
## 20 self        past       past  pers       123         0           0
## 21 self        past       past  val        123         0           0
## 22 self        past       fut   pref       123         0           0
## 23 self        past       fut   pers       123         0           0
## 24 self        past       fut   val        123         0           0

No missing values. As expected.

Outliers

outlier_summary <- long_data %>%
  group_by(perspective, temporalDO, TIME, DOMAIN) %>%
  summarise(
    n          = n(),
    n_outliers = sum(abs(scale(DGEN_SCORE)) > 3),
    .groups    = "drop"
  )

print(outlier_summary, n = Inf)
## # A tibble: 24 × 6
##    perspective temporalDO TIME  DOMAIN     n n_outliers
##    <fct>       <fct>      <fct> <fct>  <int>      <int>
##  1 other       future     past  pref     122          0
##  2 other       future     past  pers     122          0
##  3 other       future     past  val      122          0
##  4 other       future     fut   pref     122          0
##  5 other       future     fut   pers     122          0
##  6 other       future     fut   val      122          0
##  7 other       past       past  pref     139          0
##  8 other       past       past  pers     139          0
##  9 other       past       past  val      139          0
## 10 other       past       fut   pref     139          0
## 11 other       past       fut   pers     139          0
## 12 other       past       fut   val      139          0
## 13 self        future     past  pref     138          0
## 14 self        future     past  pers     138          0
## 15 self        future     past  val      138          0
## 16 self        future     fut   pref     138          0
## 17 self        future     fut   pers     138          0
## 18 self        future     fut   val      138          0
## 19 self        past       past  pref     123          0
## 20 self        past       past  pers     123          0
## 21 self        past       past  val      123          0
## 22 self        past       fut   pref     123          0
## 23 self        past       fut   pers     123          0
## 24 self        past       fut   val      123          0

No outliers present. Good. Same as DS anova.

Homogeneity of variance

homogeneity_between <- long_data %>%
  group_by(TIME, DOMAIN) %>%
  rstatix::levene_test(DGEN_SCORE ~ perspective * temporalDO)

print(homogeneity_between, n = Inf)
## # A tibble: 6 × 6
##   TIME  DOMAIN   df1   df2 statistic      p
##   <fct> <fct>  <int> <int>     <dbl>  <dbl>
## 1 past  pref       3   518    0.0902 0.965 
## 2 past  pers       3   518    0.407  0.748 
## 3 past  val        3   518    2.69   0.0460
## 4 fut   pref       3   518    0.824  0.481 
## 5 fut   pers       3   518    0.876  0.454 
## 6 fut   val        3   518    0.238  0.870

Levene’s test is signiicant for 1 cell only: past-val. However, variance ratios and F-max tests show that any heteroscedasticity is mild.

Normality (within-subjects residuals)

resid_within <- long_data %>%
  group_by(pID) %>%
  mutate(person_mean = mean(DGEN_SCORE, na.rm = TRUE)) %>%
  ungroup() %>%
  mutate(resid = DGEN_SCORE - person_mean) %>%
  pull(resid)
resid_within <- resid_within[!is.na(resid_within)]

n_resid <- length(resid_within)
if (n_resid < 3L) {
  message("Too few within-subjects residuals (n < 3); skipping Shapiro-Wilk.")
} else {
  resid_for_shapiro <- if (n_resid > 5000L) {
    set.seed(1L)
    sample(resid_within, 5000L)
  } else {
    resid_within
  }
  print(shapiro.test(resid_for_shapiro))
}
## 
##  Shapiro-Wilk normality test
## 
## data:  resid_for_shapiro
## W = 0.94192, p-value < 0.00000000000000022

Q-Q plot

qqnorm(resid_within)
qqline(resid_within)

Shapiro-Wilk test is significant but is sensitive to large sample size.
QQ plot shows that strict centre residuals are normally distributed, but there is some deviation from normality. ANOVA is robust to violations of normality w/ large sample size.

Overall, ANOVA can proceed.

Mixed ANOVA

aov_afex <- aov_ez(
  id      = "pID",
  dv      = "DGEN_SCORE",
  data    = long_data,
  between = c("perspective", "temporalDO"),
  within  = c("TIME", "DOMAIN"),
  type    = 3,
  anova_table = list(correction = "none")
)

print(aov_afex)
## Anova Table (Type 3 tests)
## 
## Response: DGEN_SCORE
##                                Effect      df   MSE        F   ges p.value
## 1                         perspective  1, 518 36.26     1.04  .002    .309
## 2                          temporalDO  1, 518 36.26     0.03 <.001    .862
## 3              perspective:temporalDO  1, 518 36.26     0.00 <.001    .981
## 4                                TIME  1, 518  3.11  8.39 **  .001    .004
## 5                    perspective:TIME  1, 518  3.11     0.02 <.001    .890
## 6                     temporalDO:TIME  1, 518  3.11   2.94 + <.001    .087
## 7         perspective:temporalDO:TIME  1, 518  3.11   3.44 + <.001    .064
## 8                              DOMAIN 2, 1036  2.12 7.85 ***  .001   <.001
## 9                  perspective:DOMAIN 2, 1036  2.12     1.17 <.001    .312
## 10                  temporalDO:DOMAIN 2, 1036  2.12  5.00 ** <.001    .007
## 11      perspective:temporalDO:DOMAIN 2, 1036  2.12     0.39 <.001    .680
## 12                        TIME:DOMAIN 2, 1036  1.52     0.77 <.001    .461
## 13            perspective:TIME:DOMAIN 2, 1036  1.52     0.67 <.001    .513
## 14             temporalDO:TIME:DOMAIN 2, 1036  1.52     0.44 <.001    .643
## 15 perspective:temporalDO:TIME:DOMAIN 2, 1036  1.52   3.12 * <.001    .045
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1

Mauchly’s test of sphericity is not significant. Using uncorrected values for interpretation and analysis.

Significant main effects and interactions:
Effect df MSE F ges p 4 TIME 1, 518 3.11 8.39 ** .001 .004
8 DOMAIN 2, 1036 2.13 7.85 *** .001 <.001
10 temporalDO:DOMAIN 2, 1036 2.13 5.00 ** <.001 .007
15 perspective:temporalDO:TIME:DOMAIN 2, 1036 1.52 3.12 * <.001 .045

Mauchly and epsilon

anova_wide <- anova_data %>%
  select(pID, perspective, temporalDO, all_of(within_vars)) %>%
  filter(if_all(all_of(within_vars), ~ !is.na(.)))
response_matrix <- as.matrix(anova_wide[, within_vars])
rm_model <- lm(response_matrix ~ perspective * temporalDO, data = anova_wide)
idata <- data.frame(
  TIME   = factor(rep(c("past", "fut"), each = 3), levels = c("past", "fut")),
  DOMAIN = factor(rep(c("pref", "pers", "val"), 2), levels = c("pref", "pers", "val"))
)
rm_anova <- car::Anova(rm_model, idata = idata, idesign = ~ TIME * DOMAIN, type = 3)
rm_summary <- summary(rm_anova, multivariate = FALSE)
## Warning in summary.Anova.mlm(rm_anova, multivariate = FALSE): HF eps > 1
## treated as 1
if (!is.null(rm_summary$sphericity.tests)) {
  print(rm_summary$sphericity.tests)
}
##                                    Test statistic p-value
## DOMAIN                                    0.99806 0.60476
## perspective:DOMAIN                        0.99806 0.60476
## temporalDO:DOMAIN                         0.99806 0.60476
## perspective:temporalDO:DOMAIN             0.99806 0.60476
## TIME:DOMAIN                               0.99654 0.40772
## perspective:TIME:DOMAIN                   0.99654 0.40772
## temporalDO:TIME:DOMAIN                    0.99654 0.40772
## perspective:temporalDO:TIME:DOMAIN        0.99654 0.40772
if (!is.null(rm_summary$epsilon)) {
  print(rm_summary$epsilon)
}

Post hoc comparisons

TIME (main effect)

emm_TIME <- emmeans(aov_afex, ~ TIME)
print(pairs(emm_TIME, adjust = "none"))
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut    0.183 0.0632 518   2.897  0.0039
## 
## Results are averaged over the levels of: perspective, temporalDO, DOMAIN

Supports presence of EHI effect.

DOMAIN (main effect)

emm_DOMAIN <- emmeans(aov_afex, ~ DOMAIN)
print(pairs(emm_DOMAIN, adjust = "tukey"))
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers    0.129 0.0629 518   2.043  0.1032
##  pref - val     0.253 0.0634 518   3.991  0.0002
##  pers - val     0.124 0.0652 518   1.908  0.1375
## 
## Results are averaged over the levels of: perspective, temporalDO, TIME 
## P value adjustment: tukey method for comparing a family of 3 estimates

Only preference to values contrast is significant.

temporalDO:DOMAIN

emmeans(aov_afex, pairwise ~ temporalDO | DOMAIN, adjust = "none")$contrasts
## DOMAIN = pref:
##  contrast      estimate    SE  df t.ratio p.value
##  future - past -0.00838 0.220 518  -0.038  0.9697
## 
## DOMAIN = pers:
##  contrast      estimate    SE  df t.ratio p.value
##  future - past -0.25252 0.230 518  -1.096  0.2735
## 
## DOMAIN = val:
##  contrast      estimate    SE  df t.ratio p.value
##  future - past  0.14817 0.233 518   0.636  0.5248
## 
## Results are averaged over the levels of: perspective, TIME
emmeans(aov_afex, pairwise ~ DOMAIN | temporalDO, adjust = "tukey")$contrasts
## temporalDO = future:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers  0.25065 0.0892 518   2.810  0.0142
##  pref - val   0.17474 0.0898 518   1.945  0.1271
##  pers - val  -0.07591 0.0924 518  -0.821  0.6900
## 
## temporalDO = past:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers  0.00651 0.0888 518   0.073  0.9970
##  pref - val   0.33129 0.0895 518   3.702  0.0007
##  pers - val   0.32478 0.0921 518   3.527  0.0013
## 
## Results are averaged over the levels of: perspective, TIME 
## P value adjustment: tukey method for comparing a family of 3 estimates

When grouped by domain, no contrasts are significant.

When grouped by temporalDO, some contrasts are significant:

Future-first temporal display order:
contrast estimate SE df t.ratio p.value
pref - pers 0.25065 0.0892 518 2.810 0.0142

Past-first temporal display order:
contrast estimate SE df t.ratio p.value
pref - val 0.33129 0.0895 518 3.702 0.0007
pers - val 0.32478 0.0921 518 3.527 0.0013

perspective:temporalDO:TIME:DOMAIN

contrasts for TIME grouped by perspective, temporalDO, and DOMAIN

emm_fourway <- emmeans(aov_afex, pairwise ~ TIME | perspective * temporalDO * DOMAIN, adjust = "tukey")
print(emm_fourway$contrasts)
## perspective = other, temporalDO = future, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.0492 0.182 518   0.270  0.7873
## 
## perspective = self, temporalDO = future, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.0362 0.171 518  -0.212  0.8326
## 
## perspective = other, temporalDO = past, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.3237 0.171 518   1.897  0.0584
## 
## perspective = self, temporalDO = past, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.1870 0.181 518   1.031  0.3032
## 
## perspective = other, temporalDO = future, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.2049 0.179 518   1.142  0.2540
## 
## perspective = self, temporalDO = future, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.0145 0.169 518   0.086  0.9316
## 
## perspective = other, temporalDO = past, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.2878 0.168 518   1.712  0.0875
## 
## perspective = self, temporalDO = past, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.5285 0.179 518   2.957  0.0032
## 
## perspective = other, temporalDO = future, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.2951 0.188 518   1.568  0.1175
## 
## perspective = self, temporalDO = future, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.0797 0.177 518  -0.450  0.6526
## 
## perspective = other, temporalDO = past, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.1151 0.176 518  -0.653  0.5141
## 
## perspective = self, temporalDO = past, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.5366 0.187 518   2.863  0.0044

Significant contrasts:

contrast estimate SE df t.ratio p.value
past - fut 0.5285 0.179 518 2.957 0.0032 (self-perspective, personality domain, past-first temporal display order)
past - fut 0.5366 0.187 518 2.863 0.0044 (self-perspective, values domain, past-first temporal display order)

### contrasts for DOMAIN grouped by perspective, TIME, and temporalDO

emm_fourway2 <- emmeans(aov_afex, pairwise ~ DOMAIN | perspective * TIME * temporalDO, adjust = "tukey")
print(emm_fourway2$contrasts)
## perspective = other, TIME = past, temporalDO = future:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.2459 0.168 518   1.468  0.3073
##  pref - val    0.0164 0.177 518   0.093  0.9953
##  pers - val   -0.2295 0.171 518  -1.343  0.3718
## 
## perspective = self, TIME = past, temporalDO = future:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.1522 0.158 518   0.966  0.5986
##  pref - val    0.2319 0.166 518   1.395  0.3445
##  pers - val    0.0797 0.161 518   0.496  0.8732
## 
## perspective = other, TIME = fut, temporalDO = future:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.4016 0.177 518   2.268  0.0613
##  pref - val    0.2623 0.169 518   1.552  0.2678
##  pers - val   -0.1393 0.175 518  -0.798  0.7046
## 
## perspective = self, TIME = fut, temporalDO = future:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.2029 0.167 518   1.219  0.4427
##  pref - val    0.1884 0.159 518   1.185  0.4625
##  pers - val   -0.0145 0.164 518  -0.088  0.9957
## 
## perspective = other, TIME = past, temporalDO = past:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.1367 0.157 518   0.871  0.6589
##  pref - val    0.6259 0.166 518   3.778  0.0005
##  pers - val    0.4892 0.160 518   3.056  0.0066
## 
## perspective = self, TIME = past, temporalDO = past:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers  -0.2764 0.167 518  -1.657  0.2230
##  pref - val    0.0813 0.176 518   0.462  0.8892
##  pers - val    0.3577 0.170 518   2.102  0.0903
## 
## perspective = other, TIME = fut, temporalDO = past:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.1007 0.166 518   0.607  0.8163
##  pref - val    0.1871 0.158 518   1.181  0.4650
##  pers - val    0.0863 0.164 518   0.528  0.8579
## 
## perspective = self, TIME = fut, temporalDO = past:
##  contrast    estimate    SE  df t.ratio p.value
##  pref - pers   0.0650 0.176 518   0.369  0.9278
##  pref - val    0.4309 0.168 518   2.559  0.0290
##  pers - val    0.3659 0.174 518   2.103  0.0902
## 
## P value adjustment: tukey method for comparing a family of 3 estimates

Significant contrasts:

contrast estimate SE df t.ratio p.value
pref - val 0.6259 0.166 518 3.778 0.0005 (other-perspective, past-directed questions, past-first temporal display order)
pers - val 0.4892 0.160 518 3.056 0.0066 (other-perspective, past-directed questions, past-first temporal display order)
pref - val 0.4309 0.168 518 2.559 0.0290 (self-perspective, future-directed questions, past-first temporal display order)

Cohen’s d (significant contrasts only)

d_data <- anova_data %>%
  mutate(
    past_mean = (past_pref_DGEN + past_pers_DGEN + past_val_DGEN) / 3,
    fut_mean  = (fut_pref_DGEN + fut_pers_DGEN + fut_val_DGEN) / 3,
    pref_mean = (past_pref_DGEN + fut_pref_DGEN) / 2,
    pers_mean = (past_pers_DGEN + fut_pers_DGEN) / 2,
    val_mean  = (past_val_DGEN + fut_val_DGEN) / 2
  )

cohens_d_results <- tibble(
  contrast = character(),
  condition = character(),
  d = double()
)

# TIME main: past vs fut
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "overall",
    d = suppressMessages(effectsize::cohens_d(d_data$past_mean, d_data$fut_mean, paired = TRUE)$Cohens_d)
  )

# DOMAIN main: pref vs val
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "overall",
    d = suppressMessages(effectsize::cohens_d(d_data$pref_mean, d_data$val_mean, paired = TRUE)$Cohens_d)
  )

# temporalDO:DOMAIN - future: pref vs pers
d_fut <- d_data %>% filter(temporalDO == "future")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - pers)",
    condition = "temporalDO = future",
    d = suppressMessages(effectsize::cohens_d(d_fut$pref_mean, d_fut$pers_mean, paired = TRUE)$Cohens_d)
  )

# temporalDO:DOMAIN - past: pref vs val, pers vs val
d_past <- d_data %>% filter(temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "temporalDO = past",
    d = suppressMessages(effectsize::cohens_d(d_past$pref_mean, d_past$val_mean, paired = TRUE)$Cohens_d)
  ) %>%
  add_row(
    contrast = "DOMAIN (pers - val)",
    condition = "temporalDO = past",
    d = suppressMessages(effectsize::cohens_d(d_past$pers_mean, d_past$val_mean, paired = TRUE)$Cohens_d)
  )

# 4-way TIME: self, past temporalDO, pers
d_self_past <- d_data %>% filter(perspective == "self", temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "self, past temporalDO, pers domain",
    d = suppressMessages(effectsize::cohens_d(d_self_past$past_pers_DGEN, d_self_past$fut_pers_DGEN, paired = TRUE)$Cohens_d)
  )

# 4-way TIME: self, past temporalDO, val
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "self, past temporalDO, val domain",
    d = suppressMessages(effectsize::cohens_d(d_self_past$past_val_DGEN, d_self_past$fut_val_DGEN, paired = TRUE)$Cohens_d)
  )

# 4-way DOMAIN: other, past TIME, past temporalDO - pref vs val
d_other_past_tpast <- d_data %>% filter(perspective == "other", temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "other, past TIME, past temporalDO",
    d = suppressMessages(effectsize::cohens_d(d_other_past_tpast$past_pref_DGEN, d_other_past_tpast$past_val_DGEN, paired = TRUE)$Cohens_d)
  )

# 4-way DOMAIN: other, past TIME, past temporalDO - pers vs val
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pers - val)",
    condition = "other, past TIME, past temporalDO",
    d = suppressMessages(effectsize::cohens_d(d_other_past_tpast$past_pers_DGEN, d_other_past_tpast$past_val_DGEN, paired = TRUE)$Cohens_d)
  )

# 4-way DOMAIN: self, fut TIME, past temporalDO - pref vs val
d_self_fut_tpast <- d_data %>% filter(perspective == "self", temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "self, fut TIME, past temporalDO",
    d = suppressMessages(effectsize::cohens_d(d_self_fut_tpast$fut_pref_DGEN, d_self_fut_tpast$fut_val_DGEN, paired = TRUE)$Cohens_d)
  )

cohens_d_results %>%
  mutate(d = round(d, 3)) %>%
  print(n = Inf)
## # A tibble: 10 × 3
##    contrast             condition                              d
##    <chr>                <chr>                              <dbl>
##  1 TIME (past - fut)    overall                            0.122
##  2 DOMAIN (pref - val)  overall                            0.178
##  3 DOMAIN (pref - pers) temporalDO = future                0.174
##  4 DOMAIN (pref - val)  temporalDO = past                  0.242
##  5 DOMAIN (pers - val)  temporalDO = past                  0.239
##  6 TIME (past - fut)    self, past temporalDO, pers domain 0.26 
##  7 TIME (past - fut)    self, past temporalDO, val domain  0.254
##  8 DOMAIN (pref - val)  other, past TIME, past temporalDO  0.348
##  9 DOMAIN (pers - val)  other, past TIME, past temporalDO  0.241
## 10 DOMAIN (pref - val)  self, fut TIME, past temporalDO    0.257

Size d Interpretation
Small 0.2 Weak effect
Medium 0.5 Moderate effect
Large 0.8 Strong effect