# LUCID’s Three Model Architectures: Early, Parallel, and Serial – Binary Outcome (HELIX Example)

## 1) Tutorial Goal and Scope

This tutorial is designed as a hands-on, end-to-end guide for fitting LUCID models on HELIX-style multi-omics data, with a binary outcome. Its companion vignette, `lucid_3models_normal_outcome.Rmd`, walks through the identical pipeline step by step for a continuous outcome, including the `lucid()` tuning wrapper, missing-data diagnostics, and prediction/g-computation – material this vignette does not repeat. Visualization (Sankey diagram, cluster omics profiles) is shown here too, since neither depends on the outcome family.

What you will learn here:

- How to prepare common inputs (`G`, `Z`, `Y_binary`, `CoG`, `CoY`) from one dataset.
- How to fit and interpret, for a binary outcome:
  - LUCID early integration
  - LUCID in parallel
  - LUCID in serial (all-early stages and mixed parallel+early stages)
- How to run the practical two-step strategy:
  1. Penalized screening fit (`Rho_* > 0`) for selection.
  2. Zero-penalty refit (`Rho_* = 0`) for stable inference + bootstrap CI.
- How `predict_lucid()`’s `response` argument controls whether a binary outcome comes back as class labels or as probabilities.

Important runtime note:

- Sections 7-10 fix `K` and do the screen-then-refit by hand, mirroring the continuous-outcome companion vignette’s early/parallel/serial sections.
- We keep `K` fixed and use small bootstrap `R` so the vignette remains reproducible and fast.

## 2) Data Objects and Statistical Roles

The HELIX example data (`simulated_HELIX_data.rda`) provides:

- `phenotype`: exposure/covariate/outcome-style variables.
- three omics layers: `methylome`, `transcriptome`, `miRNA`.

Model inputs used throughout:

- `G`: exposure matrix (main predictors of latent cluster assignment). Built with a known signal structure – three exposures correlated with the outcome and six pure-noise exposures – so that feature selection can be checked against the truth rather than taken on trust. The outcome itself is deliberately kept out of `G`.
- `Z`: omics input (matrix for early, list for parallel, nested list for serial mixed).
- `Y`: continuous outcome vector; `Y_binary` is its median split, used to demonstrate the binary outcome model on the same subjects.
- `CoG`: covariates for `G -> X` model.
- `CoY`: covariates for `X -> Y` model.

## 3) Hyperparameter Guide (Practical)

Hyperparameter | Meaning | Tutorial choice and rationale
--- | --- | ---
`K` | number of latent clusters | fixed to small values for speed and interpretability
`Rho_G` | penalty on `G -> X` coefficients | positive in screening fit, zero in inference refit
`Rho_Z_Mu` | penalty on cluster-specific omics means | positive in screening fit, zero in inference refit
`Rho_Z_Cov` | penalty on omics covariance matrices | positive in screening fit, zero in inference refit
`max_itr`, `max_tot.itr`, `tol` | EM controls | modest values to balance speed/stability
`family` | outcome model family | `normal` for this tutorial
`seed` | reproducibility | fixed before each fit/bootstrap

## 4) Setup and Source Package Code

    # Keep knitting on error, so that one failing step reports itself and the rest of
    # the tutorial still runs. The status table in section 13 records what happened.
    knitr::opts_chunk$set(error = TRUE)

    # Lightweight registry so the document can verify itself rather than relying on
    # the reader to notice a missing output.
    .reg <- new.env(parent = emptyenv()); .reg$rows <- list()
    check_obj <- function(name, expected_class = NULL, section = "") {
      ok <- exists(name, envir = globalenv())
      cls <- if (ok) class(get(name, envir = globalenv()))[1] else NA_character_
      status <- if (!ok) "MISSING"
                else if (!is.null(expected_class) && !identical(cls, expected_class)) "unexpected class"
                else "ok"
      .reg$rows[[length(.reg$rows) + 1L]] <-
        data.frame(section = section, object = name, class = cls,
                   status = status, stringsAsFactors = FALSE)
      invisible(NULL)
    }

    library(LUCIDus)

    # The HELIX simulation object bundled with the package.
    data(simulated_HELIX_data)

## 5) Build Modeling Inputs (With Missingness Injection)

This chunk creates a compact tutorial dataset and deliberately injects both:

- listwise missingness (whole row missing for a layer), and
- sporadic missingness (some cells missing),

so we can observe missing-data handling in summaries.

    # Use a smaller subset for vignette speed while preserving model behavior.
    idx <- 1:90
    ph <- simulated_HELIX_data$phenotype[idx, ]
    n <- nrow(ph)

    set.seed(2026)

    # ---------------------------------------------------------------------------
    # A tutorial dataset with a KNOWN answer.
    #
    # The HELIX omics matrices are real simulated data with their own structure, and
    # the exposures shipped with them have no relationship to it. That is fine for
    # demonstrating that code runs, but it makes feature selection impossible to
    # judge: there is no right answer to compare against. So we plant one.
    #
    # The generating story, which is the DAG LUCID assumes:
    #
    #     causal exposures  ->  latent subgroup  ->  omics profile
    #                                            ->  outcome
    #
    # Three exposures carry the subgroup signal with graded strength; six are pure
    # noise. Half the features of each omics layer are shifted by subgroup
    # membership; the rest are left as they came. Selection therefore has an
    # unambiguous target, and the tutorial can check its answer instead of asserting
    # it.
    # ---------------------------------------------------------------------------

    # The true latent subgroup. Retained so every selection claim below can be
    # checked against it.
    x_true <- rbinom(n, 1, 0.5)

    # Exposures. g_causal_* predict subgroup membership; g_noise_* do not.
    # Effect sizes are deliberately moderate. Stronger exposures make selection
    # look better but drive the G -> X model to saturation, where every subject sits
    # at posterior probability 1 and no counterfactual shift can move anything --
    # which would make the g-computation demonstration in the continuous-outcome
    # companion vignette vacuous.
    G <- cbind(
      g_causal_1 =  1.0 * (x_true - 0.5) + rnorm(n, sd = 0.8),   # strongest
      g_causal_2 = -0.8 * (x_true - 0.5) + rnorm(n, sd = 0.8),   # moderate, negative
      g_causal_3 =  0.6 * (x_true - 0.5) + rnorm(n, sd = 0.8),   # weakest
      g_noise_1 = rnorm(n), g_noise_2 = rnorm(n), g_noise_3 = rnorm(n),
      g_noise_4 = rnorm(n), g_noise_5 = rnorm(n), g_noise_6 = rnorm(n)
    )
    G <- as.matrix(scale(G))

    causal_exposures <- c("g_causal_1", "g_causal_2", "g_causal_3")

    # Exposure penalty used throughout. The continuous-outcome companion vignette
    # shows what this value recovers and sweeps it, along with the omics penalty,
    # separately.
    RHO_G <- 0.05

    # Covariates for G->X (CoG) and X->Y (CoY).
    # Here we use age-related and sex covariates from phenotype.
    CoG <- cbind(
      hs_child_age_yrs_None = as.numeric(ph$hs_child_age_yrs_None),
      sex_male = as.numeric(ph$e3_sex_None == "male")
    )
    CoY <- CoG

    # Two outcomes on the SAME subjects, so the normal and binary results below are
    # directly comparable: the only thing that changes between them is the outcome
    # model, not the sample, the omics, or the injected missingness.
    #
    # Continuous outcome: the real CK-18 measurement, plus a subgroup effect so the
    # cluster -> outcome arm of the model has something to estimate.
    Y <- as.numeric(ph$ck18_scaled) + 1.2 * x_true

    # Binary outcome: median split. The median is used rather than a higher
    # threshold because it splits these 90 subjects 45/45, and a balanced outcome
    # gives the K = 2 outcome model the most to work with at this sample size.
    Y_binary <- as.integer(Y > median(Y))
    cat("binary outcome balance:\n"); print(table(Y_binary))

    ## binary outcome balance:

    ## Y_binary
    ##  0  1
    ## 45 45

    # Construct three omics layers and standardize each, then plant the subgroup
    # signal in the first three features of every layer. The remaining seven per
    # layer are left as they came and act as omics noise.
    meth <- scale(simulated_HELIX_data$methylome[idx, 1:10, drop = FALSE])
    tran <- scale(simulated_HELIX_data$transcriptome[idx, 1:10, drop = FALSE])
    mir  <- scale(simulated_HELIX_data$miRNA[idx, 1:10, drop = FALSE])

    signal_features <- 1:3
    omics_shift <- 3.0
    meth[, signal_features] <- meth[, signal_features] + omics_shift * x_true
    tran[, signal_features] <- tran[, signal_features] - omics_shift * x_true
    mir[,  signal_features] <- mir[,  signal_features] + omics_shift * x_true

    # Column positions of the signal features once the layers are stacked for the
    # early model, so selection can be scored against them later.
    signal_cols_early <- c(signal_features,
                           ncol(meth) + signal_features,
                           ncol(meth) + ncol(tran) + signal_features)

    # Early model uses one combined Z matrix.
    Z_early <- cbind(meth, tran, mir)

    # Parallel model uses list-of-layers.
    Z_parallel <- list(methylome = meth, transcriptome = tran, miRNA = mir)

    # Inject listwise + sporadic missingness for demonstration.
    Z_early_miss <- Z_early
    Z_early_miss[1, ] <- NA      # listwise row
    Z_early_miss[2:4, 1] <- NA   # sporadic block
    Z_early_miss[5, 3] <- NA     # sporadic cell

    Z_parallel_miss <- Z_parallel
    Z_parallel_miss[[1]][1, ] <- NA  # listwise in layer 1
    Z_parallel_miss[[2]][2, 2] <- NA # sporadic in layer 2
    Z_parallel_miss[[3]][3, 1] <- NA # sporadic in layer 3

    # Quick structural sanity check.
    str(list(
      G = G,
      CoG = CoG,
      CoY = CoY,
      Y = Y,
      Z_early = Z_early_miss,
      Z_parallel = Z_parallel_miss
    ), max.level = 1)

    ## List of 6
    ##  $ G         : num [1:90, 1:9] 1.587 1.051 -0.744 0.168 0.834 ...
    ##   ..- attr(*, "dimnames")=List of 2
    ##   ..- attr(*, "scaled:center")= Named num [1:9] -0.0377 0.0866 0.0539 -0.0695 0.1933 ...
    ##   .. ..- attr(*, "names")= chr [1:9] "g_causal_1" "g_causal_2" "g_causal_3" "g_noise_1" ...
    ##   ..- attr(*, "scaled:scale")= Named num [1:9] 1.053 0.864 0.732 1.038 1.211 ...
    ##   .. ..- attr(*, "names")= chr [1:9] "g_causal_1" "g_causal_2" "g_causal_3" "g_noise_1" ...
    ##  $ CoG       : num [1:90, 1:2] 7.48 7.21 8.59 8.48 6.06 ...
    ##   ..- attr(*, "dimnames")=List of 2
    ##  $ CoY       : num [1:90, 1:2] 7.48 7.21 8.59 8.48 6.06 ...
    ##   ..- attr(*, "dimnames")=List of 2
    ##  $ Y         : num [1:90] 1.817 1.67 0.479 -0.281 0.394 ...
    ##  $ Z_early   : num [1:90, 1:30] NA NA NA NA 3.17 ...
    ##   ..- attr(*, "dimnames")=List of 2
    ##  $ Z_parallel:List of 3

## 6) Helper Functions for Selected-Feature Refit

These helpers implement a robust refit pipeline:

