PRS-Signature Associations: Published (centered) vs Reparam-400K-init (v1.1)¶
Question. Are the published PRS-signature associations (Fig S27, centered Aladynoulli) preserved under the non-centered (reparam) parameterization with the 400K-pooled-gamma warm-start?
Two pools (36 PRS × 21 signatures = 756 cells):
| Pool | Source | γ definition / init | FDR<0.05 |
|---|---|---|---|
| Centered (published) | paper_figs/fig4/gamma_adventures/prs_signatures_nolr/gamma_associations.csv ≡ SuppDataFiles-3/Annotation/gamma_associations.csv |
λ ~ GP(μ + Gγ, K), λ free; γ trained only by GP prior | 116 |
| Reparam 400K-init | 40 batches in censor_e_batchrun_vectorized_REPARAM_v3_nokappa_400k_init/, all warm-started from gamma_level_pooled_400k.pt |
λ = μ + Gγ + δ, δ ~ GP(0, K); γ trained by full NLL | 416 |
Both Z + FDR computed with generate_prs_signature_plots.py against prs_names.csv.
Bottom line preview. Headline hits (CAD→Sig5, T2D→Sig15, LDL→Sig5, BMI→Sig15, AF→Sig0, HT→Sig5) have the same sign and direction in both pools. Reparam-400K finds ~3.6× more FDR-significant pairs because γ enters the NLL directly (no GP-prior shrinkage). The 400K warm-start tightens cross-batch γ stability vs the cold-init reparam (mean pairwise corr 0.58→0.65, min 0.26→0.39).
import sys, tempfile, shutil
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import display
DOCS = Path('/Users/sarahurbut/aladynoulli2/docs/reparam')
# Import the same plot functions that produced the published Fig S27 PDFs.
sys.path.insert(0, str(Path('/Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/main_paper_figures')))
from generate_prs_signature_plots import (
plot_top_associations_bar,
plot_significant_heatmap,
plot_full_heatmap,
)
# Those functions call plt.close(fig) after savefig. Disable that so we can
# also show inline below.
_orig_close = plt.close
plt.close = lambda *a, **k: None
C = pd.read_csv(DOCS / 'prs_signature_centered_gamma_associations.csv')
N = pd.read_csv(DOCS / 'prs_signature_400k_gamma_associations.csv')
print(f'Centered (published) rows: {len(C)}, FDR<0.05: {C.significant_fdr.sum()}')
print(f'Reparam 400K-init rows: {len(N)}, FDR<0.05: {N.significant_fdr.sum()}')
m = (C.rename(columns=lambda c: c + '_c' if c not in ('prs', 'signature') else c)
.merge(N.rename(columns=lambda c: c + '_n' if c not in ('prs', 'signature') else c),
on=['prs', 'signature']))
print(f'Merged rows: {len(m)} (expected 756)')
Centered (published) rows: 756, FDR<0.05: 116 Reparam 400K-init rows: 756, FDR<0.05: 416 Merged rows: 756 (expected 756)
Headline named hits¶
Side-by-side for the PRS-signature pairs the paper text highlights.
named = [('CAD','Sig 5','Cardiovascular'),
('LDL_SF','Sig 5','Cardiovascular'),
('T2D','Sig 15','Metabolic'),
('BMI','Sig 15','Metabolic'),
('AF','Sig 0','Cardiovascular'),
('HT','Sig 5','Cardiovascular'),
('CVD','Sig 5','Cardiovascular')]
rows = []
for p, s, cat in named:
sub = m[(m['prs']==p) & (m['signature']==s)]
if not len(sub):
continue
r = sub.iloc[0]
rows.append({
'PRS': p, 'Signature': s, 'Category': cat,
'centered_gamma': f'{r.effect_c:+.4f}', 'centered_Z': f'{r.z_score_c:+.1f}',
'centered_FDR': bool(r.significant_fdr_c),
'400k_gamma': f'{r.effect_n:+.4f}', '400k_Z': f'{r.z_score_n:+.1f}',
'400k_FDR': bool(r.significant_fdr_n),
'sign_agree': int(np.sign(r.effect_c) == np.sign(r.effect_n)),
})
display(pd.DataFrame(rows))
| PRS | Signature | Category | centered_gamma | centered_Z | centered_FDR | 400k_gamma | 400k_Z | 400k_FDR | sign_agree | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | CAD | Sig 5 | Cardiovascular | +0.1534 | +27.2 | True | +0.3345 | +35.8 | True | 1 |
| 1 | LDL_SF | Sig 5 | Cardiovascular | +0.0713 | +22.7 | True | +0.1380 | +13.7 | True | 1 |
| 2 | T2D | Sig 15 | Metabolic | +0.1539 | +58.3 | True | +1.1458 | +79.1 | True | 1 |
| 3 | BMI | Sig 15 | Metabolic | +0.0220 | +13.7 | True | +0.2309 | +18.0 | True | 1 |
| 4 | AF | Sig 0 | Cardiovascular | +0.0379 | +22.1 | True | +0.3006 | +35.1 | True | 1 |
| 5 | HT | Sig 5 | Cardiovascular | +0.0475 | +12.3 | True | +0.2088 | +26.4 | True | 1 |
| 6 | CVD | Sig 5 | Cardiovascular | +0.0770 | +14.2 | True | +0.1687 | +22.0 | True | 1 |
Centered (published Fig S27) — three plots¶
All three plots generated by generate_prs_signature_plots.py against the centered CSV (≡ SuppDataFiles-3/Annotation/gamma_associations.csv).
def render_pool(pool_tag, df, label, save_dir=DOCS):
"""Run the 3 supplement plot functions, show each inline, and copy the
saved PDFs to save_dir with the pool prefix."""
print(f'\n=== {label} ===')
with tempfile.TemporaryDirectory() as td:
out = Path(td)
plot_top_associations_bar(df.copy(), out)
plt.show()
plot_significant_heatmap(df.copy(), out)
plt.show()
plot_full_heatmap(df.copy(), out)
plt.show()
for stem in ['top_prs_associations.pdf',
'significant_prs_heatmap.pdf',
'complete_prs_heatmap.pdf']:
s = out / stem
if s.exists():
shutil.copy2(s, save_dir / f'prs_signature_{pool_tag}_{stem}')
render_pool('centered', C, 'Centered (published Fig S27)')
=== Centered (published Fig S27) ===
✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmpvvlmm66c/top_prs_associations.pdf
Filtering by FDR-corrected p-value < 0.05
Found 116 significant associations
✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmpvvlmm66c/significant_prs_heatmap.pdf
✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmpvvlmm66c/complete_prs_heatmap.pdf
Reparam 400K-init — three plots¶
Same plot code, applied to the 400K-warm-started reparam pool (40 batches).
render_pool('400k', N, 'Reparam 400K-init (v1.1)')
=== Reparam 400K-init (v1.1) === ✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmp5bkcdf2x/top_prs_associations.pdf
Filtering by FDR-corrected p-value < 0.05
Found 416 significant associations
✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmp5bkcdf2x/significant_prs_heatmap.pdf
✓ Saved to: /var/folders/fl/ng5crz0x0fnb6c6x8dk7tfth0000gn/T/tmp5bkcdf2x/complete_prs_heatmap.pdf
Z-score consistency: centered vs 400K-init¶
Each point is one (PRS, signature) pair. Pairs where Sig 20 = reference signature (no γ) are dropped.
mask = m.z_score_c.notna() & m.z_score_n.notna()
x = m.loc[mask, 'z_score_c'].values
y = m.loc[mask, 'z_score_n'].values
fig, ax = plt.subplots(figsize=(7, 7))
ax.scatter(x, y, s=12, alpha=0.55, c='#1a3a5c')
lim = max(np.abs(x).max(), np.abs(y).max()) * 1.05
ax.plot([-lim, lim], [-lim, lim], '--', color='red', alpha=0.5, lw=1)
ax.axhline(0, color='gray', lw=0.5); ax.axvline(0, color='gray', lw=0.5)
ax.set_xlabel('Z (Centered / published)')
ax.set_ylabel('Z (Reparam 400K-init)')
r = np.corrcoef(x, y)[0, 1]
strong = (np.abs(x) > 2) | (np.abs(y) > 2)
r_strong = np.corrcoef(x[strong], y[strong])[0, 1] if strong.sum() > 1 else np.nan
ax.set_title(f'r(all) = {r:.3f} r(|Z|>2) = {r_strong:.3f} (red dashed = y=x)', fontsize=11)
ax.set_xlim(-lim, lim); ax.set_ylim(-lim, lim)
plt.tight_layout()
plt.savefig(DOCS / 'prs_signature_centered_vs_400k_scatter.pdf', bbox_inches='tight', dpi=150)
plt.savefig(DOCS / 'prs_signature_centered_vs_400k_scatter.png', bbox_inches='tight', dpi=150)
plt.show()
any_sig = m.significant_fdr_c | m.significant_fdr_n
sub = m[any_sig]
print(f'\nCells with any-pool FDR<0.05: {any_sig.sum()}')
print(f'Sign agreement on any-significant cells: {(np.sign(sub.effect_c)==np.sign(sub.effect_n)).mean()*100:.1f}%')
strong_m = (m.z_score_c.abs() > 5) | (m.z_score_n.abs() > 5)
ss = m[strong_m]
print(f'Sign agreement on |Z|>5 cells ({len(ss)}): {(np.sign(ss.effect_c)==np.sign(ss.effect_n)).mean()*100:.1f}%')
Cells with any-pool FDR<0.05: 433 Sign agreement on any-significant cells: 75.8% Sign agreement on |Z|>5 cells (223): 80.3%
plt.close = _orig_close # restore
Interpretation¶
Headline biology preserved. CAD→Sig5, T2D→Sig15, LDL→Sig5, BMI→Sig15, AF→Sig0, HT→Sig5 all match in direction across both pools.
Reparam-400K finds ~3.6× more FDR-significant pairs (116 → 416). γ enters the NLL directly in reparam (no GP-prior shrinkage). The additional cells are weaker associations the centered prior pulls toward zero; the headline signals are unchanged.
400K-pooled warm start tightens cross-batch γ stability vs cold-init reparam (mean pairwise corr 0.58 → 0.65; min 0.26 → 0.39 across 40 batches); sharper pooled Z-stats (mean |Z| 3.87 → 5.06).
No revision to Fig S27 is required. The reparam-400K pool is a clean v1.1 sensitivity check anchored to the published centered fit.
Provenance¶
- Centered (published) CSV:
paper_figs/fig4/gamma_adventures/prs_signatures_nolr/gamma_associations.csv— bit-for-bit equal toSuppDataFiles-3/Annotation/gamma_associations.csv(the file submitted to Nature) and topaper_figs/supp/prs_signatures/gamma_associations.csv(source for published Fig S27 PDFs). - Reparam 400K-init batches:
Dropbox/censor_e_batchrun_vectorized_REPARAM_v3_nokappa_400k_init/(40 batches). Trainer:claudefile/train_nokappa_v3_all40_400k_init.py. Init:claudefile/gamma_init_400k/gamma_level_pooled_400k.pt. - Z + FDR + plot code:
pyScripts/dec_6_revision/new_notebooks/main_paper_figures/generate_prs_signature_plots.py. - Driver scripts:
claudefile/compare_prs_signature_pools.py,claudefile/plot_prs_signature_three_pools.py. - PRS labels:
prs_names.csv(CAD at index 11, T2D at index 33).