11  重复测量方差分析

重复测量方差分析用于分析重复测量数据中组间和组内自变量对因变量的影响。模型形式为:

\[ Y_{ijk}=\mu +\alpha_i +\beta_j +(\alpha\beta)_{ij}+S_k+(\beta S)_{jk}+\epsilon_{ijk} \] 其中,\(Y_{ijk}\) 是第i 个组、第j 个时间点或条件下、第k 个受试者的测量值,\(\alpha_i\) 是组间效应,\(\beta_j\) 时间效应或条件效应,\((\alpha\beta)_{ij}\) 是组效应与时间效应的交互效应,\(S_k\) 是第 k 个受试者的效应(通常被视为随机效应),\((\beta S)_{jk}\) 是时间效应与受试者效应的交互作用(也通常被视为随机效应), \(\epsilon_{ijk}\) 是误差项。

11.1 假设条件

  • 独立性假设,即不同受试者之间的观测值是独立的。

  • 观测值在每个时间点或条件下的正态分布。

  • 各组内的方差齐性(sphericity),即组内相关性结构是已知的,通常假设为复合对称(compound symmetry)。协方差矩阵的球形检验(W=1)

Note
  • 复合对称假设:RM-ANOVA假设误差项具有复合对称性,即组内相关性和方差齐性。如果复合对称假设不成立,结果可能不准确,可以使用更为灵活的线性混合模型(LMM)或广义估计方程(GEE)进行分析。

  • 检验方法:RM-ANOVA的主要检验方法包括F检验,用于检验组效应、时间效应和交互作用是否显著。

11.2 单因素重复测量方差分析

11.2.1 原理公式

\[ SS_T=\sum_{j=1}^{t}\sum_{i=1}^{n}X_{ij}^2-\frac{(\sum_{j=1}^{t}\sum_{i=1}^{n}X_{ij})^2}{nt}=SS_{受试者间}+SS_{不同时间点}+SS_{E\ \ 随机误差},\nu_T=nt-1 \]

\[ SS_{受试者间}=\frac{\sum_{i}(\sum_{j}X_{ij})^2}{t}-C,\nu_{受试者}=n-1 \]反映了在受试者间的变异。

\[ SS_{不同时间点}=\frac{\sum_{j}(\sum_{i}X_{ij})^2}{n}-C ,\nu_{时间点}=t-1 \]反映了在每个受试者内不同时间点的重复测量变异。

\[ SS_E=SS_T-SS_{受试者间}-SS_{不同时间点},\nu_E=(n-1)(t-1) \]

\(H_0:\mu_1=\mu_2=...=\mu_t\),检验统计量

\[ F=\frac{MS_{不同时间点}}{MS_E}=\frac{SS_{不同时间点}/(t-1)}{SS_E/((n-1)(t-1))} \]

Code
df <- tribble(
    ~id,~zero,~ten,~twenty,~thirty,
    1,186,122,134,110,
    2,345,312,268,176,
    3,98,84,52,61,
    4,288,98,91,85,
    5,176,86,130,99,
    6,210,188,143,120,
    7,271,322,86,65,
    8,415,332,265,186,
    9,171,126,130,135,
    10,243,330,95,64,
)
df_long <-
    df |> pivot_longer(cols = -1,
                       names_to = "week",
                       values_to = "ALT") |> mutate(week = factor(week))


# 假设检验
shapiro.test(df$zero)
#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  df$zero
#> W = 0.97166, p-value = 0.9058
shapiro.test(df$ten)
#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  df$ten
#> W = 0.7963, p-value = 0.01307
shapiro.test(df$twenty)
#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  df$twenty
#> W = 0.83443, p-value = 0.03783
shapiro.test(df$thirty)
#> 
#>  Shapiro-Wilk normality test
#> 
#> data:  df$thirty
#> W = 0.90722, p-value = 0.2624
bartlett.test(df[-1])
#> 
#>  Bartlett test of homogeneity of variances
#> 
#> data:  df[-1]
#> Bartlett's K-squared = 6.7482, df = 3, p-value = 0.08037
    
# 总变异
xij_sum <- sum(df_long$ALT)
xij_square_sum <- sum(df_long$ALT^2)
C <- xij_sum^2/40
SS_T <- xij_square_sum-C

# 受试者间  
xi._sum <- rowSums(df[,-1])
xi._sum
#>  [1]  552 1101  295  562  491  661  744 1198  562  732
SS_B <- sum(xi._sum^2)/4-C #行和平方和
MS_B <- SS_B/9

# 不同时间点
x.j_sum <- colSums(df[,-1])
x.j_sum
#>   zero    ten twenty thirty 
#>   2403   2000   1394   1101
SS_W <- sum(x.j_sum^2)/10-C #列和平方和
MS_W <- SS_W/3



SS_E <- SS_T-SS_B-SS_W
MS_E <- SS_E/(9*3)

F_stat <- MS_W/MS_E

11.2.2 线性混合模型nlme::lme()

Code
library(nlme)
model <- lme(fixed=ALT ~ week, random = ~1| id/week, data = df_long)
summary(model)
#> Linear mixed-effects model fit by REML
#>   Data: df_long 
#>        AIC      BIC    logLik
#>   431.0046 442.0892 -208.5023
#> 
#> Random effects:
#>  Formula: ~1 | id
#>         (Intercept)
#> StdDev:    62.83357
#> 
#>  Formula: ~1 | week %in% id
#>         (Intercept) Residual
#> StdDev:     50.6942 22.91705
#> 
#> Fixed effects:  ALT ~ week 
#>             Value Std.Error DF   t-value p-value
#> (Intercept) 200.0  26.53893 27  7.536098  0.0000
#> weekthirty  -89.9  24.88008 27 -3.613332  0.0012
#> weektwenty  -60.6  24.88008 27 -2.435683  0.0217
#> weekzero     40.3  24.88008 27  1.619770  0.1169
#>  Correlation: 
#>            (Intr) wkthrt wktwnt
#> weekthirty -0.469              
#> weektwenty -0.469  0.500       
#> weekzero   -0.469  0.500  0.500
#> 
#> Standardized Within-Group Residuals:
#>         Min          Q1         Med          Q3         Max 
#> -0.55743919 -0.26471921 -0.01206831  0.19777410  0.89724666 
#> 
#> Number of Observations: 40
#> Number of Groups: 
#>           id week %in% id 
#>           10           40
anova_results <- anova(model)
anova_results
numDF denDF F-value p-value
(Intercept) 1 27 62.98194 0.00e+00
week 3 27 11.13855 6.17e-05

