Package {dyadMLM}


Title: Tools for Dyadic Multilevel Models
Version: 0.1.0
Description: Provides tools for dyadic multilevel modeling with linear and generalized linear mixed-effects models. It validates and prepares long-format cross-sectional and intensive longitudinal data, including ecological momentary assessment designs, for distinguishable and exchangeable dyads. It also supports datasets containing multiple observed dyad compositions. It constructs composition-aware, model-ready variables for Actor-Partner Interdependence Models (APIMs), Dyadic Score Models (DSMs), and Dyad-Individual Models (DIMs). Prepared data can be used with model engines such as 'glmmTMB' and 'brms' for Gaussian and non-Gaussian outcomes, including counts, proportions, and skewed continuous responses. Post-estimation tools compare compatible fitted models and back-transform exchangeable sum-and-difference random-effect covariance structures into member-level quantities. The APIM and DSM specifications and their relationships follow Iida et al. (2018) <doi:10.1177/0265407517725407>; the multilevel sum-and-difference random-effects implementation for exchangeable dyads adapts del Rosario and West (2025) <doi:10.1177/25152459251351286>.
License: MIT + file LICENSE
Encoding: UTF-8
RoxygenNote: 8.0.0
Suggests: brms, glmmTMB, htmltools, knitr, lavaan, marginaleffects, rmarkdown, spelling, testthat (≥ 3.1.7)
Config/testthat/edition: 3
Imports: dplyr (≥ 1.1.0), pillar, rlang (≥ 0.4.0), tidyselect, tibble (≥ 3.0.0)
VignetteBuilder: knitr, rmarkdown
Depends: R (≥ 4.1)
LazyData: true
URL: https://pascal-kueng.github.io/dyadMLM/, https://github.com/Pascal-Kueng/dyadMLM
BugReports: https://github.com/Pascal-Kueng/dyadMLM/issues
Language: en-US
NeedsCompilation: no
Packaged: 2026-07-22 01:10:11 UTC; pascalkueng
Author: Pascal Küng ORCID iD [aut, cre, cph]
Maintainer: Pascal Küng <kueng.pascal@gmail.com>
Repository: CRAN
Date/Publication: 2026-07-30 17:20:23 UTC

Add actor and partner predictor columns

Description

Adds APIM-style actor and partner columns for the predictors recorded in an dyadMLM_data object. For uncentered predictors, this will create actor and partner versions of the raw predictor. For centered intensive longitudinal predictors, this will create actor and partner versions of the raw predictor and each recorded predictor component, such as the within-person and between-person components created by center_predictors(). Selected lag predictors additionally create lag-1 raw and within-person actor and partner columns.

Usage

add_actor_partner_columns(data)

Arguments

data

A dyadMLM_data object returned by prepare_dyad_data().

Details

The function will use the predictor decomposition metadata stored in attr(data, "dyadMLM")$temporal_decompositions, so downstream code does not need to infer generated predictor columns from their names. It stores the constructed APIM columns in attr(data, "dyadMLM")$apim_predictors.

Value

A dyadMLM_data object with actor and partner predictor columns added and APIM predictor metadata recorded.


Add dyad-individual predictor columns

Description

Adds Dyad-Individual Model (DIM) style dyad-mean and within-dyad member-deviation columns for the predictors recorded in a dyadMLM_data object. For currently supported DIMs, the data must contain one exchangeable dyad composition. This means distinguishable dyads and multiple exchangeable compositions are not supported by DIM construction until explicit role-contrast, composition-specific, or pooling support is added. For intensive longitudinal predictors decomposed by center_predictors(), raw predictors and within-person components are decomposed within each dyad-time occasion, while between-person components are decomposed once within each dyad. For raw predictors, the dyad-mean column is centered around the grand mean of dyad means, or dyad-occasion means in longitudinal data, while the within-dyad member-deviation column is the person's deviation from the uncentered dyad mean. Selected lag predictors additionally create lag-1 raw and within-person dyad-mean and within-dyad member-deviation columns.

Usage

add_dyad_individual_columns(data)

Arguments

data

A dyadMLM_data object returned by prepare_dyad_data().

Details

The function reads attr(data, "dyadMLM")$temporal_decompositions and stores the constructed DIM columns in attr(data, "dyadMLM")$dim_predictors.

Value

A dyadMLM_data object with dyad-mean and within-dyad member-deviation predictor columns added and DIM predictor metadata recorded.


Add dyadic-score model predictor columns and contrast

Description

Adds Dyadic Score Model (DSM) dyad-mean and signed dyad-difference columns for the predictors recorded in a dyadMLM_data object, together with a DSM role contrast coded +0.5 and -0.5. DSM differences follow the role order recorded in attr(data, "dyadMLM")$dsm_role_order. The supported DSM structure contains one distinguishable dyad composition; exchangeable dyads and multiple compositions are not supported.

