Skip to content

Commit c121692

Browse files
authored
Merge pull request #2424 from MetOffice/2403_freqbias
Scores [Categorical] Frequency Bias
2 parents 77a00b9 + d2f046d commit c121692

4 files changed

Lines changed: 110 additions & 0 deletions

File tree

src/CSET/cset_workflow/meta/verification/rose-meta.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,13 @@ compulsory=true
357357
sort-key=scoresPOD3
358358

359359
[Verification/Scores Categorical]
360+
[template variables=SCORES_CATEGORICAL_FB]
361+
ns=Verification/Scores Categorical
362+
description=Compute Scores Frequency Bias.
363+
type=python_boolean
364+
compulsory=true
365+
sort-key=scoresPOD4
366+
360367
[template variables=SCORES_CATEGORICAL_PFD]
361368
ns=Verification/Scores Categorical
362369
description=Compute Scores Probability of False Detection.

src/CSET/cset_workflow/rose-suite.conf.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ POINT_OBS=False
129129
!!POINT_OBS_USE_WMO_STATION_NUMBERS=False
130130
!!POINT_OBS_WMO_BLOCK_STTN_NUMBERS=[]
131131
PRESSURE_LEVELS=[]
132+
SCORES_CATEGORICAL_FB=False
132133
SCORES_CATEGORICAL_ETS=False
133134
SCORES_CATEGORICAL_PFD=False
134135
SCORES_CATEGORICAL_POD=False

src/CSET/loaders/verification.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ def _get_scores_timeseries_categorical(conf):
7171
scores_timeseries_categorical.append("pod")
7272
if conf.SCORES_CATEGORICAL_ETS or conf.SCORES_ALL:
7373
scores_timeseries_categorical.append("ets")
74+
if conf.SCORES_CATEGORICAL_FB or conf.SCORES_ALL:
75+
scores_timeseries_categorical.append("fb")
7476
if conf.SCORES_CATEGORICAL_PFD or conf.SCORES_ALL:
7577
scores_timeseries_categorical.append("pfd")
7678
return scores_timeseries_categorical
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
category: Scores Categorical
2+
title: Timeseries of FB for "$VARNAME over $SUBAREA_NAME, using threshold $OPERATOR $THRESHOLD"
3+
description: |
4+
5+
Extracts and plots the FB for $VARNAME for each model against observations as a timeseries.
6+
7+
The Frequency Bias (FB) in [`scores`](https://scores.readthedocs.io/en/stable/api.html#scores.categorical.BasicContingencyManager.frequency_bias) measures whether a forecasting system predicts an
8+
event too frequently or too infrequently compared to observations. Unlike
9+
metrics such as the Probability of Detection (POD) or Equitable Threat Score (ETS),
10+
Frequency Bias does not assess the accuracy of forecast locations or timings,
11+
only the overall frequency with which events are forecast.
12+
13+
For example, if the threshold is 290 K and op_func is gt (greater than), and
14+
events exceeding this threshold are forecast twice as often as they are observed,
15+
the frequency bias would be approximately 2. Conversely, if events are forecast
16+
only half as often as they occur, the frequency bias would be approximately 0.5.
17+
18+
It is calculated as:
19+
20+
.. math::
21+
22+
Frequency\ Bias = \frac{hits + false\ alarms}
23+
{hits + misses}
24+
25+
where
26+
27+
hits
28+
Number of occasions where an event was forecast and observed.
29+
30+
false alarms
31+
Number of occasions where an event was forecast but not observed.
32+
33+
misses
34+
Number of occasions where an event was observed but not forecast.
35+
36+
Frequency Bias ranges from 0 to infinity, where:
37+
38+
* 1 indicates the forecast predicts events at the correct frequency.
39+
* Greater than 1 indicates overforecasting of events.
40+
* Less than 1 indicates underforecasting of events.
41+
42+
A perfect frequency bias score is therefore 1, although a value of 1 does not
43+
necessarily imply a skillful forecast, as hits and false alarms may compensate
44+
for one another.
45+
46+
References
47+
https://scores.readthedocs.io/en/stable/api.html#scores.categorical.BasicContingencyManager.frequency_bias
48+
49+
steps:
50+
- operator: read.read_cubes
51+
file_paths: $INPUT_PATHS
52+
model_names: $MODEL_NAME
53+
constraint:
54+
operator: constraints.combine_constraints
55+
varname_constraint:
56+
operator: constraints.generate_var_constraint
57+
varname: ['observed_$VARNAME', '$VARNAME']
58+
cell_methods_constraint:
59+
operator: constraints.generate_cell_methods_constraint
60+
cell_methods: []
61+
pressure_level_constraint:
62+
operator: constraints.generate_level_constraint
63+
coordinate: "pressure"
64+
levels: []
65+
subarea_type: $SUBAREA_TYPE
66+
subarea_extent: $SUBAREA_EXTENT
67+
68+
- operator: misc.extract_common_points
69+
coordinate: time
70+
71+
- operator: misc.combine_cubes_into_cubelist
72+
first:
73+
operator: filters.filter_cubes
74+
constraint:
75+
operator: constraints.generate_var_constraint
76+
varname: observed_$VARNAME
77+
second:
78+
operator: regrid.interpolate_to_point_cube
79+
fld:
80+
operator: filters.filter_multiple_cubes
81+
constraint:
82+
operator: constraints.combine_constraints
83+
var_constraint:
84+
operator: constraints.generate_var_constraint
85+
varname: $VARNAME
86+
point_cube:
87+
operator: filters.filter_cubes
88+
constraint:
89+
operator: constraints.generate_var_constraint
90+
varname: observed_$VARNAME
91+
92+
- operator: scoreswrappers.scores_frequency_bias
93+
preserved_coordinates: "time"
94+
threshold: $THRESHOLD
95+
op_func: $OPERATOR
96+
97+
- operator: plot.plot_line_series
98+
99+
- operator: write.write_cube_to_nc
100+
overwrite: True

0 commit comments

Comments
 (0)