11.2.3 事后检验

Code
library(multcomp)
glht_result <-glht(model, linfct = mcp(week =c("ten - zero = 0 ",
                                               "twenty - zero == 0",
                                               "thirty - zero = 0 ")))
summary(glht_result)
#> 
#>   Simultaneous Tests for General Linear Hypotheses
#> 
#> Multiple Comparisons of Means: User-defined Contrasts
#> 
#> 
#> Fit: lme.formula(fixed = ALT ~ week, data = df_long, random = ~1 | 
#>     id/week)
#> 
#> Linear Hypotheses:
#>                    Estimate Std. Error z value Pr(>|z|)    
#> ten - zero == 0      -40.30      24.88  -1.620    0.248    
#> twenty - zero == 0  -100.90      24.88  -4.055   <0.001 ***
#> thirty - zero == 0  -130.20      24.88  -5.233   <0.001 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> (Adjusted p values reported -- single-step method)
glht_result
#> 
#>   General Linear Hypotheses
#> 
#> Multiple Comparisons of Means: User-defined Contrasts
#> 
#> 
#> Linear Hypotheses:
#>                    Estimate
#> ten - zero == 0       -40.3
#> twenty - zero == 0   -100.9
#> thirty - zero == 0   -130.2

11.2.4 第二个示例

Code
df2 <- tribble(
    ~Participant,~A1,~A2,~A3,
    "P1",5  ,6  ,7,
    "P2",7  ,13 ,13,
    "P3",2  ,4  ,6,
    "P4",6  ,9  ,12
)
df2
Participant A1 A2 A3
P1 5 6 7
P2 7 13 13
P3 2 4 6
P4 6 9 12

自由度

Code
df_A <- 3-1

df_P <- 4-1

df_error <- df_A * df_P


df_total <- df_A + df_P + df_error
# 或者
df_total <- 3*4-1 

均方和

SStotal ,SSA,SSP

11.3 含组间因子和组内因子的重复测量

\[ \begin{aligned} SS_总&= SS_{受试对象间}+SS_{受试对象内} \\ &=(SS_{处理方法}+SS_{个体间差异})+(SS_{时间}+SS_{处理与时间交互}+SS_{个体内差异}) \end{aligned} \]

https://personality-project.org/r/r.guide/r.anova.html#oneway

11.3.1 第一个案例

Code
df <- read_rds("data/SLAMF6+-PBS.rds")

df %>% 
    DT::datatable()
Code
# 2x2 mixed: 独立变量(被试间) : age
# 独立变量(被试内) : time 依赖变量: value
# aov_age_time <- aov(value ~ age * time + Error(subject/time),
#   data = data_long)
# summary(aov_age_time)

# 两个被试内变量 
#aov.ww <- aov(y ~ w1*w2 +Error(subject/(w1*w2)), data=data_long) 

# 1个被试间变量,两个被试内变量 
#aov.bww <- aov(y ~b1*w1*w2 + Error(subject/(w1*w2)) + b1,data=data_long) 

# 两个被试间变量,一个被试内变量
# aov.bww <- aov(y ~ b1*b2*w1 + Error(subject/(w1)) + b1*b2, data=data_long)

11.3.1.1 stats::aov()

Code
# 第一种  有混合效应无法事后检验

aov_1 <- aov(volume ~ method*Days+Error(id/Days),data = df)
summary(aov_1)
#> 
#> Error: id
#>           Df Sum Sq Mean Sq F value   Pr(>F)    
#> method     2   3572  1786.0   13.13 0.000953 ***
#> Residuals 12   1633   136.1                     
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Error: id:Days
#>             Df Sum Sq Mean Sq F value   Pr(>F)    
#> Days         2    301   150.3   1.893    0.172    
#> method:Days  4   4363  1090.8  13.742 5.81e-06 ***
#> Residuals   24   1905    79.4                     
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

11.3.1.2 nlme::lme()

