::p_load(dplyr,tidyr, rio,ggplot2,psych, nlme,GGally,sjPlot,osfr,vtable,emmeans,RSA,car) pacman
Dyadic Analysis Workshop, SPR 2025
Intro to 3-level MLM
Libraries
Loading the data from OSF
<- osf_retrieve_file("68467733c28a254a2fd6a394")
file #Change to your local dowonloads folder
setwd("C:\\Users\\97254\\Downloads")
osf_download(file, progress = T,conflicts = "overwrigt")
<- import("SimData3Level.csv")
temp1
names(temp1) <- c("Alliance","PosEmo","Client","Therapist")
<- temp1 %>% group_by(Therapist,Client) %>%
temp1 mutate(Session=row_number()) %>%
ungroup()
setwd("G:/My Drive/Research/Conferences & Workshops/2025- SPR/Workshop")
Descriptive stats
head(temp1)
# A tibble: 6 × 5
Alliance PosEmo Client Therapist Session
<dbl> <dbl> <int> <int> <int>
1 1.22 5.84 1 1 1
2 3.02 3.45 1 1 2
3 0.739 5.12 1 1 3
4 2.08 5.50 1 1 4
5 1.34 5.61 1 1 5
6 0.420 3.68 1 1 6
st(temp1)
Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
---|---|---|---|---|---|---|---|
Alliance | 16000 | 6 | 3.1 | -8 | 3.8 | 8 | 18 |
PosEmo | 16000 | 4 | 1 | -0.12 | 3.3 | 4.7 | 7.8 |
Client | 16000 | 500 | 289 | 1 | 251 | 750 | 1000 |
Therapist | 16000 | 50 | 29 | 1 | 26 | 75 | 100 |
Session | 16000 | 8.5 | 4.6 | 1 | 4.8 | 12 | 16 |
Association between Clients’ Positive Emotions and Alliance
ggplot(temp1 %>% filter(Client < 500), aes(x = PosEmo, y = Alliance)) +
geom_point(alpha = 0.6) + # Scatter plot
geom_smooth(method = "lm", se = FALSE, color = "red", size = 1.2) + # Regression line
labs(
title = "Scatter Plot with Regression Line",
x = "Positive Emotions",
y = "Alliance"
+
) theme_minimal()
Regression model
corr.test(temp1 %>% select(Alliance,PosEmo))
Call:corr.test(x = temp1 %>% select(Alliance, PosEmo))
Correlation matrix
Alliance PosEmo
Alliance 1.00 0.09
PosEmo 0.09 1.00
Sample Size
[1] 16000
Probability values (Entries above the diagonal are adjusted for multiple tests.)
Alliance PosEmo
Alliance 0 0
PosEmo 0 0
To see confidence intervals of the correlations, print with the short=FALSE option
<- lm(Alliance~PosEmo,data=temp1)
Model1 tab_model(Model1,show.r2=F)
Alliance | |||
Predictors | Estimates | CI | p |
(Intercept) | 4.85 | 4.65 – 5.05 | <0.001 |
PosEmo | 0.28 | 0.23 – 0.32 | <0.001 |
Observations | 16000 |
Scatter plot for the first 10 clients
ggplot(temp1 %>% filter(Client <= 10), aes(x = PosEmo, y = Alliance)) +
geom_point(alpha = 0.6,aes(color=as.factor(Client))) + # Scatter plot
geom_smooth(method = "lm", se = FALSE, color = "red", size = 1.2) + # Regression line
labs(
title = "Scatter Plot with Regression Line",
x = "Positive Emotions",
y = "Alliance"
+
) theme_minimal()
<- temp1 %>% mutate(residuals=Model1$residuals,
temp1 fitted=Model1$fitted.values)
# Plot residuals vs fitted, color by therapist
ggplot(temp1 %>% filter(Client<10), aes(x = fitted, y = residuals, color = as.factor(Client))) +
geom_point() +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(title = "Residuals vs Fitted: OLS ignoring nesting",
x = "Fitted values", y = "Residuals") +
theme_minimal()
Spaghetti Plot Per Client
ggplot(temp1 %>% filter(Client<50), aes(x = PosEmo, y = Alliance, group = Client)) +
#geom_line(alpha = 0.5) +
geom_smooth(method = lm, se = FALSE, fullrange = TRUE, aes(group = interaction(Client)), color = "gray", size = 0.5) + # Individual regression lines
geom_smooth(aes(group = 1), method = "lm", se = FALSE, color = "red", size = 1.2) +
labs(title = "Spaghetti Plot Per Client",
x = "Positive Emotions", y = "Alliance") +
theme_minimal()
<- lme(Alliance~PosEmo,
Model2 random = ~ 1 + PosEmo| Therapist/Client,
data = temp1)
<- as.data.frame(Model2$coefficients$random$Therapist)
foo <- lme(Alliance~PosEmo,
Model2 random = ~ 1 + PosEmo| Therapist/Client,
data = temp1)
ggplot(temp1 %>% mutate(THERAPIST=as.factor(Therapist)) %>%
filter(Therapist%in%c(which.max(foo$PosEmo),
which.min(foo$PosEmo))),
aes(x = PosEmo, y = Alliance, group = Client)) +
geom_smooth(
method = lm, se = FALSE, fullrange = TRUE,
aes(group = interaction(Client), color = THERAPIST),
size = 0.5
+ # Individual regression lines colored by therapist
) geom_smooth(
aes(group = 1),
method = "lm", se = FALSE, color = "red", size = 1.2
+ # Overall regression line
) labs(
title = "Spaghetti Plot Per Client and Therapist",
x = "Positive Emotions", y = "Alliance"
+
) theme_minimal()
3-level multi-level model
<- lme(Alliance~PosEmo,
Model2 random = ~ 1 + PosEmo| Therapist/Client,
data = temp1)
summary(Model2)
Linear mixed-effects model fit by REML
Data: temp1
AIC BIC logLik
49516.24 49585.36 -24749.12
Random effects:
Formula: ~1 + PosEmo | Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 1.0331539 (Intr)
PosEmo 0.3157697 0.075
Formula: ~1 + PosEmo | Client %in% Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 0.9765288 (Intr)
PosEmo 0.4621859 0.382
Residual 0.9610456
Fixed effects: Alliance ~ PosEmo
Value Std.Error DF t-value p-value
(Intercept) 4.903089 0.11278744 14999 43.47194 0
PosEmo 0.262182 0.03571402 14999 7.34115 0
Correlation:
(Intr)
PosEmo 0.04
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-3.649017326 -0.647822488 0.001778542 0.646254809 3.701130771
Number of Observations: 16000
Number of Groups:
Therapist Client %in% Therapist
100 1000
tab_model(Model2,show.r2=F)
Alliance | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | 4.90 | 4.68 – 5.12 | <0.001 |
PosEmo | 0.26 | 0.19 – 0.33 | <0.001 |
Random Effects | |||
σ2 | 0.92 | ||
τ00 Client | 1.03 | ||
τ00 Therapist | 0.98 | ||
τ11 Client.PosEmo | 0.32 | ||
τ11 Therapist.PosEmo | 0.46 | ||
ρ01 Client | 0.07 | ||
ρ01 Therapist | 0.38 | ||
ICC | 0.95 | ||
N Client | 1000 | ||
N Therapist | 100 | ||
Observations | 16000 |
T&B Model
Loading the data from OSF
<- osf_retrieve_file("6846bf952b204a6ecd5396a6")
file #Change to your local dowonloads folder
setwd("C:\\Users\\97254\\Downloads")
osf_download(file, progress = T,conflicts = "overwrigt")
<- import("T_B_Data.csv")
temp2
names(temp2) <- c("J_PosEmo","C_PosEmo","T_PosEmo","Experience","Depression","Client","Therapist")
<- temp2 %>% group_by(Therapist,Client) %>%
temp2 mutate(Session=row_number()) %>%
ungroup() %>% relocate(Client:Session,.before = J_PosEmo)
Descriptive stats
head(temp2)
# A tibble: 6 × 8
Client Therapist Session J_PosEmo C_PosEmo T_PosEmo Experience Depression
<int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 1 1 14.1 5.91 6.09 -2.61 -0.548
2 1 1 2 12.2 5.19 5.20 -2.61 -0.548
3 1 1 3 10.4 4.59 5.37 -2.61 -0.548
4 1 1 4 14.2 6.68 5.96 -2.61 -0.548
5 1 1 5 13.7 5.94 6.16 -2.61 -0.548
6 1 1 6 13.6 6.33 5.35 -2.61 -0.548
st(temp2)
Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
---|---|---|---|---|---|---|---|
Client | 16000 | 500 | 289 | 1 | 251 | 750 | 1000 |
Therapist | 16000 | 50 | 29 | 1 | 26 | 75 | 100 |
Session | 16000 | 8.5 | 4.6 | 1 | 4.8 | 12 | 16 |
J_PosEmo | 16000 | 4.5 | 4.4 | -14 | 1.5 | 7.3 | 20 |
C_PosEmo | 16000 | 5 | 1 | 0.94 | 4.3 | 5.7 | 9 |
T_PosEmo | 16000 | 6 | 1 | 1.8 | 5.3 | 6.7 | 9.8 |
Experience | 16000 | 0.052 | 1.1 | -2.6 | -0.56 | 0.87 | 2.8 |
Depression | 16000 | 0.023 | 0.97 | -2.6 | -0.62 | 0.68 | 3.2 |
Centering the Variables on the Truth Variable
<- temp2 %>% group_by(Therapist,Client) %>%
temp2 mutate(
Avg_Truth=mean(C_PosEmo),
Judg=J_PosEmo-mean(C_PosEmo),
Truth=C_PosEmo-mean(C_PosEmo),
Bias=T_PosEmo-mean(C_PosEmo)
%>%
) ungroup()
st(temp2)
Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
---|---|---|---|---|---|---|---|
Client | 16000 | 500 | 289 | 1 | 251 | 750 | 1000 |
Therapist | 16000 | 50 | 29 | 1 | 26 | 75 | 100 |
Session | 16000 | 8.5 | 4.6 | 1 | 4.8 | 12 | 16 |
J_PosEmo | 16000 | 4.5 | 4.4 | -14 | 1.5 | 7.3 | 20 |
C_PosEmo | 16000 | 5 | 1 | 0.94 | 4.3 | 5.7 | 9 |
T_PosEmo | 16000 | 6 | 1 | 1.8 | 5.3 | 6.7 | 9.8 |
Experience | 16000 | 0.052 | 1.1 | -2.6 | -0.56 | 0.87 | 2.8 |
Depression | 16000 | 0.023 | 0.97 | -2.6 | -0.62 | 0.68 | 3.2 |
Avg_Truth | 16000 | 5 | 0.25 | 4.2 | 4.8 | 5.2 | 5.9 |
Judg | 16000 | -0.55 | 4.4 | -19 | -3.5 | 2.3 | 15 |
Truth | 16000 | -0.0000000000000000029 | 0.97 | -4 | -0.66 | 0.65 | 3.9 |
Bias | 16000 | 0.99 | 1 | -3.3 | 0.29 | 1.7 | 4.7 |
head(temp2 %>% select(contains("PosEmo"),Avg_Truth,Judg,Truth,Bias),n=20)
# A tibble: 20 × 7
J_PosEmo C_PosEmo T_PosEmo Avg_Truth Judg Truth Bias
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 14.1 5.91 6.09 4.95 9.15 0.965 1.15
2 12.2 5.19 5.20 4.95 7.25 0.250 0.256
3 10.4 4.59 5.37 4.95 5.49 -0.357 0.423
4 14.2 6.68 5.96 4.95 9.25 1.74 1.02
5 13.7 5.94 6.16 4.95 8.72 0.995 1.21
6 13.6 6.33 5.35 4.95 8.69 1.39 0.408
7 15.9 4.73 7.67 4.95 10.9 -0.210 2.73
8 13.8 5.00 5.77 4.95 8.86 0.0559 0.827
9 15.0 4.88 7.14 4.95 10.1 -0.0615 2.19
10 16.8 6.59 7.17 4.95 11.9 1.64 2.23
11 13.9 3.48 5.95 4.95 8.96 -1.46 1.00
12 12.0 4.53 6.02 4.95 7.03 -0.411 1.07
13 13.4 4.32 6.23 4.95 8.42 -0.624 1.28
14 12.3 4.20 5.83 4.95 7.37 -0.746 0.884
15 12.1 3.27 6.17 4.95 7.16 -1.67 1.23
16 12.2 3.46 5.69 4.95 7.29 -1.48 0.740
17 6.97 3.27 6.05 5.14 1.82 -1.87 0.901
18 5.70 4.22 5.02 5.14 0.560 -0.919 -0.126
19 7.93 6.59 6.53 5.14 2.79 1.45 1.39
20 6.08 6.10 7.43 5.14 0.934 0.961 2.29
Model 1: T&B
<- lme(Judg~Truth,
TB1 random = ~ 1 + Truth| Therapist/Client,
data = temp2)
tab_model(TB1,show.r2=F)
Judg | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | -0.55 | -1.13 – 0.03 | 0.063 |
Truth | 0.32 | 0.24 – 0.41 | <0.001 |
Random Effects | |||
σ2 | 1.26 | ||
τ00 Client | 2.78 | ||
τ00 Therapist | 3.20 | ||
τ11 Client.Truth | 0.38 | ||
τ11 Therapist.Truth | 0.50 | ||
ρ01 Client | 0.54 | ||
ρ01 Therapist | 0.66 | ||
ICC | 0.84 | ||
N Client | 1000 | ||
N Therapist | 100 | ||
Observations | 16000 |
Model 2: Moderated T&B, Level-2 Moderator (Client’s Depression)
<- lme(
TB2 ~ Truth * Depression,
Judg random = list(
Therapist = ~ Depression+Truth,
Client = ~ Truth
),
data = temp2
)summary(TB2)
Linear mixed-effects model fit by REML
Data: temp2
AIC BIC logLik
55228.91 55336.43 -27600.45
Random effects:
Formula: ~Depression + Truth | Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 2.7961278 (Intr) Dprssn
Depression 0.4302474 0.350
Truth 0.3845697 0.545 0.184
Formula: ~Truth | Client %in% Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 3.0286570 (Intr)
Truth 0.4691316 0.634
Residual 1.1214130
Fixed effects: Judg ~ Truth * Depression
Value Std.Error DF t-value p-value
(Intercept) -0.5390417 0.29585330 14998 -1.821990 0.0685
Truth 0.3263256 0.04229916 14998 7.714707 0.0000
Depression -0.9551760 0.11220278 899 -8.512944 0.0000
Truth:Depression -0.1603469 0.01863527 14998 -8.604483 0.0000
Correlation:
(Intr) Truth Dprssn
Truth 0.540
Depression 0.121 0.059
Truth:Depression -0.004 -0.010 0.486
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-4.61138349 -0.59998947 -0.01230018 0.60297132 4.93291035
Number of Observations: 16000
Number of Groups:
Therapist Client %in% Therapist
100 1000
# Simple slopes of PosEmo at different levels of Depression
emtrends(TB2, ~ Depression, var = "Truth", at = list(Depression = c(-1, 0, 1)))
Depression Truth.trend SE df lower.CL upper.CL
-1 0.487 0.0464 14998 0.3957 0.578
0 0.326 0.0423 14998 0.2434 0.409
1 0.166 0.0461 14998 0.0757 0.256
Degrees-of-freedom method: containment
Confidence level used: 0.95
emmip(TB2, Depression ~ Truth,
at = list(Truth = seq(min(temp2$Truth), max(temp2$Truth), length.out = 20),
Depression = c(-1, 0, 1)),
CIs = TRUE)
Model 3: Moderated T&B, Level-3 Moderator (Therapist’s Experience)
<- lme(
TB3 ~ Truth * Depression+Truth*Experience,
Judg random = list(
Therapist = ~ Depression+Truth,
Client = ~ Truth
),
data = temp2
)summary(TB3)
Linear mixed-effects model fit by REML
Data: temp2
AIC BIC logLik
55215.31 55338.19 -27591.65
Random effects:
Formula: ~Depression + Truth | Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 2.6351611 (Intr) Dprssn
Depression 0.4311979 0.361
Truth 0.3365549 0.457 0.195
Formula: ~Truth | Client %in% Therapist
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 3.028508 (Intr)
Truth 0.469176 0.634
Residual 1.121415
Fixed effects: Judg ~ Truth * Depression + Truth * Experience
Value Std.Error DF t-value p-value
(Intercept) -0.5839883 0.28098461 14997 -2.078364 0.0377
Truth 0.3175634 0.03802809 14997 8.350760 0.0000
Depression -0.9539401 0.11216979 899 -8.504429 0.0000
Experience 0.8567644 0.24645606 98 3.476337 0.0008
Truth:Depression -0.1602282 0.01860836 14997 -8.610549 0.0000
Truth:Experience 0.1674730 0.03362128 14997 4.981161 0.0000
Correlation:
(Intr) Truth Dprssn Exprnc Trth:D
Truth 0.465
Depression 0.124 0.061
Experience -0.046 -0.021 0.004
Truth:Depression -0.004 -0.011 0.485 -0.001
Truth:Experience -0.021 -0.047 0.002 0.460 0.000
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-4.6130337 -0.5995688 -0.0118541 0.6012764 4.9300565
Number of Observations: 16000
Number of Groups:
Therapist Client %in% Therapist
100 1000
# Simple slopes of PosEmo at different levels of Experience
emtrends(TB3, ~ Experience, var = "Truth", at = list(Experience = c(-1, 0, 1)))
Experience Truth.trend SE df lower.CL upper.CL
-1 0.146 0.0519 14997 0.0446 0.248
0 0.314 0.0380 14997 0.2393 0.388
1 0.481 0.0496 14997 0.3842 0.579
Degrees-of-freedom method: containment
Confidence level used: 0.95
emmip(TB3, Experience ~ Truth,
at = list(Truth = seq(min(temp2$Truth), max(temp2$Truth), length.out = 20),
Experience = c(-1, 0, 1)),
CIs = TRUE)
Model 4: Adding the Bias Force (therapist’s emotions)
<- lme(Judg~Truth+Bias,
TB4 random = ~ 1 + Truth+Bias| Therapist/Client,
data = temp2)
tab_model(TB4,show.r2=F)
Judg | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | -1.02 | -1.55 – -0.48 | <0.001 |
Truth | 0.33 | 0.24 – 0.41 | <0.001 |
Bias | 0.48 | 0.40 – 0.55 | <0.001 |
Random Effects | |||
σ2 | 0.72 | ||
τ00 Client | 2.56 | ||
τ00 Therapist | 2.92 | ||
τ11 Client.Truth | 0.37 | ||
τ11 Client.Bias | 0.34 | ||
τ11 Therapist.Truth | 0.58 | ||
τ11 Therapist.Bias | 0.44 | ||
ρ01 | 0.63 | ||
0.63 | |||
0.73 | |||
0.62 | |||
ICC | 0.93 | ||
N Client | 1000 | ||
N Therapist | 100 | ||
Observations | 16000 |
Dyadic Data
<- osf_retrieve_file("68493f15c941fea4dfa4db8c")
file #Change to your local dowonloads folder
setwd("C:\\Users\\97254\\Downloads")
osf_download(file, progress = T,conflicts = "overwrigt")
<- import("T_B_Dyadic_Data.csv")
temp3
names(temp3) <- c("M_J_Vul","W_J_Vul","W_Vul","M_Vul","Couple")
<- temp3 %>% group_by(Couple) %>%
temp3 mutate(Session=row_number()) %>%
ungroup() %>% relocate(Couple:Session,.before = M_J_Vul)
Descriptive Stat
head(temp3)
# A tibble: 6 × 6
Couple Session M_J_Vul W_J_Vul W_Vul M_Vul
<int> <int> <dbl> <dbl> <dbl> <dbl>
1 1 1 -4.20 17.1 6.84 7.35
2 1 2 -4.32 14.9 6.12 6.65
3 1 3 -3.05 12.3 6.61 4.73
4 1 4 -3.21 11.2 5.09 4.30
5 1 5 -3.71 10.2 5.19 4.30
6 1 6 -2.26 9.24 2.83 3.96
st(temp3)
Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
---|---|---|---|---|---|---|---|
Couple | 1600 | 50 | 29 | 1 | 26 | 75 | 100 |
Session | 1600 | 8.5 | 4.6 | 1 | 4.8 | 12 | 16 |
M_J_Vul | 1600 | 3.6 | 2.6 | -5.7 | 2.1 | 5.2 | 12 |
W_J_Vul | 1600 | 3.7 | 2.5 | -3.3 | 2.1 | 4.9 | 18 |
W_Vul | 1600 | 5.1 | 0.98 | 1.8 | 4.4 | 5.7 | 8.6 |
M_Vul | 1600 | 5 | 1 | 1.1 | 4.3 | 5.7 | 8.1 |
corr.test(temp3)
Call:corr.test(x = temp3)
Correlation matrix
Couple Session M_J_Vul W_J_Vul W_Vul M_Vul
Couple 1.00 0.00 -0.03 -0.01 0.01 0.01
Session 0.00 1.00 0.01 0.00 -0.03 -0.03
M_J_Vul -0.03 0.01 1.00 -0.58 0.12 0.04
W_J_Vul -0.01 0.00 -0.58 1.00 0.07 0.15
W_Vul 0.01 -0.03 0.12 0.07 1.00 0.35
M_Vul 0.01 -0.03 0.04 0.15 0.35 1.00
Sample Size
[1] 1600
Probability values (Entries above the diagonal are adjusted for multiple tests.)
Couple Session M_J_Vul W_J_Vul W_Vul M_Vul
Couple 0.00 1.00 1.00 1 1.00 1
Session 1.00 0.00 1.00 1 1.00 1
M_J_Vul 0.24 0.81 0.00 0 0.00 1
W_J_Vul 0.56 0.97 0.00 0 0.04 0
W_Vul 0.65 0.29 0.00 0 0.00 0
M_Vul 0.63 0.24 0.15 0 0.00 0
To see confidence intervals of the correlations, print with the short=FALSE option
Data stacking
<- temp3 %>%
temp3_long mutate(M_T_Vul=W_Vul,
W_T_Vul=M_Vul) %>%
pivot_longer(
cols = c(M_J_Vul, W_J_Vul, M_T_Vul, W_T_Vul),
names_to = c("Person", ".value"),
names_pattern = "([MW])_(.*)"
%>% mutate(Female=ifelse(Person=="W",1,0),
) Male=ifelse(Person=="M",1,0)
%>%
) #centering
group_by(Couple,Person) %>%
mutate(avg_T_Vul=mean(T_Vul),
J_Vulmc=J_Vul-mean(T_Vul),
T_Vulmc=T_Vul-mean(T_Vul)
%>% ungroup()
) head(temp3_long,30)
# A tibble: 30 × 12
Couple Session W_Vul M_Vul Person J_Vul T_Vul Female Male avg_T_Vul J_Vulmc
<int> <int> <dbl> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 1 6.84 7.35 M -4.20 6.84 0 1 5.66 -9.86
2 1 1 6.84 7.35 W 17.1 7.35 1 0 5.28 11.8
3 1 2 6.12 6.65 M -4.32 6.12 0 1 5.66 -9.98
4 1 2 6.12 6.65 W 14.9 6.65 1 0 5.28 9.66
5 1 3 6.61 4.73 M -3.05 6.61 0 1 5.66 -8.71
6 1 3 6.61 4.73 W 12.3 4.73 1 0 5.28 6.99
7 1 4 5.09 4.30 M -3.21 5.09 0 1 5.66 -8.86
8 1 4 5.09 4.30 W 11.2 4.30 1 0 5.28 5.94
9 1 5 5.19 4.30 M -3.71 5.19 0 1 5.66 -9.37
10 1 5 5.19 4.30 W 10.2 4.30 1 0 5.28 4.89
# ℹ 20 more rows
# ℹ 1 more variable: T_Vulmc <dbl>
Model 5: Dyadic T&B
<- lme(J_Vulmc ~ -1 +
TB4+ Female:T_Vulmc+
Female+ Male:T_Vulmc
Male
,random = ~ -1 +
+Female:T_Vulmc+
Female+Male:T_Vulmc| Couple,
Maleweights = varIdent(form=~1|Person),
# corr=corAR1(form = ~1 | Couple/DiaryDay),
correlation = corCompSymm(form = ~1|Couple/Session),
data = temp3_long,na.action = na.exclude)
summary(TB4)
Linear mixed-effects model fit by REML
Data: temp3_long
AIC BIC logLik
9058.325 9161.509 -4512.162
Random effects:
Formula: ~-1 + Female + Female:T_Vulmc + Male + Male:T_Vulmc | Couple
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
Female 2.2173090 Female Male Fm:T_V
Male 2.3921426 -0.732
Female:T_Vulmc 0.5049870 0.960 -0.770
T_Vulmc:Male 0.4931943 -0.701 0.930 -0.688
Residual 0.8711177
Correlation Structure: Compound symmetry
Formula: ~1 | Couple/Session
Parameter estimate(s):
Rho
0.2828992
Variance function:
Structure: Different standard deviations per stratum
Formula: ~1 | Person
Parameter estimates:
M W
1.0000000 0.9950838
Fixed effects: J_Vulmc ~ -1 + Female + Female:T_Vulmc + Male + Male:T_Vulmc
Value Std.Error DF t-value p-value
Female -1.3629909 0.22278739 3097 -6.117900 0
Male -1.4186450 0.24020354 3097 -5.906012 0
Female:T_Vulmc 0.3156361 0.05469998 3097 5.770314 0
T_Vulmc:Male 0.3544787 0.05423213 3097 6.536322 0
Correlation:
Female Male Fm:T_V
Male -0.723
Female:T_Vulmc 0.882 -0.708
T_Vulmc:Male -0.634 0.842 -0.562
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-3.16772997 -0.63694388 -0.01781197 0.63908689 3.42718090
Number of Observations: 3200
Number of Groups: 100
tab_model(TB4,show.r2=F)
J Vulmc | |||
---|---|---|---|
Predictors | Estimates | CI | p |
Female | -1.36 | -1.80 – -0.93 | <0.001 |
Male | -1.42 | -1.89 – -0.95 | <0.001 |
Female × T Vulmc | 0.32 | 0.21 – 0.42 | <0.001 |
T Vulmc × Male | 0.35 | 0.25 – 0.46 | <0.001 |
Random Effects | |||
σ2 | 0.76 | ||
τ00 | |||
τ00 | |||
τ11 Couple.Male | 5.72 | ||
τ11 Couple.Female:T_Vulmc | 0.26 | ||
τ11 Couple.T_Vulmc:Male | 0.24 | ||
ρ01 | -0.73 | ||
0.96 | |||
-0.70 | |||
ICC | 0.88 | ||
N Couple | 100 | ||
Observations | 3200 |
RSA Model
Loading the data from OSF
<- osf_retrieve_file("6857a9c8fc6aa8ceafa1c717")
file #Change to your local dowonloads folder
setwd("C:\\Users\\97254\\Downloads")
osf_download(file, progress = T,conflicts = "overwrigt")
<- import("RSA.csv")
temp4
setwd("G:/My Drive/Research/Conferences & Workshops/2025- SPR/Workshop")
Descriptive stats
head(temp4)
id Alliance Pos Perceived_Pos n_rows
1 1231 6.44 0.822973 0.822973 37
2 1231 3.38 -0.177027 -0.677027 37
3 1231 6.86 0.822973 -4.177027 37
4 1231 5.74 -4.077027 -1.077027 37
5 1231 2.70 0.822973 0.822973 37
6 1231 6.26 0.822973 0.822973 37
st(temp4)
Variable | N | Mean | Std. Dev. | Min | Pctl. 25 | Pctl. 75 | Max |
---|---|---|---|---|---|---|---|
id | 6211 | 109702 | 237828 | 1231 | 4247 | 9708 | 988945 |
Alliance | 6186 | 5.1 | 2.1 | 0 | 3.7 | 6.6 | 10 |
Pos | 6202 | 0.049 | 1.6 | -9.4 | -0.62 | 0.99 | 5 |
Perceived_Pos | 6202 | -0.049 | 1.7 | -9.2 | -0.78 | 1 | 5 |
n_rows | 6211 | 33 | 11 | 16 | 26 | 37 | 79 |
Person-mean centering and computation of RSA variables
<- temp4 %>%
temp4 group_by(id) %>%
mutate(x=Pos-mean(Pos,na.rm=T),
y=Perceived_Pos-mean(Perceived_Pos,na.rm=T)
%>%
) ungroup() %>%
mutate(x2=x*x,xy=x*y,y2=y*y)
head(temp4,20)
# A tibble: 20 × 10
id Alliance Pos Perceived_Pos n_rows x y x2 xy
<int> <dbl> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
1 1231 6.44 0.823 0.823 37 0.697 0.949 0.486 0.661
2 1231 3.38 -0.177 -0.677 37 -0.303 -0.551 0.0916 0.167
3 1231 6.86 0.823 -4.18 37 0.697 -4.05 0.486 -2.82
4 1231 5.74 -4.08 -1.08 37 -4.20 -0.951 17.7 4.00
5 1231 2.7 0.823 0.823 37 0.697 0.949 0.486 0.661
6 1231 6.26 0.823 0.823 37 0.697 0.949 0.486 0.661
7 1231 0.1 -5.68 -6.88 37 -5.80 -6.75 33.7 39.2
8 1231 3.76 0.823 0.823 37 0.697 0.949 0.486 0.661
9 1231 5.24 0.823 0.823 37 0.697 0.949 0.486 0.661
10 1231 4.26 -0.977 -1.48 37 -1.10 -1.35 1.22 1.49
11 1231 3.68 0.823 0.823 37 0.697 0.949 0.486 0.661
12 1231 7 0.823 0.823 37 0.697 0.949 0.486 0.661
13 1231 6.52 0.823 0.723 37 0.697 0.849 0.486 0.592
14 1231 5.74 0.823 0.823 37 0.697 0.949 0.486 0.661
15 1231 5.44 0.623 0.823 37 0.497 0.949 0.247 0.472
16 1231 6.46 0.523 0.823 37 0.397 0.949 0.158 0.377
17 1231 6.1 0.823 0.823 37 0.697 0.949 0.486 0.661
18 1231 3.68 -2.38 -2.78 37 -2.50 -2.65 6.26 6.64
19 1231 6.48 0.823 0.823 37 0.697 0.949 0.486 0.661
20 1231 7.32 0.823 0.823 37 0.697 0.949 0.486 0.661
# ℹ 1 more variable: y2 <dbl>
Plotting Terciles of difference scores
# Step 1: Create the difference between the two variables
<- temp4%>%
temp mutate(difference = x - y )
# Step 2: Calculate quantiles (terciles)
<- quantile(temp$difference, probs = c(1/3, 2/3), na.rm = TRUE)
tercile_cutoffs
# Step 3: Assign tercile based on the cutoffs
<- temp %>%
temp mutate(tercile = case_when(
<= tercile_cutoffs[1] ~ "Lower",
difference > tercile_cutoffs[1] & difference <= tercile_cutoffs[2] ~ "Medium",
difference > tercile_cutoffs[2] ~ "High"
difference
))
# Step 4: Plot histogram of the difference scores by tercile
ggplot(temp, aes(x = difference, fill = tercile)) +
geom_histogram(bins = 30, position = "identity", alpha = 0.7) +
labs(title = "Histogram of Difference Scores by Tercile",
x = "Difference (Pos vs. Perceived)",
y = "Count",
fill = "Tercile") +
theme_minimal()
Multilevel Response Surface Analysis (MLRSA)
<- lme(Alliance ~ 1 + x+y+x2+xy+y2,
MLRSA
random = ~ 1 + x+y| id,
data = temp4,na.action = na.exclude,
)summary(MLRSA)
Linear mixed-effects model fit by REML
Data: temp4
AIC BIC logLik
23427.1 23514.57 -11700.55
Random effects:
Formula: ~1 + x + y | id
Structure: General positive-definite, Log-Cholesky parametrization
StdDev Corr
(Intercept) 1.1826257 (Intr) x
x 0.2177434 0.054
y 0.1597083 0.060 -0.646
Residual 1.5011529
Fixed effects: Alliance ~ 1 + x + y + x2 + xy + y2
Value Std.Error DF t-value p-value
(Intercept) 5.130118 0.08579764 5966 59.79323 0.0000
x 0.397927 0.02687157 5966 14.80847 0.0000
y 0.212022 0.02321558 5966 9.13276 0.0000
x2 -0.012335 0.00753353 5966 -1.63738 0.1016
xy 0.041036 0.00885452 5966 4.63442 0.0000
y2 -0.021120 0.00613937 5966 -3.44012 0.0006
Correlation:
(Intr) x y x2 xy
x 0.006
y -0.010 -0.678
x2 -0.089 0.356 -0.230
xy 0.047 -0.019 -0.031 -0.599
y2 -0.102 -0.246 0.442 -0.091 -0.487
Standardized Within-Group Residuals:
Min Q1 Med Q3 Max
-4.64901893 -0.55237973 0.04123639 0.61576559 3.87027444
Number of Observations: 6179
Number of Groups: 208
tab_model(MLRSA,show.r2=F)
Alliance | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | 5.13 | 4.96 – 5.30 | <0.001 |
x | 0.40 | 0.35 – 0.45 | <0.001 |
y | 0.21 | 0.17 – 0.26 | <0.001 |
x2 | -0.01 | -0.03 – 0.00 | 0.102 |
xy | 0.04 | 0.02 – 0.06 | <0.001 |
y2 | -0.02 | -0.03 – -0.01 | 0.001 |
Random Effects | |||
σ2 | 2.25 | ||
τ00 id | 1.40 | ||
τ11 id.x | 0.05 | ||
τ11 id.y | 0.03 | ||
ρ01 | 0.05 | ||
0.06 | |||
ICC | 0.40 | ||
N id | 208 | ||
Observations | 6179 |
Plotting surface
# Plotting
<- fixef(MLRSA)) (fixed_effects
(Intercept) x y x2 xy y2
5.13011816 0.39792673 0.21202230 -0.01233522 0.04103556 -0.02112013
plotRSA(x=fixed_effects[2],
y=fixed_effects[3],
x2=fixed_effects[4],
xy=fixed_effects[5],
y2=fixed_effects[6]
)
Getting estimates and their SE using the deltaMethod
# Define a function that receives var_names and builds the expression list
<- function(model, vars) {
build_and_evaluate_expressions
# Get the fixed effects and variance-covariance matrix from the model
<- fixef(model)
fixed_effects <- vcov(model)
vcov_matrix
# Build the expressions dynamically based on the provided variable names
<- list(
expressions_list "a1" = paste(vars[1], "+", vars[2]),
"a2" = paste(vars[3], "+", vars[4], "+", vars[5]),
"a3" = paste(vars[1], "-", vars[2]),
"a4" = paste(vars[3], "-", vars[4], "+", vars[5]),
"X0" = paste("(", vars[2], "*", vars[4], "- 2 *", vars[1], "*", vars[5], ") / (4 *",
3], "*", vars[5], "-", vars[4], "^2)"),
vars["Y0" = paste("(", vars[1], "*", vars[4], "- 2 *", vars[2], "*", vars[3], ") / (4 *",
3], "*", vars[5], "-", vars[4], "^2)"),
vars["p10" = paste("((", vars[1], "*", vars[4], "- 2 *", vars[2], "*", vars[3], ") / (4 *",
3], "*", vars[5], "-", vars[4], "^2)) - ((", vars[5], "-",
vars[3], "+ sqrt((", vars[3], "-", vars[5], ")^2 +", vars[4], "^2))/",
vars[4], ") * ((", vars[2], "*", vars[4], "- 2 *", vars[1], "*",
vars[5], ") / (4 *", vars[3], "*", vars[5], "-", vars[4], "^2))"),
vars["p11" = paste("(", vars[5], "-", vars[3], "+ sqrt((", vars[3], "-", vars[5], ")^2 +",
4], "^2)) /", vars[4]),
vars["p20" = paste("((", vars[1], "*", vars[4], "- 2 *", vars[2], "*", vars[3], ") / (4 *",
3], "*", vars[5], "-", vars[4], "^2)) - ((", vars[5], "-",
vars[3], "- sqrt((", vars[3], "-", vars[5], ")^2 +", vars[4], "^2))/",
vars[4], ") * ((", vars[2], "*", vars[4], "- 2 *", vars[1], "*",
vars[5], ") / (4 *", vars[3], "*", vars[5], "-", vars[4], "^2))"),
vars["p21" = paste("(", vars[5], "-", vars[3], "- sqrt((", vars[3], "-", vars[5], ")^2 +",
4], "^2)) /", vars[4])
vars[
)
# Initialize an empty dataframe to store results
<- data.frame(Expression = character(), Estimate = numeric(), SE = numeric(),CI_2.5= numeric(),CI_97.5= numeric(), Z = numeric(), p_value = numeric(), stringsAsFactors = FALSE)
results_df
# Iterate over the expressions and apply deltaMethod for each
for (expr_name in names(expressions_list)) {
<- expressions_list[[expr_name]]
expr
# Apply deltaMethod using the current expression
<- deltaMethod(object = fixed_effects,
result g = expr,
vcov. = vcov_matrix)
# Calculate Z-score and p-value
<- result$Estimate / result$SE
z_score
<- round(2 * (1 - pnorm(abs(z_score))),4)
p_value
# Append result to the dataframe
<- results_df %>%
results_df add_row(Expression = expr_name, Estimate = result$Estimate, SE = result$SE,CI_2.5=result$`2.5 %`,CI_97.5=result$`97.5 %`, Z = z_score, p_value = p_value)
}
# Return the results dataframe
return(results_df)
}
# Define the male and female variable names
<- c("x", "y", "x2", "xy", "y2")
vars
# Apply the function to the model (in this case Model1.1)
<- build_and_evaluate_expressions(MLRSA, vars)
results
# View the results
print(results)
Expression Estimate SE CI_2.5 CI_97.5 Z
1 a1 0.609949035 0.020385818 5.699936e-01 0.64990450 29.920263
2 a2 0.007580209 0.005627002 -3.448512e-03 0.01860893 1.347113
3 a3 0.185904428 0.045896704 9.594854e-02 0.27586032 4.050496
4 a4 -0.074490917 0.017241104 -1.082829e-01 -0.04069897 -4.320542
5 X0 -39.744061765 27.177601473 -9.301118e+01 13.52305831 -1.462383
6 Y0 -33.591116632 25.437462253 -8.344763e+01 16.26539324 -1.320537
7 p10 -1.454938813 0.927085202 -3.271992e+00 0.36211479 -1.569369
8 p11 0.808578097 0.204101582 4.085463e-01 1.20860985 3.961645
9 p20 -82.744144552 54.915981174 -1.903775e+02 24.88920073 -1.506741
10 p21 -1.236738917 0.312178095 -1.848597e+00 -0.62488109 -3.961645
p_value
1 0.0000
2 0.1779
3 0.0001
4 0.0000
5 0.1436
6 0.1867
7 0.1166
8 0.0001
9 0.1319
10 0.0001