|
54 | 54 | 'load_gridded_forecast', |
55 | 55 | 'load_catalog_forecast', |
56 | 56 | 'load_alarm_forecast', |
| 57 | + 'load_temporal_forecast', |
| 58 | + 'compute_temporal_observations', |
57 | 59 | 'utc_now_datetime', |
58 | 60 | 'strptime_to_utc_datetime', |
59 | 61 | 'datetime_to_utc_epoch', |
@@ -640,3 +642,111 @@ def load_alarm_forecast(fname, name=None, score_field='alarm_score', |
640 | 642 |
|
641 | 643 | return forecast |
642 | 644 |
|
| 645 | + |
| 646 | +def load_temporal_forecast(fname, time_col='time', probability_col='probability', |
| 647 | + delimiter=',', **kwargs): |
| 648 | + """ Load temporal probability forecast from CSV file. |
| 649 | +
|
| 650 | + This function loads time-series probability forecasts where each row represents |
| 651 | + a time window (e.g., day) with an associated probability of earthquake occurrence. |
| 652 | + Observations should be computed separately from a catalog using |
| 653 | + compute_temporal_observations(). |
| 654 | +
|
| 655 | + Args: |
| 656 | + fname (str): Path to temporal forecast CSV file |
| 657 | + time_col (str): Name of time/index column (default: 'time') |
| 658 | + probability_col (str): Name of probability column (default: 'probability') |
| 659 | + delimiter (str): CSV delimiter character. Default: ',' |
| 660 | +
|
| 661 | + Returns: |
| 662 | + dict: Dictionary containing: |
| 663 | + - 'times': Time indices (numpy array or DatetimeIndex) |
| 664 | + - 'probabilities': Forecast probabilities (numpy array) |
| 665 | + - 'metadata': Additional information (dict) |
| 666 | +
|
| 667 | + Raises: |
| 668 | + FileNotFoundError: If the CSV file does not exist |
| 669 | + ValueError: If required columns are missing |
| 670 | +
|
| 671 | + Example: |
| 672 | + >>> # Load temporal forecast |
| 673 | + >>> data = csep.load_temporal_forecast('daily_forecast.csv') |
| 674 | + >>> catalog = csep.load_catalog('events.csv') |
| 675 | + >>> |
| 676 | + >>> # Compute observations from catalog |
| 677 | + >>> observations = csep.compute_temporal_observations( |
| 678 | + ... catalog, data['times'], magnitude_min=4.0 |
| 679 | + ... ) |
| 680 | + >>> |
| 681 | + >>> # Evaluate with temporal Molchan diagram |
| 682 | + >>> from csep.utils import plots |
| 683 | + >>> ax, ass, sigma = plots.plot_temporal_Molchan_diagram( |
| 684 | + ... data['probabilities'], observations, name='DailyM4+' |
| 685 | + ... ) |
| 686 | + """ |
| 687 | + # Sanity checks |
| 688 | + if not os.path.exists(fname): |
| 689 | + raise FileNotFoundError( |
| 690 | + f"Could not locate file {fname}. Unable to load temporal forecast.") |
| 691 | + |
| 692 | + times, probabilities, metadata = readers.temporal_forecast_csv( |
| 693 | + filename=fname, |
| 694 | + time_col=time_col, |
| 695 | + probability_col=probability_col, |
| 696 | + delimiter=delimiter |
| 697 | + ) |
| 698 | + |
| 699 | + return { |
| 700 | + 'times': times, |
| 701 | + 'probabilities': probabilities, |
| 702 | + 'metadata': metadata |
| 703 | + } |
| 704 | + |
| 705 | + |
| 706 | +def compute_temporal_observations(catalog, times, magnitude_min=None, |
| 707 | + start_time=None, time_delta=None): |
| 708 | + """ Compute binary observations from a catalog for temporal forecast evaluation. |
| 709 | +
|
| 710 | + This function counts whether one or more earthquakes occurred in each time |
| 711 | + window, creating a binary observation vector suitable for temporal ROC |
| 712 | + and Molchan evaluation. |
| 713 | +
|
| 714 | + Args: |
| 715 | + catalog: A CSEPCatalog object containing observed earthquakes |
| 716 | + times (array-like): Array representing forecast time windows. |
| 717 | + Can be datetime objects OR integer indices (1, 2, 3, ...). |
| 718 | + If integers, use start_time and time_delta. |
| 719 | + magnitude_min (float, optional): Minimum magnitude threshold. |
| 720 | + start_time (str or datetime, optional): Start time of the forecast. |
| 721 | + Required if times are integer indices. |
| 722 | + Example: '2024-01-01' |
| 723 | + time_delta (str, optional): Duration of each time window. |
| 724 | + Required if times are integer indices. |
| 725 | + Examples: '1D' (1 day), '1H' (1 hour), '7D' (1 week) |
| 726 | +
|
| 727 | + Returns: |
| 728 | + numpy.ndarray: Binary array (1 if event occurred in window, 0 otherwise) |
| 729 | +
|
| 730 | + Example: |
| 731 | + >>> # With datetime times in CSV |
| 732 | + >>> data = csep.load_temporal_forecast('forecast.csv') |
| 733 | + >>> catalog = csep.load_catalog('events.csv') |
| 734 | + >>> observations = csep.compute_temporal_observations( |
| 735 | + ... catalog, data['times'], magnitude_min=4.0 |
| 736 | + ... ) |
| 737 | + >>> |
| 738 | + >>> # With integer indices (1, 2, 3, ...) |
| 739 | + >>> observations = csep.compute_temporal_observations( |
| 740 | + ... catalog, data['times'], |
| 741 | + ... start_time='2024-01-01', |
| 742 | + ... time_delta='1D', |
| 743 | + ... magnitude_min=4.0 |
| 744 | + ... ) |
| 745 | + """ |
| 746 | + return readers.compute_temporal_observations( |
| 747 | + catalog=catalog, |
| 748 | + times=times, |
| 749 | + magnitude_min=magnitude_min, |
| 750 | + start_time=start_time, |
| 751 | + time_delta=time_delta |
| 752 | + ) |
0 commit comments