Model structure by epidemiological outcome (#158), DAG update (#162) - #167
Model structure by epidemiological outcome (#158), DAG update (#162)#167kathsherratt wants to merge 6 commits into
Conversation
Code and prose only. Model outputs under output/ are deliberately not included: a refit is still running and will regenerate them, so they are left for the follow-up commit once it lands. Specification: replace s(Method) with s(Method, Epi_target). mgcv's bs = "re" interaction is an unconstrained zero-mean prior over all cells, so its target-average is exactly what a main effect represents; fitted together mgcv gave the main effect 0.001 edf against 4.9 for the interaction. Dropping it removes the aliasing. The pooled per-structure effect is recovered as a contrast across cells. New R/utils-effects.R replaces gammit::extract_ranef(), which cannot handle a factor-by-factor random effect: it reads only the last variable name of the interaction, collects 5 labels for a 10-coefficient term and fails, taking down extraction for every term in the fit. The replacement rebuilds each smooth's design matrix from the formula mgcv stores on it, so labels map to coefficients exactly. Validated against gammit on a no-interaction fit: identical to the last decimal on all columns. DAG (#162): add the epidemiological outcome as a confounder, not merely a covariate -- forecasters chose which outcomes to submit for, opening a backdoor through modeller strategy. Querying the updated diagram returns our exact covariate set as a minimal sufficient adjustment set for the DIRECT effect, and returns no valid set for the total effect, since latent modeller strategy cannot be blocked. This formally supports the paper's partial-effect framing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the specification change committed as WIP: regenerated model outputs for both scales, and the reporting layer for the new term. Dropping the aliased s(Method) main effect changed the fit by nothing -- identical AIC, deviance explained and residuals on both scales, confirming it carried no information. It changed materially what can be reported. The old main effect was shrunk flat to 1.000 (0.994-1.007) for every structure; the pooled contrast across cells gives real estimates with honest intervals, from 0.977 (0.878-1.088) for judgement models to 1.046 (0.946-1.155) for semi-mechanistic. Table 2 therefore now reports a quantity that carries information. Adding the crossed term improves AIC by 93 on the log scale and by 16,724 on the natural scale, where it also cuts residual skew from 4.60 to 4.38. New reporting: plot_method_target() and print_table_method_target() for the per-outcome effects, added to the results as Figure 4 and Table 3, with the raw per-cell effects added to the supplement. Findings are reported conservatively. Human judgement models perform relatively better on cases than deaths (0.96 against 0.99), the same direction as Bosse et al. (2022). The widest separation is among agent-based models (cases 1.09, deaths 0.88), which rests on three models and is flagged as such in text; every interval spans the grand mean. The direction of these contrasts is stable across error families but the magnitude is not -- under a Gaussian family the judgement separation is larger and the agent-based separation disappears -- so that is recorded as a supplementary sensitivity. Uses a plain chunk label for the new table rather than a tbl- prefix: a tbl- label combined with a kableExtra caption left an unresolved cross-reference and leaked the div as literal text. This matches the convention the existing tables already use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Discussion: the claim that adjusted estimates "were no different from the overall average" described a term since shown to be shrunk to nothing. They are imprecise rather than identical to the mean, so the wording now says so, and covers both the pooled and per-outcome estimates. Checked for consistency against the abstract and results, which state the conclusion in terms that remain accurate. Adds a discussion paragraph identifying the structure-by-outcome interaction as the single specification choice that materially changed the results. A model-based evaluation of this kind is highly flexible, and the substantive conclusion proved robust to covariate selection, link function and error family; assuming a shared effect across cases and deaths was the one assumption that was not neutral. Draws out the implication for other evaluations pooling across distinct targets. Adds a main-text pointer to the Gaussian sensitivity so it is visible without reading the supplement, with the detail kept in the supplement. The direction of every structure-by-outcome contrast is stable across error families while the magnitudes are not, so the ordering of structures within an outcome is described as indicative. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The central interpretive point was implied by the numbers but never stated: structural differences pointed in opposite directions for cases and deaths, so any term averaging over outcomes recovers close to zero. This explains both why the shared structure effect was always flat and why the crossed term carries signal, and it reframes an existing null literature. Six places updated: - Discussion, prior work: offers a mechanism for null findings in earlier structure comparisons. A pooled null is consistent with either an absence of differences or with differences that offset across targets, and the two cannot be separated without letting the effect vary by target. Closes with an explicit limit: we demonstrate the mechanism, we do not establish any particular contrast. - Discussion, specification: states why assuming a shared effect is not neutral. The average is near zero by arithmetic, and penalisation then takes it to zero exactly. - Results, pooled: notes an estimate near the grand mean has two possible causes, motivating the per-outcome split. - Results, per-outcome: leads with the offsetting, illustrated by the widest-separated structure, then human judgement against Bosse et al. - Methods: gives the substantive cause alongside the technical one, so the 0.001 edf does not read as a fitting artefact. - Abstract: one sentence noting the overall null concealed differences that cancelled. Keeps the widest-separation claim data-driven rather than naming a structure, so a refit cannot silently falsify it. Consolidates a duplicate description of agent-based models and gathers the three caveats -- intervals spanning the mean, three contributing models, family sensitivity -- into one place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Added: the offsetting result, brought out through the textFollow-up commits While reviewing why the shared structure effect was always flat, the underlying reason turned out to be worth stating explicitly, and it had never been written down: Structural differences pointed in opposite directions for cases and deaths, so any term averaging over outcomes recovers close to zero.
Cell effects of ±0.04–0.12 average to ±0.001–0.045. A shared effect is that average, so it was near zero before any penalty applied — and a penalised term near zero is shrunk to zero exactly. This explains both why the main effect was always flat (it was flat in every earlier specification too, before the interaction existed) and why the crossed term carries 4.9 edf. Worth noting what this is not: the signal did not migrate out of Where the text changed
Deliberate limitsEvery per-outcome interval still spans the grand mean, so the text says we demonstrate the mechanism rather than establish any particular contrast. The widest separation rests on three models and does not survive a Gaussian family; all three caveats are stated together rather than scattered. The widest-separation claim is computed rather than hardcoded, so a refit cannot silently falsify it. |
The same aliasing that ruled out a structure main effect also applies to the epidemiological target: the within-target average of the crossed cells is what a target main effect represents. The difference is that only one of the two terms is penalised, so the unpenalised fixed effect takes the component common to all structures and the crossed term keeps only departures from it. That separation holds in the fitted model. The crossed effects average to zero within each target to ~1e-13, so the entire deaths-versus-cases difference sits in the fixed coefficient (-1.03) and none leaks into the structure estimates (largest cell 0.12). This is emergent rather than imposed: the smooth retains all ten coefficients, so mgcv applied no centring constraint. Retaining the fixed effect is therefore what licenses reading the crossed term as structure-specific. Dropping it would force the single largest effect in the model through a penalised term and bias it toward zero; making the target random would attempt to estimate a variance from two levels. Recorded in the Methods and alongside the formula in analysis-model.R. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Above messages generated by LLM. This sequence of changes was developed iteratively based on LLM and detailed human review. My TLDR from a long sequence of working out what means what where:
|
Covers the two preceding commits, which changed the manuscript text but had not been logged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Notes for review: PRs are stacked on top of each other - so should be merged in order - but I've left them open hoping this helps to review the three major changes one at a time. #164, Include the Hub baseline model
#166, Change the error family
#167, Let effect of model structure differ by outcome
|
Draft. Stacked on #166, which is stacked on #164 — review those first, and this diff will shrink once they merge.
Closes #158 and #162.
What changed
The model assumed each structure predicted cases and deaths equally well. Replaced
s(Method, bs = "re")withs(Method, Epi_target, bs = "re"), so a structure may predict one outcome relatively better than the other.There is deliberately no structure main effect alongside it. mgcv's
bs = "re"interaction is an unconstrained zero-mean prior over all cells, so its average across outcomes is exactly what a main effect represents. With both terms penalised, the split between them follows the relative variance estimates rather than the data — and fitted together, mgcv gave the main effect 0.001 effective degrees of freedom against 4.9 for the crossed term. Dropping it changed AIC, deviance explained and residuals by nothing on either scale.The pooled per-structure effect is instead recovered as a contrast averaging a structure's two cells, evaluated against the full covariance matrix.
Why this matters beyond answering the issue
The old main effect was shrunk flat, so Table 2 was reporting a term carrying no information:
Adding the crossed term improves AIC by 93 on the log scale and by 16,724 on the natural scale, where it also cuts residual skew from 4.60 to 4.38.
Findings
Human judgement models perform relatively better on cases than deaths (0.96 against 0.99), the same direction as Bosse et al. (2022). The widest separation is among agent-based models (cases 1.09, deaths 0.88) — from three models, flagged as such in text. Every interval spans the grand mean.
The direction of these contrasts is stable across error families; the magnitude is not. Under a Gaussian family the judgement separation widens and the agent-based separation disappears, because a Gaussian fit is dominated by the largest scores, which fall disproportionately among case forecasts. Recorded as a supplementary sensitivity with a pointer from the main text. Including or excluding the Hub baseline makes no material difference.
Required detour: replacing the effect extraction
gammit::extract_ranef()cannot handle a factor-by-factor random effect. It reads only the last variable name of the interaction and looks up that factor's levels, so it collects 5 labels for a 10-coefficient term and fails — taking down extraction for every term in the fit, not just the interaction. #158 could not be delivered without replacing this layer.New
R/utils-effects.Rrebuilds each smooth's design matrix from the formula mgcv stores on the smooth object ($form), so labels map to coefficients exactly without assuming how mgcv orders interactions. Validated against gammit on a no-interaction fit: identical on every column to the last decimal, 48 rows. Astopifnotguards the mapping in case mgcv's construction changes.DAG (#162)
The epidemiological outcome is added as a confounder, not merely a covariate: forecasters chose which outcomes to submit for, and that choice is associated with structure, opening a backdoor through modeller strategy.
This earned more than expected. Querying the updated diagram returns our exact covariate set as a minimal sufficient adjustment set for the direct effect, and returns no valid set for the total effect, because latent modeller strategy cannot be blocked. That turns the paper's partial-effect framing from an assertion into a derivation. The crossed term is effect modification, which a DAG does not encode, so it does not alter the adjustment set.
Prose
Corrected the discussion claim that adjusted estimates "were no different from the overall average" — that described the collapsed term; they are imprecise, not identical to the mean.
Added a discussion paragraph identifying this interaction as the one specification choice that materially changed the results, against an approach otherwise robust to covariate selection, link function and error family.
Review notes
output/are regenerated; both scales refit with no convergence warnings.output/diagnostics/fit-summary.csvkeeps the both-terms fit alongside the final one, so the 0.001-edf claim is checkable.R/sensitivity/check-autocorrelation.Rsources the deletedmodel-logresp.Rand does not run.🤖 Generated with Claude Code