Usage

add_dyadic_score_columns(data)

Arguments

data

A dyadMLM_data object returned by prepare_dyad_data().

Details

For ILD predictors, raw and within-person scores are computed within dyad-time and between-person scores within dyad. Raw dyad means are grand-mean centered. Both partners' predictor values are required for each score pair. Selected lag predictors additionally create lag-1 raw and within-person dyad-mean and signed-difference columns.

Constructed predictor columns are recorded in attr(data, "dyadMLM")$dsm_predictors, and the contrast column name is recorded in attr(data, "dyadMLM")$dsm_role_contrast_column.

Value

A dyadMLM_data object with dyad-mean and signed dyad-difference predictor columns and a DSM role contrast added.


Add lagged temporal predictor columns

Description

Adds lag-1 raw and within-person columns for predictors selected through lag1_predictors. Values are matched at exactly time - 1, so construction does not depend on row order and does not bridge gaps in the measurement index. Stable between-person components are not lagged.

Usage

add_temporal_lag_columns(data)

Arguments

data

A dyadMLM_data object returned by prepare_dyad_data().

Value

A dyadMLM_data object with lagged temporal predictor columns and updated predictor metadata.


Assign arbitrary member roles within dyads

Description

Creates a member-level lookup with one arbitrary role per observed member. The assignment is stable across longitudinal rows because it is made once per group x member.

Usage

assign_arbitrary_member_roles(data, group_name, member_name, seed = NULL)

Arguments

data

A data frame.

group_name

Name of the dyad/group column.

member_name

Name of the member/person column.

seed

Optional seed for random arbitrary partner-role assignment. If NULL, the current R session's RNG state is used.

Value

A data frame with group_name, member_name, and .dy_arbitrary_role.


Create a canonical dyad composition label

Description

Sorts role labels before pasting them so composition labels do not depend on row order.

Usage

canonical_composition(roles, sep = dyad_composition_sep)

composition_role_label(composition, role, sep = dyad_composition_role_sep)

Arguments

roles

A vector of role labels.

sep

Separator used between label components.

composition

A composition label.

role

A row-level role label.

Value

A single composition label.


Center predictor variables for dyadic models

Description

Adds centered predictor columns to a dyadMLM_data object. It currently supports two-level temporal centering for intensive longitudinal predictors: a within-person component and a between-person component. The original predictor remains available as a raw component for model-specific column construction. For two-level temporal centering, the between-person component is centered around the grand mean of person means, not the grand mean of all observed rows. This gives each person equal weight even when people have different numbers of observed measurement occasions.

Usage

center_predictors(data)

Arguments

data

A dyadMLM_data object returned by prepare_dyad_data().

Details

The function uses the structural metadata stored by prepare_dyad_data().

Value

A dyadMLM_data object with centered predictor columns added and updated predictor metadata.


Compare nested glmmTMB models fitted to equivalent data

Description

Performs a likelihood-ratio test for two nested glmmTMB models. The models may use ordinary data frames or dyadMLM_data objects, and their calls do not need to refer to the same R object. Models may be supplied in either order. The model with fewer estimated parameters is shown first in the result.

Usage

compare_nested_glmmTMB_models(model1, model2)

Arguments

model1, model2

Two fitted glmmTMB models to compare.

Details

Both model calls must use named data-frame objects that remain available when the models are compared. The checks assume these objects have not been modified since fitting. All ordinary data columns must be identical, including their types and attributes. For dyadMLM_data, generated .dy_ columns may differ, but the original columns must be identical. Ordinary and prepared data may be compared with each other. Dyad metadata are checked when both models use dyadMLM_data. The function also checks fitted rows, outcomes, weights and offsets, model family and link, maximum-likelihood estimation, and model convergence. Each model must use the same untransformed response column.

These checks establish that the models use equivalent observations. They cannot establish that one model is mathematically nested within the other. The caller remains responsible for supplying genuinely nested models. The usual chi-squared reference distribution may also be inappropriate when tested variance parameters are on the boundary.

Value

An anova-style data frame containing model degrees of freedom, information criteria, log-likelihoods, the likelihood-ratio statistic, and its chi-squared p-value. When printed, a short conclusion interprets the test at the 5% significance level.

Examples