Code
# 第二种 最好
library(nlme)
model <- lme(volume ~ method*Days, random = ~1|id/Days, data = df)
summary(model)
#> Linear mixed-effects model fit by REML
#>   Data: df 
#>        AIC      BIC    logLik
#>   304.5845 323.5868 -140.2923
#> 
#> Random effects:
#>  Formula: ~1 | id
#>         (Intercept)
#> StdDev:    4.346709
#> 
#>  Formula: ~1 | Days %in% id
#>         (Intercept) Residual
#> StdDev:    8.366812 3.060766
#> 
#> Fixed effects:  volume ~ method * Days 
#>                                           Value Std.Error DF   t-value p-value
#> (Intercept)                            25.43718  4.433186 24  5.737900  0.0000
#> methodSLAMF6+ PD-1+ CD8+ cells         10.81993  6.269472 12  1.725813  0.1100
#> methodSLAMF6- PD-1+ CD8+ cells          4.63341  6.269472 12  0.739043  0.4741
#> Days7                                   9.55505  5.634601 24  1.695781  0.1029
#> Days14                                 22.41864  5.634601 24  3.978745  0.0006
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7  -37.58056  7.968529 24 -4.716123  0.0001
#> methodSLAMF6- PD-1+ CD8+ cells:Days7   -8.90525  7.968529 24 -1.117552  0.2748
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14 -55.79891  7.968529 24 -7.002410  0.0000
#> methodSLAMF6- PD-1+ CD8+ cells:Days14 -14.68420  7.968529 24 -1.842774  0.0777
#>  Correlation: 
#>                                       (Intr) mtSLAMF6+PD-1+CD8+c
#> methodSLAMF6+ PD-1+ CD8+ cells        -0.707                    
#> methodSLAMF6- PD-1+ CD8+ cells        -0.707  0.500             
#> Days7                                 -0.636  0.449             
#> Days14                                -0.636  0.449             
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7   0.449 -0.636             
#> methodSLAMF6- PD-1+ CD8+ cells:Days7   0.449 -0.318             
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14  0.449 -0.636             
#> methodSLAMF6- PD-1+ CD8+ cells:Days14  0.449 -0.318             
#>                                       mtSLAMF6-PD-1+CD8+c Days7  Days14
#> methodSLAMF6+ PD-1+ CD8+ cells                                         
#> methodSLAMF6- PD-1+ CD8+ cells                                         
#> Days7                                  0.449                           
#> Days14                                 0.449               0.500       
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7  -0.318              -0.707 -0.354
#> methodSLAMF6- PD-1+ CD8+ cells:Days7  -0.636              -0.707 -0.354
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14 -0.318              -0.354 -0.707
#> methodSLAMF6- PD-1+ CD8+ cells:Days14 -0.636              -0.354 -0.707
#>                                       mSLAMF6+PD-1+CD8+c:D7
#> methodSLAMF6+ PD-1+ CD8+ cells                             
#> methodSLAMF6- PD-1+ CD8+ cells                             
#> Days7                                                      
#> Days14                                                     
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7                       
#> methodSLAMF6- PD-1+ CD8+ cells:Days7   0.500               
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14  0.500               
#> methodSLAMF6- PD-1+ CD8+ cells:Days14  0.250               
#>                                       mSLAMF6-PD-1+CD8+c:D7
#> methodSLAMF6+ PD-1+ CD8+ cells                             
#> methodSLAMF6- PD-1+ CD8+ cells                             
#> Days7                                                      
#> Days14                                                     
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7                       
#> methodSLAMF6- PD-1+ CD8+ cells:Days7                       
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14  0.250               
#> methodSLAMF6- PD-1+ CD8+ cells:Days14  0.500               
#>                                       mSLAMF6+PD-1+CD8+c:D1
#> methodSLAMF6+ PD-1+ CD8+ cells                             
#> methodSLAMF6- PD-1+ CD8+ cells                             
#> Days7                                                      
#> Days14                                                     
#> methodSLAMF6+ PD-1+ CD8+ cells:Days7                       
#> methodSLAMF6- PD-1+ CD8+ cells:Days7                       
#> methodSLAMF6+ PD-1+ CD8+ cells:Days14                      
#> methodSLAMF6- PD-1+ CD8+ cells:Days14  0.500               
#> 
#> Standardized Within-Group Residuals:
#>         Min          Q1         Med          Q3         Max 
#> -0.62401382 -0.15515176 -0.03440848  0.17260010  0.72287328 
#> 
#> Number of Observations: 45
#> Number of Groups: 
#>           id Days %in% id 
#>           15           45
df_aov <- anova(model)
df_aov
numDF denDF F-value p-value
(Intercept) 1 24 263.954417 0.0000000
method 2 12 13.127286 0.0009528
Days 2 24 1.893203 0.1724036
method:Days 4 24 13.742424 0.0000058
Code

#成对比较
library(emmeans)
method_means <- emmeans(model, ~method)  
print(method_means)  
#>  method                   emmean   SE df lower.CL upper.CL
#>  PBS                        36.1 3.01 14    29.64     42.6
#>  SLAMF6+ PD-1+ CD8+ cells   15.8 3.01 12     9.23     22.4
#>  SLAMF6- PD-1+ CD8+ cells   32.9 3.01 12    26.30     39.4
#> 
#> Results are averaged over the levels of: Days 
#> Degrees-of-freedom method: containment 
#> Confidence level used: 0.95
method_comparisons <- pairs(method_means)  
print(method_comparisons)
#>  contrast                                                estimate   SE df
#>  PBS - (SLAMF6+ PD-1+ CD8+ cells)                           20.31 4.26 12
#>  PBS - (SLAMF6- PD-1+ CD8+ cells)                            3.23 4.26 12
#>  (SLAMF6+ PD-1+ CD8+ cells) - (SLAMF6- PD-1+ CD8+ cells)   -17.08 4.26 12
#>  t.ratio p.value
#>    4.768  0.0012
#>    0.758  0.7345
#>   -4.009  0.0046
#> 
#> Results are averaged over the levels of: Days 
#> Degrees-of-freedom method: containment 
#> P value adjustment: tukey method for comparing a family of 3 estimates

11.3.1.3 afex::aov_*()

Code
library("afex")     # needed for ANOVA functions.
library("emmeans")  # emmeans must now be loaded explicitly for follow-up tests.
library("multcomp") # for advanced control for multiple testing/Type 1 errors.
library("ggplot2")  # for customizing plots.


a1 <- aov_ez(id = "id",dv = "volume", df, between ="method" , within = c("Days"))
a1
#> Anova Table (Type 3 tests)
#> 
#> Response: volume
#>        Effect          df    MSE         F  ges p.value
#> 1      method       2, 12 136.05 13.13 *** .502   <.001
#> 2        Days 1.33, 15.94 119.49      1.89 .078    .188
#> 3 method:Days 2.66, 15.94 119.49 13.74 *** .552   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG

aov_car(volume ~ method + Error(id/Days), data = df)
#> Anova Table (Type 3 tests)
#> 
#> Response: volume
#>        Effect          df    MSE         F  ges p.value
#> 1      method       2, 12 136.05 13.13 *** .502   <.001
#> 2        Days 1.33, 15.94 119.49      1.89 .078    .188
#> 3 method:Days 2.66, 15.94 119.49 13.74 *** .552   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG
aov_4(volume ~ method + (Days|id), data = df)
#> Anova Table (Type 3 tests)
#> 
#> Response: volume
#>        Effect          df    MSE         F  ges p.value
#> 1      method       2, 12 136.05 13.13 *** .502   <.001
#> 2        Days 1.33, 15.94 119.49      1.89 .078    .188
#> 3 method:Days 2.66, 15.94 119.49 13.74 *** .552   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG



