Getting Started

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(dyadMLM)

Installation

Install the stable release from CRAN:

install.packages("dyadMLM")

To try the latest changes and help test upcoming features, install the development version from R-universe:

install.packages(
  "dyadMLM",
  repos = c(
    "https://pascal-kueng.r-universe.dev",
    "https://cloud.r-project.org"
  )
)

Development versions may change more frequently. Feedback is welcome through GitHub issues.

About this vignette

dyadMLM helps researchers prepare cross-sectional and intensive longitudinal dyadic data for (generalized) multilevel models.

This vignette focuses on automatic data preparation for multilevel models (MLMs). For a comparison of MLM and structural equation modeling (SEM) approaches to dyadic data, see @ledermannAnalyzingDyadicData2017.

The model-fitting examples in the model-specific vignettes use the glmmTMB package.

Post-estimation functions in dyadMLM, including model comparison and back-transformation of exchangeable random-effect covariance structures, are described in the Actor-Partner Interdependence Model vignette.

Other vignettes cover the Dyad-Individual Model and its equivalence to the exchangeable APIM, and the Dyadic Score Model and its equivalence to the distinguishable APIM. They also describe the corresponding coefficient and covariance transformations.

The online package overview provides the current online versions of these vignettes and the complete function reference.

Prerequisites

The basic data structure needed for dyadMLM is a long data frame where dyads are stacked on top of each other and both members of a dyad appear as separate rows.

Roughly, the expected structure for dyadMLM is:

| dyad | member | x | y | |---:|---:|---:|---:| | 1 | 1 | 4.2 | 7.1 | | 1 | 2 | 5.0 | 6.4 | | 2 | 1 | 3.8 | 5.9 | | 2 | 2 | 4.5 | 6.8 |

| dyad | time | member | x | y | |---:|---:|---:|---:|---:| | 1 | 1 | 1 | 4.2 | 7.1 | | 1 | 1 | 2 | 5.0 | 6.4 | | 1 | 2 | 1 | 4.0 | 6.9 | | 1 | 2 | 2 | 5.3 | 6.6 |

Measured variables may contain missing values, but dyad, member, and optional time must be complete; missing measurement occasions may instead be represented by absent rows.

If your raw data are currently in wide format (for time or dyads or both), reshape them to this long structure first. See the tidyr pivoting vignette or the pivot_longer() reference.

Data preparation for distinguishable dyads

dyads_cross is a simulated cross-sectional dataset that contains three dyad compositions.

Because these example data contain multiple compositions, keep_compositions selects the composition modeled below. It can be omitted when the supplied data already contain only the intended composition.

head(dyadMLM::dyads_cross)

We validate and prepare the data with the function dyadMLM::prepare_dyad_data().

cross_distinguishable_data <- dyadMLM::prepare_dyad_data(
  data = dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  predictors = provided_support,
  model_types = "apim",
  add_apim_gmc_predictors = TRUE, # Optional grand-mean centering

  # All three observed compositions in `dyads_cross` are detected and retained by
  # default. This example focuses on `female-male` dyads, so we restrict the
  # analysis here.
  keep_compositions = "female-male"
)

print(cross_distinguishable_data, n = 4)

The function retained 120 female-male dyads and created raw and grand-mean centered APIM variables [@kennyPartnerEffectsRelationship1999].

For fitted APIM examples using these columns, see the Actor-Partner Interdependence Model vignette.

Data preparation for exchangeable dyads

For this example, we pretend that we have a dataset with no distinguishable variable (e.g., same-sex friend dyads). Then, we simply omit the role argument:

cross_exchangeable_data <- dyadMLM::prepare_dyad_data(
  data = dyads_cross,
  dyad = coupleID,
  member = personID,
  predictors = provided_support,
  model_types = "apim",
  add_apim_gmc_predictors = TRUE,
  seed = 123
)

print(cross_exchangeable_data, n = 4)

The generated .member_contrast_arbitrary contrast assigns -1 and 1 to the two members of each exchangeable dyad [@delrosarioPracticalGuideSpecifying2025]. Its direction is arbitrary, and seed makes the assignment reproducible.

Refer to the APIM vignette's exchangeable APIM section for how to use these columns to specify an exchangeable dyadic APIM and recover the constrained actor-partner variance-covariance structure with dyadMLM::recover_exchangeable_covariance().

Generating DIM and DSM columns

For exchangeable dyads, we can request DIM predictor columns. DIM preparation requires exactly one exchangeable composition. This can be achieved by omitting role. For more control over compositions, see Working with multiple dyad compositions.

cross_dim_data <- dyadMLM::prepare_dyad_data(
  data = dyads_cross,
  dyad = coupleID,
  member = personID,
  predictors = provided_support,
  model_types = "dim",
  seed = 123
)

print(cross_dim_data, n = 4)

For distinguishable dyads, we can request DSM columns. DSM preparation currently requires exactly one distinguishable composition. To compute these columns, an explicit role order is required. The role order defines the direction of all DSM predictor differences and the DSM role contrast [@iidaModelsInterdependentIndividuals2018].

cross_dsm_data <- dyadMLM::prepare_dyad_data(
  data = dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  predictors = provided_support,
  model_types = "dsm",
  dsm_role_order = c("female", "male"),
  keep_compositions = "female-male"
)

print(cross_dsm_data, n = 4)

APIM GMC uses all retained non-missing values. DIM and DSM center complete-pair dyad means. The constants may differ with one-sided missingness.

In DIM and DSM, mean predictors are grand-mean centered by convention; thus, no additional argument is required to request them.

Intensive longitudinal dyadic data