if (requireNamespace("glmmTMB", quietly = TRUE)) {
  restricted_data <- prepare_dyad_data(
    dyads_cross,
    dyad = coupleID,
    member = personID,
    role = gender,
    # 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"
  )
  full_data <- restricted_data

  restricted_model <- glmmTMB::glmmTMB(
    closeness ~ 1 + us(1 | coupleID),
    data = restricted_data
  )
  full_model <- glmmTMB::glmmTMB(
    closeness ~ gender + us(1 | coupleID),
    data = full_data
  )

  compare_nested_glmmTMB_models(restricted_model, full_model)
}


Collect dyadMLM-generated columns

Description

Creates a normalized, one-row-per-column view over temporal predictor, APIM, DIM, and DSM columns stored in a dyadMLM attribute. This is a derived lookup table; the model-specific metadata tables remain the source records.

Usage

dyad_generated_columns(meta)

Arguments

meta

The dyadMLM metadata attribute from a dyadMLM_data object.

Value

A tibble with one row per generated temporal predictor, APIM, DIM, or DSM column. The lag column is 0 for contemporaneous columns and 1 for lag-1 columns.


Example Gaussian cross-sectional dyadic data

Description

A simulated long-format dataset containing distinguishable female-male dyads and exchangeable female-female and male-male dyads. Each dyad has two members and each member has one row. closeness and provided_support are the member's averages across the 14 observations in dyads_ild.

Usage

dyads_cross

Format

A data frame with 720 rows and 6 variables:

personID

Unique person identifier.

coupleID

Dyad identifier.

gender

Gender role, with levels female and male.

dyad_composition

Observed dyad composition, with levels female_x_male, female_x_female, and male_x_male.

closeness

Mean simulated Gaussian closeness score across 14 days.

provided_support

Mean simulated provided-support score across 14 days.

Details

Prepare with dyad = coupleID, member = personID, and role = gender. These data contain three compositions. Use keep_compositions in prepare_dyad_data() when an analysis should retain only selected compositions; omit it when all supplied compositions should remain.

Source

Simulated for dyadMLM; design adapted from https://github.com/Pascal-Kueng/05DyadicDataAnalysis. See Küng, P. M. (2026). Distinguishable and Exchangeable Dyads: Bayesian Multilevel Modelling (v2.0.9). Zenodo. doi:10.5281/zenodo.20720321.


Example Gaussian intensive longitudinal dyadic data

Description

A simulated long-format dataset containing distinguishable female-male dyads and exchangeable female-female and male-male dyads. Each dyad has two members observed on 14 diary days.

Usage

dyads_ild

Format

A data frame with 10,080 rows and 7 variables:

personID

Unique person identifier.

coupleID

Dyad identifier.

diaryday

Measurement day, from 0 through 13.

gender

Gender role, with levels female and male.

dyad_composition

Observed dyad composition, with levels female_x_male, female_x_female, and male_x_male.

closeness

Simulated Gaussian closeness outcome.

provided_support

Simulated provided-support score.

Details

Prepare with dyad = coupleID, member = personID, role = gender, and time = diaryday. These data contain three compositions. Use keep_compositions in prepare_dyad_data() when an analysis should retain only selected compositions; omit it when all supplied compositions should remain.

Source

Simulated for dyadMLM; design adapted from https://github.com/Pascal-Kueng/05DyadicDataAnalysis. See Küng, P. M. (2026). Distinguishable and Exchangeable Dyads: Bayesian Multilevel Modelling (v2.0.9). Zenodo. doi:10.5281/zenodo.20720321.


Example negative-binomial cross-sectional dyadic data

Description

A simulated long-format dataset containing the same dyads, members, and dyad compositions as dyads_cross. Each member has one row. stress is the member's average across the 14 observations in dyads_nbinom_ild. The count outcome is a separate negative-binomial draw using the NB2 variance function; it is not an average or sum of the daily counts.

Usage

dyads_nbinom_cross

Format

A data frame with 720 rows and 6 variables:

personID

Unique person identifier.

coupleID

Dyad identifier.

gender

Gender role, with levels female and male.

dyad_composition

Observed dyad composition, with levels female_x_male, female_x_female, and male_x_male.

conflict_count

Simulated number of conflictual interactions reported by a member.

stress

Mean simulated stress score across 14 days.

Details

Prepare with dyad = coupleID, member = personID, and role = gender. These data contain three compositions. Use keep_compositions in prepare_dyad_data() when an analysis should retain only selected compositions; omit it when all supplied compositions should remain.

Source

Simulated for dyadMLM; design adapted from https://github.com/Pascal-Kueng/05DyadicDataAnalysis. See Küng, P. M. (2026). Distinguishable and Exchangeable Dyads: Bayesian Multilevel Modelling (v2.0.9). Zenodo. doi:10.5281/zenodo.20720321.


Example negative-binomial intensive longitudinal dyadic data

Description

