Distinguishable dyads: SEM with moderation

Two-level SEM with within-dyad gender interactions

The MLM moderator tutorial added gender × predictor interactions to a long-format multilevel model. This tutorial does the same thing in lavaan, using the two-level SEM specification that decomposes variance into within-dyad and between-dyad components.

The two specifications should give you nearly identical estimates for the within-dyad effects. The SEM version is more transparent about which interactions are at the within level and which covariates are at the between level.

Setup

Code
library(lavaan)
library(dplyr)

load("../../../data/dyad_data.RData")

# Numeric gender indicator for interactions
# Male = 1, Female = 2 (matches R's default factor coding)
ddl$gender_num <- as.numeric(ddl$gender)

# Manual interaction columns
ddl$wnc_x_gender        <- ddl$wnc * ddl$gender_num
ddl$partner_wnc_x_gender <- ddl$partner_wnc * ddl$gender_num

cat("Long format:", nrow(ddl), "rows\n")
Long format: 200 rows
Code
cat("Gender distribution:\n")
Gender distribution:
Code
print(table(ddl$gender))

  male female 
   100    100 

Baseline: two-level SEM without gender moderation

Place the four within-dyad predictors at the within level and the two dyad-level covariates at the between level. The two-level SEM is the SEM analogue of the MLM tutorial.

Code
model_base <- '
  level: within
    satisfaction ~ a_w*wnc + p_w*partner_wnc + ar_w*recovery +
      pr_w*partner_recovery

  level: between
    satisfaction ~ c_child*has_children + c_dual*dual_earner
'

fit_base <- sem(model_base, data = ddl, cluster = "dyad_id")
summary(fit_base, standardized = TRUE, fit.measures = TRUE)
lavaan 0.6-21 ended normally after 22 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         9

  Number of observations                           200
  Number of clusters [dyad_id]                     100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               133.662
  Degrees of freedom                                 6
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -129.318
  Loglikelihood unrestricted model (H1)       -129.319
                                                      
  Akaike (AIC)                                 276.635
  Bayesian (BIC)                               306.320
  Sample-size adjusted Bayesian (SABIC)        277.807

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual (corr metric):

  SRMR (within covariance matrix)                0.000
  SRMR (between covariance matrix)               0.003

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Observed
  Observed information based on                Hessian


Level 1 [within]:

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  satisfaction ~                                                        
    wnc      (a_w)   -0.256    0.040   -6.461    0.000   -0.256   -0.377
    prtnr_w  (p_w)   -0.178    0.040   -4.491    0.000   -0.178   -0.262
    recovry (ar_w)    0.226    0.038    5.956    0.000    0.226    0.309
    prtnr_r (pr_w)    0.125    0.038    3.286    0.001    0.125    0.171

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      0.188    0.027    7.071    0.000    0.188    0.409


Level 2 [dyad_id]:

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  satisfaction ~                                                        
    hs_chld (c_ch)    0.026    0.078    0.338    0.735    0.026    0.059
    dul_rnr (c_dl)    0.305    0.078    3.928    0.000    0.305    0.647

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      4.824    0.166   29.099    0.000    4.824   22.366

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      0.027    0.022    1.246    0.213    0.027    0.581

Add gender × predictor interactions

The within-dyad level now includes the gender main effect and the gender × actor/partner WNC interactions. The between-dyad level is unchanged.

Code
model_moderator <- '
  level: within
    satisfaction ~ a_w*wnc + p_w*partner_wnc + ar_w*recovery +
      pr_w*partner_recovery +
      g_w*gender_num +
      aw_int*wnc_x_gender + pw_int*partner_wnc_x_gender

  level: between
    satisfaction ~ c_child*has_children + c_dual*dual_earner

  # ── Simple slopes ──────────────────────────────
  # gender_num = 1 (male), gender_num = 2 (female)

  # Actor WNC → satisfaction
  ss_wnc_M  := a_w + aw_int * 1
  ss_wnc_F  := a_w + aw_int * 2

  # Partner WNC → satisfaction
  ss_pwnc_M := p_w + pw_int * 1
  ss_pwnc_F := p_w + pw_int * 2
