dat_pp <- subset(dat_long, cond == "posi" & metric == "具体性" & time %in% c("pre","post"))
dat_pp$group <- factor(dat_pp$group, levels = c("control","training"))
dat_pp$time  <- factor(dat_pp$time,  levels = c("pre","post"))

# 1) 线性混合模型
fit_pp <- lmer(score ~ group * time + (1|ID), data = dat_pp, REML = TRUE)
summary(fit_pp)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: score ~ group * time + (1 | ID)
##    Data: dat_pp
## 
## REML criterion at convergence: 416.3
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -1.92915 -0.60060 -0.02392  0.56008  1.54255 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  ID       (Intercept) 0.9304   0.9646  
##  Residual             1.2784   1.1307  
## Number of obs: 118, groups:  ID, 59
## 
## Fixed effects:
##                        Estimate Std. Error      df t value Pr(>|t|)    
## (Intercept)              2.9310     0.2760 96.8202  10.620  < 2e-16 ***
## grouptraining            0.1690     0.3870 96.8202   0.437  0.66340    
## timepost                 0.5862     0.2969 57.0000   1.974  0.05320 .  
## grouptraining:timepost   1.3138     0.4164 57.0000   3.155  0.00256 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) grptrn timpst
## grouptranng -0.713              
## timepost    -0.538  0.384       
## grptrnng:tm  0.384 -0.538 -0.713
# 2) 固定效应的 d(= estimate / sigma)—— 修正版
sig <- sigma(fit_pp)
co  <- as.data.frame(summary(fit_pp)$coefficients)

fixed_d_pp <- tibble(
  term      = rownames(co),
  estimate  = co[,"Estimate"],
  std.error = co[,"Std. Error"],
  df        = co[,"df"],
  t         = co[,"t value"],
  p         = co[,"Pr(>|t|)"]
) %>%
  dplyr::mutate(
    d      = estimate / sig,
    SE_d   = std.error / sig,
    d_low  = d + qt(0.025, df) * SE_d,
    d_high = d + qt(0.975, df) * SE_d
  )

fixed_d_pp
## # A tibble: 4 × 10
##   term      estimate std.error    df      t        p     d  SE_d    d_low d_high
##   <chr>        <dbl>     <dbl> <dbl>  <dbl>    <dbl> <dbl> <dbl>    <dbl>  <dbl>
## 1 (Interce…    2.93      0.276  96.8 10.6   6.30e-18 2.59  0.244  2.11     3.08 
## 2 grouptra…    0.169     0.387  96.8  0.437 6.63e- 1 0.149 0.342 -0.530    0.829
## 3 timepost     0.586     0.297  57.0  1.97  5.32e- 2 0.518 0.263 -0.00740  1.04 
## 4 grouptra…    1.31      0.416  57.0  3.16  2.56e- 3 1.16  0.368  0.425    1.90
# 3) 组内简单效应(post − pre)+ d —— 如不需要可删掉
emm <- emmeans(fit_pp, ~ time | group)
w   <- summary(contrast(emm, list(post_minus_pre = c(-1, 1)),
                        by = "group", adjust = "none"))
within_d <- as.data.frame(w) %>%
  dplyr::mutate(
    d    = estimate / sig,
    SE_d = SE / sig
  ) %>%
  dplyr::mutate(
    d_low  = d + qt(0.025, df) * SE_d,
    d_high = d + qt(0.975, df) * SE_d
  )
within_d
##         contrast    group  estimate        SE df  t.ratio      p.value
## 1 post_minus_pre  control 0.5862069 0.2969236 57 1.974268 5.320382e-02
## 2 post_minus_pre training 1.9000000 0.2919329 57 6.508344 2.088828e-08
##           d      SE_d        d_low   d_high
## 1 0.5184683 0.2626129 -0.007404917 1.044341
## 2 1.6804472 0.2581989  1.163412819 2.197482
dat_pf <- subset(dat_long, cond == "posi" & metric == "具体性" & time %in% c("pre","fu"))
dat_pf$group <- factor(dat_pf$group, levels = c("control","training"))
dat_pf$time  <- factor(dat_pf$time,  levels = c("pre","fu"))