A simulated long-format dataset containing the same dyads, members, diary days, and dyad compositions as dyads_ild. The outcome is a count generated from a negative-binomial distribution using the NB2 variance function.

Usage

dyads_nbinom_ild

Format

A data frame with 10,080 rows and 7 variables:

personID

Unique person identifier.

coupleID

Dyad identifier.

diaryday

Measurement day, from 0 through 13.

gender

Gender role, with levels female and male.

dyad_composition

Observed dyad composition, with levels female_x_male, female_x_female, and male_x_male.

conflict_count

Simulated number of conflictual interactions reported by a member on that day.

stress

Simulated stress score.

Details

Prepare with dyad = coupleID, member = personID, role = gender, and time = diaryday. These data contain three compositions. Use keep_compositions in prepare_dyad_data() when an analysis should retain only selected compositions; omit it when all supplied compositions should remain.

Source

Simulated for dyadMLM; design adapted from https://github.com/Pascal-Kueng/05DyadicDataAnalysis. See Küng, P. M. (2026). Distinguishable and Exchangeable Dyads: Bayesian Multilevel Modelling (v2.0.9). Zenodo. doi:10.5281/zenodo.20720321.


Extract exchangeable random-effect blocks from a fitted model

Description

Normalizes the random-effect coefficients and fitted covariance parameters needed by recover_exchangeable_covariance() while keeping backend-specific work in two small adapters.

Usage

extract_exchangeable_residual_blocks(model)

Arguments

model

A fitted model. Supported classes are glmmTMB and brmsfit.

Value

A list containing the model backend, one normalized record per random-effect block, and one grouping-factor ID per fitted row. Every block record contains group, coefficients, correlated, term, and an estimate/draw-by-coefficients covariance array.


Format counted group identifiers for validation messages

Description

Converts a vector of dyad or group identifiers into text that includes both the number of groups and a compact list of their identifiers.

Usage

format_group_count(groups, singular = "dyad", plural = "dyads", max = 10)

Arguments

groups

A vector of group identifiers.

singular

Singular label for one group.

plural

Plural label for multiple groups.

max

Maximum number of identifiers to show before truncating the list.

Value

A single character string.


Format group identifiers for validation messages

Description

Converts a vector of dyad or group identifiers into a compact comma-separated string for use in validation errors and warnings.

Usage

format_group_list(groups, max = 10)

Arguments

groups

A vector of group identifiers.

max

Maximum number of identifiers to show before truncating the list.

Value

A single character string.


Infer dyad compositions

Description

Builds a dyad-level summary of role compositions from a validated dyadMLM_data object.

Usage

infer_dyad_compositions(
  data,
  seed = NULL,
  keep_compositions = NULL,
  set_exchangeable_compositions = NULL,
  pool_compositions = NULL
)

Arguments

data

A dyadMLM_data object returned by validate_dyad_data().

seed

Optional seed for random ⁠.dy_member_contrast_*⁠ sign assignment in exchangeable dyads. If NULL, the current R session's RNG state is used.

keep_compositions

Optional observed dyad compositions to keep before exchangeability overrides and pooling.

set_exchangeable_compositions

Optional dyad compositions to treat as exchangeable for analysis.

pool_compositions

Optional named list that pools exchangeable dyad compositions into user-named final composition labels. Each pool must resolve to at least two distinct observed compositions.

Value

A dyadMLM_data object with added .dy_composition and .dy_composition_role factor columns, ⁠.dy_is_*⁠ numeric indicator columns, composition-specific numeric ⁠.dy_member_contrast_*⁠ columns coded -1 and 1 for the two members of matching exchangeable dyads and 0 otherwise, and dyad composition metadata.


Create safe suffixes for generated dyadMLM columns

Description

Create safe suffixes for generated dyadMLM columns

Usage

make_dyad_suffixes(
  labels,
  label_type = "labels",
  rename_hint = "role or composition labels"
)

Arguments

labels

Labels that will be used to build generated column names.

Value

A named character vector. Names are the original labels; values are sanitized column-name suffixes.


Prepare dyadic data for multilevel models

Description

Validates dyadic data, records the structural variables, and adds metadata and model-ready columns for dyadic multilevel model parameterizations.

Usage

prepare_dyad_data(
  data,
  dyad,
  member,
  role = NULL,
  time = NULL,
  predictors = NULL,
  lag1_predictors = NULL,
  model_types = "apim",
  dsm_role_order = NULL,
  temporal_decomposition = c("auto", "2l", "none"),
  set_exchangeable_compositions = NULL,
  keep_compositions = NULL,
  pool_compositions = NULL,
  incomplete_dyads = c("error", "drop"),
  missing_role = c("error", "drop"),
  seed = NULL
)