1. Read feature-selection indicators from penalized fit.
2. Build selected-only `G`/`Z` inputs.
3. Refit with all penalties set to zero for bootstrap inference.

    # get_selected_G()/get_selected_Z() (from the package itself) already return a
    # well-shaped, aligned logical mask straight from the fitted object -- no
    # length mismatch is possible, since they derive it from the model's own
    # recorded fields. The one thing left for a tutorial to decide is what to do
    # if a penalty happened to deselect EVERY feature: refitting on zero columns
    # would fail, so this keeps everything instead in that one edge case.
    keep_or_all <- function(mask) if (any(mask, na.rm = TRUE)) mask else rep(TRUE, length(mask))

    # Build selected-only inputs for early model.
    prepare_early_selected_inputs <- function(fit_pen, G, Z) {
      list(
        G = as.matrix(G[, keep_or_all(get_selected_G(fit_pen)), drop = FALSE]),
        Z = as.matrix(Z[, keep_or_all(get_selected_Z(fit_pen)), drop = FALSE])
      )
    }

    # Build selected-only inputs for parallel model.
    prepare_parallel_selected_inputs <- function(fit_pen, G, Z) {
      keep_g <- keep_or_all(get_selected_G(fit_pen))
      Z_sel <- lapply(seq_along(Z), function(i) {
        zi <- as.matrix(Z[[i]])
        zi[, keep_or_all(get_selected_Z(fit_pen, layer = i)), drop = FALSE]
      })
      names(Z_sel) <- names(Z)
      list(
        G = as.matrix(G[, keep_g, drop = FALSE]),
        Z = Z_sel
      )
    }

    # Serial stage>1 uses latent-cluster-derived "G" internally.
    # We therefore subset stage-1 original G and each stage's Z where applicable.
    prepare_serial_selected_inputs <- function(fit_pen, G, Z) {
      G_refit <- as.matrix(G)
      keep_g1 <- get_selected_G(fit_pen)
      if (length(keep_g1) == ncol(G_refit)) {
        G_refit <- G_refit[, keep_or_all(keep_g1), drop = FALSE]
      }

      selected_z <- get_selected_Z(fit_pen)
      Z_refit <- Z
      for (i in seq_along(fit_pen$submodel)) {
        sm <- fit_pen$submodel[[i]]
        if (inherits(sm, "early_lucid")) {
          zi <- as.matrix(Z_refit[[i]])
          Z_refit[[i]] <- zi[, keep_or_all(selected_z[[i]]), drop = FALSE]
        } else if (inherits(sm, "lucid_parallel")) {
          zi_list <- Z_refit[[i]]
          for (j in seq_along(zi_list)) {
            zij <- as.matrix(zi_list[[j]])
            zi_list[[j]] <- zij[, keep_or_all(selected_z[[i]][[j]]), drop = FALSE]
          }
          Z_refit[[i]] <- zi_list
        }
      }

      list(G = G_refit, Z = Z_refit)
    }

    # Zero-penalty refit, for any model type.
    #
    # The three model types previously had three byte-identical wrappers differing
    # only in `lucid_model` and whether `useY` was forwarded; they are one function
    # here. Everything about the model -- family, K, initialization, EM controls --
    # is carried over from the screening fit, so the ONLY difference between the
    # screening fit and this one is that the penalties are zero. That is what makes
    # the refit estimates unshrunk and therefore suitable for bootstrap inference.
    refit_selected <- function(model_type, fit_pen, inputs, Y,
                               CoG = NULL, CoY = NULL, seed = 1, verbose = FALSE) {
      args <- list(
        lucid_model = model_type,
        G = inputs$G,
        Z = inputs$Z,
        Y = Y,
        CoG = CoG,
        CoY = CoY,
        family = fit_pen$family,
        K = fit_pen$K,
        init_omic.data.model = fit_pen$init_omic.data.model,
        init_impute = fit_pen$init_impute,
        init_par = fit_pen$init_par,
        Rho_G = 0,
        Rho_Z_Mu = 0,
        Rho_Z_Cov = 0,
        max_itr = fit_pen$em_control$max_itr,
        max_tot.itr = fit_pen$em_control$max_tot.itr,
        tol = fit_pen$em_control$tol,
        seed = seed,
        verbose = verbose
      )
      # Every fitted class records useY, so it is carried over for all three model
      # types. The original three wrappers omitted it on the early path, which meant
      # an unsupervised screening fit would have been silently refitted supervised.
      args$useY <- fit_pen$useY
      do.call(estimate_lucid, args)
    }

    # Dispatcher for the three input-preparation helpers above.
    prepare_selected_inputs <- function(model_type, fit_pen, G, Z) {
      switch(model_type,
        early    = prepare_early_selected_inputs(fit_pen, G, Z),
        parallel = prepare_parallel_selected_inputs(fit_pen, G, Z),
        serial   = prepare_serial_selected_inputs(fit_pen, G, Z),
        stop("unknown model_type: ", model_type)
      )
    }

    # Compact stage-wise feature-selection report for serial fits, built entirely
    # from get_selected_G()/get_selected_Z() -- no per-stage dispatch of its own.
    serial_selection_report <- function(fit_serial_pen) {
      selected_z <- get_selected_Z(fit_serial_pen)
      out <- vector("list", length(fit_serial_pen$submodel))
      for (i in seq_along(fit_serial_pen$submodel)) {
        sm <- fit_serial_pen$submodel[[i]]
        if (inherits(sm, "early_lucid")) {
          out[[i]] <- list(
            stage = i,
            model = "early",
            selected_G = if (i == 1) sum(get_selected_G(fit_serial_pen)) else NA,
            total_G = if (i == 1) length(get_selected_G(fit_serial_pen)) else NA,
            selected_Z = sum(selected_z[[i]]),
            total_Z = length(selected_z[[i]])
          )
        } else {
          out[[i]] <- list(
            stage = i,
            model = "parallel",
            selected_G = if (i == 1) sum(get_selected_G(fit_serial_pen)) else NA,
            total_G = if (i == 1) length(get_selected_G(fit_serial_pen)) else NA,
            selected_Z_by_layer = sapply(selected_z[[i]], sum),
            total_Z_by_layer = sapply(selected_z[[i]], length)
          )
        }
      }
      out
    }

## 7) Early Model Tutorial: Binary Outcome

`Y_binary` is the median split of the continuous outcome built in section 5, on the same subjects, with the same injected missingness. Fitting proceeds in the same three explicit steps as the continuous-outcome companion vignette: penalized screening fit, zero-penalty refit on the survivors, then bootstrap. Only the outcome model changes.

### 7.1 Penalized screening fit

    set.seed(1105)

    early_pen_bin <- estimate_lucid(
      lucid_model = "early",
      G = G,
      Z = Z_early_miss,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      family = "binary",
      K = 2,
      Rho_G = RHO_G,
      Rho_Z_Mu = 0,
      Rho_Z_Cov = 0,
      max_itr = 15,
      max_tot.itr = 40,
      tol = 1e-2,
      seed = 1105,
      verbose = FALSE
    )

    ## Fitting LUCID early model (K = 2)...
    ## Finished LUCID early model. Selected G: 4/9; Selected Z: 30/30.

    summary(early_pen_bin)

    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 4 / 90 (4.4%)
    ##   Missing cells total    : 34 / 2700 (1.3%)
    ##
    ## Feature selection overview
    ##   G features selected    : 4 / 9 (44.4%)
    ##   Z features selected    : 30 / 30 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2905.97
    ##   BIC                    : 10316.25
    ##   Number of parameters   : 1001
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.12272266  0.8845089
    ## LC2                    0.46969468  1.5995058
    ## hs_child_age_yrs_None -0.04731969  0.9537824
    ## sex_male               0.20877813  1.2321716
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                   mu_cluster1  mu_cluster2
    ## cg_GRHL3          1.070130027  1.467807192
    ## cg_BTF3L4         1.177269715  1.448826628
    ## cg_AL358472.7     1.111915428  1.503179122
    ## cg_HDGF           0.039776680 -0.007386556
    ## cg_TDRD5          0.060508379 -0.025499538
    ## cg_CSRNP3        -0.315770044  0.106570901
    ## cg_HSPD1          0.247344951 -0.091051585
    ## cg_EPM2AIP1       0.095823370 -0.031917066
    ## cg_AC025171.1    -0.115955949  0.016990794
    ## cg_VTRNA1_3       0.008624239 -0.017515088
    ## tc_TC01006069_nc -1.032187726 -1.500901334
    ## tc_SLC9A4        -1.107493413 -1.473018657
    ## tc_RAB6C_AS1     -1.041453398 -1.509522359
    ## tc_LOC100129029   0.268771608 -0.068905489
    ## tc_BRE           -0.173495627  0.059580555
    ## tc_TC03001220_nc  0.135402054 -0.075080553
    ## tc_TC04002114_nc  0.603537655 -0.187588817
    ## tc_TC04002369_nc -0.479441927  0.160477541
    ## tc_BEND4         -0.216113295  0.076788618
    ## tc_SLC9A3         0.169377726 -0.032648173
    ## miR.101.3p        0.028992443  1.824713207
    ## miR.125a.5p       0.785178492  1.588652258
    ## miR.125b.1.3p     1.448589102  1.366871494
    ## miR.127.3p       -0.032178676  0.021562673
    ## miR.140.5p       -0.804412403  0.265078938
    ## miR.142.3p       -1.186015202  0.381068666
    ## miR.144.5p       -1.122938565  0.360302094
    ## miR.19a.3p       -1.102814044  0.358147262
    ## miR.19b.3p       -1.087845497  0.355544328
    ## miR.21.5p        -0.882421524  0.294568136
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each exposure
    ##                                       beta         OR
    ## (Intercept).cluster2            2.42580914 11.3113782
    ## g_causal_2.cluster2            -0.04231731  0.9585656
    ## g_causal_3.cluster2            -0.13739149  0.8716289
    ## g_noise_3.cluster2              0.18897532  1.2080111
    ## g_noise_4.cluster2              0.32063240  1.3779989
    ## hs_child_age_yrs_None.cluster2 -0.13946483  0.8698236
    ## sex_male.cluster2              -0.51128754  0.5997229

### 7.2 Zero-penalty selected-only refit

    set.seed(1106)

    early_inputs_bin <- prepare_early_selected_inputs(early_pen_bin, G, Z_early_miss)

    early_bin <- list(fit_pen = early_pen_bin, inputs = early_inputs_bin)
    early_bin$fit_refit <- refit_selected(
      "early",
      fit_pen = early_pen_bin,
      inputs = early_inputs_bin,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      seed = 1106
    )

    ## Fitting LUCID early model (K = 2)...
    ## Finished LUCID early model.

    summary(early_bin$fit_refit)

    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 4 / 90 (4.4%)
    ##   Missing cells total    : 34 / 2700 (1.3%)
    ##
    ## Feature selection overview
    ##   G features selected    : 4 / 4 (100.0%)
    ##   Z features selected    : 30 / 30 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2884.76
    ##   BIC                    : 10273.84
    ##   Number of parameters   : 1001
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.26156022  0.7698495
    ## LC2                    0.56721855  1.7633555
    ## hs_child_age_yrs_None -0.03726512  0.9634207
    ## sex_male               0.20745520  1.2305426
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                  mu_cluster1  mu_cluster2
    ## cg_GRHL3          1.03142172  1.485669045
    ## cg_BTF3L4         1.13354093  1.468186310
    ## cg_AL358472.7     1.10522727  1.513235444
    ## cg_HDGF           0.08486240 -0.023816710
    ## cg_TDRD5          0.13378426 -0.052344620
    ## cg_CSRNP3        -0.22314677  0.080690254
    ## cg_HSPD1          0.31181222 -0.118653340
    ## cg_EPM2AIP1       0.04510631 -0.016176138
    ## cg_AC025171.1    -0.17656817  0.040133586
    ## cg_VTRNA1_3      -0.03825754 -0.001570394
    ## tc_TC01006069_nc -0.99865039 -1.519698204
    ## tc_SLC9A4        -1.00275913 -1.515067178
    ## tc_RAB6C_AS1     -1.04855340 -1.514144958
    ## tc_LOC100129029   0.26480149 -0.072641897
    ## tc_BRE           -0.16650572  0.060678315
    ## tc_TC03001220_nc  0.12780429 -0.075623850
    ## tc_TC04002114_nc  0.52738071 -0.173039573
    ## tc_TC04002369_nc -0.47160615  0.167449463
    ## tc_BEND4         -0.19013278  0.072174181
    ## tc_SLC9A3         0.13908256 -0.025151868
    ## miR.101.3p        0.02352182  1.853848697
    ## miR.125a.5p       0.78364918  1.601368451
    ## miR.125b.1.3p     1.35534448  1.398133617
    ## miR.127.3p       -0.07994581  0.039027202
    ## miR.140.5p       -0.77362299  0.270563806
    ## miR.142.3p       -1.13144910  0.385811002
    ## miR.144.5p       -1.05248948  0.358236957
    ## miR.19a.3p       -1.08798247  0.375130264
    ## miR.19b.3p       -1.06082542  0.368012483
    ## miR.21.5p        -0.83772616  0.296835971
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each exposure
    ##                                      beta         OR
    ## (Intercept).cluster2            4.1549935 63.7515487
    ## g_causal_2.cluster2            -0.6992095  0.4969780
    ## g_causal_3.cluster2            -0.6882463  0.5024565
    ## g_noise_3.cluster2              0.4955328  1.6413725
    ## g_noise_4.cluster2              0.9527485  2.5928261
    ## hs_child_age_yrs_None.cluster2 -0.3368294  0.7140306
    ## sex_male.cluster2              -0.5743772  0.5630554