'

fit_moderator <- sem(model_moderator, data = ddl, cluster = "dyad_id")
summary(fit_moderator, standardized = TRUE, fit.measures = TRUE)
lavaan 0.6-21 ended normally after 25 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        12

  Number of observations                           200
  Number of clusters [dyad_id]                     100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               154.210
  Degrees of freedom                                 9
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -119.044
  Loglikelihood unrestricted model (H1)       -119.045
                                                      
  Akaike (AIC)                                 262.089
  Bayesian (BIC)                               301.668
  Sample-size adjusted Bayesian (SABIC)        263.651

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual (corr metric):

  SRMR (within covariance matrix)                0.000
  SRMR (between covariance matrix)               0.001

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Observed
  Observed information based on                Hessian


Level 1 [within]:

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  satisfaction ~                                                        
    wnc      (a_w)   -0.300    0.125   -2.397    0.017   -0.300   -0.449
    prtnr_w  (p_w)   -0.020    0.130   -0.151    0.880   -0.020   -0.029
    recovry (ar_w)    0.247    0.037    6.770    0.000    0.247    0.344
    prtnr_r (pr_w)    0.109    0.037    2.979    0.003    0.109    0.151
    gndr_nm  (g_w)   -0.262    0.058   -4.490    0.000   -0.262   -0.197
    wnc_x_g (aw_n)    0.007    0.081    0.090    0.928    0.007    0.017
    prtn___ (pw_n)   -0.082    0.081   -1.009    0.313   -0.082   -0.200

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      0.154    0.022    7.071    0.000    0.154    0.346


Level 2 [dyad_id]:

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  satisfaction ~                                                        
    hs_chld (c_ch)    0.032    0.078    0.408    0.683    0.032    0.061
    dul_rnr (c_dl)    0.303    0.077    3.909    0.000    0.303    0.553

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      5.206    0.188   27.673    0.000    5.206   20.725

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .satisfaction      0.044    0.020    2.164    0.030    0.044    0.694

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ss_wnc_M         -0.292    0.054   -5.459    0.000   -0.292   -0.432
    ss_wnc_F         -0.285    0.057   -4.967    0.000   -0.285   -0.415
    ss_pwnc_M        -0.102    0.057   -1.770    0.077   -0.102   -0.229
    ss_pwnc_F        -0.184    0.054   -3.427    0.001   -0.184   -0.429

Reading the calculated simple slopes

The := operator in lavaan defines a user-calculated parameter. The simple slopes are functions of the base coefficient and the interaction coefficient, evaluated at the male and female values of gender_num.

Code
pe <- parameterEstimates(fit_moderator)
pe[grepl("^ss_", pe$label), c("label", "est", "se", "pvalue")]
TipComparing to the MLM estimates

The within-dyad SEM aw_int coefficient (gender × actor WNC) and pw_int (gender × partner WNC) should match the corresponding wnc:genderfemale and partner_wnc:genderfemale interactions in the MLM moderator tutorial. The simple slopes for males and females should also match the sim_slopes() output.

Why “model is saturated” matters here

The two-level SEM with the within-dyad interactions has just-identified within-level equations: the four predictors plus three interaction terms and a gender main effect consume all the within-dyad degrees of freedom. This means:

  • The chi-square test of model fit is exactly zero (or near it) by construction.
  • The standard chi-square difference test is not available for comparing the baseline and moderator models.

If you want a formal LRT, the right move is to fit both models in the wide format (see the SEM wide tutorial), or to use the MLM moderator approach, where the LRT is well-defined.

What to take away

Key takeaways

  • The two-level SEM makes the within/between decomposition of effects explicit.
  • Gender × predictor interactions live at the within-dyad level.
  • Dyad-level covariates (no within-dyad variance) live at the between-dyad level.
  • Calculated simple slopes via := give you the conditional effects at each level of the moderator.
  • The model is just-identified within-dyad, so the chi-square LRT is not available; use the MLM version for hypothesis testing.