Skip to content

Commit 5152c44

Browse files
committed
docs: Add alarm-based forecast evaluation tutorial and documentation for ROC and Molchan diagrams.
1 parent 39758ed commit 5152c44

File tree

4 files changed

+502
-0
lines changed

4 files changed

+502
-0
lines changed

docs/concepts/evaluations.rst

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,117 @@ as simple as it gets.
276276

277277
If you want to use mock-forecasts and mock-catalogs for other evaluations. You can just add the additional methods
278278
that are needed onto the mock classes you have already built.
279+
280+
281+
********************************
282+
Alarm-based forecast evaluations
283+
********************************
284+
285+
Alarm-based forecasts are evaluated using different metrics than rate-based forecasts. Instead of likelihood-based
286+
tests, we use threshold-based approaches that assess how well the forecast ranks spatial cells by earthquake
287+
occurrence probability.
288+
289+
PyCSEP provides two main evaluation methods for alarm-based forecasts:
290+
291+
1. **ROC (Receiver Operating Characteristic) curves** - Plot hit rate vs false alarm rate
292+
2. **Molchan diagrams** - Plot miss rate vs alarm fraction (space occupied)
293+
294+
Both methods sweep through a range of thresholds and compute contingency statistics at each level.
295+
296+
.. _alarm-forecast-evaluation:
297+
298+
ROC Diagram
299+
===========
300+
301+
The ROC diagram plots the True Positive Rate (hit rate) against the False Positive Rate (false alarm rate)
302+
as the alarm threshold varies. A perfect forecast follows the upper-left corner, while a random forecast
303+
follows the diagonal.
304+
305+
The Area Under the Curve (AUC) summarizes forecast skill:
306+
307+
- **AUC = 1.0**: Perfect forecast
308+
- **AUC = 0.5**: Random forecast (no skill)
309+
- **AUC < 0.5**: Anti-skill (worse than random)
310+
311+
.. autosummary::
312+
313+
csep.utils.plots.plot_ROC_diagram
314+
315+
Example::
316+
317+
from csep.utils import plots
318+
319+
forecast = csep.load_alarm_forecast('forecast.csv')
320+
catalog = csep.query_gns(start_time=..., end_time=...)
321+
322+
# Create ROC diagram
323+
ax, auc = plots.plot_ROC_diagram(forecast, catalog, linear=True)
324+
print(f"AUC: {auc:.3f}")
325+
326+
Molchan Diagram
327+
===============
328+
329+
The Molchan diagram plots the miss rate (fraction of earthquakes not captured) against the alarm fraction
330+
(fraction of space-time occupied by alarms). This visualization emphasizes the trade-off between capturing
331+
earthquakes and raising false alarms.
332+
333+
The Area Skill Score (ASS) summarizes forecast skill:
334+
335+
- **ASS = 1.0**: Perfect forecast (captures all events with minimal space)
336+
- **ASS = 0.5**: Random forecast (no skill)
337+
- **ASS < 0.5**: Anti-skill (worse than random)
338+
339+
.. autosummary::
340+
341+
csep.utils.plots.plot_Molchan_diagram
342+
343+
Example::
344+
345+
from csep.utils import plots
346+
347+
forecast = csep.load_alarm_forecast('forecast.csv')
348+
catalog = csep.query_gns(start_time=..., end_time=...)
349+
350+
# Create Molchan diagram
351+
ax, ass, sigma = plots.plot_Molchan_diagram(forecast, catalog, linear=True)
352+
print(f"ASS: {ass:.3f} ± {sigma:.3f}")
353+
354+
Temporal alarm-based evaluations
355+
--------------------------------
356+
357+
Temporal alarm-based forecasts (e.g., daily earthquake probability sequences) are evaluated using
358+
temporal versions of the ROC and Molchan diagrams:
359+
360+
.. autosummary::
361+
362+
csep.utils.plots.plot_temporal_ROC_diagram
363+
csep.utils.plots.plot_temporal_Molchan_diagram
364+
365+
These functions evaluate how well forecast probabilities correspond to binary event occurrence over time.
366+
367+
Example::
368+
369+
from csep.utils import plots
370+
371+
# Load forecast and compute observations
372+
data = csep.load_temporal_forecast('forecast.csv')
373+
observations = csep.compute_temporal_observations(
374+
catalog, data['times'],
375+
start_time='2016-01-01', time_delta='1D'
376+
)
377+
378+
# Temporal ROC
379+
ax, auc = plots.plot_temporal_ROC_diagram(
380+
data['probabilities'], observations, name='DailyM4+'
381+
)
382+
383+
# Temporal Molchan
384+
ax, ass, sigma = plots.plot_temporal_Molchan_diagram(
385+
data['probabilities'], observations, name='DailyM4+'
386+
)
387+
388+
Publication references
389+
======================
390+
391+
1. Molchan diagram (:ref:`Molchan, 1990<molchan-1990>`)
392+
2. ROC curves for earthquake forecasting (:ref:`Zechar and Jordan, 2008<zechar-jordan-2008>`)