### 7.3 Bootstrap CI + summary

    set.seed(1107)

    early_bin$boot <- boot_lucid(
      G = early_inputs_bin$G,
      Z = early_inputs_bin$Z,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      model = early_bin$fit_refit,
      R = 30,
      conf = 0.90
    )

    summary(early_bin$fit_refit, boot.se = early_bin$boot)

    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 4 / 90 (4.4%)
    ##   Missing cells total    : 34 / 2700 (1.3%)
    ##
    ## Feature selection overview
    ##   G features selected    : 4 / 4 (100.0%)
    ##   Z features selected    : 30 / 30 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2884.76
    ##   BIC                    : 10273.84
    ##   Number of parameters   : 1001
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds / log OR (coefficient scale) with bootstrap interval, and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                             gamma norm_lower norm_upper        OR   OR_lower
    ## (Intercept)           -0.26156022 -3.4935177  3.0652480 0.7698495 0.03039377
    ## LC2                    0.56721855 -1.8888598  2.9687038 1.7633555 0.15124417
    ## hs_child_age_yrs_None -0.03726512 -0.4559246  0.3224541 0.9634207 0.63386164
    ## sex_male               0.20745520 -0.5404650  1.1216887 1.2305426 0.58247734
    ##                        OR_upper sig
    ## (Intercept)           21.439779
    ## LC2                   19.466670
    ## hs_child_age_yrs_None  1.380512
    ## sex_male               3.070034
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                               estimate   norm_lower  norm_upper sig
    ## cg_GRHL3.cluster1          1.031421718 -1.041434639  2.62110782
    ## cg_BTF3L4.cluster1         1.133540932 -0.891976807  2.81897991
    ## cg_AL358472.7.cluster1     1.105227268 -1.189666798  3.12787952
    ## cg_HDGF.cluster1           0.084862399 -0.405275458  0.64012332
    ## cg_TDRD5.cluster1          0.133784259 -0.215996221  0.59941986
    ## cg_CSRNP3.cluster1        -0.223146771 -0.758009906 -0.06712038   *
    ## cg_HSPD1.cluster1          0.311812221  0.229274853  0.81380247   *
    ## cg_EPM2AIP1.cluster1       0.045106312 -0.239965821  0.54760410
    ## cg_AC025171.1.cluster1    -0.176568168 -0.676871135  0.12375907
    ## cg_VTRNA1_3.cluster1      -0.038257539 -0.405553718  0.30759530
    ## tc_TC01006069_nc.cluster1 -0.998650387 -2.479983249  0.94043502
    ## tc_SLC9A4.cluster1        -1.002759132 -2.884763519  1.19937153
    ## tc_RAB6C_AS1.cluster1     -1.048553404 -2.475114052  0.86323656
    ## tc_LOC100129029.cluster1   0.264801495  0.116244628  0.76860656   *
    ## tc_BRE.cluster1           -0.166505718 -0.703241428  0.05061801
    ## tc_TC03001220_nc.cluster1  0.127804285 -0.105877434  0.72023492
    ## tc_TC04002114_nc.cluster1  0.527380708  0.714871447  1.39089515   *
    ## tc_TC04002369_nc.cluster1 -0.471606146 -1.099248344 -0.45005736   *
    ## tc_BEND4.cluster1         -0.190132779 -0.701333397  0.04319672
    ## tc_SLC9A3.cluster1         0.139082560 -0.260681031  0.53331001
    ## miR.101.3p.cluster1        0.023521821 -2.848947956  0.75776580
    ## miR.125a.5p.cluster1       0.783649178 -1.152926286  1.95035873
    ## miR.125b.1.3p.cluster1     1.355344480 -0.431095108  3.31466696
    ## miR.127.3p.cluster1       -0.079945810 -0.473186009  0.33569052
    ## miR.140.5p.cluster1       -0.773622986 -1.966398967 -0.85796534   *
    ## miR.142.3p.cluster1       -1.131449104 -2.754337200 -1.42854740   *
    ## miR.144.5p.cluster1       -1.052489480 -2.634441139 -1.30119950   *
    ## miR.19a.3p.cluster1       -1.087982474 -2.645591315 -1.45107391   *
    ## miR.19b.3p.cluster1       -1.060825418 -2.680800236 -1.29351220   *
    ## miR.21.5p.cluster1        -0.837726158 -2.289345889 -0.84201118   *
    ## cg_GRHL3.cluster2          1.485669045 -0.263882845  2.65835349
    ## cg_BTF3L4.cluster2         1.468186310 -0.298081009  2.66101414
    ## cg_AL358472.7.cluster2     1.513235444 -0.658939331  2.98620735
    ## cg_HDGF.cluster2          -0.023816710 -0.381569456  0.27257908
    ## cg_TDRD5.cluster2         -0.052344620 -0.386952280  0.32044199
    ## cg_CSRNP3.cluster2         0.080690254 -0.211724472  0.44145106
    ## cg_HSPD1.cluster2         -0.118653340 -0.495060843  0.02820738
    ## cg_EPM2AIP1.cluster2      -0.016176138 -0.368312457  0.18575793
    ## cg_AC025171.1.cluster2     0.040133586 -0.260646396  0.30232956
    ## cg_VTRNA1_3.cluster2      -0.001570394 -0.259518584  0.37994818
    ## tc_TC01006069_nc.cluster2 -1.519698204 -2.655783131  0.25221012
    ## tc_SLC9A4.cluster2        -1.515067178 -2.734074292  0.47819854
    ## tc_RAB6C_AS1.cluster2     -1.514144958 -2.725067655  0.28020695
    ## tc_LOC100129029.cluster2  -0.072641897 -0.453724080  0.20992837
    ## tc_BRE.cluster2            0.060678315 -0.071177373  0.43891345
    ## tc_TC03001220_nc.cluster2 -0.075623850 -0.379528028  0.03601861
    ## tc_TC04002114_nc.cluster2 -0.173039573 -0.600881976 -0.02703229   *
    ## tc_TC04002369_nc.cluster2  0.167449463  0.018796804  0.54860117   *
    ## tc_BEND4.cluster2          0.072174181 -0.059653583  0.34990236
    ## tc_SLC9A3.cluster2        -0.025151868 -0.400995241  0.26611311
    ## miR.101.3p.cluster2        1.853848697  0.102780808  3.39972778   *
    ## miR.125a.5p.cluster2       1.601368451 -0.012499536  2.73947268
    ## miR.125b.1.3p.cluster2     1.398133617 -0.583276805  2.50101785
    ## miR.127.3p.cluster2        0.039027202 -0.413517921  0.32154618
    ## miR.140.5p.cluster2        0.270563806  0.008543285  0.87336660   *
    ## miR.142.3p.cluster2        0.385811002  0.164158653  1.11082607   *
    ## miR.144.5p.cluster2        0.358236957  0.176477535  1.13385547   *
    ## miR.19a.3p.cluster2        0.375130264  0.226647634  1.06123560   *
    ## miR.19b.3p.cluster2        0.368012483  0.175217353  1.08570335   *
    ## miR.21.5p.cluster2         0.296835971  0.050857988  0.94750634   *
    ##
    ## (3) E: cluster-assignment coefficients (log-odds scale) for each exposure, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                                  estimate norm_lower norm_upper         OR
    ## (Intercept).cluster2            4.1549935 -1.9539819 15.7829203 63.7515487
    ## g_causal_2.cluster2            -0.6992095 -3.0903442  1.4913927  0.4969780
    ## g_causal_3.cluster2            -0.6882463 -2.5809924 -0.1419855  0.5024565
    ## g_noise_3.cluster2              0.4955328  0.3014974  1.5859551  1.6413725
    ## g_noise_4.cluster2              0.9527485  0.2422198  3.0928394  2.5928261
    ## hs_child_age_yrs_None.cluster2 -0.3368294 -1.4706012  0.4035910  0.7140306
    ## sex_male.cluster2              -0.5743772 -2.6125911  0.7208267  0.5630554
    ##                                  OR_lower     OR_upper sig
    ## (Intercept).cluster2           0.14170867 7.152127e+06
    ## g_causal_2.cluster2            0.04548629 4.443280e+00
    ## g_causal_3.cluster2            0.07569884 8.676338e-01   *
    ## g_noise_3.cluster2             1.35188159 4.883954e+00   *
    ## g_noise_4.cluster2             1.27407425 2.203957e+01   *
    ## hs_child_age_yrs_None.cluster2 0.22978730 1.497192e+00
    ## sex_male.cluster2              0.07334426 2.056132e+00

Every bootstrap CI table now also carries an odds-ratio view (`OR`, `OR_lower`, `OR_upper`) alongside the coefficient-scale columns and `sig`, and the exposure/assignment `(3) E` table includes its intercept and covariate rows. `R = 30` keeps the vignette fast; a real analysis needs `R` in the hundreds. A poorly identified coefficient – the exposure-model intercept, or a covariate nearly collinear with cluster membership – can show a very wide or off-centre interval; that is the bootstrap being honest, not a bug.

Three differences are worth noting in that output whenever an outcome switches from continuous to binary – they are the whole payload of the outcome-family axis, and apply identically to the parallel and serial models fitted below:

- Outcome effects are on the log-odds scale. For a continuous outcome the cluster terms are means; here they are log-odds, and the printed exposure effects carry odds ratios alongside the coefficients.
- There is no residual standard deviation. A Bernoulli outcome has no dispersion parameter to estimate, so the `sigma` row that appears for a normal outcome is simply absent.
- The omics and exposure parts are unchanged in form. `res_Mu` and `res_Beta` mean the same thing regardless of family – only the `X -> Y` arm differs.

## 8) Parallel Model Tutorial: Binary Outcome

### 8.1 Penalized screening fit

    set.seed(1205)

    parallel_pen_bin <- estimate_lucid(
      lucid_model = "parallel",
      G = G,
      Z = Z_parallel_miss,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      family = "binary",
      K = c(2, 2, 2),
      Rho_G = RHO_G,
      Rho_Z_Mu = 0,
      Rho_Z_Cov = 0,
      max_itr = 15,
      max_tot.itr = 40,
      tol = 1e-2,
      seed = 1205,
      verbose = FALSE
    )

    ## Fitting LUCID parallel model (3 layers)...
    ## Finished LUCID parallel model. Selected G: 3/9; Selected Z by layer: 10/10, 10/10, 10/10.

    summary(parallel_pen_bin)

    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binomial
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##   Layer 3 listwise rows : 0 / 90 (0.0%)
    ##   Layer 3 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 3 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 9 (33.3%)
    ##   G features by layer
    ##     Layer 1              : 3 / 9 (33.3%)
    ##     Layer 2              : 3 / 9 (33.3%)
    ##     Layer 3              : 3 / 9 (33.3%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##     Layer 3 selected     : 10 / 10 (100.0%)
    ##     Layer 3 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3344.66
    ##   BIC                    : 8552.24
    ##   Number of parameters   : 414
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): intercept and log OR for non-reference clusters for each layer (and log OR of covariate if included)
    ##                              gamma   exp(gamma)
    ## (Intercept)           -15.27511103 2.323291e-07
    ## Layer1_LC2              0.17476067 1.190961e+00
    ## Layer2_LC2             14.66202952 2.331517e+06
    ## Layer3_LC2             16.65458497 1.709993e+07
    ## hs_child_age_yrs_None  -0.03654061 9.641189e-01
    ## sex_male               -0.21552887 8.061150e-01
    ##
    ## (2) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01956752  2.78717791
    ## cg_BTF3L4      0.02555562  2.77153309
    ## cg_AL358472.7 -0.05854745  2.85130770
    ## cg_HDGF        0.19385174 -0.19001699
    ## cg_TDRD5      -0.13904990  0.13392791
    ## cg_CSRNP3     -0.07421134  0.08042597
    ## cg_HSPD1       0.18528278 -0.20485556
    ## cg_EPM2AIP1   -0.03945871  0.03975783
    ## cg_AC025171.1 -0.07344147  0.04311865
    ## cg_VTRNA1_3    0.15848085 -0.18480017
    ##
    ## Layer 2
    ##
    ##                   mu_cluster1  mu_cluster2
    ## tc_TC01006069_nc -2.761402720 -0.148980731
    ## tc_SLC9A4        -3.020385364  0.062508410
    ## tc_RAB6C_AS1     -2.734391777 -0.173801608
    ## tc_LOC100129029  -0.047094195  0.043275766
    ## tc_BRE           -0.073350292  0.067403001
    ## tc_TC03001220_nc  0.029037497 -0.026683118
    ## tc_TC04002114_nc -0.032967723  0.030294678
    ## tc_TC04002369_nc  0.133840868 -0.122988960
    ## tc_BEND4         -0.086472203  0.079460979
    ## tc_SLC9A3        -0.007464973  0.006859708
    ##
    ## Layer 3
    ##
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each exposure for each layer
    ## Layer 1
    ##
    ##                                        beta        OR
    ## (Intercept).cluster2           -0.235055675 0.7905268
    ## g_causal_1.cluster2             0.808354645 2.2442124
    ## g_causal_2.cluster2            -0.771852992 0.4621559
    ## g_causal_3.cluster2             0.053496727 1.0549535
    ## hs_child_age_yrs_None.cluster2 -0.004506053 0.9955041
    ## sex_male.cluster2               0.629540814 1.8767486
    ##
    ## Layer 2
    ##
    ##                                       beta        OR
    ## (Intercept).cluster2            0.80863599 2.2448439
    ## g_causal_1.cluster2            -0.92667836 0.3958665
    ## g_causal_2.cluster2             0.89388887 2.4446180
    ## g_causal_3.cluster2            -0.19972945 0.8189523
    ## hs_child_age_yrs_None.cluster2 -0.05794157 0.9437051
    ## sex_male.cluster2              -0.69550430 0.4988228
    ##
    ## Layer 3
    ##
    ##                                      beta        OR
    ## (Intercept).cluster2           -1.3654847 0.2552569
    ## g_causal_1.cluster2             0.9934557 2.7005507
    ## g_causal_2.cluster2            -0.8493218 0.4277049
    ## g_causal_3.cluster2             0.1937766 1.2138251
    ## hs_child_age_yrs_None.cluster2  0.1158271 1.1228017
    ## sex_male.cluster2               0.8470880 2.3328437

