{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiho4ysv4443fthborvfgaaxqujzgifbqhgra7buws3ckxowlarwee",
    "uri": "at://did:plc:wwyqal4cnqhuwyacdj7rqq3n/app.bsky.feed.post/3mjq6atbuvlb2"
  },
  "path": "/t/power-calculations-in-longitudinal-mixed-effects-from-two-measurements-to-three-measurements/28699#post_10",
  "publishedAt": "2026-04-15T16:35:48.000Z",
  "site": "https://discourse.datamethods.org",
  "textContent": "Thank you, both.\n\nWere these the models you had in mind?\n\n\n    library(nlme)\n    library(lme4)\n    library(emmeans)\n    library(ggplot2)\n    library(dplyr)\n\n    ── 1. Data\n\n    url_raw ← “https://github.com/jorgemmteixeira/nlmeU-datset/raw/main/data/armd0.rda”\n    load(url(url_raw))\n\n    Filter to post-baseline rows (baseline visual0 remains as a fixed covariate)\n\n    armd0_sub ← armd0[armd0$time > 0, ]\n    armd0_sub$time_f_ord ← factor(armd0_sub$time.f, ordered = TRUE)\n    armd0_sub$subject ← as.factor(armd0_sub$subject)\n\n    Compute and plot empirical variogram\n\n    vgm_emp ← Variogram(gls_ols, form = ~ time | subject)\n    plot(vgm_emp, main = “Empirical Semi-variogram (Initial Check)”)\n\n    ── 2. Model Fitting (No Random Slopes) ─────────────────────────────────────\n\n    — nlme GLS (Marginal Models) —\n\n    gls_ar1   ← gls(visual ~ visual0 + time * treat.f, data = armd0_sub,\n    correlation = corAR1(form = ~ tp | subject), method = “REML”)\n\n    gls_exp   ← gls(visual ~ visual0 + time * treat.f, data = armd0_sub,\n    correlation = corExp(form = ~ time | subject), method = “REML”)\n\n    gls_unstr ← gls(visual ~ visual0 + time * treat.f, data = armd0_sub,\n    correlation = corSymm(form = ~ tp | subject),\n    weights = varIdent(form = ~ 1 | time.f), method = “REML”)\n\n    — lme4 LMM (Conditional Models - Random Intercept Only) —\n\n    lme_cs   ← lmer(visual ~ visual0 + time * treat.f + (1 | subject), data = armd0_sub)\n    lme_ar1  ← lmer(visual ~ visual0 + time * treat.f + ar1(time_f_ord + 0 | subject), data = armd0_sub)\n    lme_diag ← lmer(visual ~ visual0 + time * treat.f + diag(time.f + 0 | subject), data = armd0_sub)\n\n    — Markov Transition Model (OLS) —\n\n    armd_lag ← armd0_sub\n\n    group_by(subject)\n\n    mutate(visual_prev = lag(visual))\n\n    filter(!is.na(visual_prev))\n\n    markov_ols ← lm(visual ~ visual0 + visual_prev + time * treat.f, data = armd_lag)\n\n    ── 3. Extract Estimates and Calculate CI Widths ────────────────────────────\n\n    extract_52 ← function(mod, name) {\n    emm ← emmeans(mod, pairwise ~ treat.f | time, at = list(time = 52))\n    res ← as.data.frame(summary(emm$contrasts, infer = TRUE))\n    data.frame(Model = name, Estimate = res$estimate, Lower = res$lower.CL,\n    Upper = res$upper.CL, P_Value = res$p.value)\n    }\n\n    Combine all except Markov\n\n    results ← rbind(\n    extract_52(gls_ar1,   “GLS AR(1)”),\n    extract_52(gls_exp,   “GLS Exp”),\n    extract_52(gls_unstr, “GLS Unstr”),\n    extract_52(lme_cs,    “LMM CS”),\n    extract_52(lme_ar1,   “LMM AR(1)”),\n    extract_52(lme_diag,  “LMM Diag”)\n    )\n\n    Add Markov Manually (Calculated at t=52)\n\n    m_summ ← summary(markov_ols)$coefficients\n    m_est  ← m_summ[“treat.fActive”,1] + m_summ[“time:treat.fActive”,1] * 52\n    m_se   ← sqrt(vcov(markov_ols)[“treat.fActive”,“treat.fActive”] +\n    (52^2)vcov(markov_ols)[“time:treat.fActive”,“time:treat.fActive”])\n    results ← rbind(results, data.frame(\n    Model = “Markov OLS”, Estimate = m_est, Lower = m_est - 1.96m_se,\n    Upper = m_est + 1.96*m_se, P_Value = 2 * (1 - pnorm(abs(m_est/m_se)))\n    ))\n\n    ── 4. Summary Table (Ordered by CI Width) ──────────────────────────────────\n\n    final_table ← results\n\n    mutate(CI_Width = Upper - Lower)\n\n    arrange(CI_Width)\n\n    select(Model, Estimate, Lower, Upper, CI_Width, P_Value)\n\n    print(final_table)\n",
  "title": "Power Calculations in Longitudinal Mixed Effects - from two measurements to three measurements"
}