# 事后检验
m1 <- emmeans(a1, ~ method)
m1
#>  method                   emmean   SE df lower.CL upper.CL
#>  PBS                        36.1 3.01 12    29.53     42.7
#>  SLAMF6+ PD-1+ CD8+ cells   15.8 3.01 12     9.23     22.4
#>  SLAMF6- PD-1+ CD8+ cells   32.9 3.01 12    26.30     39.4
#> 
#> Results are averaged over the levels of: Days 
#> Confidence level used: 0.95
pairs(m1)
#>  contrast                                                estimate   SE df
#>  PBS - (SLAMF6+ PD-1+ CD8+ cells)                           20.31 4.26 12
#>  PBS - (SLAMF6- PD-1+ CD8+ cells)                            3.23 4.26 12
#>  (SLAMF6+ PD-1+ CD8+ cells) - (SLAMF6- PD-1+ CD8+ cells)   -17.08 4.26 12
#>  t.ratio p.value
#>    4.768  0.0012
#>    0.758  0.7345
#>   -4.009  0.0046
#> 
#> Results are averaged over the levels of: Days 
#> P value adjustment: tukey method for comparing a family of 3 estimates

11.3.2 第二个案例

Code
# 医学统计学 人卫版 第7版
df <- read_delim("data/麻醉诱导时相.txt")
df
id group t0 t1 t2 t3 t4
1 A 120 108 112 120 117
2 A 118 109 115 126 123
3 A 119 112 119 124 118
4 A 121 112 119 126 120
5 A 127 121 127 133 126
6 B 121 120 118 131 137
7 B 122 121 119 129 133
8 B 128 129 126 135 142
9 B 117 115 111 123 131
10 B 118 114 116 123 133
11 C 131 119 118 135 129
12 C 129 128 121 148 132
13 C 123 123 120 143 136
14 C 123 121 116 145 126
15 C 125 124 118 142 130
Code
df_long <- df |> pivot_longer(cols = starts_with("t"),
                              names_to = "time",
                              values_to = "BP") |> 
    mutate(
        id=factor(id),
        group=factor(group),
        time=factor(time)
    )
df_long %>% 
    DT::datatable()

11.3.2.1 nlme::lme()

Code
# 第一种方法
library(nlme)
model <- lme(BP ~ group*time, random = ~1 |id/time, data = df_long)
summary(model)
#> Linear mixed-effects model fit by REML
#>   Data: df_long 
#>       AIC      BIC   logLik
#>   364.496 402.1942 -164.248
#> 
#> Random effects:
#>  Formula: ~1 | id
#>         (Intercept)
#> StdDev:    3.831231
#> 
#>  Formula: ~1 | time %in% id
#>         (Intercept) Residual
#> StdDev:    2.100669 1.033856
#> 
#> Fixed effects:  BP ~ group * time 
#>               Value Std.Error DF  t-value p-value
#> (Intercept)   121.0  2.007984 48 60.25944  0.0000
#> groupB          0.2  2.839718 12  0.07043  0.9450
#> groupC          5.2  2.839718 12  1.83117  0.0920
#> timet1         -8.6  1.480766 48 -5.80781  0.0000
#> timet2         -2.6  1.480766 48 -1.75585  0.0855
#> timet3          4.8  1.480766 48  3.24157  0.0022
#> timet4         -0.2  1.480766 48 -0.13507  0.8931
#> groupB:timet1   7.2  2.094119 48  3.43820  0.0012
#> groupC:timet1   5.4  2.094119 48  2.57865  0.0130
#> groupB:timet2  -0.6  2.094119 48 -0.28652  0.7757
#> groupC:timet2  -5.0  2.094119 48 -2.38764  0.0209
#> groupB:timet3   2.2  2.094119 48  1.05056  0.2987
#> groupC:timet3  11.6  2.094119 48  5.53932  0.0000
#> groupB:timet4  14.2  2.094119 48  6.78090  0.0000
#> groupC:timet4   4.6  2.094119 48  2.19663  0.0329
#>  Correlation: 
#>               (Intr) groupB groupC timet1 timet2 timet3 timet4 grpB:1 grpC:1
#> groupB        -0.707                                                        
#> groupC        -0.707  0.500                                                 
#> timet1        -0.369  0.261  0.261                                          
#> timet2        -0.369  0.261  0.261  0.500                                   
#> timet3        -0.369  0.261  0.261  0.500  0.500                            
#> timet4        -0.369  0.261  0.261  0.500  0.500  0.500                     
#> groupB:timet1  0.261 -0.369 -0.184 -0.707 -0.354 -0.354 -0.354              
#> groupC:timet1  0.261 -0.184 -0.369 -0.707 -0.354 -0.354 -0.354  0.500       
#> groupB:timet2  0.261 -0.369 -0.184 -0.354 -0.707 -0.354 -0.354  0.500  0.250
#> groupC:timet2  0.261 -0.184 -0.369 -0.354 -0.707 -0.354 -0.354  0.250  0.500
#> groupB:timet3  0.261 -0.369 -0.184 -0.354 -0.354 -0.707 -0.354  0.500  0.250
#> groupC:timet3  0.261 -0.184 -0.369 -0.354 -0.354 -0.707 -0.354  0.250  0.500
#> groupB:timet4  0.261 -0.369 -0.184 -0.354 -0.354 -0.354 -0.707  0.500  0.250
#> groupC:timet4  0.261 -0.184 -0.369 -0.354 -0.354 -0.354 -0.707  0.250  0.500
#>               grpB:2 grpC:2 grpB:3 grpC:3 grpB:4
#> groupB                                          
#> groupC                                          
#> timet1                                          
#> timet2                                          
#> timet3                                          
#> timet4                                          
#> groupB:timet1                                   
#> groupC:timet1                                   
#> groupB:timet2                                   
#> groupC:timet2  0.500                            
#> groupB:timet3  0.500  0.250                     
#> groupC:timet3  0.250  0.500  0.500              
#> groupB:timet4  0.500  0.250  0.500  0.250       
#> groupC:timet4  0.250  0.500  0.250  0.500  0.500
#> 
#> Standardized Within-Group Residuals:
#>         Min          Q1         Med          Q3         Max 
#> -1.11748859 -0.15879013 -0.03722313  0.17409704  1.22118250 
#> 
#> Number of Observations: 75
#> Number of Groups: 
#>           id time %in% id 
#>           15           75
df_aov <- anova(model)
df_aov
numDF denDF F-value p-value
(Intercept) 1 48 14649.223399 0.0000000
group 2 12 5.782943 0.0174335
time 4 48 106.557616 0.0000000
group:time 8 48 19.100638 0.0000000
Code