### 8.2 Zero-penalty selected-only refit

    set.seed(1206)

    parallel_inputs_bin <- prepare_parallel_selected_inputs(parallel_pen_bin, G, Z_parallel_miss)

    parallel_bin <- list(fit_pen = parallel_pen_bin, inputs = parallel_inputs_bin)
    parallel_bin$fit_refit <- refit_selected(
      "parallel",
      fit_pen = parallel_pen_bin,
      inputs = parallel_inputs_bin,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      seed = 1206
    )

    ## Fitting LUCID parallel model (3 layers)...
    ## Finished LUCID parallel model.

    summary(parallel_bin$fit_refit)

    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binomial
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##   Layer 3 listwise rows : 0 / 90 (0.0%)
    ##   Layer 3 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 3 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   G features by layer
    ##     Layer 1              : 3 / 3 (100.0%)
    ##     Layer 2              : 3 / 3 (100.0%)
    ##     Layer 3              : 3 / 3 (100.0%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##     Layer 3 selected     : 10 / 10 (100.0%)
    ##     Layer 3 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3330.30
    ##   BIC                    : 8523.51
    ##   Number of parameters   : 414
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): intercept and log OR for non-reference clusters for each layer (and log OR of covariate if included)
    ##                              gamma   exp(gamma)
    ## (Intercept)           -15.25206888 2.377446e-07
    ## Layer1_LC2              0.22785005 1.255897e+00
    ## Layer2_LC2             14.64253482 2.286505e+06
    ## Layer3_LC2             16.58300835 1.591875e+07
    ## hs_child_age_yrs_None  -0.03707901 9.636000e-01
    ## sex_male               -0.21751274 8.045174e-01
    ##
    ## (2) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01909800  2.78553563
    ## cg_BTF3L4      0.02480271  2.77019739
    ## cg_AL358472.7 -0.05985293  2.85041175
    ## cg_HDGF        0.19302043 -0.18887189
    ## cg_TDRD5      -0.13877077  0.13343290
    ## cg_CSRNP3     -0.07405841  0.08015087
    ## cg_HSPD1       0.18595283 -0.20524198
    ## cg_EPM2AIP1   -0.03855514  0.03877246
    ## cg_AC025171.1 -0.07290097  0.04247616
    ## cg_VTRNA1_3    0.15834764 -0.18440057
    ##
    ## Layer 2
    ##
    ##                   mu_cluster1  mu_cluster2
    ## tc_TC01006069_nc -2.760922332 -0.144990549
    ## tc_SLC9A4        -3.015209870  0.063240291
    ## tc_RAB6C_AS1     -2.732426038 -0.171269137
    ## tc_LOC100129029  -0.046840128  0.043194826
    ## tc_BRE           -0.072030770  0.066425023
    ## tc_TC03001220_nc  0.028025357 -0.025844302
    ## tc_TC04002114_nc -0.035638034  0.032864528
    ## tc_TC04002369_nc  0.134901102 -0.124402513
    ## tc_BEND4         -0.085317482  0.078677705
    ## tc_SLC9A3        -0.005786017  0.005335724
    ##
    ## Layer 3
    ##
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each exposure for each layer
    ## Layer 1
    ##
    ##                                      beta        OR
    ## (Intercept).cluster2           -1.1608772 0.3132113
    ## g_causal_1.cluster2             1.4623917 4.3162703
    ## g_causal_2.cluster2            -1.4797223 0.2277009
    ## g_causal_3.cluster2             0.4022659 1.4952088
    ## hs_child_age_yrs_None.cluster2  0.1161202 1.1231309
    ## sex_male.cluster2               1.0255920 2.7887459
    ##
    ## Layer 2
    ##
    ##                                      beta        OR
    ## (Intercept).cluster2            2.1585328 8.6584247
    ## g_causal_1.cluster2            -1.8388979 0.1589926
    ## g_causal_2.cluster2             1.9681946 7.1577422
    ## g_causal_3.cluster2            -0.6638832 0.5148482
    ## hs_child_age_yrs_None.cluster2 -0.2222888 0.8006841
    ## sex_male.cluster2              -1.2704200 0.2807137
    ##
    ## Layer 3
    ##
    ##                                      beta         OR
    ## (Intercept).cluster2           -3.2332090 0.03943076
    ## g_causal_1.cluster2             1.9557658 7.06933069
    ## g_causal_2.cluster2            -1.9426942 0.14331731
    ## g_causal_3.cluster2             0.6703573 1.95493576
    ## hs_child_age_yrs_None.cluster2  0.3383841 1.40267912
    ## sex_male.cluster2               1.5437264 4.68200463

### 8.3 Bootstrap CI + summary

    set.seed(1207)

    parallel_bin$boot <- boot_lucid(
      G = parallel_inputs_bin$G,
      Z = parallel_inputs_bin$Z,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      model = parallel_bin$fit_refit,
      R = 30,
      conf = 0.90
    )

    summary(parallel_bin$fit_refit, boot.se = parallel_bin$boot)

    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binomial
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##   Layer 3 listwise rows : 0 / 90 (0.0%)
    ##   Layer 3 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 3 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   G features by layer
    ##     Layer 1              : 3 / 3 (100.0%)
    ##     Layer 2              : 3 / 3 (100.0%)
    ##     Layer 3              : 3 / 3 (100.0%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##     Layer 3 selected     : 10 / 10 (100.0%)
    ##     Layer 3 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3330.30
    ##   BIC                    : 8523.51
    ##   Number of parameters   : 414
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds / log OR (coefficient scale) with bootstrap interval, and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                              gamma  norm_lower  norm_upper           OR
    ## (Intercept)           -15.25206888 -43.3385142 -18.4530187 2.377446e-07
    ## Layer1_LC2              0.22785005  -7.1482369   7.5733233 1.255897e+00
    ## Layer2_LC2             14.64253482  21.2871969  41.0133855 2.286505e+06
    ## Layer3_LC2             16.58300835  21.2536701  40.5331164 1.591875e+07
    ## hs_child_age_yrs_None  -0.03707901  -0.7076933   0.8315413 9.636000e-01
    ## sex_male               -0.21751274  -1.3110143   0.8098378 8.045174e-01
    ##                           OR_lower     OR_upper sig
    ## (Intercept)           1.507726e-19 9.681793e-09   *
    ## Layer1_LC2            7.862491e-04 1.945595e+03
    ## Layer2_LC2            1.757568e+09 6.484657e+17   *
    ## Layer3_LC2            1.699619e+09 4.011518e+17   *
    ## hs_child_age_yrs_None 4.927796e-01 2.296856e+00
    ## sex_male              2.695465e-01 2.247543e+00
    ##
    ## (2) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##                                  estimate  norm_lower norm_upper sig
    ## Layer1.cg_GRHL3.cluster1       0.01909800 -1.46009609 0.21668546
    ## Layer1.cg_BTF3L4.cluster1      0.02480271 -1.52716335 0.36922908
    ## Layer1.cg_AL358472.7.cluster1 -0.05985293 -1.74036763 0.47196743
    ## Layer1.cg_HDGF.cluster1        0.19302043  0.05223872 0.64282266   *
    ## Layer1.cg_TDRD5.cluster1      -0.13877077 -0.76939031 0.09247818
    ## Layer1.cg_CSRNP3.cluster1     -0.07405841 -0.50748942 0.13799461
    ## Layer1.cg_HSPD1.cluster1       0.18595283 -0.06378638 0.99212156
    ## Layer1.cg_EPM2AIP1.cluster1   -0.03855514 -0.62348606 0.42314854
    ## Layer1.cg_AC025171.1.cluster1 -0.07290097 -0.60303409 0.48715046
    ## Layer1.cg_VTRNA1_3.cluster1    0.15834764 -0.37701053 0.82765233
    ## Layer1.cg_GRHL3.cluster2       2.78553563  1.95212832 4.38397706   *
    ## Layer1.cg_BTF3L4.cluster2      2.77019739  2.04384525 4.12661933   *
    ## Layer1.cg_AL358472.7.cluster2  2.85041175  2.18087875 4.36562734   *
    ## Layer1.cg_HDGF.cluster2       -0.18887189 -0.76136532 0.11324789
    ## Layer1.cg_TDRD5.cluster2       0.13343290 -0.09984294 0.71152009
    ## Layer1.cg_CSRNP3.cluster2      0.08015087 -0.37180240 0.87079316
    ## Layer1.cg_HSPD1.cluster2      -0.20524198 -0.82887565 0.03313008
    ## Layer1.cg_EPM2AIP1.cluster2    0.03877246 -0.47136238 0.85985415
    ## Layer1.cg_AC025171.1.cluster2  0.04247616 -0.40475333 0.59108937
    ## Layer1.cg_VTRNA1_3.cluster2   -0.18440057 -0.85159817 0.29906335
    ##
    ## Layer 2
    ##
    ##                                      estimate norm_lower norm_upper sig
    ## Layer2.tc_TC01006069_nc.cluster1 -2.760922332 -3.8027943 -2.3184864   *
    ## Layer2.tc_SLC9A4.cluster1        -3.015209870 -4.3708539 -2.3380214   *
    ## Layer2.tc_RAB6C_AS1.cluster1     -2.732426038 -4.0084271 -1.8988962   *
    ## Layer2.tc_LOC100129029.cluster1  -0.046840128 -0.3703509  0.4292682
    ## Layer2.tc_BRE.cluster1           -0.072030770 -0.4437134  0.2694303
    ## Layer2.tc_TC03001220_nc.cluster1  0.028025357 -0.5310598  0.4396495
    ## Layer2.tc_TC04002114_nc.cluster1 -0.035638034 -0.6144869  0.4221376
    ## Layer2.tc_TC04002369_nc.cluster1  0.134901102 -0.1570212  0.5338086
    ## Layer2.tc_BEND4.cluster1         -0.085317482 -0.3366813  0.1521183
    ## Layer2.tc_SLC9A3.cluster1        -0.005786017 -0.3497174  0.3832627
    ## Layer2.tc_TC01006069_nc.cluster2 -0.144990549 -0.5114290  0.9630742
    ## Layer2.tc_SLC9A4.cluster2         0.063240291 -0.4502319  1.1441677
    ## Layer2.tc_RAB6C_AS1.cluster2     -0.171269137 -0.5861227  0.7777116
    ## Layer2.tc_LOC100129029.cluster2   0.043194826 -0.4154908  0.4574189
    ## Layer2.tc_BRE.cluster2            0.066425023 -0.2710231  0.4772073
    ## Layer2.tc_TC03001220_nc.cluster2 -0.025844302 -0.3129558  0.3339352
    ## Layer2.tc_TC04002114_nc.cluster2  0.032864528 -0.2511192  0.4806943
    ## Layer2.tc_TC04002369_nc.cluster2 -0.124402513 -0.5903492  0.2524247
    ## Layer2.tc_BEND4.cluster2          0.078677705 -0.4948919  0.4431169
    ## Layer2.tc_SLC9A3.cluster2         0.005335724 -0.4666411  0.4135153
    ##
    ## Layer 3
    ##
    ##                                   estimate norm_lower norm_upper sig
    ## Layer3.miR.101.3p.cluster1    -0.006887544 -1.5197663  0.8734155
    ## Layer3.miR.125a.5p.cluster1    0.187538247 -1.0659917  0.8148742
    ## Layer3.miR.125b.1.3p.cluster1  0.027408679 -1.3495777  0.6441201
    ## Layer3.miR.127.3p.cluster1    -0.107628701 -0.7203724  0.3847126
    ## Layer3.miR.140.5p.cluster1     0.090862876 -0.5690689  0.9549083
    ## Layer3.miR.142.3p.cluster1    -0.004784527 -1.0567978  1.1443996
    ## Layer3.miR.144.5p.cluster1     0.080101772 -1.0021931  1.2937293
    ## Layer3.miR.19a.3p.cluster1     0.060465036 -0.9057336  1.1372451
    ## Layer3.miR.19b.3p.cluster1     0.108156640 -0.8777756  1.2885677
    ## Layer3.miR.21.5p.cluster1      0.102286783 -0.7642709  1.2446584
    ## Layer3.miR.101.3p.cluster2     3.007913039  2.2526113  4.5804647   *
    ## Layer3.miR.125a.5p.cluster2    2.785670575  2.3786735  4.0269633   *
    ## Layer3.miR.125b.1.3p.cluster2  2.968675795  2.5018567  4.3822032   *
    ## Layer3.miR.127.3p.cluster2     0.123004230 -0.2468372  0.4903003
    ## Layer3.miR.140.5p.cluster2    -0.103843286 -0.6262179  0.2415598
    ## Layer3.miR.142.3p.cluster2     0.005468031 -0.5860243  0.5069875
    ## Layer3.miR.144.5p.cluster2    -0.091544883 -0.6330889  0.3672734
    ## Layer3.miR.19a.3p.cluster2    -0.069102898 -0.6190875  0.3827707
    ## Layer3.miR.19b.3p.cluster2    -0.123607589 -0.7152743  0.2893261
    ## Layer3.miR.21.5p.cluster2     -0.116899180 -0.6703657  0.2526464
    ##
    ## (3) E: cluster-assignment coefficients (log-odds scale) for each exposure for each layer, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ## Layer 1
    ##
    ##                                  estimate  norm_lower norm_upper        OR
    ## (Intercept).cluster2           -1.1608772 -10.8937757 10.2132865 0.3132113
    ## g_causal_1.cluster2             1.4623917  -0.1887755  3.5095107 4.3162703
    ## g_causal_2.cluster2            -1.4797223  -3.7335958 -0.7895102 0.2277009
    ## g_causal_3.cluster2             0.4022659  -0.8438636  1.3544238 1.4952088
    ## hs_child_age_yrs_None.cluster2  0.1161202  -1.4183621  1.4808978 1.1231309
    ## sex_male.cluster2               1.0255920  -0.3812848  3.0170137 2.7887459
    ##                                    OR_lower     OR_upper sig
    ## (Intercept).cluster2           1.857348e-05 2.726302e+04
    ## g_causal_1.cluster2            8.279724e-01 3.343190e+01
    ## g_causal_2.cluster2            2.390672e-02 4.540672e-01   *
    ## g_causal_3.cluster2            4.300458e-01 3.874528e+00
    ## hs_child_age_yrs_None.cluster2 2.421102e-01 4.396892e+00
    ## sex_male.cluster2              6.829833e-01 2.043019e+01
    ##
    ## Layer 2
    ##
    ##                                  estimate norm_lower norm_upper        OR
    ## (Intercept).cluster2            2.1585328 -6.5270667 8.49320172 8.6584247
    ## g_causal_1.cluster2            -1.8388979 -4.6504817 0.81248775 0.1589926
    ## g_causal_2.cluster2             1.9681946  1.4340937 4.46087182 7.1577422
    ## g_causal_3.cluster2            -0.6638832 -1.8024519 0.04017108 0.5148482
    ## hs_child_age_yrs_None.cluster2 -0.2222888 -0.9152189 0.92599442 0.8006841
    ## sex_male.cluster2              -1.2704200 -4.9230340 1.43235562 0.2807137
    ##                                   OR_lower    OR_upper sig
    ## (Intercept).cluster2           0.001463292 4881.470187
    ## g_causal_1.cluster2            0.009556997    2.253507
    ## g_causal_2.cluster2            4.195840789   86.562944   *
    ## g_causal_3.cluster2            0.164894088    1.040989
    ## hs_child_age_yrs_None.cluster2 0.400428977    2.524377
    ## sex_male.cluster2              0.007277019    4.188554
    ##
    ## Layer 3
    ##
    ##                                  estimate  norm_lower norm_upper         OR
    ## (Intercept).cluster2           -3.2332090 -14.4091428  5.8280543 0.03943076
    ## g_causal_1.cluster2             1.9557658   0.9720615  4.1327537 7.06933069
    ## g_causal_2.cluster2            -1.9426942  -4.1101167 -0.4182668 0.14331731
    ## g_causal_3.cluster2             0.6703573  -0.3371013  1.7119390 1.95493576
    ## hs_child_age_yrs_None.cluster2  0.3383841  -0.8655262  1.7141677 1.40267912
    ## sex_male.cluster2               1.5437264  -0.1192999  3.9772211 4.68200463
    ##                                    OR_lower    OR_upper sig
    ## (Intercept).cluster2           5.523175e-07 339.6970858
    ## g_causal_1.cluster2            2.643388e+00  62.3493769   *
    ## g_causal_2.cluster2            1.640586e-02   0.6581866   *
    ## g_causal_3.cluster2            7.138366e-01   5.5396924
    ## hs_child_age_yrs_None.cluster2 4.208301e-01   5.5520525
    ## sex_male.cluster2              8.875416e-01  53.3685229