docs/concepts/forecasts.rst

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,158 @@ catalog_id should be assigned to correspond with the total number of catalogs in
191191
contains zero forecasted events, you would specify the forecasting using (2). The *catalog_id* should be assigned to
192192
correspond with the total number of catalogs in the forecast.
193193

194+
195+
*********************
196+
Alarm-based forecasts
197+
*********************
198+
199+
Alarm-based forecasts represent a different paradigm from traditional rate-based forecasts. Instead of providing
200+
earthquake rates, alarm-based forecasts assign a score (e.g., probability, alarm level, or hazard metric) to each
201+
spatial cell indicating the relative likelihood of earthquake occurrence. These forecasts are evaluated using
202+
Receiver Operating Characteristic (ROC) curves and Molchan diagrams rather than likelihood-based tests.
203+
204+
Alarm-based forecasts are particularly useful for:
205+
206+
1. Binary classification models (e.g., "high risk" vs "low risk" areas)
207+
2. Machine learning classifiers
208+
3. Pattern-based approaches
209+
4. Any model that produces relative rankings rather than absolute rates
210+
211+
Working with alarm-based forecasts
212+
##################################
213+
214+
PyCSEP provides the :func:`load_alarm_forecast<csep.load_alarm_forecast>` function to load alarm-based forecasts
215+
from CSV files. The loaded forecast is returned as a :class:`GriddedForecast<csep.core.forecasts.GriddedForecast>`
216+
object that can be evaluated using ROC curves and Molchan diagrams.
217+
218+
.. autosummary:: csep.load_alarm_forecast
219+
220+
Default file format
221+
-------------------
222+
223+
The default file format for alarm-based forecasts is a comma-separated ASCII file with the following columns::
224+
225+
lon,lat,alarm_score,probability,rate_per_day,magnitude_min,magnitude_target
226+
165.0,-47.0,0.234,0.199,0.000117,4.0,7.0
227+
165.1,-47.0,0.156,0.133,0.000078,4.0,7.0
228+
229+
Required columns:
230+
231+
- **lon**: Longitude of cell center
232+
- **lat**: Latitude of cell center
233+
- At least one score column (e.g., **alarm_score**, **probability**, **rate_per_day**)
234+
235+
Optional columns:
236+
237+
- **magnitude_min**: Minimum magnitude threshold (default: 4.0)
238+
- **magnitude_target**: Maximum magnitude threshold (default: magnitude_min + 3.0)
239+
- **start_time**: Forecast start time
240+
- **end_time**: Forecast end time
241+
242+
The spatial grid is automatically inferred from the provided coordinates, making this format
243+
suitable for any geographic region worldwide.
244+
245+
Loading alarm forecasts
246+
-----------------------
247+
248+
Basic usage::
249+
250+
import csep
251+
252+
# Load alarm forecast
253+
forecast = csep.load_alarm_forecast('alarm_forecast.csv', name='MyModel')
254+
255+
# Load with specific score field
256+
forecast = csep.load_alarm_forecast('forecast.csv', score_field='probability')
257+
258+
# Load tab-delimited file
259+
forecast = csep.load_alarm_forecast('forecast.tsv', delimiter='\t')
260+
261+
Evaluating alarm forecasts
262+
--------------------------
263+
264+
Alarm forecasts are evaluated using ROC curves and Molchan diagrams::
265+
266+
from csep.utils import plots
267+
268+
# Load forecast and catalog
269+
forecast = csep.load_alarm_forecast('forecast.csv')
270+
catalog = csep.query_gns(start_time=..., end_time=...)
271+
272+
# ROC curve
273+
ax, auc = plots.plot_ROC_diagram(forecast, catalog, linear=True)
274+
275+
# Molchan diagram
276+
ax, ass, sigma = plots.plot_Molchan_diagram(forecast, catalog, linear=True)
277+
278+
See :ref:`alarm-forecast-evaluation` for a complete tutorial on evaluating alarm-based forecasts.
279+
280+
281+
Temporal alarm-based forecasts
282+
##############################
283+
284+
Temporal forecasts are a time-based variant of alarm-based forecasts. Instead of assigning scores to
285+
spatial cells, temporal forecasts assign probability scores to a sequence of time windows (e.g., daily
286+
forecasts). The evaluation is analogous: observations are computed from an earthquake catalog to determine
287+
whether events occurred in each time window, and the forecast probabilities are compared against these
288+
binary observations using ROC curves and Molchan diagrams.
289+
290+
Working with temporal forecasts
291+
###############################
292+
293+
PyCSEP provides functions to load temporal forecasts and compute observations from catalogs:
294+
295+
.. autosummary::
296+
297+
csep.load_temporal_forecast
298+
csep.compute_temporal_observations
299+
300+
Default file format
301+
-------------------
302+
303+
Temporal forecasts use a simple two-column CSV format::
304+
305+
time,probability
306+
1,0.0234
307+
2,0.0221
308+
3,0.0228
309+
310+
The **time** column can contain either:
311+
312+
- Integer indices (1, 2, 3, ...) - requires ``start_time`` and ``time_delta`` parameters
313+
- Datetime strings ('2016-01-01', '2016-01-02', ...) - automatically parsed
314+
315+
Loading and evaluating temporal forecasts
316+
-----------------------------------------
317+
318+
Example workflow::
319+
320+
import csep
321+
from csep.utils import plots
322+
323+
# Load temporal forecast (500 daily windows)
324+
data = csep.load_temporal_forecast('forecast.csv')
325+
326+
# Query observed catalog
327+
catalog = csep.query_gns(
328+
start_time='2016-01-01',
329+
end_time='2017-06-01',
330+
min_magnitude=4.0
331+
)
332+
333+
# Compute observations from catalog
334+
observations = csep.compute_temporal_observations(
335+
catalog,
336+
data['times'],
337+
start_time='2016-01-01', # Map integer times to dates
338+
time_delta='1D', # 1 day per window
339+
magnitude_min=4.0
340+
)
341+
342+
# Evaluate with ROC and Molchan diagrams
343+
ax, auc = plots.plot_temporal_ROC_diagram(
344+
data['probabilities'], observations, name='DailyM4+'
345+
)
346+
ax, ass, sigma = plots.plot_temporal_Molchan_diagram(
347+
data['probabilities'], observations, name='DailyM4+'
348+
)

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pyCSEP: Tools for Earthquake Forecast Developers
2121
tutorials/quadtree_gridded_forecast_evaluation.rst
2222
tutorials/working_with_catalog_forecasts.rst
2323
tutorials/catalog_forecast_evaluation.rst
24+
tutorials/alarm_forecast_evaluation.rst
2425
tutorials/plot_customizations.rst
2526

2627
.. toctree::

0 commit comments

Comments
 (0)