mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-03-27 07:09:27 +08:00
feat(animation): extend forecasts to final date with dynamic horizon
- Each forecast now extends to 2025-12 regardless of historical data length - Step 1 (12 points): forecasts 36 months ahead to 2025-12 - Step 25 (36 points): forecasts 12 months ahead to 2025-12 - GIF shows full forecast horizon at every animation step
This commit is contained in:
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 644 KiB After Width: | Height: | Size: 776 KiB |
@@ -3,7 +3,7 @@
|
|||||||
Generate animation data for interactive forecast visualization.
|
Generate animation data for interactive forecast visualization.
|
||||||
|
|
||||||
This script runs TimesFM forecasts incrementally, starting with minimal data
|
This script runs TimesFM forecasts incrementally, starting with minimal data
|
||||||
and adding one point at a time, saving all forecasts for an interactive slider.
|
and adding one point at a time. Each forecast extends to the final date (2025-12).
|
||||||
|
|
||||||
Output: animation_data.json with all forecast steps
|
Output: animation_data.json with all forecast steps
|
||||||
"""
|
"""
|
||||||
@@ -19,7 +19,10 @@ import timesfm
|
|||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
MIN_CONTEXT = 12 # Minimum points to start forecasting
|
MIN_CONTEXT = 12 # Minimum points to start forecasting
|
||||||
HORIZON = 12 # Always forecast 12 months ahead
|
MAX_HORIZON = (
|
||||||
|
36 # Max forecast length (when we have 12 points, forecast 36 months to 2025-12)
|
||||||
|
)
|
||||||
|
TOTAL_MONTHS = 48 # Total months from 2022-01 to 2025-12 (graph extent)
|
||||||
INPUT_FILE = Path(__file__).parent / "temperature_anomaly.csv"
|
INPUT_FILE = Path(__file__).parent / "temperature_anomaly.csv"
|
||||||
OUTPUT_FILE = Path(__file__).parent / "animation_data.json"
|
OUTPUT_FILE = Path(__file__).parent / "animation_data.json"
|
||||||
|
|
||||||
@@ -27,6 +30,7 @@ OUTPUT_FILE = Path(__file__).parent / "animation_data.json"
|
|||||||
def main() -> None:
|
def main() -> None:
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print(" TIMESFM ANIMATION DATA GENERATOR")
|
print(" TIMESFM ANIMATION DATA GENERATOR")
|
||||||
|
print(" Dynamic horizon - forecasts always reach 2025-12")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
# Load data
|
# Load data
|
||||||
@@ -42,9 +46,9 @@ def main() -> None:
|
|||||||
)
|
)
|
||||||
print(f" Animation steps: {len(all_values) - MIN_CONTEXT + 1}")
|
print(f" Animation steps: {len(all_values) - MIN_CONTEXT + 1}")
|
||||||
|
|
||||||
# Load TimesFM
|
# Load TimesFM with max horizon (will truncate output for shorter forecasts)
|
||||||
print("\n🤖 Loading TimesFM 1.0 (200M) PyTorch...")
|
print(f"\n🤖 Loading TimesFM 1.0 (200M) PyTorch (horizon={MAX_HORIZON})...")
|
||||||
hparams = timesfm.TimesFmHparams(horizon_len=HORIZON)
|
hparams = timesfm.TimesFmHparams(horizon_len=MAX_HORIZON)
|
||||||
checkpoint = timesfm.TimesFmCheckpoint(
|
checkpoint = timesfm.TimesFmCheckpoint(
|
||||||
huggingface_repo_id="google/timesfm-1.0-200m-pytorch"
|
huggingface_repo_id="google/timesfm-1.0-200m-pytorch"
|
||||||
)
|
)
|
||||||
@@ -57,23 +61,32 @@ def main() -> None:
|
|||||||
step_num = n_points - MIN_CONTEXT + 1
|
step_num = n_points - MIN_CONTEXT + 1
|
||||||
total_steps = len(all_values) - MIN_CONTEXT + 1
|
total_steps = len(all_values) - MIN_CONTEXT + 1
|
||||||
|
|
||||||
print(f"\n📈 Step {step_num}/{total_steps}: Using {n_points} points...")
|
# Calculate dynamic horizon: forecast enough to reach 2025-12
|
||||||
|
horizon = TOTAL_MONTHS - n_points
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"\n📈 Step {step_num}/{total_steps}: Using {n_points} points, forecasting {horizon} months..."
|
||||||
|
)
|
||||||
|
|
||||||
# Get historical data up to this point
|
# Get historical data up to this point
|
||||||
historical_values = all_values[:n_points]
|
historical_values = all_values[:n_points]
|
||||||
historical_dates = all_dates[:n_points]
|
historical_dates = all_dates[:n_points]
|
||||||
|
|
||||||
# Run forecast
|
# Run forecast (model outputs MAX_HORIZON, we truncate to actual horizon)
|
||||||
point, quantiles = model.forecast(
|
point, quantiles = model.forecast(
|
||||||
[historical_values],
|
[historical_values],
|
||||||
freq=[0],
|
freq=[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Truncate to actual horizon
|
||||||
|
point = point[0][:horizon]
|
||||||
|
quantiles = quantiles[0, :horizon, :]
|
||||||
|
|
||||||
# Determine forecast dates
|
# Determine forecast dates
|
||||||
last_date = historical_dates[-1]
|
last_date = historical_dates[-1]
|
||||||
forecast_dates = pd.date_range(
|
forecast_dates = pd.date_range(
|
||||||
start=last_date + pd.DateOffset(months=1),
|
start=last_date + pd.DateOffset(months=1),
|
||||||
periods=HORIZON,
|
periods=horizon,
|
||||||
freq="MS",
|
freq="MS",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -81,22 +94,24 @@ def main() -> None:
|
|||||||
step_data = {
|
step_data = {
|
||||||
"step": step_num,
|
"step": step_num,
|
||||||
"n_points": n_points,
|
"n_points": n_points,
|
||||||
|
"horizon": horizon,
|
||||||
"last_historical_date": historical_dates[-1].strftime("%Y-%m"),
|
"last_historical_date": historical_dates[-1].strftime("%Y-%m"),
|
||||||
"historical_dates": [d.strftime("%Y-%m") for d in historical_dates],
|
"historical_dates": [d.strftime("%Y-%m") for d in historical_dates],
|
||||||
"historical_values": historical_values.tolist(),
|
"historical_values": historical_values.tolist(),
|
||||||
"forecast_dates": [d.strftime("%Y-%m") for d in forecast_dates],
|
"forecast_dates": [d.strftime("%Y-%m") for d in forecast_dates],
|
||||||
"point_forecast": point[0].tolist(),
|
"point_forecast": point.tolist(),
|
||||||
"q10": quantiles[0, :, 0].tolist(),
|
"q10": quantiles[:, 0].tolist(),
|
||||||
"q20": quantiles[0, :, 1].tolist(),
|
"q20": quantiles[:, 1].tolist(),
|
||||||
"q80": quantiles[0, :, 7].tolist(),
|
"q80": quantiles[:, 7].tolist(),
|
||||||
"q90": quantiles[0, :, 8].tolist(),
|
"q90": quantiles[:, 8].tolist(),
|
||||||
}
|
}
|
||||||
|
|
||||||
animation_steps.append(step_data)
|
animation_steps.append(step_data)
|
||||||
|
|
||||||
# Show summary
|
# Show summary
|
||||||
print(f" Last date: {historical_dates[-1].strftime('%Y-%m')}")
|
print(f" Last date: {historical_dates[-1].strftime('%Y-%m')}")
|
||||||
print(f" Forecast mean: {point[0].mean():.3f}°C")
|
print(f" Forecast to: {forecast_dates[-1].strftime('%Y-%m')}")
|
||||||
|
print(f" Forecast mean: {point.mean():.3f}°C")
|
||||||
|
|
||||||
# Create output
|
# Create output
|
||||||
output = {
|
output = {
|
||||||
@@ -104,7 +119,8 @@ def main() -> None:
|
|||||||
"model": "TimesFM 1.0 (200M) PyTorch",
|
"model": "TimesFM 1.0 (200M) PyTorch",
|
||||||
"total_steps": len(animation_steps),
|
"total_steps": len(animation_steps),
|
||||||
"min_context": MIN_CONTEXT,
|
"min_context": MIN_CONTEXT,
|
||||||
"horizon": HORIZON,
|
"max_horizon": MAX_HORIZON,
|
||||||
|
"total_months": TOTAL_MONTHS,
|
||||||
"data_source": "NOAA GISTEMP Global Temperature Anomaly",
|
"data_source": "NOAA GISTEMP Global Temperature Anomaly",
|
||||||
"full_date_range": f"{all_dates[0].strftime('%Y-%m')} to {all_dates[-1].strftime('%Y-%m')}",
|
"full_date_range": f"{all_dates[0].strftime('%Y-%m')} to {all_dates[-1].strftime('%Y-%m')}",
|
||||||
},
|
},
|
||||||
@@ -124,7 +140,7 @@ def main() -> None:
|
|||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
print(f"\n📁 Output: {OUTPUT_FILE}")
|
print(f"\n📁 Output: {OUTPUT_FILE}")
|
||||||
print(f" Total steps: {len(animation_steps)}")
|
print(f" Total steps: {len(animation_steps)}")
|
||||||
print(f" Each step shows forecast as one more data point is added")
|
print(f" Each forecast extends to 2025-12")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user