One structural difference between the families is specific to the parallel model and easy to miss. For a normal outcome, the early model estimates a per-cluster residual standard deviation – one value per latent cluster – whereas the parallel model estimates a single pooled standard deviation across the joint cluster configuration. For a binary outcome neither exists. So if you are comparing dispersion across model types, compare like with like.

## 9) Serial Model Tutorial A (All-Early Stages): Binary Outcome

### 9.1 Penalized screening fit

    # Serial structure: list of early-stage matrices.
    Z_serial_all_early <- list(
      methylome = Z_parallel_miss[[1]],
      transcriptome = Z_parallel_miss[[2]],
      miRNA = Z_parallel_miss[[3]]
    )

    set.seed(1305)

    serial_ae_pen_bin <- estimate_lucid(
      lucid_model = "serial",
      G = G,
      Z = Z_serial_all_early,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      family = "binary",
      K = list(2, 2, 2),
      Rho_G = RHO_G,
      Rho_Z_Mu = 0,
      Rho_Z_Cov = 0,
      max_itr = 15,
      max_tot.itr = 40,
      tol = 1e-2,
      seed = 1305,
      verbose = FALSE
    )

    ## Fitting LUCID serial model (3 stages)...
    ##   Stage 1/3 (early) finished: log-likelihood = -1202.568. Selected G: 3/9; Selected Z: 10/10.
    ##   Stage 2/3 (early) finished: log-likelihood = -1217.744.
    ##   Stage 3/3 (early) finished: log-likelihood = -865.953.
    ## Finished LUCID serial model.

    summary(serial_ae_pen_bin)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 3
    ##   Stage 1               : early (K = 2)
    ##   Stage 2               : early (K = 2)
    ##   Stage 3               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Listwise rows         : 1
    ##     Sporadic rows         : 0
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##   Stage 3
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3286.26
    ##   BIC                    : 8390.45
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 0 / 90 (0.0%)
    ##   Missing cells total    : 10 / 900 (1.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 9 (33.3%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1202.57
    ##   BIC                    : 3017.11
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01961961  2.78734239
    ## cg_BTF3L4      0.02563074  2.77167224
    ## cg_AL358472.7 -0.05842807  2.85141440
    ## cg_HDGF        0.19392675 -0.19012409
    ## cg_TDRD5      -0.13906521  0.13396510
    ## cg_CSRNP3     -0.07423022  0.08045749
    ## cg_HSPD1       0.18520055 -0.20480198
    ## cg_EPM2AIP1   -0.03954436  0.03985187
    ## cg_AC025171.1 -0.07348909  0.04317664
    ## cg_VTRNA1_3    0.15848766 -0.18483418
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each exposure
    ##                                        beta        OR
    ## (Intercept).cluster2           -0.233840269 0.7914882
    ## g_causal_1.cluster2             0.807430367 2.2421391
    ## g_causal_2.cluster2            -0.771389025 0.4623704
    ## g_causal_3.cluster2             0.053240477 1.0546832
    ## hs_child_age_yrs_None.cluster2 -0.004675195 0.9953357
    ## sex_male.cluster2               0.628334926 1.8744868
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1217.74
    ##   BIC                    : 3029.46
    ##   Number of parameters   : 132
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##                   mu_cluster1   mu_cluster2
    ## tc_TC01006069_nc -2.770982314 -0.2004046469
    ## tc_SLC9A4        -3.075746204  0.0375636749
    ## tc_RAB6C_AS1     -2.807651591 -0.1683194085
    ## tc_LOC100129029  -0.052357560  0.0458123240
    ## tc_BRE           -0.112174125  0.0981512000
    ## tc_TC03001220_nc  0.060238428 -0.0527080015
    ## tc_TC04002114_nc -0.036339642  0.0317968112
    ## tc_TC04002369_nc  0.078990827 -0.0691161574
    ## tc_BEND4         -0.063253000  0.0553457209
    ## tc_SLC9A3        -0.001011716  0.0008852407
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                                beta           OR
    ## (Intercept).cluster2       7.781379 2.395576e+03
    ## Stage1.cluster2.cluster2 -10.462010 2.860268e-05
    ##
    ## --- Stage 3 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2343.88
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.73799854  0.4780698
    ## LC2                    2.18407289  8.8824098
    ## hs_child_age_yrs_None -0.02423243  0.9760588
    ## sex_male              -0.18297636  0.8327878
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                               beta           OR
    ## (Intercept).cluster2      20.28449 6.448268e+08
    ## Stage2.cluster2.cluster2 -33.53361 2.732364e-15

### 9.2 Zero-penalty selected-input refit

    set.seed(1306)

    serial_ae_inputs_bin <- prepare_serial_selected_inputs(serial_ae_pen_bin, G, Z_serial_all_early)

    serial_ae_bin <- list(fit_pen = serial_ae_pen_bin, inputs = serial_ae_inputs_bin)
    serial_ae_bin$fit_refit <- refit_selected(
      "serial",
      fit_pen = serial_ae_pen_bin,
      inputs = serial_ae_inputs_bin,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      seed = 1306
    )

    ## Fitting LUCID serial model (3 stages)...
    ##   Stage 1/3 (early) finished: log-likelihood = -1198.662.
    ##   Stage 2/3 (early) finished: log-likelihood = -1217.524.
    ##   Stage 3/3 (early) finished: log-likelihood = -865.953.
    ## Finished LUCID serial model.

    summary(serial_ae_bin$fit_refit)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 3
    ##   Stage 1               : early (K = 2)
    ##   Stage 2               : early (K = 2)
    ##   Stage 3               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Listwise rows         : 1
    ##     Sporadic rows         : 0
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##   Stage 3
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3282.14
    ##   BIC                    : 8382.20
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 0 / 90 (0.0%)
    ##   Missing cells total    : 10 / 900 (1.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1198.66
    ##   BIC                    : 3009.30
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01915163  2.78569034
    ## cg_BTF3L4      0.02487628  2.77033010
    ## cg_AL358472.7 -0.05975885  2.85053597
    ## cg_HDGF        0.19307580 -0.18895749
    ## cg_TDRD5      -0.13876555  0.13344819
    ## cg_CSRNP3     -0.07408559  0.08019037
    ## cg_HSPD1       0.18585702 -0.20517357
    ## cg_EPM2AIP1   -0.03863075  0.03885569
    ## cg_AC025171.1 -0.07293798  0.04252278
    ## cg_VTRNA1_3    0.15834584 -0.18442470
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each exposure
    ##                                      beta        OR
    ## (Intercept).cluster2           -1.1615344 0.3130055
    ## g_causal_1.cluster2             1.4615136 4.3124821
    ## g_causal_2.cluster2            -1.4789346 0.2278803
    ## g_causal_3.cluster2             0.4021607 1.4950515
    ## hs_child_age_yrs_None.cluster2  0.1162000 1.1232205
    ## sex_male.cluster2               1.0246400 2.7860923
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1217.52
    ##   BIC                    : 3029.02
    ##   Number of parameters   : 132
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##                   mu_cluster1   mu_cluster2
    ## tc_TC01006069_nc -2.770982428 -0.2004050510
    ## tc_SLC9A4        -3.075854117  0.0375632833
    ## tc_RAB6C_AS1     -2.807651890 -0.1683196643
    ## tc_LOC100129029  -0.052357769  0.0458124875
    ## tc_BRE           -0.112174379  0.0981513805
    ## tc_TC03001220_nc  0.060238499 -0.0527080414
    ## tc_TC04002114_nc -0.036339702  0.0317968501
    ## tc_TC04002369_nc  0.078990703 -0.0691160197
    ## tc_BEND4         -0.063252955  0.0553456585
    ## tc_SLC9A3        -0.001011683  0.0008852121
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                               beta           OR
    ## (Intercept).cluster2      35.88858 3.856685e+15
    ## Stage1.cluster2.cluster2 -38.54939 1.812230e-17
    ##
    ## --- Stage 3 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2343.88
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.73799854  0.4780698
    ## LC2                    2.18407289  8.8824098
    ## hs_child_age_yrs_None -0.02423243  0.9760588
    ## sex_male              -0.18297636  0.8327878
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                               beta           OR
    ## (Intercept).cluster2      20.28449 6.448276e+08
    ## Stage2.cluster2.cluster2 -33.53361 2.732356e-15