# LMM
fit_pf <- lmer(score ~ group * time + (1 | ID), data = dat_pf, REML = TRUE)
summary(fit_pf)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: score ~ group * time + (1 | ID)
##    Data: dat_pf
## 
## REML criterion at convergence: 417.5
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -1.8436 -0.5957  0.0470  0.6314  1.8777 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  ID       (Intercept) 0.887    0.9418  
##  Residual             1.324    1.1507  
## Number of obs: 118, groups:  ID, 59
## 
## Fixed effects:
##                      Estimate Std. Error      df t value Pr(>|t|)    
## (Intercept)            2.9310     0.2761 98.1983  10.615  < 2e-16 ***
## grouptraining          0.1690     0.3872 98.1983   0.436  0.66355    
## timefu                 0.9655     0.3022 57.0000   3.195  0.00228 ** 
## grouptraining:timefu   0.5555     0.4238 57.0000   1.311  0.19521    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) grptrn timefu
## grouptranng -0.713              
## timefu      -0.547  0.390       
## grptrnng:tm  0.390 -0.547 -0.713
# 固定效应 d(= estimate / sigma)
sig_pf <- sigma(fit_pf)
co_pf  <- as.data.frame(summary(fit_pf)$coefficients)
fixed_d_pf <- tibble::tibble(
  term      = rownames(co_pf),
  estimate  = co_pf[,"Estimate"],
  std.error = co_pf[,"Std. Error"],
  df        = co_pf[,"df"],
  t         = co_pf[,"t value"],
  p         = co_pf[,"Pr(>|t|)"],
  d         = estimate / sig_pf,
  SE_d      = std.error / sig_pf,
  d_low     = d + qt(0.025, df) * SE_d,
  d_high    = d + qt(0.975, df) * SE_d
)
fixed_d_pf
## # A tibble: 4 × 10
##   term        estimate std.error    df      t        p     d  SE_d  d_low d_high
##   <chr>          <dbl>     <dbl> <dbl>  <dbl>    <dbl> <dbl> <dbl>  <dbl>  <dbl>
## 1 (Intercept)    2.93      0.276  98.2 10.6   5.51e-18 2.55  0.240  2.07   3.02 
## 2 grouptrain…    0.169     0.387  98.2  0.436 6.64e- 1 0.147 0.337 -0.521  0.815
## 3 timefu         0.966     0.302  57.0  3.19  2.28e- 3 0.839 0.263  0.313  1.36 
## 4 grouptrain…    0.555     0.424  57.0  1.31  1.95e- 1 0.483 0.368 -0.255  1.22
# 组内简单效应(fu − pre)及 d
emm_pf <- emmeans(fit_pf, ~ time | group)
w_pf <- summary(contrast(emm_pf, list(fu_minus_pre = c(-1, 1)),
                         by = "group", adjust = "none"))

within_d_pf <- w_pf %>%
  as.data.frame() %>%
  dplyr::mutate(
    d      = estimate / sig_pf,
    SE_d   = SE / sig_pf,
    d_low  = d + qt(0.025, df) * SE_d,
    d_high = d + qt(0.975, df) * SE_d
  )
within_d_pf
##       contrast    group  estimate        SE df  t.ratio      p.value        d
## 1 fu_minus_pre  control 0.9655172 0.3021992 57 3.194969 2.280038e-03 0.839040
## 2 fu_minus_pre training 1.5210000 0.2971199 57 5.119146 3.769345e-06 1.321758
##        SE_d     d_low   d_high
## 1 0.2626129 0.3131668 1.364913
## 2 0.2581989 0.8047234 1.838792
dat_pfu <- subset(dat_long, cond == "posi" & metric == "具体性" & time %in% c("post","fu"))
dat_pfu$group <- factor(dat_pfu$group, levels = c("control","training"))
dat_pfu$time  <- factor(dat_pfu$time,  levels = c("post","fu"))

# LMM
fit_pfu <- lmer(score ~ group * time + (1 | ID), data = dat_pfu, REML = TRUE)
summary(fit_pfu)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: score ~ group * time + (1 | ID)
##    Data: dat_pfu
## 
## REML criterion at convergence: 389.8
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -2.5245 -0.4560  0.1945  0.5785  1.6985 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  ID       (Intercept) 0.7992   0.8940  
##  Residual             0.9794   0.9897  
## Number of obs: 118, groups:  ID, 59
## 
## Fixed effects:
##                      Estimate Std. Error      df t value Pr(>|t|)    
## (Intercept)            3.5172     0.2476 94.8508  14.202  < 2e-16 ***
## grouptraining          1.4828     0.3473 94.8508   4.269 4.65e-05 ***
## timefu                 0.3793     0.2599 57.0000   1.459    0.150    
## grouptraining:timefu  -0.7583     0.3645 57.0000  -2.081    0.042 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) grptrn timefu
## grouptranng -0.713              
## timefu      -0.525  0.374       
## grptrnng:tm  0.374 -0.525 -0.713
# 如需 Type III: anova(fit_pfu, type = 3, ddf = "Kenward-Roger")

# 固定效应 d(= estimate / sigma)
sig_pfu <- sigma(fit_pfu)
co_pfu  <- as.data.frame(summary(fit_pfu)$coefficients)
fixed_d_pfu <- tibble::tibble(
  term      = rownames(co_pfu),
  estimate  = co_pfu[,"Estimate"],
  std.error = co_pfu[,"Std. Error"],
  df        = co_pfu[,"df"],
  t         = co_pfu[,"t value"],
  p         = co_pfu[,"Pr(>|t|)"],
  d         = estimate / sig_pfu,
  SE_d      = std.error / sig_pfu,
  d_low     = d + qt(0.025, df) * SE_d,
  d_high    = d + qt(0.975, df) * SE_d
)
fixed_d_pfu
## # A tibble: 4 × 10
##   term       estimate std.error    df     t        p      d  SE_d  d_low  d_high
##   <chr>         <dbl>     <dbl> <dbl> <dbl>    <dbl>  <dbl> <dbl>  <dbl>   <dbl>
## 1 (Intercep…    3.52      0.248  94.9 14.2  3.27e-25  3.55  0.250  3.06   4.05  
## 2 grouptrai…    1.48      0.347  94.9  4.27 4.65e- 5  1.50  0.351  0.802  2.19  
## 3 timefu        0.379     0.260  57.0  1.46 1.50e- 1  0.383 0.263 -0.143  0.909 
## 4 grouptrai…   -0.758     0.364  57.0 -2.08 4.20e- 2 -0.766 0.368 -1.50  -0.0288
# 组内简单效应(fu − post)及 d
emm_pfu <- emmeans(fit_pfu, ~ time | group)