#成对比较
library(emmeans)
method_means <- emmeans(model, ~group)  
print(method_means)  
#>  group emmean   SE df lower.CL upper.CL
#>  A        120 1.78 14      116      123
#>  B        124 1.78 12      121      128
#>  C        128 1.78 12      124      132
#> 
#> Results are averaged over the levels of: time 
#> Degrees-of-freedom method: containment 
#> Confidence level used: 0.95
method_comparisons <- pairs(method_means)  
print(method_comparisons)
#>  contrast estimate   SE df t.ratio p.value
#>  A - B       -4.80 2.51 12  -1.911  0.1780
#>  A - C       -8.52 2.51 12  -3.392  0.0137
#>  B - C       -3.72 2.51 12  -1.481  0.3338
#> 
#> Results are averaged over the levels of: time 
#> Degrees-of-freedom method: containment 
#> P value adjustment: tukey method for comparing a family of 3 estimates

11.3.2.2 rstatix::anova_test()

Code
library(rstatix)
a2 <- anova_test(data = df_long,
           dv = BP,
           wid = id,
           within = time,
           between = group
           )

a2
#> ANOVA Table (type II tests)
#> 
#> $ANOVA
#>       Effect DFn DFd       F        p p<.05   ges
#> 1      group   2  12   5.783 1.70e-02     * 0.430
#> 2       time   4  48 106.558 3.02e-23     * 0.659
#> 3 group:time   8  48  19.101 1.62e-12     * 0.409
#> 
#> $`Mauchly's Test for Sphericity`
#>       Effect     W     p p<.05
#> 1       time 0.293 0.178      
#> 2 group:time 0.293 0.178      
#> 
#> $`Sphericity Corrections`
#>       Effect   GGe      DF[GG]    p[GG] p[GG]<.05   HFe      DF[HF]    p[HF]
#> 1       time 0.679 2.71, 32.58 1.87e-16         * 0.896 3.59, 43.03 4.65e-21
#> 2 group:time 0.679 5.43, 32.58 4.26e-09         * 0.896 7.17, 43.03 2.04e-11
#>   p[HF]<.05
#> 1         *
#> 2         *

11.3.2.3 afex::aov_*()

https://cran.r-project.org/web/packages/afex/vignettes/afex_anova_example.html

Code
library("afex")     # needed for ANOVA functions.
library("emmeans")  # emmeans must now be loaded explicitly for follow-up tests.
library("multcomp") # for advanced control for multiple testing/Type 1 errors.
library("ggplot2")  # for customizing plots.



a3 <- aov_ez("id", "BP", df_long, between ="group" , 
       within = c("time"))
a3
#> Anova Table (Type 3 tests)
#> 
#> Response: BP
#>       Effect          df   MSE          F  ges p.value
#> 1      group       2, 12 78.87     5.78 * .430    .017
#> 2       time 2.71, 32.58  8.08 106.56 *** .659   <.001
#> 3 group:time 5.43, 32.58  8.08  19.10 *** .409   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG


aov_car(BP ~ group + Error(id/time), data = df_long)
#> Anova Table (Type 3 tests)
#> 
#> Response: BP
#>       Effect          df   MSE          F  ges p.value
#> 1      group       2, 12 78.87     5.78 * .430    .017
#> 2       time 2.71, 32.58  8.08 106.56 *** .659   <.001
#> 3 group:time 5.43, 32.58  8.08  19.10 *** .409   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG
aov_4(BP ~ group + (time|id), data = df_long)
#> Anova Table (Type 3 tests)
#> 
#> Response: BP
#>       Effect          df   MSE          F  ges p.value
#> 1      group       2, 12 78.87     5.78 * .430    .017
#> 2       time 2.71, 32.58  8.08 106.56 *** .659   <.001
#> 3 group:time 5.43, 32.58  8.08  19.10 *** .409   <.001
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
#> 
#> Sphericity correction method: GG


# 事后检验
m3 <- emmeans(a3, ~ group)
m3
#>  group emmean   SE df lower.CL upper.CL
#>  A        120 1.78 12      116      124
#>  B        124 1.78 12      121      128
#>  C        128 1.78 12      124      132
#> 
#> Results are averaged over the levels of: time 
#> Confidence level used: 0.95
pairs(m3)
#>  contrast estimate   SE df t.ratio p.value
#>  A - B       -4.80 2.51 12  -1.911  0.1780
#>  A - C       -8.52 2.51 12  -3.392  0.0137
#>  B - C       -3.72 2.51 12  -1.481  0.3338
#> 
#> Results are averaged over the levels of: time 
#> P value adjustment: tukey method for comparing a family of 3 estimates
summary(as.glht(pairs(m1)), test=adjusted("fdr"))
#> 
#>   Simultaneous Tests for General Linear Hypotheses
#> 
#> Linear Hypotheses:
#>                                                              Estimate
#> PBS - (SLAMF6+ PD-1+ CD8+ cells) == 0                          20.307
#> PBS - (SLAMF6- PD-1+ CD8+ cells) == 0                           3.230
#> (SLAMF6+ PD-1+ CD8+ cells) - (SLAMF6- PD-1+ CD8+ cells) == 0  -17.077
#>                                                              Std. Error t value
#> PBS - (SLAMF6+ PD-1+ CD8+ cells) == 0                             4.259   4.768
#> PBS - (SLAMF6- PD-1+ CD8+ cells) == 0                             4.259   0.758
#> (SLAMF6+ PD-1+ CD8+ cells) - (SLAMF6- PD-1+ CD8+ cells) == 0      4.259  -4.009
#>                                                              Pr(>|t|)   
#> PBS - (SLAMF6+ PD-1+ CD8+ cells) == 0                         0.00137 **
#> PBS - (SLAMF6- PD-1+ CD8+ cells) == 0                         0.46290   
#> (SLAMF6+ PD-1+ CD8+ cells) - (SLAMF6- PD-1+ CD8+ cells) == 0  0.00260 **
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> (Adjusted p values reported -- fdr method)
p1 <- afex_plot(a3, x = "time",# trace = "BP",
                panel = "group", error = "none",
                mapping = c("color", "fill"),
                data_geom = geom_boxplot, data_arg = list(width = 0.4),
                point_arg = list(size = 1.5), line_arg = list(size = 1))
