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_MEAN", "past_pers_MEAN", "past_val_MEAN",
  "fut_pref_MEAN",  "fut_pers_MEAN",  "fut_val_MEAN"
)

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 = "MEAN_SCORE"
  ) %>%
  mutate(
    time = case_when(
      grepl("^past_", variable) ~ "past",
      grepl("^fut_",  variable) ~ "fut",
      TRUE ~ NA_character_
    ),
    domain = case_when(
      grepl("_pref_MEAN$", variable) ~ "pref",
      grepl("_pers_MEAN$", variable) ~ "pers",
      grepl("_val_MEAN$",  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, MEAN_SCORE) %>%
  filter(!is.na(MEAN_SCORE))

Descriptive statistics

desc_stats <- long_data %>%
  group_by(perspective, temporalDO, TIME, DOMAIN) %>%
  summarise(
    n        = n(),
    mean     = round(mean(MEAN_SCORE), 5),
    variance = round(var(MEAN_SCORE), 5),
    sd       = round(sd(MEAN_SCORE), 5),
    median   = round(median(MEAN_SCORE), 5),
    q1       = round(quantile(MEAN_SCORE, 0.25), 5),
    q3       = round(quantile(MEAN_SCORE, 0.75), 5),
    min      = round(min(MEAN_SCORE), 5),
    max      = round(max(MEAN_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.73     5.75  2.40   3.67  1.67
##  2 other       future     past  pers     122  3.60     5.65  2.38   3.6   1.4 
##  3 other       future     past  val      122  3.60     6.27  2.50   3.5   1.33
##  4 other       future     fut   pref     122  3.65     4.76  2.18   3.67  1.83
##  5 other       future     fut   pers     122  3.50     4.74  2.18   3.4   1.65
##  6 other       future     fut   val      122  3.38     5.17  2.27   3     1.5 
##  7 other       past       past  pref     139  3.95     5.86  2.42   4     2   
##  8 other       past       past  pers     139  3.82     6.58  2.56   3.6   1.6 
##  9 other       past       past  val      139  3.60     5.75  2.40   3.33  1.83
## 10 other       past       fut   pref     139  3.67     6.24  2.50   3.33  1.75
## 11 other       past       fut   pers     139  3.65     6.86  2.62   3.4   1.5 
## 12 other       past       fut   val      139  3.56     7.14  2.67   3.17  1.5 
## 13 self        future     past  pref     138  3.60     5.77  2.40   3.42  1.5 
## 14 self        future     past  pers     138  3.74     6.00  2.45   3.6   1.65
## 15 self        future     past  val      138  3.79     6.52  2.55   3.67  1.67
## 16 self        future     fut   pref     138  3.83     5.36  2.32   3.75  2   
## 17 self        future     fut   pers     138  3.83     6.24  2.50   3.9   1.4 
## 18 self        future     fut   val      138  3.86     6.15  2.48   4     1.83
## 19 self        past       past  pref     123  3.84     5.82  2.41   3.83  1.83
## 20 self        past       past  pers     123  4.19     6.60  2.57   4.2   1.9 
## 21 self        past       past  val      123  4.30     7.29  2.70   4.33  1.83
## 22 self        past       fut   pref     123  3.65     6.76  2.60   3.33  1.33
## 23 self        past       fut   pers     123  3.75     7.09  2.66   3.4   1.6 
## 24 self        past       fut   val      123  3.57     7.93  2.82   3.17  1   
##       q3   min   max
##    <dbl> <dbl> <dbl>
##  1  5.5      0 10   
##  2  5        0 10   
##  3  5.29     0 10   
##  4  5.25     0 10   
##  5  5.2      0  8   
##  6  5.17     0  8.67
##  7  5.5      0 10   
##  8  5.8      0 10   
##  9  5.33     0 10   
## 10  5.17     0 10   
## 11  5.6      0 10   
## 12  5.25     0 10   
## 13  5.29     0 10   
## 14  5.75     0  8.6 
## 15  5.62     0  9   
## 16  5.5      0 10   
## 17  5.95     0  8.6 
## 18  5.62     0  9.17
## 19  5.58     0 10   
## 20  6.2      0 10   
## 21  6.5      0 10   
## 22  5.67     0 10   
## 23  5.5      0 10   
## 24  5.67     0 10

Interpretations:
1. Mean and median values are similar, indicating distribution is relatively symmetric and any skew is minimal. Any outliers are not extreme.
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.67 (7.93/4.74). 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, which is still higher than the highest observed F-max ratio of 1.53.

Assumption checks

Missing values

missing_summary <- long_data %>%
  group_by(perspective, temporalDO, TIME, DOMAIN) %>%
  summarise(
    n_total     = n(),
    n_missing   = sum(is.na(MEAN_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(MEAN_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.

Homogeneity of variance

homogeneity_between <- long_data %>%
  group_by(TIME, DOMAIN) %>%
  rstatix::levene_test(MEAN_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.0857 0.968 
## 2 past  pers       3   518    0.974  0.405 
## 3 past  val        3   518    1.81   0.144 
## 4 fut   pref       3   518    1.68   0.170 
## 5 fut   pers       3   518    2.82   0.0383
## 6 fut   val        3   518    2.87   0.0360

Levene’s test is sigifnicant for two cells: fut-pers and fut-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(MEAN_SCORE, na.rm = TRUE)) %>%
  ungroup() %>%
  mutate(resid = MEAN_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.9431, p-value < 0.00000000000000022

Q-Q plot

qqnorm(resid_within)
qqline(resid_within)

Shapiro-Wilk is significant but is sensitive to large sample size. QQ plot shows that centre residuals are normally distributed, with some tail heaviness. ANOVA is robust to violations of normality w/ large sample size.

Overall, ANOVA can proceed.

Mixed ANOVA

aov_afex <- aov_ez(
  id      = "pID",
  dv      = "MEAN_SCORE",
  data    = long_data,
  between = c("perspective", "temporalDO"),
  within  = c("TIME", "DOMAIN"),
  type    = 3
)

cat("\n--- ANOVA Table (Type 3, uncorrected) ---\n")
## 
## --- ANOVA Table (Type 3, uncorrected) ---
print(nice(aov_afex, correction = "none"))
## Anova Table (Type 3 tests)
## 
## Response: MEAN_SCORE
##                                Effect      df   MSE        F   ges p.value
## 1                         perspective  1, 518 31.39     0.88  .001    .349
## 2                          temporalDO  1, 518 31.39     0.36 <.001    .551
## 3              perspective:temporalDO  1, 518 31.39     0.00 <.001    .955
## 4                                TIME  1, 518  1.86 10.05 ** <.001    .002
## 5                    perspective:TIME  1, 518  1.86     0.02 <.001    .895
## 6                     temporalDO:TIME  1, 518  1.86  9.81 ** <.001    .002
## 7         perspective:temporalDO:TIME  1, 518  1.86  7.91 ** <.001    .005
## 8                              DOMAIN 2, 1036  1.17     0.63 <.001    .534
## 9                  perspective:DOMAIN 2, 1036  1.17 7.79 *** <.001   <.001
## 10                  temporalDO:DOMAIN 2, 1036  1.17     0.76 <.001    .470
## 11      perspective:temporalDO:DOMAIN 2, 1036  1.17     0.17 <.001    .843
## 12                        TIME:DOMAIN 2, 1036  0.75     2.00 <.001    .136
## 13            perspective:TIME:DOMAIN 2, 1036  0.75   3.58 * <.001    .028
## 14             temporalDO:TIME:DOMAIN 2, 1036  0.75     0.01 <.001    .987
## 15 perspective:temporalDO:TIME:DOMAIN 2, 1036  0.75   3.11 * <.001    .045
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
cat("\n--- ANOVA Table (Type 3, Greenhouse–Geisser correction) ---\n")
## 
## --- ANOVA Table (Type 3, Greenhouse–Geisser correction) ---
print(nice(aov_afex, correction = "GG"))
## Anova Table (Type 3 tests)
## 
## Response: MEAN_SCORE
##                                Effect            df   MSE        F   ges
## 1                         perspective        1, 518 31.39     0.88  .001
## 2                          temporalDO        1, 518 31.39     0.36 <.001
## 3              perspective:temporalDO        1, 518 31.39     0.00 <.001
## 4                                TIME        1, 518  1.86 10.05 ** <.001
## 5                    perspective:TIME        1, 518  1.86     0.02 <.001
## 6                     temporalDO:TIME        1, 518  1.86  9.81 ** <.001
## 7         perspective:temporalDO:TIME        1, 518  1.86  7.91 ** <.001
## 8                              DOMAIN 1.94, 1004.66  1.21     0.63 <.001
## 9                  perspective:DOMAIN 1.94, 1004.66  1.21 7.79 *** <.001
## 10                  temporalDO:DOMAIN 1.94, 1004.66  1.21     0.76 <.001
## 11      perspective:temporalDO:DOMAIN 1.94, 1004.66  1.21     0.17 <.001
## 12                        TIME:DOMAIN 2.00, 1034.51  0.75     2.00 <.001
## 13            perspective:TIME:DOMAIN 2.00, 1034.51  0.75   3.58 * <.001
## 14             temporalDO:TIME:DOMAIN 2.00, 1034.51  0.75     0.01 <.001
## 15 perspective:temporalDO:TIME:DOMAIN 2.00, 1034.51  0.75   3.11 * <.001
##    p.value
## 1     .349
## 2     .551
## 3     .955
## 4     .002
## 5     .895
## 6     .002
## 7     .005
## 8     .529
## 9    <.001
## 10    .466
## 11    .837
## 12    .136
## 13    .028
## 14    .987
## 15    .045
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
## 
## Sphericity correction method: GG

Mauchly’s test of sphericity is sig for DOMAIN main effect and interaction (except w/ TIME). Use GG correction for these:
- 8 DOMAIN 1.94, 1004.66 1.21 0.63 <.001, p = .529
## 9 perspective:DOMAIN 1.94, 1004.66 1.21 7.79 *** <.001, p <.001
## 10 temporalDO:DOMAIN 1.94, 1004.66 1.21 0.76 <.001, p = .466
## 11 perspective:temporalDO:DOMAIN 1.94, 1004.66 1.21 0.17 <.001, p = .837

The following are significant main effects and interactions:
## 15 perspective:temporalDO:TIME:DOMAIN 2, 1036 0.75 3.11 * <.001 .045
## 13 perspective:TIME:DOMAIN 2, 1036 0.75 3.58 * <.001 .028
## 9 perspective:DOMAIN 1.94, 1004.66 1.21 7.79 *** <.001, p <.001 (GG corrected)
## 6 temporalDO:TIME 1, 518 1.86 9.81 ** <.001 .002
## 7 perspective:temporalDO:TIME 1, 518 1.86 7.91 ** <.001 .005
## 4 TIME 1, 518 1.86 10.05 ** <.001 .002

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)) {
  cat("\nMauchly's Test of Sphericity:\n")
  print(rm_summary$sphericity.tests)
}
## 
## Mauchly's Test of Sphericity:
##                                    Test statistic p-value
## DOMAIN                                    0.96880 0.00028
## perspective:DOMAIN                        0.96880 0.00028
## temporalDO:DOMAIN                         0.96880 0.00028
## perspective:temporalDO:DOMAIN             0.96880 0.00028
## TIME:DOMAIN                               0.99856 0.68858
## perspective:TIME:DOMAIN                   0.99856 0.68858
## temporalDO:TIME:DOMAIN                    0.99856 0.68858
## perspective:temporalDO:TIME:DOMAIN        0.99856 0.68858
if (!is.null(rm_summary$epsilon)) {
  cat("\nEpsilon (GG, HF):\n")
  print(rm_summary$epsilon)
}

Post-hoc comparisons

TIME (main effect)

emm_TIME <- emmeans(aov_afex, ~ TIME)
print(pairs(emm_TIME, adjust = "bonferroni"))
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut    0.155 0.0488 518   3.170  0.0016
## 
## Results are averaged over the levels of: perspective, temporalDO, DOMAIN

Pairwise comparison provide supprot for EHI effect.

temporalDO:TIME

emm_temporalDO_TIME <- emmeans(aov_afex, ~ TIME | temporalDO)
print(pairs(emm_temporalDO_TIME, adjust = "bonferroni"))
## temporalDO = future:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut  0.00186 0.0692 518   0.027  0.9786
## 
## temporalDO = past:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut  0.30772 0.0689 518   4.465 <0.0001
## 
## Results are averaged over the levels of: perspective, DOMAIN

Contrast significant only for temporal display order of past first, then future. When grouped by time instead of temporalDO, no contrasts are significant.

perspective:temporalDO:TIME

emm_pt_TIME <- emmeans(aov_afex, ~ TIME | perspective + temporalDO)
print(pairs(emm_pt_TIME, adjust = "bonferroni"))
## perspective = other, temporalDO = future:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut    0.133 0.1010 518   1.317  0.1883
## 
## perspective = self, temporalDO = future:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   -0.129 0.0948 518  -1.362  0.1739
## 
## perspective = other, temporalDO = past:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut    0.164 0.0944 518   1.736  0.0831
## 
## perspective = self, temporalDO = past:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut    0.451 0.1000 518   4.497 <0.0001
## 
## Results are averaged over the levels of: DOMAIN

EHI is significant only for self perspective and past first temporal display order.

When grouped by perspective or temporalDO instead of TIME, no contrasts are significant.

perspective:DOMAIN

emm_perspective_DOMAIN <- emmeans(aov_afex, ~ perspective | DOMAIN)
print(pairs(emm_perspective_DOMAIN, adjust = "bonferroni"))
## DOMAIN = pref:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self   0.0191 0.202 518   0.094  0.9248
## 
## DOMAIN = pers:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.2379 0.208 518  -1.141  0.2543
## 
## DOMAIN = val:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.3459 0.213 518  -1.622  0.1053
## 
## Results are averaged over the levels of: temporalDO, TIME
emm_perspective_DOMAIN_domain <- emmeans(aov_afex, ~ DOMAIN | perspective)
print(pairs(emm_perspective_DOMAIN_domain, adjust = "bonferroni"))
## perspective = other:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers  0.11064 0.0696 518   1.591  0.3369
##  pref - val   0.21702 0.0706 518   3.074  0.0067
##  pers - val   0.10638 0.0610 518   1.744  0.2454
## 
## perspective = self:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers -0.14631 0.0695 518  -2.104  0.1075
##  pref - val  -0.14800 0.0706 518  -2.097  0.1093
##  pers - val  -0.00169 0.0610 518  -0.028  1.0000
## 
## Results are averaged over the levels of: temporalDO, TIME 
## P value adjustment: bonferroni method for 3 tests

significance is driven by the change from preferences to values in the “other” perspective.

perspective:TIME:DOMAIN

emm_pt_TIME_domain <- emmeans(aov_afex, ~ TIME | perspective + DOMAIN)
print(pairs(emm_pt_TIME_domain, adjust = "bonferroni"))
## perspective = other, DOMAIN = pref:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   0.1799 0.0862 518   2.087  0.0374
## 
## perspective = self, DOMAIN = pref:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut  -0.0228 0.0862 518  -0.265  0.7912
## 
## perspective = other, DOMAIN = pers:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   0.1351 0.0950 518   1.422  0.1555
## 
## perspective = self, DOMAIN = pers:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   0.1759 0.0949 518   1.852  0.0645
## 
## perspective = other, DOMAIN = val:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   0.1301 0.0968 518   1.344  0.1797
## 
## perspective = self, DOMAIN = val:
##  contrast   estimate     SE  df t.ratio p.value
##  past - fut   0.3306 0.0968 518   3.416  0.0007
## 
## Results are averaged over the levels of: temporalDO

EHI effects present for other-perspective in the preferences domain and for self-perspective in the values domain.
Estimate is higher in the self-perspective than in the other-perspective.

emm_pt_domain_domain <- emmeans(aov_afex, ~ DOMAIN | perspective + TIME)
print(pairs(emm_pt_domain_domain, adjust = "bonferroni"))
## perspective = other, TIME = past:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers   0.1330 0.0894 518   1.488  0.4122
##  pref - val    0.2419 0.0936 518   2.584  0.0301
##  pers - val    0.1089 0.0791 518   1.377  0.5072
## 
## perspective = self, TIME = past:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers  -0.2457 0.0894 518  -2.749  0.0186
##  pref - val   -0.3247 0.0936 518  -3.470  0.0017
##  pers - val   -0.0791 0.0790 518  -1.001  0.9526
## 
## perspective = other, TIME = fut:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers   0.0882 0.0850 518   1.038  0.8993
##  pref - val    0.1921 0.0843 518   2.280  0.0690
##  pers - val    0.1039 0.0839 518   1.239  0.6480
## 
## perspective = self, TIME = fut:
##  contrast    estimate     SE  df t.ratio p.value
##  pref - pers  -0.0470 0.0850 518  -0.553  1.0000
##  pref - val    0.0287 0.0842 518   0.341  1.0000
##  pers - val    0.0757 0.0838 518   0.903  1.0000
## 
## Results are averaged over the levels of: temporalDO 
## P value adjustment: bonferroni method for 3 tests

Significant contrasts are driven by domain changes from preferences to values in the self vs other perspectives, in the past-oriented questions.
Trends reverse depending on perspective, where values have higher estimates than preferences in the self-perspective, but lower estimates than preferences in the other-perspective.

perspective:temporalDO:TIME:DOMAIN

emm_ptt_TIME <- emmeans(aov_afex, ~ TIME | perspective + temporalDO + DOMAIN)
print(pairs(emm_ptt_TIME, adjust = "bonferroni"))
## perspective = other, temporalDO = future, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.0792 0.126 518   0.630  0.5292
## 
## perspective = self, temporalDO = future, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.2367 0.118 518  -2.001  0.0459
## 
## perspective = other, temporalDO = past, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.2806 0.118 518   2.380  0.0177
## 
## perspective = self, temporalDO = past, DOMAIN = pref:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.1911 0.125 518   1.525  0.1280
## 
## perspective = other, temporalDO = future, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.1033 0.139 518   0.745  0.4566
## 
## perspective = self, temporalDO = future, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.0841 0.130 518  -0.645  0.5193
## 
## perspective = other, temporalDO = past, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.1669 0.130 518   1.285  0.1993
## 
## perspective = self, temporalDO = past, DOMAIN = pers:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.4358 0.138 518   3.156  0.0017
## 
## perspective = other, temporalDO = future, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.2158 0.141 518   1.527  0.1273
## 
## perspective = self, temporalDO = future, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut  -0.0664 0.133 518  -0.500  0.6174
## 
## perspective = other, temporalDO = past, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.0444 0.132 518   0.335  0.7377
## 
## perspective = self, temporalDO = past, DOMAIN = val:
##  contrast   estimate    SE  df t.ratio p.value
##  past - fut   0.7276 0.141 518   5.169 <0.0001

EHI effects are present for three contrasts:
- past - fut 0.2806 0.118 518 2.380 0.0177 (other-perspective, preferences domain, past-first temporal display order)
- past - fut 0.4358 0.138 518 3.156 0.0017 (self-perspective, personality domain, past-first temporal display order)
- past - fut 0.7276 0.141 518 5.169 <0.0001 (self-perspective, values domain, past-first temporal display order)

A reverse EHI effect is present for 1 contrast:
- past - fut -0.2367 0.118 518 -2.001 0.0459 (self-personality, preferences domain, future-first temporal display order)

emm_ptt_perspective <- emmeans(aov_afex, ~ perspective | temporalDO + TIME + DOMAIN)
print(pairs(emm_ptt_perspective, adjust = "bonferroni"))
## temporalDO = future, TIME = past, DOMAIN = pref:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self   0.1343 0.299 518   0.449  0.6540
## 
## temporalDO = past, TIME = past, DOMAIN = pref:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self   0.1067 0.298 518   0.358  0.7207
## 
## temporalDO = future, TIME = fut, DOMAIN = pref:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.1817 0.299 518  -0.608  0.5436
## 
## temporalDO = past, TIME = fut, DOMAIN = pref:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self   0.0171 0.298 518   0.058  0.9541
## 
## temporalDO = future, TIME = past, DOMAIN = pers:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.1451 0.310 518  -0.469  0.6396
## 
## temporalDO = past, TIME = past, DOMAIN = pers:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.3714 0.309 518  -1.204  0.2292
## 
## temporalDO = future, TIME = fut, DOMAIN = pers:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.3325 0.311 518  -1.070  0.2852
## 
## temporalDO = past, TIME = fut, DOMAIN = pers:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.1025 0.310 518  -0.331  0.7407
## 
## temporalDO = future, TIME = past, DOMAIN = val:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.1951 0.315 518  -0.619  0.5362
## 
## temporalDO = past, TIME = past, DOMAIN = val:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.6972 0.314 518  -2.220  0.0268
## 
## temporalDO = future, TIME = fut, DOMAIN = val:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.4774 0.319 518  -1.495  0.1355
## 
## temporalDO = past, TIME = fut, DOMAIN = val:
##  contrast     estimate    SE  df t.ratio p.value
##  other - self  -0.0139 0.318 518  -0.044  0.9650

1 significant contrast:
- other - self -0.6972 0.314 518 -2.220 0.0268 (values domain, past-oriented questions, past-first temporal display order)

not really of theoretical interest but speaks to the perspective:TIME:DOMAIN interaction.

no significant contrasts when grouped by temporalDO instead of TIME or perspective.

Cohen’s d (significant contrasts only)

d_data <- anova_data %>%
  mutate(
    past_mean = (past_pref_MEAN + past_pers_MEAN + past_val_MEAN) / 3,
    fut_mean  = (fut_pref_MEAN + fut_pers_MEAN + fut_val_MEAN) / 3,
    pref_mean = (past_pref_MEAN + fut_pref_MEAN) / 2,
    pers_mean = (past_pers_MEAN + fut_pers_MEAN) / 2,
    val_mean  = (past_val_MEAN + fut_val_MEAN) / 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)
  )

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

# perspective:temporalDO:TIME: past vs fut for self, past temporalDO
d_self_past <- d_data %>% filter(perspective == "self", temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "self, temporalDO = past",
    d = suppressMessages(effectsize::cohens_d(d_self_past$past_mean, d_self_past$fut_mean, paired = TRUE)$Cohens_d)
  )

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

# perspective:TIME:DOMAIN - TIME: other, pref
d_other_pref <- d_data %>% filter(perspective == "other")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "other, pref domain",
    d = suppressMessages(effectsize::cohens_d(d_other_pref$past_pref_MEAN, d_other_pref$fut_pref_MEAN, paired = TRUE)$Cohens_d)
  )

# perspective:TIME:DOMAIN - TIME: self, val
d_self_val <- d_data %>% filter(perspective == "self")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "self, val domain",
    d = suppressMessages(effectsize::cohens_d(d_self_val$past_val_MEAN, d_self_val$fut_val_MEAN, paired = TRUE)$Cohens_d)
  )

# perspective:TIME:DOMAIN - DOMAIN: other, past TIME
d_other_past <- d_data %>% filter(perspective == "other")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "other, past TIME",
    d = suppressMessages(effectsize::cohens_d(d_other_past$past_pref_MEAN, d_other_past$past_val_MEAN, paired = TRUE)$Cohens_d)
  )

# perspective:TIME:DOMAIN - DOMAIN: self, past TIME: pref - pers
d_self_past_t <- d_data %>% filter(perspective == "self")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - pers)",
    condition = "self, past TIME",
    d = suppressMessages(effectsize::cohens_d(d_self_past_t$past_pref_MEAN, d_self_past_t$past_pers_MEAN, paired = TRUE)$Cohens_d)
  )

# perspective:TIME:DOMAIN - DOMAIN: self, past TIME: pref - val
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "DOMAIN (pref - val)",
    condition = "self, past TIME",
    d = suppressMessages(effectsize::cohens_d(d_self_past_t$past_pref_MEAN, d_self_past_t$past_val_MEAN, paired = TRUE)$Cohens_d)
  )

# 4-way TIME: self, future temporalDO, pref (reverse EHI)
d_self_fut_pref <- d_data %>% filter(perspective == "self", temporalDO == "future")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut) [reverse]",
    condition = "self, future temporalDO, pref domain",
    d = suppressMessages(effectsize::cohens_d(d_self_fut_pref$past_pref_MEAN, d_self_fut_pref$fut_pref_MEAN, paired = TRUE)$Cohens_d)
  )