dyads_ild is a simulated intensive longitudinal dyadic dataset. Each dyad has repeated observations over diaryday, with one row per person-day.

head(dyads_ild)

To prepare intensive longitudinal data, pass the time variable to dyadMLM::prepare_dyad_data().

ild_apim_data <- dyadMLM::prepare_dyad_data(
  dyads_ild,
  dyad = coupleID,
  member = personID,
  role = gender,
  time = diaryday,
  predictors = provided_support,
  model_types = "apim",
  keep_compositions = "female-male",
  seed = 123
)

print(ild_apim_data, n = 6)

By default, numeric predictors in longitudinal APIM preparation are decomposed into within-person and between-person components [@bolgerIntensiveLongitudinalMethods2013]. This temporal predictor decomposition is controlled by temporal_decomposition. The default "auto" setting selects "2l" (2-level temporal decomposition).

add_apim_gmc_predictors = TRUE requires resolved temporal_decomposition = "none", since the between-person component is always already grand-mean centered by convention.

Note that observed person means used to construct the between-person (cbp) predictors can be unreliable when each member contributes few occasions, which can bias between-person estimates [@gottfredsonStraightforwardApproachCoping2019].

For fitted concurrent examples and AR(1) specifications for distinguishable and exchangeable dyads, refer to the intensive longitudinal APIM section.

Preparing lagged predictors

Lagged versions of variables, including an outcome that is also passed to predictors for dynamic models, can be obtained through the lag1_predictors argument.

Lagging respects the dyad and member structure, matches observations at exactly time - 1, and does not bridge missing occasions when rows are missing in the dataset.

ild_apim_data_dynamic <- dyadMLM::prepare_dyad_data(
  dyads_ild,
  dyad = coupleID,
  member = personID,
  role = gender,
  time = diaryday,
  predictors = closeness,
  lag1_predictors = closeness,
  model_types = "apim",
  keep_compositions = "female-female",
  seed = 123
)

print(ild_apim_data_dynamic, n = 6)

Note: Whether to use the prepared raw or within-person-centered lagged outcome depends on the research question and the data. See the dynamic ILD APIM example for a more detailed discussion and guidance.

Working with multiple dyad compositions

dyads_cross contains three dyad compositions: distinguishable female-male dyads and exchangeable female-female and male-male dyads [@Bolger2025Unified].

Let's have dyadMLM infer the compositions automatically:

mixed_cross_data <- dyadMLM::prepare_dyad_data(
  dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  seed = 123
)

print(mixed_cross_data, n = 4)

Note that when role compositions are available, each exchangeable composition receives its own difference contrast, such as .member_contrast_female_x_female_arbitrary, which is 0 for all other compositions.

We can use this data to model these dyad types as separate or in the same model.

Keeping only selected dyad compositions (filtering)

Sometimes a mixed dataset contains dyad compositions that should not be part of a given analysis. Use keep_compositions to keep only dyads whose observed composition matches the requested labels. The filtering happens before exchangeability constraints and pooling, so set_exchangeable_compositions and pool_compositions (described later) can only refer to retained dyad compositions.

mixed_cross_data_included <- dyadMLM::prepare_dyad_data(
  dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  keep_compositions = c("female-female", "male-male"),
  seed = 123
)

print(mixed_cross_data_included, n = 4)

Note that whenever you need to refer to a dyad type, the order of members does not matter (e.g., male-female and female-male will both work), and you can use different separators like male_female, male_x_female, or male female.

Setting distinguishable dyads to be treated as exchangeable

set_exchangeable_compositions changes how selected role-defined compositions are modeled while preserving their composition labels. By contrast, omitting role indicates that no distinguishing variable is available, so all dyads are treated as one exchangeable composition with a single global arbitrary-member contrast.

mixed_cross_exchangeable_data <- dyadMLM::prepare_dyad_data(
  dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  set_exchangeable_compositions = c("male-female"),
  seed = 123
)

print(mixed_cross_exchangeable_data, n = 4)

Pooling different dyad compositions

Sometimes, we may want to pool selected exchangeable dyad compositions and analyze them as if they were one. Pooling can impose equality constraints among compositions. After fitting nested pooled and unpooled models to the same observations, these constraints can be tested with dyadMLM::compare_nested_models(); see testing distinguishability in the APIM vignette for the model-comparison workflow.

For instance, let's pool male-male and female-female dyads and name them same-sex dyads:

mixed_cross_data_pooled <- dyadMLM::prepare_dyad_data(
  dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  pool_compositions = list(
    "same-sex" = c("male-male", "female_female")
  ),
  seed = 123
)

print(mixed_cross_data_pooled)

Note that you cannot pool distinguishable dyads. If we wanted to pool female-male with male-male, we would first have to treat female-male as exchangeable:

mixed_cross_data_pooled_constrained <- dyadMLM::prepare_dyad_data(
  dyads_cross,
  dyad = coupleID,
  member = personID,
  role = gender,
  set_exchangeable_compositions = "male female",
  pool_compositions = list(
    "pooled_exchangeable" = c("male-male", "male_female")
  ),
  seed = 123
)

print(mixed_cross_data_pooled_constrained)

Citation

If you use dyadMLM, please cite the version of the package you use. Obtain the citation via:

citation("dyadMLM")

Continue with the Actor-Partner Interdependence Model (APIM) vignette.

Related model-specific vignettes:

or return to About this vignette.

References

::: {#refs} :::



Try the dyadMLM package in your browser

Any scripts or data that you put into this service are public.

dyadMLM documentation built on Oct. 28, 2026, 1:06 a.m.