p1

\[ \begin{aligned} SS_总&= SS_{受试对象间}+SS_{受试对象内} \\ &=(SS_{处理方法}+SS_{个体间差异})+(SS_{时间}+SS_{处理与时间交互}+SS_{个体内差异}) \end{aligned} \]

\[ \begin{aligned} \nu_总&= \nu_{受试对象间}+\nu_{受试对象内} \\ &=(\nu_{处理方法}+\nu_{个体差异})+(\nu_{时间}+\nu_{处理与时间交互}+\nu_{个体差异}) \end{aligned} \]

Page 86-87 变异 SS v MS F CP
受试对象间 处理 912.24 2 456.12 5.78 0.0174
个体差异 946.48 12 78.87
1858.72
受试对象内 时间 2336.45 4 584.11 106.56 <0.0001
处理与时间交互 837.63 8 104.70 19.10 <0.0001
个体差异 263.12 48 5.48
3437.2
5295.92 3×15-1=74

11.3.2.4 SS

\[ SS_总=\sum_i^n{(X_i-\bar X)^2} \]

其中,n是观测值的总数,Xi 为每个观测值,\(\bar X\) 是所有观测值的均值。

Code
options(digits = 4)
BP <- c(df$t0,df$t1,df$t2,df$t3,df$t4)
BP_mean <- mean(BP) 
SS_total <- sum((BP-BP_mean)^2)

SS_total    
#> [1] 5296

11.3.2.5 SS受试对象间

\[ SS_{受试对象间}=\sum_{j=1}^m n_{.j}(\bar X_{.j}-\bar X)^2 \]

其中,m是受试者数量(15),n.j 是第j 个受试对象的观测值数量,\bar X.j 是第j 个受试对象的观测值的均值,\bar X 是所有观测值的均值。

Code
id_mean <- df |> dplyr::select(c(-1,-2)) |> rowMeans()

SS_between <- 0
for (i in 1:nrow(df)) {
    SS_between <- SS_between + 5*(id_mean[i]-BP_mean)^2
}
SS_between
#> [1] 1859
Code
group__summary <- df_long |> group_by(group) |> 
    summarise(n=n(),mean=mean(BP),sum=sum(BP))
group__summary 
group n mean sum
A 25 119.68 2992
B 25 124.48 3112
C 25 128.20 3205
SS处理

\[ SS_{处理}= \sum_i^k n_k (\bar X_k - \bar X)^2 \]

其中,k为不同处理组的组数,nk 为第 k 处理组观测值的总数,\(\bar X_k\) 为第k 处理组观测值的均数,\(\bar X\) 是所有观测值的均值。

Code
SS_处理 <- 25*( (group__summary$mean[1] - BP_mean )^2 + 
                       (group__summary$mean[2] - BP_mean )^2 +
                       (group__summary$mean[3] - BP_mean )^2) 
SS_处理
#> [1] 912.2
Code
SS_between_error <- SS_between-SS_处理

SS_between_error
#> [1] 946.5

11.3.2.6 SS受试对象内

\[ SS_{受试对象内}=SS_总-SS_{受试对象间} \]

Code
SS_within <- SS_total-SS_between
SS_within
#> [1] 3437
SS时间

\[ SS_{时间}=\sum _{t=1}^T n_t (\bar X_{.t}-\bar X)^2 \]

其中,T是时间点的数量,nt 是在时间点t的观测值数量,\bar X.t 是在时间点 t 的均值。

Code
t_mean <- df |> dplyr::select(c(-1,-2)) |> colMeans()

SS_time <- 0
for (i in seq_along(t_mean)) {
    
    SS_time <- SS_time + 15*(t_mean[i]-BP_mean)^2
    
}
names(SS_time) <- "SS_time"
SS_time
#> SS_time 
#>    2336
SS处理与时间交互

\[ SS_{处理与时间交互}=\sum_{i=1}^{k}\sum_{j=1}^{T}n_{ij}\left (\bar X_{ij.} -\bar X_{i..}-\bar X_{.j.} + \bar X_{...} \right )^2 \]

其中,

  • k 是处理方法的数量,3

  • T 是时间点的数量,5

  • nij 是第 i 个处理方法和第 j 个时间点的观测次数,25/5=5

  • Xij . 是第 i 个处理方法、第 j 个时间点的观测值的平均值

  • Xi . . 是第 i 个处理方法观测值的平均值,不考虑时间点

  • X. j . 是第 j 个时间点观测值的平均值,不考虑处理方法

  • X. . . 是所有观测值的平均值,不考虑处理方法和时间点

Code
interaction_effect_summary <- df_long |>
    summarise(
        #n = n(),
        mean = mean(BP),
       # sum = sum(BP),
        .by = c(group , time)
    )
interaction_effect_mean <- interaction_effect_summary |> pivot_wider(names_from = c("time"),values_from = "mean")



SS_interaction_effect <- 0
for (i in 1:3) {
    for (j in 1:5) {
        SS_interaction_effect <- SS_interaction_effect + 5 * (interaction_effect_mean[i, j + 1] -group__summary$mean[i] - t_mean[j] + BP_mean) ^ 2
    }
    
}
names(SS_interaction_effect) <- "SS_interaction_effect"
SS_interaction_effect
SS_interaction_effect
837.6
Code
SS_within_error <- SS_within-SS_time-SS_interaction_effect

names(SS_within_error) <- "SS_within_error"
SS_within_error 
SS_within_error
263.1

11.4 球形度检验 sphericity

重复测量方差分析假设相关条件(或组水平)的所有组合之间的差异方差相等。这被称为球形度假设

R中球形度检验

球度仅针对具有两个以上水平的变量进行评估,因为球形度必然适用于只有两个水平的条件。