# 4-way TIME: other, past temporalDO, pref
d_other_past_pref <- d_data %>% filter(perspective == "other", temporalDO == "past")
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "TIME (past - fut)",
    condition = "other, past temporalDO, pref domain",
    d = suppressMessages(effectsize::cohens_d(d_other_past_pref$past_pref_MEAN, d_other_past_pref$fut_pref_MEAN, paired = TRUE)$Cohens_d)
  )

# 4-way TIME: self, past temporalDO, pers
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_MEAN, d_self_past$fut_pers_MEAN, 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_MEAN, d_self_past$fut_val_MEAN, paired = TRUE)$Cohens_d)
  )

# 4-way perspective: past temporalDO, past TIME, val domain (other - self, between-subjects)
d_ptt_val <- d_data %>%
  filter(temporalDO == "past") %>%
  select(perspective, past_val_MEAN)
d_other_ptt <- d_ptt_val %>% filter(perspective == "other") %>% pull(past_val_MEAN)
d_self_ptt <- d_ptt_val %>% filter(perspective == "self") %>% pull(past_val_MEAN)
cohens_d_results <- cohens_d_results %>%
  add_row(
    contrast = "perspective (other - self)",
    condition = "past temporalDO, past TIME, val domain",
    d = suppressMessages(effectsize::cohens_d(d_other_ptt, d_self_ptt, paired = FALSE)$Cohens_d)
  )

cohens_d_results %>%
  mutate(d = round(d, 3)) %>%
  print(n = Inf)
## # A tibble: 14 × 3
##    contrast                    condition                                   d
##    <chr>                       <chr>                                   <dbl>
##  1 TIME (past - fut)           overall                                 0.13 
##  2 TIME (past - fut)           temporalDO = past                       0.249
##  3 TIME (past - fut)           self, temporalDO = past                 0.413
##  4 DOMAIN (pref - val)         perspective = other                     0.185
##  5 TIME (past - fut)           other, pref domain                      0.126
##  6 TIME (past - fut)           self, val domain                        0.189
##  7 DOMAIN (pref - val)         other, past TIME                        0.159
##  8 DOMAIN (pref - pers)        self, past TIME                        -0.171
##  9 DOMAIN (pref - val)         self, past TIME                        -0.218
## 10 TIME (past - fut) [reverse] self, future temporalDO, pref domain   -0.174
## 11 TIME (past - fut)           other, past temporalDO, pref domain     0.172
## 12 TIME (past - fut)           self, past temporalDO, pers domain      0.309
## 13 TIME (past - fut)           self, past temporalDO, val domain       0.435
## 14 perspective (other - self)  past temporalDO, past TIME, val domain -0.274