Arguments

data

A data frame or tibble. Data must be in long format. For cross-sectional dyadic data, each observed member of each dyad has one row. For intensive longitudinal dyadic data, each observed member of each dyad has one row per observed time point.

dyad

Column identifying the dyad.

member

Column identifying a person or the member within dyad.

role

Optional column identifying a stable member role, such as gender. Non-missing values must be consistent within each dyad x member and must not contain ⁠_x_⁠. In repeated-measures data, an observed role is propagated to missing rows for the same member within a dyad. missing_role controls dyads in which a member has no non-missing role on any row. If no role is supplied, all dyads are treated as the same type of exchangeable dyads.

time

Optional column identifying time or measurement order of repeated measures.

predictors

Optional variables to use for temporal predictor decomposition and model-ready predictor construction.

lag1_predictors

Optional subset of predictors for which lag-1 model-ready columns should be created. Requires time to be a finite, integer-valued numeric measurement index. Lagging respects the dyad and member structure, matches observations at exactly time - 1, and does not bridge missing occasions. Only raw and within-person predictors are lagged. Stable between-person versions are not.

model_types

Model-ready column families to construct. Can contain one or more of "apim", "dim", and "dsm". "apim" creates actor and partner predictors. "dim" creates dyad-mean and within-dyad member-deviation predictors. "dsm" creates dyadic-score model predictor columns. "none" skips model-specific predictor construction after validation, composition inference, and optional temporal predictor decomposition, and must be used alone. "dim" and "dsm" must be requested in separate calls.

dsm_role_order

For model_types = "dsm", a character vector giving the two distinguishable roles in the order used for directional differences. For example, c("female", "male") defines predictor differences as female minus male and assigns the DSM role contrast +0.5 to female partners and -0.5 to male partners. Required when DSM columns are requested and must be NULL otherwise.

temporal_decomposition

Temporal decomposition strategy for predictors. "none" leaves predictors undecomposed before model-specific columns are constructed. "2l" indicates a two-level temporal predictor decomposition into within-person and between-person components. "auto" resolves to "2l" when both time and predictors are supplied, and to "none" otherwise. "2l" retains raw model-ready predictors in addition to their within-person and between-person components. For longitudinal DIM and DSM construction, raw and within-person dyadic scores are computed within each dyad occasion, while between-person scores are computed within dyads. Raw DIM and DSM dyad means are grand-mean centered. Do not include the raw, within-person, and between-person versions of the same contemporaneous predictor in one model because they are linearly dependent.

set_exchangeable_compositions

Optionally specify dyad compositions to treat as exchangeable, when their roles would otherwise imply distinguishability. Requires role. Compositions that are already exchangeable should not be listed. Each composition must be supplied as one string, using ⁠_x_⁠, -, ⁠_⁠, or whitespace (⁠ ⁠) between the two role labels, for example "female_x_male", "female-male", "female_male", or "female male", in arbitrary order. To set multiple compositions, use a character vector of such strings.

keep_compositions

Optional observed dyad compositions to keep before exchangeability overrides and pooling. Requires role. Composition references use the same format as set_exchangeable_compositions. NULL keeps all observed compositions.

pool_compositions

Optionally pool exchangeable dyad compositions into a shared final composition label. Must be a named list where each name is the final composition label and each value is a character vector of composition references, for example list(same_sex_couples = c("female-female", "male-male")). Only exchangeable compositions can be pooled. Each pool must contain at least two distinct observed compositions after composition references are resolved.

incomplete_dyads

How to handle dyads with fewer than two unique members across all rows in data. "error" stops with an error and "drop" removes the entire dyad. A dyad with more than two unique members is invalid and always causes an error, regardless of this setting.

missing_role

How to handle dyads in which at least one member has no non-missing role value on any row. A consistent non-missing role observed for a member is propagated to that member's other rows before this policy is applied. "error" stops with an error and "drop" removes the entire dyad. Conflicting non-missing roles always cause an error. Ignored when no role column is supplied.

seed

Optional seed for random ⁠.dy_member_contrast_*⁠ sign assignment in exchangeable dyads. If NULL, the current R session's RNG state is used.

Details

Data must be in long format. Cross-sectional dyadic data may contain at most one row per member within dyad. Intensive longitudinal dyadic data may contain at most one row per member and observed measurement occasion within dyad. Measured variables may contain missing values. Structural completeness is assessed across all rows. incomplete_dyads controls dyads with fewer than two members; dyads with more than two members always cause an error. When role is supplied, stable member roles are resolved across repeated rows before missing_role is applied.

Dyad composition labels are canonical: role labels are sorted alphabetically before being combined, so labels do not depend on row or member order.

