Skip to content

Commit 2ce4687

Browse files
authored
Merge pull request #32 from dfeen87/copilot/create-figure-8-null-comparison
Add Figure 8: null-test comparison for ΔΦ(t) validation
2 parents 73ea679 + 7ef7dd0 commit 2ce4687

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

245 KB
Loading

src/viz/generate_figure_8.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""
2+
Generate Figure 8 — Null-Test Comparison.
3+
4+
Produces a single publication-ready PNG comparing four ΔΦ(t) trajectories:
5+
1. Observed synthetic benchmark
6+
2. Shuffled-time null model
7+
3. Phase-randomized null model
8+
4. Stable control baseline
9+
10+
Output: figures/final/figure_8_null_comparison.png
11+
"""
12+
13+
from __future__ import annotations
14+
15+
import os
16+
import sys
17+
18+
import numpy as np
19+
import matplotlib
20+
matplotlib.use("Agg")
21+
import matplotlib.pyplot as plt
22+
23+
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
24+
sys.path.insert(0, _REPO_ROOT)
25+
26+
from src.simulation.generate_synthetic import generate_synthetic_ice # noqa: E402
27+
from src.simulation.null_tests import ( # noqa: E402
28+
shuffled_time_null,
29+
phase_randomized_null,
30+
stable_control_null,
31+
)
32+
33+
# ---------------------------------------------------------------------------
34+
# Manuscript color palette
35+
# ---------------------------------------------------------------------------
36+
_COLORS = {
37+
"observed": "#C62828", # deep crimson — observed signal
38+
"shuffled": "#1565C0", # deep blue — shuffled-time null
39+
"surrogate": "#E65100", # deep orange — phase-randomized null
40+
"stable": "#2E7D32", # deep green — stable control
41+
}
42+
43+
_LINEWIDTH = 1.6
44+
45+
46+
def generate_figure_8(seed: int = 0) -> str:
47+
"""Generate and save Figure 8.
48+
49+
Parameters
50+
----------
51+
seed : int
52+
Random seed used for all surrogates (ensures reproducibility).
53+
54+
Returns
55+
-------
56+
save_path : str
57+
Absolute path to the saved PNG file.
58+
"""
59+
# ------------------------------------------------------------------
60+
# 1. Generate observed synthetic benchmark
61+
# ------------------------------------------------------------------
62+
series, t, _ = generate_synthetic_ice(seed=seed)
63+
delta_E = series.delta_E
64+
delta_I = series.delta_I
65+
delta_C = series.delta_C
66+
observed = np.sqrt(delta_E ** 2 + delta_I ** 2 + delta_C ** 2)
67+
68+
# ------------------------------------------------------------------
69+
# 2. Generate null surrogates
70+
# ------------------------------------------------------------------
71+
shuffled = shuffled_time_null(observed, seed=seed)
72+
surrogate = phase_randomized_null(delta_E, delta_I, delta_C, seed=seed)
73+
control = stable_control_null(t, seed=seed)
74+
75+
# ------------------------------------------------------------------
76+
# 3. Plot
77+
# ------------------------------------------------------------------
78+
fig, ax = plt.subplots(figsize=(12, 4))
79+
80+
ax.plot(t, observed, lw=_LINEWIDTH, color=_COLORS["observed"],
81+
label="Observed", zorder=4)
82+
ax.plot(t, shuffled, lw=_LINEWIDTH, color=_COLORS["shuffled"],
83+
label="Shuffled-time null", zorder=3, alpha=0.85)
84+
ax.plot(t, surrogate, lw=_LINEWIDTH, color=_COLORS["surrogate"],
85+
label="Phase-randomized null", zorder=3, alpha=0.85)
86+
ax.plot(t, control, lw=_LINEWIDTH, color=_COLORS["stable"],
87+
label="Stable control", zorder=2, alpha=0.85)
88+
89+
ax.set_xlabel("Time (s)", fontsize=12)
90+
ax.set_ylabel("ΔΦ(t)", fontsize=12)
91+
ax.set_title("Figure 8 — Null-Test Comparison", fontsize=13, fontweight="bold")
92+
ax.legend(loc="upper right", fontsize=10, framealpha=0.9)
93+
ax.tick_params(labelsize=10)
94+
ax.set_xlim(t[0], t[-1])
95+
96+
plt.tight_layout()
97+
98+
# ------------------------------------------------------------------
99+
# 4. Save
100+
# ------------------------------------------------------------------
101+
save_path = os.path.join(_REPO_ROOT, "figures", "final",
102+
"figure_8_null_comparison.png")
103+
os.makedirs(os.path.dirname(save_path), exist_ok=True)
104+
fig.savefig(save_path, dpi=150, bbox_inches="tight")
105+
plt.close(fig)
106+
return save_path
107+
108+
109+
if __name__ == "__main__":
110+
path = generate_figure_8()
111+
print(f"Saved → {path}")

0 commit comments

Comments
 (0)