You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Identified during PR #71 review. duration.method = \"joint_lm\" fits a covariate-adjusted log-linear regression of partnership duration on ego + partner + matching terms, which corrects for confounding of the duration estimate by other attributes — the direct analog of the marginal-vs-joint fix that method = \"joint\" applies to formation stats in #61 / #62.
But currently the aggregation step (stratum-level medians fed into the geometric transformation for mean.dur.adj) is done by predicting at the ARTnet observations, not the synthetic target population:
# In R/NetParams.R::fit_joint_lm_dur():fitted_dur<- exp(predict(fit, newdata=ongoing)) # ongoing = ARTnet observations# In R/NetParams.R::compute_alt_durs() for joint_lm:pred_sub<- exp(predict(res$model, newdata=sub)) # sub = ARTnet observations in stratum k
This gives a "confounding-corrected within ARTnet" estimate — strictly better than empirical, but not fully g-computed against whatever target population the user builds in build_netstats.
Proposed approach
Move the stratum-level aggregation from build_netparams into build_netstats under method = \"joint\". Specifically:
In build_netparams(..., duration.method = \"joint_lm\", method = \"joint\"), store the fitted lm at netparams$<layer>$joint_duration_model (already done) but do not compute stratum-level medians at this stage. Emit only the model.
In build_netstats(..., method = \"joint\"), after the synthetic population is constructed, pull netparams$<layer>$joint_duration_model and predict per-synthetic-dyad expected log-duration. Aggregate per stratum (non-matched, matched × index.age.grp) by taking medians of the per-dyad predicted durations. Feed through the existing geometric transformation to produce mean.dur.adj for dissolution_coefs().
The per-dyad prediction requires synthetic partnerships, not just synthetic nodes. For nonmatched-strata medians, pair synthetic nodes across age groups; for matched, within age group. This is the same (ego, alter) pair construction problem #63 raised for nodematch/absdiff — the dyad-level infrastructure from there is reusable.
Interaction with sex.cess.mod
The current sex.cess.mod code path sets dissolution rates to 1 for the post-cessation age group. That post-processing should still run after the new synthetic-population aggregation. No semantic change needed; just make sure the override logic fires in the right order.
Acceptance criteria
duration.method = \"joint_lm\" with method = \"joint\" in build_netstats produces stratum-level mean.dur.adj from per-synthetic-dyad predictions, not from ARTnet observations.
duration.method = \"joint_lm\" with method = \"existing\" continues to produce the current within-ARTnet estimates (since there's no synthetic population to predict against at that stage).
Backward-compat snapshot harness still matches 3/3 under method = \"existing\".
Where this lives
This is a direct continuation of the ARTnet joint g-comp refactor (#61-#63, #69, #71). Doesn't require any upstream tergm changes — the output still feeds the existing geometric-dissolution offset the same way.
Alternative location: the ARTnetPredict project, as part of the Phase 2+ forward-projection work. The logic there is: if we're going to post-stratify ARTnet to NHBS MSM demographics or to AMIS 2022-24 projections, the synthetic target population is more directly specified, and the per-dyad duration prediction matters more. In that context, this gap should be closed before the forward-projection is considered complete.
Context
Identified during PR #71 review.
duration.method = \"joint_lm\"fits a covariate-adjusted log-linear regression of partnership duration on ego + partner + matching terms, which corrects for confounding of the duration estimate by other attributes — the direct analog of the marginal-vs-joint fix thatmethod = \"joint\"applies to formation stats in #61 / #62.But currently the aggregation step (stratum-level medians fed into the geometric transformation for
mean.dur.adj) is done by predicting at the ARTnet observations, not the synthetic target population:This gives a "confounding-corrected within ARTnet" estimate — strictly better than
empirical, but not fully g-computed against whatever target population the user builds inbuild_netstats.Proposed approach
Move the stratum-level aggregation from
build_netparamsintobuild_netstatsundermethod = \"joint\". Specifically:build_netparams(..., duration.method = \"joint_lm\", method = \"joint\"), store the fittedlmatnetparams$<layer>$joint_duration_model(already done) but do not compute stratum-level medians at this stage. Emit only the model.build_netstats(..., method = \"joint\"), after the synthetic population is constructed, pullnetparams$<layer>$joint_duration_modeland predict per-synthetic-dyad expected log-duration. Aggregate per stratum (non-matched, matched × index.age.grp) by taking medians of the per-dyad predicted durations. Feed through the existing geometric transformation to producemean.dur.adjfordissolution_coefs().The per-dyad prediction requires synthetic partnerships, not just synthetic nodes. For nonmatched-strata medians, pair synthetic nodes across age groups; for matched, within age group. This is the same
(ego, alter)pair construction problem #63 raised for nodematch/absdiff — the dyad-level infrastructure from there is reusable.Interaction with sex.cess.mod
The current sex.cess.mod code path sets dissolution rates to 1 for the post-cessation age group. That post-processing should still run after the new synthetic-population aggregation. No semantic change needed; just make sure the override logic fires in the right order.
Acceptance criteria
duration.method = \"joint_lm\"withmethod = \"joint\"inbuild_netstatsproduces stratum-levelmean.dur.adjfrom per-synthetic-dyad predictions, not from ARTnet observations.duration.method = \"joint_lm\"withmethod = \"existing\"continues to produce the current within-ARTnet estimates (since there's no synthetic population to predict against at that stage).race.prop = c(0.35, 0.25, 0.40)) visibly diverge from the current ARTnet-conditional estimates, paralleling the nodefactor/nodematch divergence PR Use joint GLM g-computation for build_netstats target stats (#62) #68 / Joint dyad-level modeling: nodematch + absdiff (#63 phases 1 & 2) #69 showed for formation stats.method = \"existing\".Where this lives
This is a direct continuation of the ARTnet joint g-comp refactor (#61-#63, #69, #71). Doesn't require any upstream
tergmchanges — the output still feeds the existing geometric-dissolution offset the same way.Alternative location: the ARTnetPredict project, as part of the Phase 2+ forward-projection work. The logic there is: if we're going to post-stratify ARTnet to NHBS MSM demographics or to AMIS 2022-24 projections, the synthetic target population is more directly specified, and the per-dyad duration prediction matters more. In that context, this gap should be closed before the forward-projection is considered complete.
Related
duration.method = \"joint_lm\"but aggregates at ARTnet, not synthetic.method = \"joint\"g-computation infrastructure inbuild_netstats; same pattern extended here.