### 9.3 Bootstrap CI + summary

    set.seed(1307)

    serial_ae_bin$boot <- boot_lucid(
      G = serial_ae_inputs_bin$G,
      Z = serial_ae_inputs_bin$Z,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      model = serial_ae_bin$fit_refit,
      R = 30,
      conf = 0.90
    )

    summary(serial_ae_bin$fit_refit, boot.se = serial_ae_bin$boot)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 3
    ##   Stage 1               : early (K = 2)
    ##   Stage 2               : early (K = 2)
    ##   Stage 3               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Listwise rows         : 1
    ##     Sporadic rows         : 0
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##   Stage 3
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3282.14
    ##   BIC                    : 8382.20
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 1 / 90 (1.1%)
    ##   Sporadic missing rows  : 0 / 90 (0.0%)
    ##   Missing cells total    : 10 / 900 (1.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1198.66
    ##   BIC                    : 3009.30
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##                           estimate  norm_lower  norm_upper sig
    ## cg_GRHL3.cluster1       0.06681215 -1.27006593  0.49829762
    ## cg_BTF3L4.cluster1      0.08824897 -1.13702947  0.60541148
    ## cg_AL358472.7.cluster1  0.01833116 -1.30766405  0.55147097
    ## cg_HDGF.cluster1        0.20733160 -0.07784096  0.81328218
    ## cg_TDRD5.cluster1      -0.08436881 -0.46226263  0.25597650
    ## cg_CSRNP3.cluster1     -0.12269705 -0.60109025  0.21521381
    ## cg_HSPD1.cluster1       0.18409138  0.01758084  0.74762854   *
    ## cg_EPM2AIP1.cluster1   -0.05814074 -0.60129935  0.27058038
    ## cg_AC025171.1.cluster1 -0.07418987 -0.53832569  0.20612353
    ## cg_VTRNA1_3.cluster1    0.14615140 -0.28194782  0.65622169
    ## cg_GRHL3.cluster2       2.79971240  2.23441330  4.28175880   *
    ## cg_BTF3L4.cluster2      2.76702779  2.13641106  4.13274828   *
    ## cg_AL358472.7.cluster2  2.83534778  2.18182392  4.31550919   *
    ## cg_HDGF.cluster2       -0.21321149 -0.94254377  0.11114270
    ## cg_TDRD5.cluster2       0.08158921 -0.26712175  0.58260642
    ## cg_CSRNP3.cluster2      0.13588404 -0.13405823  0.69864943
    ## cg_HSPD1.cluster2      -0.21247948 -0.80484368 -0.02935195   *
    ## cg_EPM2AIP1.cluster2    0.06157427 -0.36736063  0.72756786
    ## cg_AC025171.1.cluster2  0.04657923 -0.23926466  0.43600644
    ## cg_VTRNA1_3.cluster2   -0.17942581 -0.68732099  0.13437893
    ##
    ## (2) E: cluster-assignment coefficients (log-odds scale) for each exposure, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                                  estimate  norm_lower norm_upper         OR
    ## (Intercept).cluster2           -2.7684029 -12.5449519   4.582686 0.06276216
    ## g_causal_1.cluster2             1.5645468   0.8401193   3.165157 4.78050792
    ## g_causal_2.cluster2            -1.7029761  -4.1143046  -1.102762 0.18214064
    ## g_causal_3.cluster2             0.3481920  -0.8750307   1.220376 1.41650414
    ## hs_child_age_yrs_None.cluster2  0.3133094  -0.5876576   1.492721 1.36794467
    ## sex_male.cluster2               1.2888638   0.7497167   3.588766 3.62866118
    ##                                    OR_lower   OR_upper sig
    ## (Intercept).cluster2           3.562843e-06 97.7766414
    ## g_causal_1.cluster2            2.316643e+00 23.6924633   *
    ## g_causal_2.cluster2            1.633730e-02  0.3319531   *
    ## g_causal_3.cluster2            4.168492e-01  3.3884623
    ## hs_child_age_yrs_None.cluster2 5.556272e-01  4.4491834
    ## sex_male.cluster2              2.116400e+00 36.1893831   *
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : normal
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -1217.52
    ##   BIC                    : 3029.02
    ##   Number of parameters   : 132
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster
    ##                                estimate norm_lower norm_upper sig
    ## tc_TC01006069_nc.cluster1 -2.7709816731 -3.9443535 -2.5077308   *
    ## tc_SLC9A4.cluster1        -3.0757930546 -4.6692462 -2.6110419   *
    ## tc_RAB6C_AS1.cluster1     -2.8076439589 -4.1131875 -2.4255074   *
    ## tc_LOC100129029.cluster1  -0.0523583618 -0.5001806  0.3957887
    ## tc_BRE.cluster1           -0.1121705628 -0.6012665  0.2187060
    ## tc_TC03001220_nc.cluster1  0.0602358241 -0.3297386  0.4080575
    ## tc_TC04002114_nc.cluster1 -0.0363419251 -0.2958403  0.3294316
    ## tc_TC04002369_nc.cluster1  0.0789956165 -0.4111971  0.6550852
    ## tc_BEND4.cluster1         -0.0632529889 -0.4465417  0.2156369
    ## tc_SLC9A3.cluster1        -0.0010102349 -0.2977764  0.2660058
    ## tc_TC01006069_nc.cluster2 -0.2003989285 -0.7816762  0.6639629
    ## tc_SLC9A4.cluster2         0.0375636721 -0.5842171  1.1559122
    ## tc_RAB6C_AS1.cluster2     -0.1683196395 -0.6348304  0.6541110
    ## tc_LOC100129029.cluster2   0.0458132652 -0.4439629  0.4914498
    ## tc_BRE.cluster2            0.0981485967 -0.2917023  0.6524542
    ## tc_TC03001220_nc.cluster2 -0.0527059993 -0.4156113  0.2700773
    ## tc_TC04002114_nc.cluster2  0.0317989753 -0.4591333  0.5238996
    ## tc_TC04002369_nc.cluster2 -0.0691207097 -0.6006294  0.3679671
    ## tc_BEND4.cluster2          0.0553460012 -0.3577722  0.5117037
    ## tc_SLC9A3.cluster2         0.0008839497 -0.4137068  0.3959030
    ##
    ## (2) E: cluster-assignment coefficients (log-odds scale) for each cluster from the previous serial stage, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                           estimate norm_lower norm_upper           OR
    ## (Intercept).cluster2      3.810201    4.83269   8.438552 45.159521923
    ## Stage1.cluster2.cluster2 -6.431894  -33.55342  22.272864  0.001609401
    ##                              OR_lower     OR_upper sig
    ## (Intercept).cluster2     1.255482e+02 4.621857e+03   *
    ## Stage1.cluster2.cluster2 2.678766e-15 4.709579e+09
    ##
    ## --- Stage 3 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 1 / 1 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2343.88
    ##   Number of parameters   : 136
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds / log OR (coefficient scale) with bootstrap interval, and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                             gamma norm_lower norm_upper        OR  OR_lower
    ## (Intercept)           -0.73799854 -3.5669487  2.8554853 0.4780698 0.0282419
    ## LC2                    2.18407289  1.2272477  4.5597308 8.8824098 3.4118263
    ## hs_child_age_yrs_None -0.02423243 -0.4908852  0.2814207 0.9760588 0.6120843
    ## sex_male              -0.18297636 -1.2940243  0.6700197 0.8327878 0.2741652
    ##                        OR_upper sig
    ## (Intercept)           17.382871
    ## LC2                   95.557750   *
    ## hs_child_age_yrs_None  1.325011
    ## sex_male               1.954276
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                            estimate norm_lower norm_upper sig
    ## miR.101.3p.cluster1    -0.006887544 -1.3631649  0.7929835
    ## miR.125a.5p.cluster1    0.187538247 -0.8825152  0.6024488
    ## miR.125b.1.3p.cluster1  0.027408679 -1.2121055  0.4182165
    ## miR.127.3p.cluster1    -0.107628701 -0.5115376  0.2630130
    ## miR.140.5p.cluster1     0.090862876 -0.5535181  1.0153620
    ## miR.142.3p.cluster1    -0.004784527 -0.6596519  0.9454788
    ## miR.144.5p.cluster1     0.080101772 -0.6413988  1.0437336
    ## miR.19a.3p.cluster1     0.060465036 -0.5322010  0.9868548
    ## miR.19b.3p.cluster1     0.108156640 -0.4577338  1.0734217
    ## miR.21.5p.cluster1      0.102286783 -0.4553698  1.0147037
    ## miR.101.3p.cluster2     3.007913039  2.1584204  5.1171452   *
    ## miR.125a.5p.cluster2    2.785670575  2.3799663  4.3638480   *
    ## miR.125b.1.3p.cluster2  2.968675795  2.4945787  4.6736662   *
    ## miR.127.3p.cluster2     0.123004230 -0.2799651  0.5677008
    ## miR.140.5p.cluster2    -0.103843286 -1.0121419  0.4999106
    ## miR.142.3p.cluster2     0.005468031 -0.9050279  0.7867316
    ## miR.144.5p.cluster2    -0.091544883 -0.9243622  0.6081175
    ## miR.19a.3p.cluster2    -0.069102898 -0.9686156  0.6543602
    ## miR.19b.3p.cluster2    -0.123607589 -1.0609950  0.5504173
    ## miR.21.5p.cluster2     -0.116899180 -0.9833316  0.4638549
    ##
    ## (3) E: cluster-assignment coefficients (log-odds scale) for each cluster from the previous serial stage, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                           estimate norm_lower norm_upper           OR
    ## (Intercept).cluster2      20.28446   26.08505   49.28775 6.448084e+08
    ## Stage2.cluster2.cluster2 -33.53367 -179.64247  118.19081 2.732191e-15
    ##                              OR_lower     OR_upper sig
    ## (Intercept).cluster2     2.131046e+11 2.543296e+21   *
    ## Stage2.cluster2.cluster2 9.599853e-79 2.136077e+51

For a serial model the outcome family applies to the final stage only. Upstream stages are fitted unsupervised regardless of what you pass, because the outcome enters the chain once, at the end – so the binary/normal distinction shows up in the last stage’s report and nowhere else.

## 10) Serial Model Tutorial B (Mixed Parallel + Early): Binary Outcome

### 10.1 Penalized screening fit

    # Nested list signals a parallel submodel at stage 1, followed by early stage 2.
    Z_serial_mixed <- list(
      list(
        methylome = Z_parallel_miss[[1]],
        transcriptome = Z_parallel_miss[[2]]
      ),
      miRNA = Z_parallel_miss[[3]]
    )

    set.seed(1405)

    serial_mixed_pen_bin <- estimate_lucid(
      lucid_model = "serial",
      G = G,
      Z = Z_serial_mixed,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      family = "binary",
      K = list(list(2, 2), 2),
      Rho_G = RHO_G,
      Rho_Z_Mu = 0,
      Rho_Z_Cov = 0,
      max_itr = 15,
      max_tot.itr = 40,
      tol = 1e-2,
      seed = 1405,
      verbose = FALSE
    )

    ## Fitting LUCID serial model (2 stages)...
    ##   Stage 1/2 (parallel) finished: log-likelihood = -2444.642. Selected G: 3/9; Selected Z by layer: 10/10, 10/10.
    ##   Stage 2/2 (early) finished: log-likelihood = -865.953.
    ## Finished LUCID serial model.

    summary(serial_mixed_pen_bin)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 2
    ##   Stage 1               : parallel (K = 2,2)
    ##   Stage 2               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Layer 1 listwise/sporadic rows : 1 / 0
    ##     Layer 2 listwise/sporadic rows : 0 / 1
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3310.59
    ##   BIC                    : 8461.61
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (parallel) ---
    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : gaussian
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 9 (33.3%)
    ##   G features by layer
    ##     Layer 1              : 3 / 9 (33.3%)
    ##     Layer 2              : 3 / 9 (33.3%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2444.64
    ##   BIC                    : 6113.23
    ##   Number of parameters   : 272
    ##
    ## Regularization
    ##   Rho_G                  : 0.050
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01960667  2.78730293
    ## cg_BTF3L4      0.02561217  2.77163898
    ## cg_AL358472.7 -0.05845803  2.85138969
    ## cg_HDGF        0.19390770 -0.19009725
    ## cg_TDRD5      -0.13906115  0.13395573
    ## cg_CSRNP3     -0.07422600  0.08045023
    ## cg_HSPD1       0.18521867 -0.20481312
    ## cg_EPM2AIP1   -0.03952313  0.03982859
    ## cg_AC025171.1 -0.07347676  0.04316178
    ## cg_VTRNA1_3    0.15848563 -0.18482556
    ##
    ## Layer 2
    ##
    ##                  mu_cluster1  mu_cluster2
    ## tc_TC01006069_nc -2.76171772 -0.151431669
    ## tc_SLC9A4        -3.02348512  0.061983212
    ## tc_RAB6C_AS1     -2.73581445 -0.175182553
    ## tc_LOC100129029  -0.04727047  0.043342620
    ## tc_BRE           -0.07428108  0.068108837
    ## tc_TC03001220_nc  0.02974284 -0.027271414
    ## tc_TC04002114_nc -0.03156595  0.028943037
    ## tc_TC04002369_nc  0.13303025 -0.121976355
    ## tc_BEND4         -0.08701134  0.079781296
    ## tc_SLC9A3        -0.00834082  0.007647755
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each exposure for each layer
    ## Layer 1
    ##
    ##                                        beta        OR
    ## (Intercept).cluster2           -0.233782173 0.7915342
    ## g_causal_1.cluster2             0.807531827 2.2423666
    ## g_causal_2.cluster2            -0.771459521 0.4623378
    ## g_causal_3.cluster2             0.053227567 1.0546696
    ## hs_child_age_yrs_None.cluster2 -0.004683089 0.9953279
    ## sex_male.cluster2               0.628480825 1.8747603
    ##
    ## Layer 2
    ##
    ##                                       beta        OR
    ## (Intercept).cluster2            0.83779071 2.3112551
    ## g_causal_1.cluster2            -0.92504506 0.3965136
    ## g_causal_2.cluster2             0.89187493 2.4396996
    ## g_causal_3.cluster2            -0.20286113 0.8163916
    ## hs_child_age_yrs_None.cluster2 -0.06111639 0.9407137
    ## sex_male.cluster2              -0.70169073 0.4957464
    ##
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 2 / 2 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2348.38
    ##   Number of parameters   : 137
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.73799854  0.4780698
    ## LC2                    2.18407289  8.8824098
    ## hs_child_age_yrs_None -0.02423243  0.9760588
    ## sex_male              -0.18297636  0.8327878
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                                      beta           OR
    ## (Intercept).cluster2             -9.92645 4.886498e-05
    ## Stage1.Layer1.cluster2.cluster2  24.14059 3.048780e+10
    ## Stage1.Layer2.cluster2.cluster2 -26.02694 4.973275e-12