w_pfu <- summary(
  contrast(emm_pfu, list(fu_minus_post = c(-1, 1)),
           by = "group", adjust = "none")
)

within_d_pfu <- w_pfu %>%
  as.data.frame() %>%
  dplyr::mutate(
    d      = estimate / sig_pfu,
    SE_d   = SE / sig_pfu,
    d_low  = d + qt(0.025, df) * SE_d,
    d_high = d + qt(0.975, df) * SE_d
  )
within_d_pfu
##        contrast    group   estimate        SE df   t.ratio   p.value          d
## 1 fu_minus_post  control  0.3793103 0.2598981 57  1.459458 0.1499286  0.3832724
## 2 fu_minus_post training -0.3790000 0.2555298 57 -1.483193 0.1435317 -0.3829588
##        SE_d      d_low    d_high
## 1 0.2626129 -0.1426008 0.9091456
## 2 0.2581989 -0.8999932 0.1340756
dat_pp2 <- subset(dat_long, cond=="posi" & metric=="可能性" & time %in% c("pre","post"))
dat_pp2$group <- factor(dat_pp2$group, levels=c("control","training"))  # ← 用 dat_pp2
dat_pp2$time  <- factor(dat_pp2$time,  levels=c("pre","post"))

# 1) 模型
fit_pp2 <- lmer(score ~ group * time + (1|ID), data = dat_pp2, REML = TRUE)
summary(fit_pp2)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: score ~ group * time + (1 | ID)
##    Data: dat_pp2
## 
## REML criterion at convergence: 294
## 
## Scaled residuals: 
##      Min       1Q   Median       3Q      Max 
## -2.54184 -0.52023  0.04243  0.46514  2.17333 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  ID       (Intercept) 0.1778   0.4217  
##  Residual             0.5302   0.7281  
## Number of obs: 118, groups:  ID, 59
## 
## Fixed effects:
##                        Estimate Std. Error       df t value Pr(>|t|)    
## (Intercept)              6.9703     0.1562 107.2345  44.610  < 2e-16 ***
## grouptraining           -0.3393     0.2191 107.2345  -1.549  0.12441    
## timepost                -0.1079     0.1912  57.0000  -0.564  0.57466    
## grouptraining:timepost   0.7486     0.2682  57.0000   2.792  0.00712 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) grptrn timpst
## grouptranng -0.713              
## timepost    -0.612  0.436       
## grptrnng:tm  0.436 -0.612 -0.713
# 2) 固定效应 d
sig <- sigma(fit_pp2)
co  <- as.data.frame(coef(summary(fit_pp2)))
fixed_d_pp2 <- tibble::rownames_to_column(co, "term") |>
  dplyr::transmute(
    term,
    estimate  = Estimate,
    std.error = `Std. Error`,
    df,
    t         = `t value`,
    p         = `Pr(>|t|)`,
    d         = estimate / sig
  )
fixed_d_pp2
##                     term   estimate std.error       df         t            p
## 1            (Intercept)  6.9703448 0.1562494 107.2345 44.610366 4.564472e-71
## 2          grouptraining -0.3393448 0.2191209 107.2345 -1.548665 1.244087e-01
## 3               timepost -0.1079310 0.1912152  57.0000 -0.564448 5.746639e-01
## 4 grouptraining:timepost  0.7485977 0.2681562  57.0000  2.791648 7.123765e-03
##            d
## 1  9.5729949
## 2 -0.4660525
## 3 -0.1482313
## 4  1.0281159
# 3) 组内(post − pre)效应量 d
emm <- emmeans(fit_pp2, ~ time | group)
w   <- summary(contrast(emm, list(post_minus_pre = c(-1, 1)),
                        by = "group", adjust = "none"))

within_d_pp2 <- as.data.frame(w) |>
  dplyr::mutate(
    d    = estimate / sig,
    SE_d = SE / sig
  ) |>
  dplyr::mutate(
    d_low  = d + qt(0.025, df) * SE_d,
    d_high = d + qt(0.975, df) * SE_d
  )
within_d_pp2
##         contrast    group   estimate        SE df   t.ratio     p.value
## 1 post_minus_pre  control -0.1079310 0.1912152 57 -0.564448 0.574663872
## 2 post_minus_pre training  0.6406667 0.1880013 57  3.407778 0.001207626
##            d      SE_d      d_low    d_high
## 1 -0.1482313 0.2626129 -0.6741045 0.3776419
## 2  0.8798846 0.2581989  0.3628502 1.3969189