违反球形度假设可能会扭曲方差计算,从而导致更宽松的重复测量方差分析检验(即 I 类错误率增加)。在这种情况下,必须根据违反球形度的程度适当校正重复测量方差分析。文献中使用了两种常见的校正:Greenhouse-Geisser epsilon (GGe) 和 Huynh-Feldt epsilon (HFe)。

形度的 Mauchly 检验用于评估是否满足球形度的假设。使用rstatix::anova_test() 时,会自动报告此问题。尽管该方法受到严厉批评,通常无法检测到小样本中的球形偏离,而在大样本中则过度检测到它们,但它仍然是一种常用的方法。

11.4.1 计算 sphericity 和 Mauchly 检验

具体操作步骤如下:

  1. 计算相关组的每个组合之间的差异

  2. 计算每个组差的方差

Code
d <- df |> mutate(
    `t0-t1`=t0-t1,
    `t0-t2`=t0-t2,
    `t0-t3`=t0-t3,
    `t0-t4`=t0-t4,
    `t1-t2`=t1-t2,
    `t1-t3`=t1-t3,
    `t1-t4`=t1-t4,
    `t2-t3`=t2-t3,
    `t2-t4`=t2-t4,
    `t3-t4`=t3-t4,
) |> select(8:17)
d
t0-t1 t0-t2 t0-t3 t0-t4 t1-t2 t1-t3 t1-t4 t2-t3 t2-t4 t3-t4
12 8 0 3 -4 -12 -9 -8 -5 3
9 3 -8 -5 -6 -17 -14 -11 -8 3
7 0 -5 1 -7 -12 -6 -5 1 6
9 2 -5 1 -7 -14 -8 -7 -1 6
6 0 -6 1 -6 -12 -5 -6 1 7
1 3 -10 -16 2 -11 -17 -13 -19 -6
1 3 -7 -11 2 -8 -12 -10 -14 -4
-1 2 -7 -14 3 -6 -13 -9 -16 -7
2 6 -6 -14 4 -8 -16 -12 -20 -8
4 2 -5 -15 -2 -9 -19 -7 -17 -10
12 13 -4 2 1 -16 -10 -17 -11 6
1 8 -19 -3 7 -20 -4 -27 -11 16
0 3 -20 -13 3 -20 -13 -23 -16 7
2 7 -22 -3 5 -24 -5 -29 -10 19
1 7 -17 -5 6 -18 -6 -24 -12 12
Code

d|> map(var)
#> $`t0-t1`
#> [1] 19.54286
#> 
#> $`t0-t2`
#> [1] 12.8381
#> 
#> $`t0-t3`
#> [1] 45.25714
#> 
#> $`t0-t4`
#> [1] 49.6381
#> 
#> $`t1-t2`
#> [1] 24.49524
#> 
#> $`t1-t3`
#> [1] 27.31429
#> 
#> $`t1-t4`
#> [1] 23.12381
#> 
#> $`t2-t3`
#> [1] 65.55238
#> 
#> $`t2-t4`
#> [1] 47.98095
#> 
#> $`t3-t4`
#> [1] 77.38095
 
### 单因素
t <- df |> select(3:7) |> as.matrix()
t
#>        t0  t1  t2  t3  t4
#>  [1,] 120 108 112 120 117
#>  [2,] 118 109 115 126 123
#>  [3,] 119 112 119 124 118
#>  [4,] 121 112 119 126 120
#>  [5,] 127 121 127 133 126
#>  [6,] 121 120 118 131 137
#>  [7,] 122 121 119 129 133
#>  [8,] 128 129 126 135 142
#>  [9,] 117 115 111 123 131
#> [10,] 118 114 116 123 133
#> [11,] 131 119 118 135 129
#> [12,] 129 128 121 148 132
#> [13,] 123 123 120 143 136
#> [14,] 123 121 116 145 126
#> [15,] 125 124 118 142 130

mauchly.test(lm(t~1),X = ~1)
#> 
#>  Mauchly's test of sphericity
#>  Contrasts orthogonal to
#>  ~1
#> 
#> 
#> data:  SSD matrix from lm(formula = t ~ 1)
#> W = 0.11312, p-value = 0.0015


### 双因素

df_split <- df |> group_split(group)

df[1:5,3:7]
t0 t1 t2 t3 t4
120 108 112 120 117
118 109 115 126 123
119 112 119 124 118
121 112 119 126 120
127 121 127 133 126
Code

df2 <- as.matrix(cbind(df_split[[1]][3:7],df_split[[2]][3:7],df_split[[3]][3:7]))
times <- ordered(rep(1:5,3))
group <- factor(rep(c("A","B","C"),each=5))





mauchly.test(lm(df2~1),M=~group+times,X=~ times)
#> 
#>  Mauchly's test of sphericity
#>  Contrasts orthogonal to
#>  ~times
#> 
#>  Contrasts spanned by
#>  ~group + times
#> 
#> 
#> data:  SSD matrix from lm(formula = df2 ~ 1)
#> W = 0.427, p-value = 0.279

11.4.2 方差分析

11.4.2.1 rstatix::anova_test()

Code

a2 <- rstatix::anova_test(data = df_long,
           dv = BP,
           wid = id,
           within = time,
           between = group
           )

a2
#> ANOVA Table (type II tests)
#> 
#> $ANOVA
#>       Effect DFn DFd       F        p p<.05   ges
#> 1      group   2  12   5.783 1.70e-02     * 0.430
#> 2       time   4  48 106.558 3.02e-23     * 0.659
#> 3 group:time   8  48  19.101 1.62e-12     * 0.409
#> 
#> $`Mauchly's Test for Sphericity`
#>       Effect     W     p p<.05
#> 1       time 0.293 0.178      
#> 2 group:time 0.293 0.178      
#> 
#> $`Sphericity Corrections`
#>       Effect   GGe      DF[GG]    p[GG] p[GG]<.05   HFe      DF[HF]    p[HF]
#> 1       time 0.679 2.71, 32.58 1.87e-16         * 0.896 3.59, 43.03 4.65e-21
#> 2 group:time 0.679 5.43, 32.58 4.26e-09         * 0.896 7.17, 43.03 2.04e-11
#>   p[HF]<.05
#> 1         *
#> 2         *

