-
Notifications
You must be signed in to change notification settings - Fork 0
Usage
This page walks from a 20-line Quick Start to a fully annotated example mirroring the bundled main.py.
For a per-stage internal tour, see Pipeline Walkthrough; for parameter-level detail, see API Reference.
from eruption_forecast import ForecastModel fm = ForecastModel( station="OJN", channel="EHZ", network="VG", location="00", day_to_forecast=2, # also threaded as window_size into Training/Prediction n_jobs=4, verbose=True, ) ( fm.calculate( start_date="2025-01-01", end_date="2025-12-31", source="sds", sds_dir=r"D:\Data\OJN", methods=["rsam", "dsar", "entropy"], ) .train( start_date="2025-01-01", end_date="2025-07-26", eruption_dates=["2025-03-20", "2025-04-22", "2025-05-18"], window_step=6, window_step_unit="hours", classifiers=["rf", "xgb"], seeds=25, ) .predict( start_date="2025-07-27", end_date="2025-08-22", window_step=10, window_step_unit="minutes", plot_threshold=0.7, ) .evaluate(model="prediction") .explain(model="prediction") )
That's the whole pipeline. The five stages - calculate → train → predict → evaluate → explain - each return self, so they chain.
| Stage | What it does | Key output (under {station_dir}/) |
|---|---|---|
calculate() |
Reads SDS/FDSN waveforms, computes RSAM / DSAR / entropy per band | tremor/{nslc}_{start}_{end}.csv |
train() |
Builds labels → extracts tsfresh features → fits one SeedEnsemble per classifier into a ClassifierEnsemble
|
training/... (cache pickle: training/{hash}.TrainingModel.pkl) |
predict() |
Re-extracts features over the forecast grid → runs ensemble inference |
prediction/... (cache pickle: prediction/{hash}.PredictionModel.pkl) |
evaluate() |
Re-uses the in-session TrainingModel or PredictionModel for per-seed metrics + aggregate plots |
evaluation/{training|prediction}/... |
explain() |
Runs per-seed SHAP over the fitted ClassifierEnsemble (tree classifiers only) - bar / beeswarm / per-eruption waterfalls |
explanation/{training|prediction}/... (cache pickle: explanation/{kind}/{hash}.ExplanationModel.pkl) |
All paths root at {output_dir}/{nslc}/, where nslc = "{network}.{station}.{location}.{channel}" - see Output Structure for the full tree.
This is the bundled Research Workflow. Every kwarg below is the actual value in the repo's main.py.
from eruption_forecast import ForecastModel from eruption_forecast.decorators import timer, notify @timer("Run Forecasting") @notify("Run Forecasting") # Telegram push when this function finishes def main(sds_dir: str, n_jobs: int = 2): fm = ForecastModel( network="VG", station="OJN", location="00", channel="EHZ", day_to_forecast=2, # window_size used by Training + Prediction n_jobs=n_jobs, verbose=True, ) # 1. Tremor metrics - RSAM + DSAR + Shannon Entropy fm.calculate( start_date="2025-01-01", end_date="2025-12-31", source="sds", sds_dir=sds_dir, methods=["rsam", "dsar", "entropy"], remove_tremor_anomalies=False, interpolate=True, # fill miniSEED gaps linearly plot_daily=True, # save per-day tremor PNG save_plot=True, plot_overwrite=True, overwrite=False, # skip days already on disk n_jobs=n_jobs, ) # 2. Train four classifiers across 25 seeds fm.train( start_date="2025-01-01", end_date="2025-07-26", classifiers=["lite-rf", "rf", "gb", "xgb"], eruption_dates=[ "2025-03-20", "2025-04-10", "2025-04-22", "2025-05-18", "2025-06-17", "2025-07-07", "2025-08-02", "2025-08-18", ], window_step=6, window_step_unit="hours", label_builder="standard", cv_strategy="shuffle-stratified", scoring="recall", # maximise eruption recall during GridSearchCV select_tremor_columns=[ # restrict tsfresh input columns "rsam_f2", "rsam_f3", "rsam_f4", "dsar_f3-f4", "entropy", ], exclude_features=[ # drop slow / collinear tsfresh kinds "agg_linear_trend", "linear_trend_timewise", "length", "has_duplicate_max", "has_duplicate_min", "has_duplicate", ], seeds=25, resample_method="under", # RandomUnderSampler for class imbalance plot_features=True, # feature-importance PNG per seed n_jobs=4, n_grids=4, # 4 seed workers ×ばつ 4 GridSearchCV workers ) # 3. Forecast the next 4 weeks at 10-minute resolution fm.predict( start_date="2025-07-27", end_date="2025-08-22", window_step=10, window_step_unit="minutes", save_seed_result=True, # write per-seed probability CSV plot_threshold=0.7, # forecast plot threshold use_cache=False, # ignore any earlier cached prediction verbose=True, ) # 4. Evaluate the forecast against ground truth fm.evaluate(model="prediction", plot_per_seed=True) # 5. Cross-classifier ranking (follow-up on the evaluation stage) if fm.EvaluationModel: comparator = fm.EvaluationModel.compare() comparator.get_ranking() comparator.plot_all() # 6. Per-seed SHAP explanations (tree classifiers only) fm.explain( model="prediction", # reuse PredictionModel; "training" is also valid eruption_dates=[ "2025-03-20", "2025-04-10", "2025-04-22", "2025-05-18", "2025-06-17", "2025-07-07", "2025-08-02", "2025-08-18", ], save_per_seed=True, # write per-seed shap.Explanation pickles plot_per_seed=False, # skip per-seed bar/beeswarm PNGs (aggregate only) max_display=20, dpi=150, ) if __name__ == "__main__": main(sds_dir=r"D:\Data\OJN", n_jobs=8)
| Parameter | Why |
|---|---|
day_to_forecast=2 |
Mark the 2-day window before each eruption as positive; doubles as the tsfresh window_size
|
window_step=6, "hours" (train)
|
Coarse stride keeps the labelled set manageable while preserving multiple windows per eruption |
window_step=10, "minutes" (predict)
|
Dense stride during forecasting - 144 forecasts/day |
scoring="recall" |
False negatives are far more costly than false positives in eruption forecasting |
select_tremor_columns=[...] |
High-frequency RSAM bands and entropy carry most of the precursor signal at OJN |
resample_method="under" |
Eruption-positive windows are < 5 % of the training set → undersample the majority |
n_jobs=4, n_grids=4 |
4 outer seed workers ×ばつ 4 inner CV workers ≈ 16 cores busy on a 16-core box |
plot_threshold=0.7 |
A 0.7 probability cut-off mirrors the operational alert threshold at the observatory |
After the pipeline runs, every artefact is reachable from the fm object:
fm.tremor_df # pd.DataFrame - merged tremor CSV fm.TrainingModel # TrainingModel - labels, features, fit state fm.ClassifierEnsemble # ClassifierEnsemble - fitted across classifiers + seeds fm.PredictionModel # PredictionModel - forecast grid + caching state fm.results # pd.DataFrame - per-window forecast probabilities fm.EvaluationModel # EvaluationModel - matrix CSVs + in-memory metrics fm.evaluation_results # dict[classifier_name, pd.DataFrame] - per-seed metrics fm.ExplanationModel # ExplanationModel (after .explain()) - SHAP payloads fm.ExplanationModel.explanations # list[ClassifierExplanation] - per-classifier SHAP
Probability columns in fm.results:
{clf}_eruption_probability # mean across seeds
{clf}_uncertainty # std across seeds
{clf}_confidence # 1 - normalised uncertainty
{clf}_prediction # binary at plot_threshold
consensus_eruption_probability # mean across classifiers
consensus_* # same suite at the consensus level
fm.calculate( start_date="2025-01-01", end_date="2025-12-31", source="fdsn", client_url="https://service.iris.edu", # any FDSN endpoint methods=["rsam", "dsar", "entropy"], )
Downloads are cached locally as SDS - see Data Sources.
fm.save_config() # {station_dir}/forecast.config.yaml # ...later... fm2 = ForecastModel.from_config("output/VG.OJN.00.EHZ/forecast.config.yaml") fm2.run() # idempotently replays every captured stage
See Configuration for the YAML schema.
fm.TrainingModel.save() # → {station_dir}/TrainingModel_{basename}.pkl fm.PredictionModel.save() # → {station_dir}/PredictionModel_{basename}.pkl fm.EvaluationModel.save() # → {station_dir}/EvaluationModel_{basename}.pkl
Each stage model also auto-saves its own *.config.yaml at the end of its main
run method, so a standalone run leaves a YAML snapshot next to its artefacts
without any extra wiring:
{station_dir}/training/training.config.yaml # auto at end of fit()
{station_dir}/prediction/prediction.config.yaml # auto at end of forecast()
{station_dir}/evaluation/{kind}/evaluation.config.yaml # auto at end of evaluate()
{station_dir}/explanation/{kind}/explanation.config.yaml # auto at end of explain()
Call tm.save_config(path=..., fmt="json") etc. manually for a custom path or
JSON output. See Configuration.
By default fm.predict(...) extracts every tsfresh feature over the forecast grid, but use_features_from narrows the extraction to the features the training run kept:
fm.predict( start_date="2025-07-27", end_date="2025-08-22", window_step=10, window_step_unit="minutes", use_features_from="training", # tsfresh only computes features # any seed picked during train() )
-
"all"(default) — extracts every tsfresh feature (select_features=None). -
"training"— reusesfm.TrainingModel.features_selected_df.indexas the tsfresh whitelist, so the forecast-window matrix stays consistent with the ensemble's training-time schema. -
"files"— skips tsfresh entirely and loads a pre-built matrix from disk; both file paths are required (raises otherwise) anduse_cacheis forced toFalse:
fm.predict( start_date="2025-07-27", end_date="2025-08-22", window_step=10, window_step_unit="minutes", use_features_from="files", features_matrix_path="output/VG.OJN.00.EHZ/prediction/features/features-matrix_2025年07月27日_2025年08月22日_step-10-minutes.parquet", label_features_csv="output/VG.OJN.00.EHZ/prediction/features/features-label_2025年07月27日_2025年08月22日_step-10-minutes.csv", )
See Prediction Workflow → Feature Scoping for the mode table and validation rules.
from eruption_forecast import EvaluationModel em = EvaluationModel.from_file( "output/VG.OJN.00.EHZ/PredictionModel_2025年07月27日_2025年08月22日.pkl", eruption_dates=["2025-08-02", "2025-08-18"], ) em.evaluate(plot_aggregate=True) em.compare().plot_all()
Use scenarios.py - see Pipeline Walkthrough → Scenarios Workflow.