--- title: "Getting Started" author: "Pascal Küng" description: > Core workflow for validating dyadic data, identifying dyad compositions, and preparing model-ready columns for dyadic multilevel models. bibliography: references.bib output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(dyadMLM) ``` ## Installation You can install the released version of `dyadMLM` from CRAN with: ```{r installation-cran, eval=FALSE} install.packages("dyadMLM") ``` You can install the development version from GitHub with: ```{r installation, eval=FALSE} # install.packages("pak") pak::pak("Pascal-Kueng/dyadMLM") ``` ## About this Vignette `dyadMLM` helps researchers prepare cross-sectional and intensive longitudinal dyadic data for (generalized) multilevel models. It automatically creates model-ready columns for dyadic multilevel model parameterizations such as Actor-Partner Interdependence Models (APIM), Dyad-Individual Models (DIM), and Dyadic Score Models (DSM). DIM currently supports one exchangeable dyad composition, whereas DSM supports one distinguishable dyad composition. **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 available model-specific vignettes include the [Actor-Partner Interdependence Model](apim.html), [Dyad-Individual Model](dim.html), and [Dyadic Score Model](dsm.html). The [online package overview](https://pascal-kueng.github.io/dyadMLM/) 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. 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](https://tidyr.tidyverse.org/articles/pivot.html) or the [`pivot_longer()` reference](https://tidyr.tidyverse.org/reference/pivot_longer.html). Roughly, the expected structure for `dyadMLM` is: - For cross-sectional data: one row per `dyad x member` | 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 | - For intensive longitudinal data: at most one row per `dyad x time x member` | 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. The structural `dyad`, `member`, and optional `time` variables must not contain missing values. In intensive longitudinal data, missing measurement occasions can be represented by absent rows, as long as the time variable preserves the observed measurement occasions. For example: | dyad | personID | time | x | y | |---:|---:|---:|---:|---:| | 1 | 1 | 1 | 4.2 | 7.1 | | 1 | 1 | 3 | 4.0 | 6.9 | | 1 | 2 | 1 | 5.3 | 6.6 | | 1 | 2 | 2 | 4.7 | 6.1 | | 1 | 2 | 3 | 5.1 | 6.4 | In this example, the row for person 1 at time 2 is absent. The time variable preserves the observed measurement occasions and skips from time 1 to time 3 for that person. `dyadMLM` accepts this structure without requiring a placeholder row for the missing occasion. ## Data preparation for distinguishable dyads `dyads_cross` contains three dyad compositions. Each dyad has two rows: one for each member. The closeness and provided-support scores are member-level averages across the 14 days in `dyads_ild`. 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. ```{r cross-sectional-raw, echo = FALSE} head(dyads_cross) ``` We validate and prepare the data with the function `dyadMLM::prepare_dyad_data()`. ```{r prepare-cross-distinguishable} cross_distinguishable_data <- dyadMLM::prepare_dyad_data( data = dyads_cross, dyad = coupleID, member = personID, role = gender, # In this example, we optionally specify a predictor variable # and a model type to generate the columns needed for that model type. predictors = provided_support, model_types = "apim", # 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 and recognized 120 female-male dyads and created APIM-relevant variables [@kennyPartnerEffectsRelationship1999]. These generated `.dy_*` columns can be used directly in model formulas. For fitted APIM examples using these columns, see the [Actor-Partner Interdependence Model vignette](apim.html). ## Data preparation for exchangeable dyads To work with one exchangeable dyad composition, use `keep_compositions` to retain it during preparation: ```{r prepare-cross-exchangeable-basic} cross_exchangeable_data <- dyadMLM::prepare_dyad_data( data = dyads_cross, dyad = coupleID, member = personID, role = gender, keep_compositions = "female-female", seed = 123 ) print(cross_exchangeable_data, n = 4) ``` The generated `.dy_member_contrast_female_x_female_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 [exchangeable APIM section](apim.html#exchangeable-residual-structure) 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()`](https://pascal-kueng.github.io/dyadMLM/reference/recover_exchangeable_covariance.html). Alternatively, we can explicitly set a dyad composition to exchangeable: ```{r prepare-cross-set-exchangeable} cross_exchangeable_data <- dyadMLM::prepare_dyad_data( data = dyads_cross, dyad = coupleID, member = personID, role = gender, keep_compositions = "female-male", set_exchangeable_compositions = "male-female", seed = 123 ) print(cross_exchangeable_data, 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`. ## Generating DIM and DSM columns For exchangeable dyads, we can request DIM predictor columns. DIM preparation requires exactly one exchangeable composition, which we retain here with `keep_compositions`. ```{r prepare-cross-dim} cross_dim_data <- dyadMLM::prepare_dyad_data( data = dyads_cross, dyad = coupleID, member = personID, role = gender, predictors = provided_support, model_types = "dim", keep_compositions = "female-female", seed = 123 ) print(cross_dim_data, n = 4) ``` Generating DSM columns for distinguishable dyads additionally requires an explicit role order. The role order defines the direction of all DSM predictor differences and the DSM role contrast [@iidaModelsInterdependentIndividuals2018]. ```{r prepare-cross-dsm} 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) ``` ## Intensive longitudinal dyadic data `dyads_ild` is an intensive longitudinal dyadic dataset. Each dyad has repeated observations over `diaryday`, with one row per person-day. ```{r ild-raw, echo=FALSE} head(dyads_ild) ``` To prepare intensive longitudinal data, pass the `time` variable to `dyadMLM::prepare_dyad_data()`. ```{r prepare-ild-apim} 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"` for this longitudinal setup and retains raw actor and partner columns alongside both components. 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]. ### Preparing lagged predictors Lagged versions of variables, including an outcome that is also passed to `predictors`, can be obtained through the `lag1_predictors` argument. `dyadMLM::prepare_dyad_data()` then returns lag-1 raw and within-person-centered actor and partner columns alongside their contemporaneous versions. Lagging respects the dyad and member structure, matches observations at exactly `time - 1`, and does not bridge missing occasions. For a simpler dynamic model, this example retains the exchangeable female-female composition: ```{r prepare-ild-apim-dynamic} 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 raw or within-person-centered lagged outcome depends on the research question and the data. Including a lagged **outcome** in dynamic models can introduce bias, especially in shorter time series [@hamakerCenterNotCenter2015; @nickellBiasesDynamicModels1981; @gistelinckMultilevelAutoregressiveSmall2021]. See the [dynamic ILD APIM example](apim.html#dynamic-models) 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: ```{r prepare-mixed-cross-sectional} 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 `.dy_member_contrast_female_x_female_arbitrary`, which is `0` for all other compositions [@delrosarioPracticalGuideSpecifying2025]. 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` can only refer to retained dyad compositions. ```{r prepare-mixed-cross-sectional-included} 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) ``` ### Setting distinguishable dyads to be treated as exchangeable As mentioned earlier, a distinguishable dyad composition can be treated as exchangeable. In a mixed dyad composition dataset, this specification keeps the differentiation between the kinds of dyads (e.g., `male-male`, `female-female`, and `male-female`), as opposed to omitting role, which would pool all dyad compositions into one exchangeable composition. ```{r prepare-mixed-cross-sectional-exchangeable} 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 for theoretical or practical reasons, 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_glmmTMB_models()`](https://pascal-kueng.github.io/dyadMLM/reference/compare_nested_glmmTMB_models.html); see [Testing distinguishability in the APIM vignette](apim.html#testing-distinguishability) for the model-comparison workflow. For instance, let's pool `male-male` and `female-female` dyads and name them `same-sex` dyads: ```{r prepare-mixed-cross-sectional_pooled} 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: ```{r prepare-mixed-cross-sectional_pooled_constrained} 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) ``` --- **Continue** with the [Actor-Partner Interdependence Model (APIM) vignette](apim.html). Related model-specific vignettes: - [Dyad-Individual Model vignette](dim.html), - [Dyadic Score Model vignette](dsm.html), or return to [About this vignette](#about-this-vignette). ## References ::: {#refs} :::