1
1
import numpy
2
- import scipy . stats
3
- import scipy .spatial
2
+ import numpy as np
3
+ from scipy .stats import poisson
4
4
5
5
from csep .models import EvaluationResult
6
6
from csep .core .exceptions import CSEPCatalogException
7
7
8
8
9
- def bier_score_ndarray (forecast , observations ):
9
+ def _brier_score_ndarray (forecast , observations ):
10
10
""" Computes the brier score
11
11
"""
12
-
13
- observations = (observations >= 1 ).astype (int )
14
- prob_success = 1 - scipy .stats .poisson .cdf (0 , forecast )
15
- brier = []
16
-
17
- for p , o in zip (prob_success .ravel (), observations .ravel ()):
18
-
19
- if o == 0 :
20
- brier .append (- 2 * p ** 2 )
21
- else :
22
- brier .append (- 2 * (p - 1 ) ** 2 )
23
- brier = numpy .sum (brier )
12
+ prob_success = 1 - poisson .cdf (0 , forecast )
13
+ brier_cell = np .square (prob_success .ravel () - (observations .ravel () > 0 ))
14
+ brier = - 2 * brier_cell .sum ()
24
15
25
16
for n_dim in observations .shape :
26
17
brier /= n_dim
27
-
28
18
return brier
29
19
30
20
31
- def _simulate_catalog (sim_cells , sampling_weights , sim_fore , random_numbers = None ):
32
- # Modified this code to generate simulations in a way that every cell gets one earthquake
33
- # Generate uniformly distributed random numbers in [0,1), this
21
+ def _simulate_catalog (sim_cells , sampling_weights , random_numbers = None ):
22
+ sim_fore = numpy . zeros ( sampling_weights . shape )
23
+
34
24
if random_numbers is None :
35
25
# Reset simulation array to zero, but don't reallocate
36
26
sim_fore .fill (0 )
37
27
num_active_cells = 0
38
28
while num_active_cells < sim_cells :
39
29
random_num = numpy .random .uniform (0 ,1 )
40
- loc = numpy .searchsorted (sampling_weights , random_num , side = 'right' )
30
+ loc = numpy .searchsorted (sampling_weights , random_num ,
31
+ side = 'right' )
41
32
if sim_fore [loc ] == 0 :
42
33
sim_fore [loc ] = 1
43
- num_active_cells = num_active_cells + 1
34
+ num_active_cells += 1
44
35
else :
45
- # Find insertion points using binary search inserting to satisfy a[i-1] <= v < a[i]
46
- pnts = numpy .searchsorted (sampling_weights , random_numbers , side = 'right' )
36
+ # Find insertion points using binary search inserting
37
+ # to satisfy a[i-1] <= v < a[i]
38
+ pnts = numpy .searchsorted (sampling_weights , random_numbers ,
39
+ side = 'right' )
47
40
# Create simulated catalog by adding to the original locations
48
41
numpy .add .at (sim_fore , pnts , 1 )
49
42
50
43
assert sim_fore .sum () == sim_cells , "simulated the wrong number of events!"
51
44
return sim_fore
52
45
53
46
54
- def _brier_score_test (forecast_data , observed_data , num_simulations = 1000 , random_numbers = None ,
55
- seed = None , use_observed_counts = True , verbose = True ):
56
- """ Computes binary conditional-likelihood test from CSEP using an efficient simulation based approach.
47
+ def _brier_score_test (forecast_data , observed_data , num_simulations = 1000 ,
48
+ random_numbers = None , seed = None , verbose = True ):
49
+ """ Computes binary conditional-likelihood test from CSEP using an
50
+ efficient simulation based approach.
57
51
58
52
Args:
59
53
60
54
"""
61
-
62
55
# Array-masking that avoids log singularities:
63
56
forecast_data = numpy .ma .masked_where (forecast_data <= 0.0 , forecast_data )
64
57
65
58
# set seed for the likelihood test
66
59
if seed is not None :
67
60
numpy .random .seed (seed )
61
+ import time
62
+ start = time .process_time ()
68
63
69
- # used to determine where simulated earthquake should be placed, by definition of cumsum these are sorted
70
- sampling_weights = numpy .cumsum (forecast_data .ravel ()) / numpy .sum (forecast_data )
64
+ # used to determine where simulated earthquake should
65
+ # be placed, by definition of cumsum these are sorted
66
+ sampling_weights = (numpy .cumsum (forecast_data .ravel ()) /
67
+ numpy .sum (forecast_data ))
71
68
72
69
# data structures to store results
73
- sim_fore = numpy .zeros (sampling_weights .shape )
74
70
simulated_brier = []
75
71
n_active_cells = len (numpy .unique (numpy .nonzero (observed_data .ravel ())))
72
+ num_cells_to_simulate = int (n_active_cells )
76
73
77
74
# main simulation step in this loop
78
75
for idx in range (num_simulations ):
79
- if use_observed_counts :
80
- num_cells_to_simulate = int (n_active_cells )
81
-
82
76
if random_numbers is None :
83
77
sim_fore = _simulate_catalog (num_cells_to_simulate ,
84
- sampling_weights ,
85
- sim_fore )
78
+ sampling_weights )
86
79
else :
87
80
sim_fore = _simulate_catalog (num_cells_to_simulate ,
88
81
sampling_weights ,
89
- sim_fore ,
90
82
random_numbers = random_numbers [idx , :])
91
83
92
84
# compute Brier score
93
- current_brier = bier_score_ndarray (forecast_data .data , sim_fore )
85
+ current_brier = _brier_score_ndarray (forecast_data .data , sim_fore )
94
86
95
87
# append to list of simulated Brier score
96
88
simulated_brier .append (current_brier )
@@ -100,8 +92,7 @@ def _brier_score_test(forecast_data, observed_data, num_simulations=1000, random
100
92
if (idx + 1 ) % 100 == 0 :
101
93
print (f'... { idx + 1 } catalogs simulated.' )
102
94
103
- # observed Brier score
104
- obs_brier = bier_score_ndarray (forecast_data .data , observed_data )
95
+ obs_brier = _brier_score_ndarray (forecast_data .data , observed_data )
105
96
106
97
# quantile score
107
98
qs = numpy .sum (simulated_brier <= obs_brier ) / num_simulations
@@ -116,10 +107,12 @@ def brier_score_test(gridded_forecast,
116
107
seed = None ,
117
108
random_numbers = None ,
118
109
verbose = False ):
119
- """ Performs the Brier conditional test on Gridded Forecast using an Observed Catalog.
120
-
121
- Normalizes the forecast so the forecasted rate are consistent with the observations. This modification
122
- eliminates the strong impact differences in the number distribution have on the forecasted rates.
110
+ """ Performs the Brier conditional test on Gridded Forecast using an
111
+ Observed Catalog.
112
+ Normalizes the forecast so the forecasted rate are consistent with the
113
+ observations. This modification
114
+ eliminates the strong impact differences in the number distribution
115
+ have on the forecasted rates.
123
116
124
117
"""
125
118
@@ -137,7 +130,6 @@ def brier_score_test(gridded_forecast,
137
130
num_simulations = num_simulations ,
138
131
seed = seed ,
139
132
random_numbers = random_numbers ,
140
- use_observed_counts = True ,
141
133
verbose = verbose
142
134
)
143
135
0 commit comments