### 10.2 Zero-penalty selected-input refit

    set.seed(1406)

    serial_mixed_inputs_bin <- prepare_serial_selected_inputs(serial_mixed_pen_bin, G, Z_serial_mixed)

    serial_mixed_bin <- list(fit_pen = serial_mixed_pen_bin, inputs = serial_mixed_inputs_bin)
    serial_mixed_bin$fit_refit <- refit_selected(
      "serial",
      fit_pen = serial_mixed_pen_bin,
      inputs = serial_mixed_inputs_bin,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      seed = 1406
    )

    ## Fitting LUCID serial model (2 stages)...
    ##   Stage 1/2 (parallel) finished: log-likelihood = -2435.579.
    ##   Stage 2/2 (early) finished: log-likelihood = -865.953.
    ## Finished LUCID serial model.

    summary(serial_mixed_bin$fit_refit)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 2
    ##   Stage 1               : parallel (K = 2,2)
    ##   Stage 2               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Layer 1 listwise/sporadic rows : 1 / 0
    ##     Layer 2 listwise/sporadic rows : 0 / 1
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3301.53
    ##   BIC                    : 8443.49
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (parallel) ---
    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : gaussian
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   G features by layer
    ##     Layer 1              : 3 / 3 (100.0%)
    ##     Layer 2              : 3 / 3 (100.0%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2435.58
    ##   BIC                    : 6095.11
    ##   Number of parameters   : 272
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##               mu_cluster1 mu_cluster2
    ## cg_GRHL3       0.01912123  2.78560954
    ## cg_BTF3L4      0.02483414  2.77026216
    ## cg_AL358472.7 -0.05982001  2.85048084
    ## cg_HDGF        0.19304072 -0.18890613
    ## cg_TDRD5      -0.13876550  0.13343713
    ## cg_CSRNP3     -0.07407272  0.08017097
    ## cg_HSPD1       0.18589857 -0.20520028
    ## cg_EPM2AIP1   -0.03858839  0.03880921
    ## cg_AC025171.1 -0.07291502  0.04249461
    ## cg_VTRNA1_3    0.15834446 -0.18440941
    ##
    ## Layer 2
    ##
    ##                   mu_cluster1  mu_cluster2
    ## tc_TC01006069_nc -2.761572789 -0.150145472
    ## tc_SLC9A4        -3.021703480  0.062198525
    ## tc_RAB6C_AS1     -2.735259250 -0.174299961
    ## tc_LOC100129029  -0.047193030  0.043320800
    ## tc_BRE           -0.073892278  0.067829351
    ## tc_TC03001220_nc  0.029444093 -0.027028179
    ## tc_TC04002114_nc -0.032501823  0.029835020
    ## tc_TC04002369_nc  0.133308600 -0.122370510
    ## tc_BEND4         -0.086573422  0.079469996
    ## tc_SLC9A3        -0.007742854  0.007107546
    ##
    ## (2) E: intercept and odds ratio of being assigned to each latent cluster for each exposure for each layer
    ## Layer 1
    ##
    ##                                      beta        OR
    ## (Intercept).cluster2           -1.1612442 0.3130964
    ## g_causal_1.cluster2             1.4619640 4.3144246
    ## g_causal_2.cluster2            -1.4793132 0.2277941
    ## g_causal_3.cluster2             0.4022122 1.4951286
    ## hs_child_age_yrs_None.cluster2  0.1161640 1.1231800
    ## sex_male.cluster2               1.0251542 2.7875254
    ##
    ## Layer 2
    ##
    ##                                      beta        OR
    ## (Intercept).cluster2            2.2583148 9.5669530
    ## g_causal_1.cluster2            -1.8289379 0.1605840
    ## g_causal_2.cluster2             1.9595122 7.0958651
    ## g_causal_3.cluster2            -0.6722936 0.5105363
    ## hs_child_age_yrs_None.cluster2 -0.2334138 0.7918258
    ## sex_male.cluster2              -1.2866733 0.2761880
    ##
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 2 / 2 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2348.38
    ##   Number of parameters   : 137
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds of Y for cluster 1 (reference) and log OR for rest cluster (and log OR of covariate if included)
    ##                             gamma exp(gamma)
    ## (Intercept)           -0.73799854  0.4780698
    ## LC2                    2.18407289  8.8824098
    ## hs_child_age_yrs_None -0.02423243  0.9760588
    ## sex_male              -0.18297636  0.8327878
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                mu_cluster1  mu_cluster2
    ## miR.101.3p    -0.006887544  3.007913039
    ## miR.125a.5p    0.187538247  2.785670575
    ## miR.125b.1.3p  0.027408679  2.968675795
    ## miR.127.3p    -0.107628701  0.123004230
    ## miR.140.5p     0.090862876 -0.103843286
    ## miR.142.3p    -0.004784527  0.005468031
    ## miR.144.5p     0.080101772 -0.091544883
    ## miR.19a.3p     0.060465036 -0.069102898
    ## miR.19b.3p     0.108156640 -0.123607589
    ## miR.21.5p      0.102286783 -0.116899180
    ##
    ## (3) E: intercept and odds ratio of being assigned to each latent cluster for each cluster from the previous serial stage
    ##                                      beta           OR
    ## (Intercept).cluster2            -10.07034 4.231622e-05
    ## Stage1.Layer1.cluster2.cluster2  23.72705 2.016173e+10
    ## Stage1.Layer2.cluster2.cluster2 -25.85493 5.906727e-12

### 10.3 Bootstrap CI + summary

    set.seed(1407)

    serial_mixed_bin$boot <- boot_lucid(
      G = serial_mixed_inputs_bin$G,
      Z = serial_mixed_inputs_bin$Z,
      Y = Y_binary,
      CoG = CoG,
      CoY = CoY,
      model = serial_mixed_bin$fit_refit,
      R = 30,
      conf = 0.90
    )

    summary(serial_mixed_bin$fit_refit, boot.se = serial_mixed_bin$boot)

    ##
    ## ====================================================
    ## LUCID Serial: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of stages       : 2
    ##   Stage 1               : parallel (K = 2,2)
    ##   Stage 2               : early (K = 2)
    ##
    ## Missing-data profile by stage
    ##   Stage 1
    ##     Layer 1 listwise/sporadic rows : 1 / 0
    ##     Layer 2 listwise/sporadic rows : 0 / 1
    ##   Stage 2
    ##     Listwise rows         : 0
    ##     Sporadic rows         : 1
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -3301.53
    ##   BIC                    : 8443.49
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Stage-wise detailed parameter estimates
    ##
    ## --- Stage 1 (parallel) ---
    ##
    ## ====================================================
    ## LUCID Parallel: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : gaussian
    ##   Number of observations : 90
    ##   Clusters per layer     : 2, 2
    ##
    ## Missing-data profile by layer
    ##   Layer 1 listwise rows : 1 / 90 (1.1%)
    ##   Layer 1 sporadic rows : 0 / 90 (0.0%)
    ##   Layer 1 missing cells : 10 / 900 (1.1%)
    ##   Layer 2 listwise rows : 0 / 90 (0.0%)
    ##   Layer 2 sporadic rows : 1 / 90 (1.1%)
    ##   Layer 2 missing cells : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 3 / 3 (100.0%)
    ##   G features by layer
    ##     Layer 1              : 3 / 3 (100.0%)
    ##     Layer 2              : 3 / 3 (100.0%)
    ##   Z features
    ##     Layer 1 selected     : 10 / 10 (100.0%)
    ##     Layer 1 multi-cluster: 10
    ##     Layer 2 selected     : 10 / 10 (100.0%)
    ##     Layer 2 multi-cluster: 10
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -2435.58
    ##   BIC                    : 6095.11
    ##   Number of parameters   : 272
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Z: mean of omics data for each latent cluster of each layer
    ## Layer 1
    ##
    ##                                  estimate  norm_lower norm_upper sig
    ## Layer1.cg_GRHL3.cluster1       0.01912123 -1.29474642 0.39718804
    ## Layer1.cg_BTF3L4.cluster1      0.02483414 -1.39716579 0.41194643
    ## Layer1.cg_AL358472.7.cluster1 -0.05982001 -1.43922288 0.41562042
    ## Layer1.cg_HDGF.cluster1        0.19304072 -0.15005192 0.66121387
    ## Layer1.cg_TDRD5.cluster1      -0.13876550 -0.62171374 0.15908751
    ## Layer1.cg_CSRNP3.cluster1     -0.07407272 -0.51895744 0.25782470
    ## Layer1.cg_HSPD1.cluster1       0.18589857  0.06630861 0.62157474   *
    ## Layer1.cg_EPM2AIP1.cluster1   -0.03858839 -0.54399332 0.34935677
    ## Layer1.cg_AC025171.1.cluster1 -0.07291502 -0.54134484 0.32365444
    ## Layer1.cg_VTRNA1_3.cluster1    0.15834446 -0.11542145 0.77856795
    ## Layer1.cg_GRHL3.cluster2       2.78560954  1.97639026 4.14105960   *
    ## Layer1.cg_BTF3L4.cluster2      2.77026216  2.16612491 4.10133822   *
    ## Layer1.cg_AL358472.7.cluster2  2.85048084  2.17365755 4.19043085   *
    ## Layer1.cg_HDGF.cluster2       -0.18890613 -1.14260967 0.36430134
    ## Layer1.cg_TDRD5.cluster2       0.13343713 -0.04376706 0.64624725
    ## Layer1.cg_CSRNP3.cluster2      0.08017097 -0.66162633 1.02527773
    ## Layer1.cg_HSPD1.cluster2      -0.20520028 -0.80992445 0.02415491
    ## Layer1.cg_EPM2AIP1.cluster2    0.03880921 -0.35754930 0.76938352
    ## Layer1.cg_AC025171.1.cluster2  0.04249461 -0.42390960 0.56448166
    ## Layer1.cg_VTRNA1_3.cluster2   -0.18440941 -0.67397997 0.09929081
    ##
    ## Layer 2
    ##
    ##                                      estimate norm_lower norm_upper sig
    ## Layer2.tc_TC01006069_nc.cluster1 -2.761572789 -4.1987505 -2.2907691   *
    ## Layer2.tc_SLC9A4.cluster1        -3.021703480 -4.8575314 -2.6154951   *
    ## Layer2.tc_RAB6C_AS1.cluster1     -2.735259250 -4.2243544 -2.1736201   *
    ## Layer2.tc_LOC100129029.cluster1  -0.047193030 -0.4888129  0.4756470
    ## Layer2.tc_BRE.cluster1           -0.073892278 -0.5261859  0.4242175
    ## Layer2.tc_TC03001220_nc.cluster1  0.029444093 -0.4043188  0.4294699
    ## Layer2.tc_TC04002114_nc.cluster1 -0.032501823 -0.3793741  0.3350861
    ## Layer2.tc_TC04002369_nc.cluster1  0.133308600 -0.1205745  0.4979795
    ## Layer2.tc_BEND4.cluster1         -0.086573422 -0.4492024  0.1081878
    ## Layer2.tc_SLC9A3.cluster1        -0.007742854 -0.3979266  0.3549023
    ## Layer2.tc_TC01006069_nc.cluster2 -0.150145472 -0.6803435  1.0901845
    ## Layer2.tc_SLC9A4.cluster2         0.062198525 -0.3845054  1.5160718
    ## Layer2.tc_RAB6C_AS1.cluster2     -0.174299961 -0.6680455  0.8203397
    ## Layer2.tc_LOC100129029.cluster2   0.043320800 -0.4255825  0.4139606
    ## Layer2.tc_BRE.cluster2            0.067829351 -0.4276048  0.5412150
    ## Layer2.tc_TC03001220_nc.cluster2 -0.027028179 -0.3404741  0.4804556
    ## Layer2.tc_TC04002114_nc.cluster2  0.029835020 -0.4454432  0.4807282
    ## Layer2.tc_TC04002369_nc.cluster2 -0.122370510 -0.7283401  0.3413130
    ## Layer2.tc_BEND4.cluster2          0.079469996 -0.2449645  0.5484087
    ## Layer2.tc_SLC9A3.cluster2         0.007107546 -0.4429583  0.3648931
    ##
    ## (2) E: cluster-assignment coefficients (log-odds scale) for each exposure for each layer, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ## Layer 1
    ##
    ##                                  estimate norm_lower norm_upper        OR
    ## (Intercept).cluster2           -1.1612442 -8.9959589   6.628511 0.3130964
    ## g_causal_1.cluster2             1.4619640  0.4506079   3.233648 4.3144246
    ## g_causal_2.cluster2            -1.4793132 -3.9201395  -0.510107 0.2277941
    ## g_causal_3.cluster2             0.4022122 -0.7488351   1.608034 1.4951286
    ## hs_child_age_yrs_None.cluster2  0.1161640 -0.7294164   1.073769 1.1231800
    ## sex_male.cluster2               1.0251542 -0.4107515   3.337839 2.7875254
    ##                                    OR_lower    OR_upper sig
    ## (Intercept).cluster2           0.0001239095 756.3554548
    ## g_causal_1.cluster2            1.5692657806  25.3720374   *
    ## g_causal_2.cluster2            0.0198383273   0.6004313   *
    ## g_causal_3.cluster2            0.4729171325   4.9929851
    ## hs_child_age_yrs_None.cluster2 0.4821903186   2.9263891
    ## sex_male.cluster2              0.6631517291  28.1582069
    ##
    ## Layer 2
    ##
    ##                                  estimate norm_lower  norm_upper        OR
    ## (Intercept).cluster2            2.2583148  -7.559708  9.76923694 9.5669530
    ## g_causal_1.cluster2            -1.8289379  -4.863797 -0.25432135 0.1605840
    ## g_causal_2.cluster2             1.9595122  -0.564913  6.10479742 7.0958651
    ## g_causal_3.cluster2            -0.6722936  -2.250518  0.70791804 0.5105363
    ## hs_child_age_yrs_None.cluster2 -0.2334138  -1.066198  0.97721203 0.7918258
    ## sex_male.cluster2              -1.2866733  -3.313562  0.04054495 0.2761880
    ##                                    OR_lower     OR_upper sig
    ## (Intercept).cluster2           0.0005210273 1.748742e+04
    ## g_causal_1.cluster2            0.0077211134 7.754426e-01   *
    ## g_causal_2.cluster2            0.5684095786 4.480019e+02
    ## g_causal_3.cluster2            0.1053446011 2.029761e+00
    ## hs_child_age_yrs_None.cluster2 0.3443150689 2.657038e+00
    ## sex_male.cluster2              0.0363863164 1.041378e+00
    ##
    ##
    ## --- Stage 2 (early) ---
    ##
    ## ====================================================
    ## LUCID Early Integration: Model Summary
    ## ====================================================
    ##
    ## Model specification
    ##   Family                 : binary
    ##   Number of observations : 90
    ##   Number of clusters (K) : 2
    ##
    ## Missing-data profile
    ##   Listwise missing rows  : 0 / 90 (0.0%)
    ##   Sporadic missing rows  : 1 / 90 (1.1%)
    ##   Missing cells total    : 1 / 900 (0.1%)
    ##
    ## Feature selection overview
    ##   G features selected    : 2 / 2 (100.0%)
    ##   Z features selected    : 10 / 10 (100.0%)
    ##
    ## Model fit statistics
    ##   Log-likelihood         : -865.95
    ##   BIC                    : 2348.38
    ##   Number of parameters   : 137
    ##
    ## Regularization
    ##   Rho_G                  : 0.000
    ##   Rho_Z_Mu               : 0.000
    ##   Rho_Z_Cov              : 0.000
    ##
    ## Detailed parameter estimates
    ## (1) Y (binary outcome): log odds / log OR (coefficient scale) with bootstrap interval, and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                             gamma norm_lower norm_upper        OR    OR_lower
    ## (Intercept)           -0.73799854 -4.6860921  4.1511053 0.4780698 0.009222657
    ## LC2                    2.18407289  0.9460154  4.8241813 8.8824098 2.575427112
    ## hs_child_age_yrs_None -0.02423243 -0.6392016  0.4648203 0.9760588 0.527713598
    ## sex_male              -0.18297636 -1.3183124  0.3371893 0.8327878 0.267586498
    ##                         OR_upper sig
    ## (Intercept)            63.504155
    ## LC2                   124.484508   *
    ## hs_child_age_yrs_None   1.591728
    ## sex_male                1.401004
    ##
    ## (2) Z: mean of omics data for each latent cluster
    ##                            estimate norm_lower norm_upper sig
    ## miR.101.3p.cluster1    -0.006887544 -1.3356987  0.9727099
    ## miR.125a.5p.cluster1    0.187538247 -0.9079494  0.7526373
    ## miR.125b.1.3p.cluster1  0.027408679 -1.2083849  0.5966891
    ## miR.127.3p.cluster1    -0.107628701 -0.4622845  0.2051805
    ## miR.140.5p.cluster1     0.090862876 -0.3546510  0.8641947
    ## miR.142.3p.cluster1    -0.004784527 -0.5719659  0.8651516
    ## miR.144.5p.cluster1     0.080101772 -0.5070735  0.8954527
    ## miR.19a.3p.cluster1     0.060465036 -0.5467444  0.9881653
    ## miR.19b.3p.cluster1     0.108156640 -0.4090748  1.0406758
    ## miR.21.5p.cluster1      0.102286783 -0.3044591  0.9012677
    ## miR.101.3p.cluster2     3.007913039  2.1203567  5.3424322   *
    ## miR.125a.5p.cluster2    2.785670575  2.1100004  4.6403942   *
    ## miR.125b.1.3p.cluster2  2.968675795  2.6018763  4.5760591   *
    ## miR.127.3p.cluster2     0.123004230 -0.2379536  0.5639691
    ## miR.140.5p.cluster2    -0.103843286 -0.8287712  0.5901997
    ## miR.142.3p.cluster2     0.005468031 -0.8463485  0.9387324
    ## miR.144.5p.cluster2    -0.091544883 -0.8991588  0.8275962
    ## miR.19a.3p.cluster2    -0.069102898 -0.8831396  0.7718153
    ## miR.19b.3p.cluster2    -0.123607589 -1.0125889  0.7170101
    ## miR.21.5p.cluster2     -0.116899180 -0.9529851  0.6744541
    ##
    ## (3) E: cluster-assignment coefficients (log-odds scale) for each cluster from the previous serial stage, with bootstrap interval and the odds ratio (exp of each column). `sig` marks intervals excluding the null.
    ##                                  estimate norm_lower norm_upper           OR
    ## (Intercept).cluster2            -10.07034  -50.16565   13.45072 4.231622e-05
    ## Stage1.Layer1.cluster2.cluster2  23.72705 -106.90783  151.18722 2.016173e+10
    ## Stage1.Layer2.cluster2.cluster2 -25.85493 -241.47825  235.38047 5.906727e-12
    ##                                      OR_lower      OR_upper sig
    ## (Intercept).cluster2             1.634318e-22  6.943401e+05
    ## Stage1.Layer1.cluster2.cluster2  3.719812e-47  4.568509e+65
    ## Stage1.Layer2.cluster2.cluster2 1.340686e-105 1.676637e+102