Value

The original data as a tibble with class dyadMLM_data, .dy_composition and .dy_composition_role factor columns, ⁠.dy_is_*⁠ numeric indicator columns, composition-specific numeric ⁠.dy_member_contrast_*⁠ columns coded -1 and 1 for the two members of matching exchangeable dyads and 0 otherwise, and a dyadMLM attribute containing structural metadata, dyad_compositions, and predictor metadata such as temporal_decompositions, lag1_predictors, apim_predictors, and dim_predictors, as well as dsm_predictors and dsm_role_order when applicable.

Examples

data <- data.frame(
  dyad_id = c(1, 1, 2, 2, 3, 3),
  person_id = c(1, 2, 3, 4, 5, 6),
  role = c("female", "male", "female", "female", "male", "male"),
  x = c(4, 7, 5, 6, 3, 8)
)

prepared <- prepare_dyad_data(
  data,
  dyad = dyad_id,
  member = person_id,
  role = role,
  predictors = x,
  model_types = "apim"
)

print(prepared)

pooled <- prepare_dyad_data(
  data,
  dyad = dyad_id,
  member = person_id,
  role = role,
  predictors = x,
  model_types = "apim",
  set_exchangeable_compositions = "female-male",
  pool_compositions = list(
    romantic_couples = c("female-female", "male-male", "female-male")
  )
)

print(pooled)

ild_data <- data.frame(
  dyad_id = rep(c(1, 2), each = 4),
  person_id = rep(c(1, 2), times = 4),
  time = rep(c(1, 1, 2, 2), times = 2),
  x = c(4, 7, 5, 8, 3, 6, 4, 7)
)

ild_prepared <- prepare_dyad_data(
  ild_data,
  dyad = dyad_id,
  member = person_id,
  time = time,
  predictors = x,
  lag1_predictors = x,
  model_types = "apim",
  seed = 123
)

print(ild_prepared)


Print recovered exchangeable residual covariance

Description

Print recovered exchangeable residual covariance

Usage

## S3 method for class 'exchangeable_rescov'
print(x, representation = c("both", "varcov", "sdcor"), digits = 3L, ...)

Arguments

x

An object returned by recover_exchangeable_covariance().

representation

Which representation to print: "both" (default), "varcov", or "sdcor".

digits

Number of decimal places to print.

...

Additional arguments passed to print() when printing matrices.

Value

x, invisibly.


Recover member-level residual covariance from exchangeable random-effect blocks

Description

Back-transforms covariance matrices from paired shared and member-difference random-effect blocks to the covariance structure of two exchangeable members. The result is on the fitted random effects' linear-predictor scale. In non-Gaussian models, it therefore describes a Gaussian latent covariance, not response-scale residual covariance. For the model specification, derivation, and interpretation, see the exchangeable APIM vignette.

Usage

recover_exchangeable_covariance(model, block_pairings = NULL)

Arguments

model

A fitted glmmTMB or single-response brmsfit model. For glmmTMB, only conditional-model random effects are processed.

block_pairings

NULL (default) for automatic block matching. Otherwise, supply one block-pair specification or a list of block-pair specifications. Each pair contains:

  • shared_block: a single string naming the shared random-effect term copied from the fitted model formula or an equivalent selector (see Details), or NULL if the entire shared block was omitted when fitting;

  • difference_block: a single string naming the member-difference random-effect term or an equivalent selector, or NULL if the entire difference block was omitted;

  • difference_indicator: the exact name of the difference-indicator column used in difference_block. It is required when difference_block selects a block and optional when difference_block = NULL;

  • shared_indicator: the exact shared composition-indicator column, needed only for composition-specific blocks in mixed-dyad models. It defaults to "1", meaning that every fitted row belongs to the pair and an ordinary intercept is the shared intercept coordinate.

Details

Automatic matching recognizes exact ⁠.dy_member_contrast_*_arbitrary⁠ and legacy ⁠.dy_diff_*_arbitrary⁠ coefficient names and first looks for the corresponding ⁠.dy_is_*⁠ shared block. It requires the two blocks to use the same grouping factor and the same underlying terms. Most models fitted with dyadMLM-generated columns therefore need only:

result <- dyadMLM::recover_exchangeable_covariance(model)
print(result)

Supply block_pairings when automatic matching is ambiguous or when a model uses custom indicators, multiple covariance levels, or deliberately omitted blocks or terms. To specify one pair with a custom difference indicator:

result <- dyadMLM::recover_exchangeable_covariance(
  model,
  block_pairings = list(
    shared_block = "(1 + time | coupleID)",
    difference_block = "(0 + my_diff + I(my_diff * time) | coupleID)",
    difference_indicator = "my_diff"
  )
)

