terraflow.temporal¶
Multi-temporal climate aggregation engine introduced in v0.5.0 (#138b).
Computes per-station aggregations of a long-format daily climate
time-series and fans them out across configured scenarios. The output
is the cell-input that terraflow.climate_impact interpolates to the
suitability grid.
Overview¶
filter_scenario(df, scenario)— slice a long-format station time-series to aScenario.periodyear window.aggregate_per_station(df, rule)— dispatch aTemporalAggregationto its kind-specific implementation. Returns one scalar per station.compute_per_station_aggregations(df, rules, scenarios)— outer product. Returns a DataFrame indexed bystation_idwith columns<rule_label>__<scenario_name>(one per rule × scenario pair).
Supported kind values (declared on ClimateConfig.temporal_aggregations):
| kind | Required fields | Notes |
|---|---|---|
annual_mean |
— | Long-run mean of temperature_c. |
seasonal_mean |
months: [1..12] |
Mean over selected calendar months. |
growing_degree_days |
base_temp_c: float |
Cumulative sum of max(0, T - base) across every row in the scenario window — divide by year count for annualised GDD. Implementation in terraflow.hazard. |
frost_days |
threshold_c: float |
Window-cumulative count of days at-or-below threshold. WMO ETCCDI FD0 (when threshold = 0) is per-year — divide by year count for the canonical annual value. |
heat_stress_days |
threshold_c: float |
Window-cumulative count of days at-or-above threshold. WMO ETCCDI TX35 (when threshold = 35) is per-year — divide by year count for the canonical annual value. |
precip_percentile |
percentile: 0..100 |
Nth percentile of daily precipitation. |
spei |
timescale_months: int > 0 |
Simplified Thornthwaite SPEI at the final month of the input window. Implementation in terraflow.hazard. |
Stations with no rows after a scenario filter resolve to NaN so downstream
kriging LOOCV sees missing values cleanly rather than silently dropping
stations.
Quick example¶
import pandas as pd
from terraflow.temporal import compute_per_station_aggregations
from terraflow.config import TemporalAggregation, Scenario
df = pd.read_csv("data/demo_timeseries.csv", parse_dates=["date"])
rules = [
TemporalAggregation(kind="annual_mean"),
TemporalAggregation(kind="growing_degree_days", base_temp_c=10.0),
]
scenarios = [
Scenario(name="historical", period=[1991, 2020]),
Scenario(name="ssp245", period=[2041, 2070]),
]
panel = compute_per_station_aggregations(df, rules, scenarios)
panel.head()
# annual_mean__historical annual_mean__ssp245 ...
# KS01 12.4 15.1
API Reference¶
temporal
¶
Multi-temporal climate aggregation engine (flagship #138b).
Takes a long-format station time-series DataFrame and produces one scalar
per station for each TemporalAggregation rule. Downstream callers
(the climate-impact pipeline path landing in #138e) interpolate those
per-station scalars onto cell centroids via the existing
ClimateInterpolator.
Expected input shape::
station_id lat lon date temperature_c precipitation_mm
KS-001 38.95 -100.50 2010-01-01 -2.3 0.0
KS-001 38.95 -100.50 2010-01-02 -1.1 1.4
...
Each aggregate_per_station call returns a pandas.Series indexed
by station_id with scalar float values. Stations with no data after
filtering yield NaN rather than raising — this lets downstream kriging
LOOCV handle missing stations cleanly.
Hazard-specific kinds (growing_degree_days, frost_days,
heat_stress_days, spei) dispatch into :mod:terraflow.hazard.
aggregate_per_station(df, rule)
¶
Apply one TemporalAggregation rule to a station time-series.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Long-format station time-series. Must contain |
required |
rule
|
TemporalAggregation
|
A validated :class: |
required |
Returns:
| Type | Description |
|---|---|
pd.Series:
|
Indexed by unique |
Source code in terraflow/temporal.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
compute_per_station_aggregations(df, rules, scenarios)
¶
Outer-product of rules × scenarios → per-station, per-(rule,scenario) scalars.
Result columns are <rule_label>__<scenario_name>. The index is the
union of station_ids observed in df. Hazard-kind rules (handled in
138d) trigger :class:NotImplementedError from the dispatcher.¶
Returns an empty DataFrame when rules or scenarios is empty — this matches the v0.4.x single-period behaviour.
Source code in terraflow/temporal.py
filter_scenario(df, scenario)
¶
Return the subset of df whose date year falls in the scenario.
The scenario period is closed-inclusive: [year_min, year_max].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Long-format station time-series. Must contain a |
required |
scenario
|
Scenario
|
A validated :class: |
required |
Returns:
| Type | Description |
|---|---|
pd.DataFrame:
|
Filtered rows. Empty DataFrames are returned unchanged when no rows match the scenario window. |