This is the most general configuration the package supports: a serial chain whose first stage is itself a parallel model over two omics layers, fitted to a binary outcome, with both listwise and sporadic missingness present. If this runs and reports sane estimates, the combination space is covered.

## 11) Visualization: Sankey Diagram and Cluster Omics Profiles

`plot()` renders an early-integration fit as a Sankey diagram: exposures flow into the latent clusters, and the clusters flow on into the omics features and the outcome. Everything about how to read it – node colour, link width and sign – is exactly as in the continuous-outcome case (see the companion vignette’s section 14); a binary outcome only changes what the final cluster -> outcome link represents (a log-odds effect rather than a mean difference).

    sankey_early_bin <- plot(early_bin$fit_refit)
    sankey_early_bin

As in the continuous case, `plot()` on a parallel or serial fit currently raises an error by design – not implemented yet.

`plot_cluster_omic_profile()` – which shows what the clusters are, via their fitted omics means – is unaffected by outcome family entirely: `res_Mu` and the separation/range/sd ranking behind it describe the `X -> Z` arm of the model, which a binary `Y` never touches. Every panel, per architecture:

    prof_early_bin <- plot_cluster_omic_profile(early_bin$fit_refit, top_n = 10)
    prof_early_bin[[1]]

    prof_par_bin <- plot_cluster_omic_profile(
      parallel_bin$fit_refit,
      layer_names = c("methylome", "transcriptome", "miRNA"),
      top_n = 8
    )
    for (nm in names(prof_par_bin)) print(prof_par_bin[[nm]])

    prof_ser_bin <- plot_cluster_omic_profile(serial_ae_bin$fit_refit, top_n = 8)
    for (nm in names(prof_ser_bin)) print(prof_ser_bin[[nm]])

The parallel model’s `methylome` panel and the serial model’s stage-1 panel above look almost identical, and that isn’t a rendering glitch: stage 1 of an all-early serial chain and the corresponding layer of a parallel fit are both fit as an early-integration model on the same `methylome` matrix, with the same `K` and the same (default, `mclust`-based) initialization. The exposure/outcome coupling that distinguishes them is comparatively weak, so both converge to nearly the same cluster solution – which is itself a useful sanity check that the methylation clustering is robust to which architecture surfaces it.

For the full discussion of what the `importance` argument measures and why (separation vs. range vs. sd), and how to pull the ranking behind a plot out as a plain table, see `lucid_3models_normal_outcome.Rmd`’s section 15 – none of that changes here, so it isn’t repeated.

## 12) Prediction: Labels or Probabilities

`predict_lucid()`’s `response` argument controls whether a binary outcome comes back as class labels or as probabilities. Both draw on the early-model fit from section 7.

    pred_lab <- predict_lucid(model = early_bin$fit_refit,
                              G = early_bin$inputs$G, Z = early_bin$inputs$Z,
                              CoG = CoG, CoY = CoY, response = TRUE)
    pred_prob <- predict_lucid(model = early_bin$fit_refit,
                               G = early_bin$inputs$G, Z = early_bin$inputs$Z,
                               CoG = CoG, CoY = CoY, response = FALSE)

    cat("response = TRUE  ->", paste(head(pred_lab$pred.y, 8), collapse = " "), "(class labels)\n")

    ## response = TRUE  -> 1 1 1 0 1 1 0 0 (class labels)

    cat("response = FALSE ->", paste(round(head(pred_prob$pred.y, 8), 3), collapse = " "), "(probabilities)\n")

    ## response = FALSE -> 0.511 0.509 0.548 0.409 0.571 0.565 0.38 0.421 (probabilities)

The two rows describe the same underlying prediction at different granularities: each label in the first row is simply the second row’s probability rounded to whichever side of 0.5 it falls on. Use `response = FALSE` when the downstream use needs the actual predicted risk (e.g. computing a mean predicted probability, or a classification threshold other than 0.5), and `response = TRUE` when a hard label is what’s needed.

## 13) Closing Notes and Session Info

- This vignette mirrors its continuous-outcome companion (`lucid_3models_normal_outcome.Rmd`) one-for-one: same data, same penalized-screen-then-refit pipeline, same three model architectures – only `Y_binary` in place of `Y`.
- For a serial model the outcome family applies to the final stage only; upstream stages are always fitted unsupervised, regardless of outcome family.
- Seeds are fixed before each fit/bootstrap step for deterministic tutorial output.

Every model this vignette fits is registered below. This is the document checking itself: if a fit failed, its object would be missing or of the wrong class, and it would be listed here rather than passing unnoticed.

    check_obj("early_bin",             "list", "7 early binary")
    check_obj("parallel_bin",          "list", "8 parallel binary")
    check_obj("serial_ae_bin",         "list", "9 serial all-early binary")
    check_obj("serial_mixed_bin",      "list", "10 serial mixed binary")
    check_obj("prof_early_bin",        "list", "11 omics profile (early)")
    check_obj("prof_par_bin",          "list", "11 omics profile (parallel)")
    check_obj("prof_ser_bin",          "list", "11 omics profile (serial)")
    check_obj("pred_lab",              "list", "12 predict (labels)")
    check_obj("pred_prob",             "list", "12 predict (probabilities)")

    status <- do.call(rbind, .reg$rows)
    print(status, row.names = FALSE)

    ##                      section           object class status
    ##               7 early binary        early_bin  list     ok
    ##            8 parallel binary     parallel_bin  list     ok
    ##    9 serial all-early binary    serial_ae_bin  list     ok
    ##       10 serial mixed binary serial_mixed_bin  list     ok
    ##     11 omics profile (early)   prof_early_bin  list     ok
    ##  11 omics profile (parallel)     prof_par_bin  list     ok
    ##    11 omics profile (serial)     prof_ser_bin  list     ok
    ##          12 predict (labels)         pred_lab  list     ok
    ##   12 predict (probabilities)        pred_prob  list     ok

    cat(sprintf("\n%d of %d registered steps ok; %d not ok\n",
                sum(status$status == "ok"), nrow(status),
                sum(status$status != "ok")))

    ##
    ## 9 of 9 registered steps ok; 0 not ok

### Session Info

    sessionInfo()

    ## R version 4.4.0 (2024-04-24)
    ## Platform: aarch64-apple-darwin20
    ## Running under: macOS 26.6.2
    ##
    ## Matrix products: default
    ## BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
    ## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
    ##
    ## locale:
    ## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
    ##
    ## time zone: America/Los_Angeles
    ## tzcode source: internal
    ##
    ## attached base packages:
    ## [1] stats     graphics  grDevices utils     datasets  methods   base
    ##
    ## other attached packages:
    ## [1] LUCIDus_3.2.1
    ##
    ## loaded via a namespace (and not attached):
    ##  [1] sass_0.4.10        generics_0.1.4     shape_1.4.6.1      stringi_1.8.7
    ##  [5] lattice_0.22-7     hms_1.1.4          digest_0.6.39      magrittr_2.0.4
    ##  [9] evaluate_1.0.5     grid_4.4.0         RColorBrewer_1.1-3 iterators_1.0.14
    ## [13] fastmap_1.2.0      foreach_1.5.2      jsonlite_2.0.0     glmnet_4.1-10
    ## [17] Matrix_1.7-4       progress_1.2.3     nnet_7.3-20        survival_3.8-3
    ## [21] mclust_6.1.2       scales_1.4.0       codetools_0.2-20   networkD3_0.4.1
    ## [25] jquerylib_0.1.4    cli_3.6.5          rlang_1.1.6        crayon_1.5.3
    ## [29] splines_4.4.0      withr_3.0.2        cachem_1.1.0       yaml_2.3.11
    ## [33] tools_4.4.0        dplyr_1.1.4        ggplot2_4.0.2      boot_1.3-32
    ## [37] vctrs_0.6.5        R6_2.6.1           lifecycle_1.0.4    htmlwidgets_1.6.4
    ## [41] pkgconfig_2.0.3    glasso_1.11        bslib_0.9.0        pillar_1.11.1
    ## [45] gtable_0.3.6       glue_1.8.0         Rcpp_1.1.0         tidyselect_1.2.1
    ## [49] xfun_0.54          tibble_3.3.0       data.tree_1.2.0    knitr_1.50
    ## [53] dichromat_2.0-0.1  farver_2.1.2       htmltools_0.5.9    igraph_2.2.1
    ## [57] labeling_0.4.3     rmarkdown_2.30     compiler_4.4.0     prettyunits_1.2.0
    ## [61] S7_0.2.1