For multiple covariance levels, wrap the pairings in an outer list. For example, in a Gaussian glmmTMB model fitted with dispformula = ~ 0, this call recovers both a stable dyad-level covariance with an omitted difference time slope and the same-occasion partner residual covariance:

result <- dyadMLM::recover_exchangeable_covariance(
  model,
  block_pairings = list(
    dyad = list(
      shared_block = "(1 + diaryday | coupleID)",
      difference_block = "(0 + .dy_member_contrast_assumed_exchangeable_arbitrary | coupleID)",
      difference_indicator =
        ".dy_member_contrast_assumed_exchangeable_arbitrary"
    ),
    same_occasion = list(
      shared_block = "(1 | coupleID:diaryday)",
      difference_block = "(0 + .dy_member_contrast_assumed_exchangeable_arbitrary | coupleID:diaryday)",
      difference_indicator =
        ".dy_member_contrast_assumed_exchangeable_arbitrary"
    )
  )
)

At the dyad level, the fitted model includes a shared time slope but no difference time slope. Thus, the two members' time random effects are identical at this level, with correlation +1 whenever the shared slope variance is non-zero; at zero variance, the correlation is undefined. Covariances involving the diary-day slope are therefore supplied entirely by the shared block.

The random-effect terms may be copied exactly from the model formula. Equivalent backend syntax is also recognized, such as (1 + time | group) and us(1 + time | group), or (0 + x || group) and diag(0 + x | group). A custom difference-indicator name is supplied literally. Difference slopes may be written in either interaction order or as a simple product inside I().

When a difference block is supplied and the fitted model frame retains the indicator columns, difference_indicator must assign -1 and +1 to the two arbitrary member positions consistently within each dyad. For composition-specific blocks, it must be zero where shared_indicator is zero.

For custom difference indicators supplied through block_pairings, the function checks whether both positions occur within each supported fitted grouping unit. It rejects coding when no group contains both positions, and warns when only some groups are one-sided, which can result from fitted-row filtering. It also warns when stable member assignments cannot be verified across repeated rows without a member identifier.

Value

An exchangeable_rescov object: a named list with one element per matched block pairing. Each element contains shared_term and difference_term (each a fitted term string or NULL), plus the member-level variance-covariance matrix in varcov and its standard-deviation/correlation representation in sdcor, with standard deviations on the diagonal and correlations off the diagonal. Covariance dimension labels member_1 and member_2 denote arbitrary exchangeable positions, not substantive roles or encodings. Caller-supplied outer names are preserved; unnamed pairings receive stable names pair_1, pair_2, and so on in resolved order. For glmmTMB, varcov and sdcor are matrices. For brms, they are posterior-draw by coefficient by coefficient arrays.

What omitted blocks and terms mean

recover_exchangeable_covariance() only describes constraints that were already imposed when the model was fitted. It does not remove a block, set a variance to zero, or otherwise constrain the supplied model. Describe only the structure that was actually fitted.

If a term occurs in only one selected block, the function represents the missing coordinate as a structural zero:

Setting difference_block = NULL or shared_block = NULL applies the corresponding rule to the entire omitted block. This is valid only when that block is truly absent from the fitted model. Do not use NULL merely to ignore an existing block; the resulting back-transformation would be incorrect.

See the constrained-block example in the exchangeable APIM vignette.

Backend note

For glmmTMB, random effects in non-conditional components are ignored with a warning. See the vignette's extension to exchangeable random slopes section for the manual calculation.

In brms, cross-sectional and same-occasion partner dependence can be represented directly with unstr(time = member_position, gr = residual_group). With Gaussian outcomes, sigma ~ 1 supplies the common residual scale. Non-Gaussian families have no sigma parameter here; unstr() instead estimates a common latent residual scale and correlation on the linear-predictor scale. Here, member_position identifies the same two arbitrary positions within every group, and residual_group identifies dyads in cross-sectional data or dyad-occasions in longitudinal data. This direct specification applies when one covariance structure is sufficient. Separate composition-specific unstr() structures for mixed dyad types are not currently supported in a standard single-response brms model. For Gaussian mixed-dyad residual covariance, use glmmTMB. Shared/difference blocks remain relevant for higher-level random effects and can represent latent link-scale covariance in non-Gaussian models.

See Also

The exchangeable APIM vignette for the model specification, covariance derivation, and constrained-block example. Run vignette("apim", package = "dyadMLM") to open the installed version.

Examples