输出是一个包含三个表的列表:

  1. 方差分析结果显示标有(generalized eta squared,ges)的列上的 p 值和效应大小;效应大小本质上是由于受试者内因素而忽略受试者效应而导致的变异量。

  2. Mauchly Sphericity检验。仅报告具有 >2 水平的变量或效应,因为球形度必然适用于只有 2 个水平的效应。原假设是组差的方差相等。因此,显著的 p 值 (p <= 0.05) 表示组差的方差不相等。

  3. 球度校正结果,以防我们无法维持球形度假设。提供了文献中使用的两种常见校正:Greenhouse-Geisser epsilon (GGe) 和 Huynh-Feldt epsilon (HFe) 及其相应的 p 值

11.4.2.2 ez::ezANOVA()

Code
library(ez)

# 进行重复测量方差分析
aov_ez <- ezANOVA(data = df_long,
                   dv = BP,
                   wid = id,
                   within = time,
                   between = group,
                   detailed = TRUE)

# 查看球形检验结果
print(aov_ez)
#> $ANOVA
#>        Effect DFn DFd          SSn    SSd            F            p p<.05
#> 1 (Intercept)   1  12 1155433.0800 946.48 14649.223396 6.784700e-20     *
#> 2       group   2  12     912.2400 946.48     5.782943 1.743351e-02     *
#> 3        time   4  48    2336.4533 263.12   106.557616 3.017101e-23     *
#> 4  group:time   8  48     837.6267 263.12    19.100638 1.621310e-12     *
#>         ges
#> 1 0.9989542
#> 2 0.4299287
#> 3 0.6588884
#> 4 0.4091519
#> 
#> $`Mauchly's Test for Sphericity`
#>       Effect         W         p p<.05
#> 3       time 0.2930746 0.1776608      
#> 4 group:time 0.2930746 0.1776608      
#> 
#> $`Sphericity Corrections`
#>       Effect      GGe        p[GG] p[GG]<.05      HFe        p[HF] p[HF]<.05
#> 3       time 0.678693 1.867336e-16         * 0.896371 4.649080e-21         *
#> 4 group:time 0.678693 4.262529e-09         * 0.896371 2.041727e-11         *

11.4.3 当满足球形度假设时

球形度的 Mauchly 检验不显著 (p > 0.05);这表明,受试者内因素水平之间的差异方差是相等的。因此,我们可以假设协方差矩阵的球形度,并解释方差分析表中可用的标准输出。

Code
# Display ANOVA table
a2$ANOVA
Effect DFn DFd F p p<.05 ges
group 2 12 5.783 0.017 * 0.430
time 4 48 106.558 0.000 * 0.659
group:time 8 48 19.101 0.000 * 0.409
  • F表示我们正在与 F 分布(F 检验)进行比较; 分别表示 time 和 Error(time) 的自由度; 表示得到的 F 统计量值(2, 18)81.8

  • p指定 p 值

  • ges(广义 eta 平方,eta2[g])是效应大小(由于受试者内因素引起的变异量)

11.4.4 当违反球形度假设时

如果数据违反了球形度假设(即 Mauchly 检验,p <= 0.05),则应解释表sphericity corrections中的结果,其中对自由度进行了调整,这会影响检验的统计显著性(即 p 值)。校正通过乘法和校正估计值(Greenhouse-Geisser (GG) 和 Huynh-Feldt (HF) ε 值)来应用。

Note

epsilon 提供了球形度被侵犯的程度的度量。值为 1 表示不偏离球形度(组差的所有方差均相等)。违反球形度会导致 epsilon 值低于 1。epsilon 离 1 越远,违规越严重。

Code
a2$`Sphericity Corrections`
Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF] p[HF]<.05
time 0.679 2.71, 32.58 0 * 0.896 3.59, 43.03 0 *
group:time 0.679 5.43, 32.58 0 * 0.896 7.17, 43.03 0 *

可以看出,即使在球形度校正(p[GG] < 0.001,p[HF] < 0.001)之后,平均自尊得分在不同时间点仍存在统计学差异。

在两种球形度校正方法中,Huynh-Feldt 校正被认为是最不保守的(高估了 epsilon),而 Greenhouse-Geisser 被认为是更保守的(当 epsilon 接近 1 时低估了 epsilon)。

一般建议使用 Greenhouse-Geisser 校正,特别是当 epsilon < 0.75 时。在 epsilon 大于 0.75 的情况下,一些统计学家建议使用 Huynh-Feldt 校正 (Girden 1992)。

Code
# correction = "auto"
get_anova_table(a2)
Effect DFn DFd F p p<.05 ges
group 2 12 5.783 0.017 * 0.430
time 4 48 106.558 0.000 * 0.659
group:time 8 48 19.101 0.000 * 0.409
Code
# correction = "GG"
get_anova_table(a2, correction = "GG")
Effect DFn DFd F p p<.05 ges
group 2.00 12.00 5.783 0.017 * 0.430
time 2.71 32.58 106.558 0.000 * 0.659
group:time 5.43 32.58 19.101 0.000 * 0.409

11.4.5 post-hoc test

Code
df_long |> pairwise_t_test(BP~time,paired = T,p.adjust.method = "bonferroni")
.y. group1 group2 n1 n2 statistic df p p.adj p.adj.signif
BP t0 t1 15 15 3.8548215 14 2.00e-03 1.80e-02 *
BP t0 t2 15 15 4.8281291 14 2.68e-04 3.00e-03 **
BP t0 t3 15 15 -5.4116527 14 9.17e-05 9.17e-04 ***
BP t0 t4 15 15 -3.3349414 14 5.00e-03 4.90e-02 *
BP t1 t2 15 15 0.0521691 14 9.59e-01 1.00e+00 ns
BP t1 t3 15 15 -10.2265652 14 1.00e-07 7.00e-07 ****
BP t1 t4 15 15 -8.4299370 14 7.00e-07 7.40e-06 ****
BP t2 t3 15 15 -6.6332058 14 1.13e-05 1.13e-04 ***
BP t2 t4 15 15 -5.8894810 14 3.94e-05 3.94e-04 ***
BP t3 t4 15 15 1.4675988 14 1.64e-01 1.00e+00 ns