if (requireNamespace("glmmTMB", quietly = TRUE)) {
  example_data <- prepare_dyad_data(
    dyads_cross,
    dyad = coupleID,
    member = personID,
    role = gender,
    model_types = "none",
    # All three observed compositions in `dyads_cross` are detected and retained
    # by default. This example focuses on `female-female` dyads, so we restrict
    # the analysis here.
    keep_compositions = "female-female",
    seed = 123
  )

  model <- glmmTMB::glmmTMB(
    closeness ~ 1 +
      us(1 | coupleID) +
      us(0 + .dy_member_contrast_female_x_female_arbitrary | coupleID),
    dispformula = ~ 0,
    data = example_data
  )

  recover_exchangeable_covariance(model)
}


Resolve user-supplied composition references

Description

Resolve user-supplied composition references

Usage

resolve_composition_references(references, observed_compositions, arg_name)

Arguments

references

A character vector of composition references.

observed_compositions

Canonical composition labels observed in the data.

arg_name

Name of the user-facing argument for error messages.

Value

Canonical observed composition labels.


Resolve the DSM role order

Description

Checks the relationship between model_types, role, and dsm_role_order, validates the requested role order, and returns its stored representation.

Usage

resolve_dsm_role_order(dsm_role_order, model_types, has_role)

Arguments

dsm_role_order

The requested DSM role order.

model_types

The normalized model types.

has_role

Whether a role column was supplied.

Value

The validated, unnamed role order, or NULL.


Validate DIM compatibility

Description

Checks whether a dyadMLM_data object can be used for the currently supported undirected DIM construction. These models currently support only data with exactly one exchangeable dyad composition. Distinguishable or multiple exchangeable compositions are rejected until explicit role-contrast, composition-specific, or pooling support is added.

Usage

validate_dim_compatibility(data)

Arguments

data

A dyadMLM_data object after composition inference.

Value

Invisibly returns data when compatible.


Validate DSM compatibility

Description

Checks whether prepared data contain the single distinguishable dyad composition required by the DSM and whether its observed roles match the declared directional role order.

Usage

validate_dsm_compatibility(data)

Arguments

data

A dyadMLM_data object after composition inference.

Value

Invisibly returns data when compatible.


Validate dyadic input data

Description

Checks whether data has a valid long-format dyadic structure and returns it as a tibble with an additional dyadMLM_data class. Cross-sectional data may contain at most one row per member within each dyad. Intensive longitudinal data may contain at most one row per member and measurement occasion within each dyad.

Usage

validate_dyad_data(
  data,
  dyad,
  member,
  role = NULL,
  time = NULL,
  predictors = NULL,
  lag1_predictors = NULL,
  model_types = "apim",
  dsm_role_order = NULL,
  temporal_decomposition = c("auto", "2l", "none"),
  incomplete_dyads = c("error", "drop"),
  missing_role = c("error", "drop")
)

Arguments

data

A long-format data frame or tibble.

dyad

Column identifying the dyad.

member

Column identifying the two people or members within each dyad, such as a person ID.

role

Optional column identifying a stable member role, such as gender. Non-missing values must be consistent within each dyad x member. In repeated-measures data, an observed role is propagated to missing rows for the same member within a dyad. If no role is supplied, all dyads are treated as exchangeable.

time

Optional column identifying time or measurement order.

predictors

Optional variables to select and store as metadata for temporal predictor decomposition and model-helper functions.

lag1_predictors

Optional subset of predictors for which lag-1 model-ready columns should be created. Requires a finite, integer-valued numeric time variable.

model_types

Requested model-ready column families. Can contain one or more of "apim", "dim", and "dsm". "none" indicates no model-specific predictor construction and must be used alone.

dsm_role_order

For model_types = "dsm", a character vector giving the two distinguishable roles in the order used for directional differences. Required when DSM columns are requested and must be NULL otherwise.

temporal_decomposition

Requested temporal predictor decomposition strategy for predictors. "none" leaves predictors undecomposed before model-specific columns are constructed. "2l" indicates a two-level temporal predictor decomposition into within-person and between-person components. "auto" resolves to "2l" when both time and predictors are supplied, and to "none" otherwise. Model-specific helpers may apply additional conventions, such as grand-mean centering raw DIM and DSM dyad means.

incomplete_dyads

How to handle dyads with fewer than two unique members across all rows in data. "error" stops with an error and "drop" removes the entire dyad. A dyad with more than two unique members is invalid and always causes an error, regardless of this setting.

missing_role

How to handle dyads in which at least one member has no non-missing role value on any row. A consistent non-missing role observed for a member is propagated to that member's other rows before this policy is applied. "error" stops with an error and "drop" removes the entire dyad. Conflicting non-missing roles always cause an error. Ignored when no role column is supplied.

Value

A tibble with class dyadMLM_data and metadata about the dyad, member, optional role, and optional time columns.