From 8b7d49f31693888b957628d95029f2570274c6b0 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Fri, 9 Aug 2024 09:29:57 -0600 Subject: [PATCH] Feature #2924 sal1l2_mae, PR 3 of 3 (#2943) * Per #2924, track SL1L2 and SAL1L2 MAE scores with separate variables since they are no longer the same value. I renamed the existing 'mae' as 'smae' and added a new 'samae' variable. Renaming the existing lets me use the compiler help find all references to it throughout the code. * Per #2924, update the User's Guide climatology details and equations. * Per #2924, some changes to aggr_stat_line.cc and series_analysis.cc to satisfy some SonarQube code smells. --- docs/Users_Guide/appendixC.rst | 74 ++++++------ docs/Users_Guide/point-stat.rst | 34 +++--- src/libcode/vx_stat_out/stat_columns.cc | 4 +- src/libcode/vx_statistics/compute_stats.cc | 8 +- src/libcode/vx_statistics/met_stats.cc | 23 ++-- src/libcode/vx_statistics/met_stats.h | 5 +- .../core/series_analysis/series_analysis.cc | 108 +++++++++++++----- .../core/stat_analysis/aggr_stat_line.cc | 66 ++++++----- .../core/stat_analysis/parse_stat_line.cc | 4 +- 9 files changed, 198 insertions(+), 128 deletions(-) diff --git a/docs/Users_Guide/appendixC.rst b/docs/Users_Guide/appendixC.rst index 15c3ab5c2d..a6bbb0fe51 100644 --- a/docs/Users_Guide/appendixC.rst +++ b/docs/Users_Guide/appendixC.rst @@ -616,23 +616,23 @@ Anomaly Correlation Coefficient Called "ANOM_CORR" and "ANOM_CORR_UNCNTR" for centered and uncentered versions in CNT output :numref:`table_PS_format_info_CNT` -The anomaly correlation coefficient is equivalent to the Pearson correlation coefficient, except that both the forecasts and observations are first adjusted according to a climatology value. The anomaly is the difference between the individual forecast or observation and the typical situation, as measured by a climatology (**c**) of some variety. It measures the strength of linear association between the forecast anomalies and observed anomalies. The anomaly correlation coefficient is defined as: +The anomaly correlation coefficient is equivalent to the Pearson correlation coefficient, except that both the forecasts and observations are first adjusted by subtracting their corresponding climatology value. The anomaly is the difference between the individual forecast or observation and the typical situation, as measured by a forecast climatology (:math:`c_f`) and observation climatology (:math:`c_o`). It measures the strength of linear association between the forecast anomalies and observed anomalies. The anomaly correlation coefficient is defined as: -.. math:: \text{Anomaly Correlation} = \frac{\sum(f_i - c)(o_i - c)}{\sqrt{\sum(f_i - c)^2} \sqrt{\sum(o_i -c)^2}} . +.. math:: \text{Anomaly Correlation} = \frac{\sum(f_i - {c_f}_i)(o_i - {c_o}_i)}{\sqrt{\sum(f_i - {c_f}_i)^2} \sqrt{\sum(o_i - {c_o}_i)^2}} . The centered anomaly correlation coefficient (ANOM_CORR) which includes the mean error is defined as: .. only:: latex - .. math:: \text{ANOM\_CORR } = \frac{ \overline{[(f - c) - \overline{(f - c)}][(a - c) - \overline{(a - c)}]}}{ \sqrt{ \overline{( (f - c) - \overline{(f - c)})^2} \overline{( (a - c) - \overline{(a - c)})^2}}} + .. math:: \text{ANOM\_CORR } = \frac{ \overline{[(f - c_f) - \overline{(f - c_f)}][(o - c_o) - \overline{(o - c_o)}]}}{ \sqrt{ \overline{( (f - c_f) - \overline{(f - c_f)})^2} \overline{( (o - c_o) - \overline{(o - c_o)})^2}}} .. only:: html - .. math:: \text{ANOM_CORR } = \frac{ \overline{[(f - c) - \overline{(f - c)}][(a - c) - \overline{(a - c)}]}}{ \sqrt{ \overline{( (f - c) - \overline{(f - c)})^2} \overline{( (a - c) - \overline{(a - c)})^2}}} + .. math:: \text{ANOM_CORR } = \frac{ \overline{[(f - c_f) - \overline{(f - c_f)}][(o - c_o) - \overline{(o - c_o)}]}}{ \sqrt{ \overline{( (f - c_f) - \overline{(f - c_f)})^2} \overline{( (o - c_o) - \overline{(o - c_o)})^2}}} The uncentered anomaly correlation coefficient (ANOM_CORR_UNCNTR) which does not include the mean errors is defined as: -.. math:: \text{Anomaly Correlation Raw } = \frac{ \overline{(f - c)(a - c)}}{ \sqrt{\overline{(f - c)^2} \overline{(a - c)^2}}} +.. math:: \text{Anomaly Correlation Raw } = \frac{ \overline{(f - c_f)(o - c_o)}}{ \sqrt{\overline{(f - c_f)^2} \overline{(o - c_o)^2}}} Anomaly correlation can range between -1 and 1; a value of 1 indicates perfect correlation and a value of -1 indicates perfect negative correlation. A value of 0 indicates that the forecast and observed anomalies are not correlated. @@ -650,56 +650,60 @@ The partial sums can be accumulated over individual cases to produce statistics Scalar L1 and L2 Values ----------------------- -Called "FBAR", "OBAR", "FOBAR", "FFBAR", and "OOBAR" in SL1L2 output :numref:`table_PS_format_info_SL1L2` +Called "FBAR", "OBAR", "FOBAR", "FFBAR", "OOBAR", and "MAE" in SL1L2 output :numref:`table_PS_format_info_SL1L2` These statistics are simply the 1st and 2nd moments of the forecasts, observations and errors: .. math:: - \text{FBAR} = \text{Mean}(f) = \bar{f} = \frac{1}{n} \sum_{i=1}^n f_i + \text{FBAR} = \text{Mean}(f) = \frac{1}{n} \sum_{i=1}^n f_i - \text{OBAR} = \text{Mean}(o) = \bar{o} = \frac{1}{n} \sum_{i=1}^n o_i + \text{OBAR} = \text{Mean}(o) = \frac{1}{n} \sum_{i=1}^n o_i - \text{FOBAR} = \text{Mean}(fo) = \bar{fo} = \frac{1}{n} \sum_{i=1}^n f_i o_i + \text{FOBAR} = \text{Mean}(fo) = \frac{1}{n} \sum_{i=1}^n f_i o_i - \text{FFBAR} = \text{Mean}(f^2) = \bar{f}^2 = \frac{1}{n} \sum_{i=1}^n f_i^2 + \text{FFBAR} = \text{Mean}(f^2) = \frac{1}{n} \sum_{i=1}^n f_i^2 - \text{OOBAR} = \text{Mean}(o^2) = \bar{o}^2 = \frac{1}{n} \sum_{i=1}^n o_i^2 + \text{OOBAR} = \text{Mean}(o^2) = \frac{1}{n} \sum_{i=1}^n o_i^2 + + \text{MAE} = \text{Mean}(|f - o|) = \frac{1}{n} \sum_{i=1}^n |f_i - o_i| Some of the other statistics for continuous forecasts (e.g., RMSE) can be derived from these moments. Scalar Anomaly L1 and L2 Values ------------------------------- -Called "FABAR", "OABAR", "FOABAR", "FFABAR", "OOABAR" in SAL1L2 output :numref:`table_PS_format_info_SAL1L2` +Called "FABAR", "OABAR", "FOABAR", "FFABAR", "OOABAR", and "MAE" in SAL1L2 output :numref:`table_PS_format_info_SAL1L2` -Computation of these statistics requires a climatological value, c. These statistics are the 1st and 2nd moments of the scalar anomalies. The moments are defined as: +Computation of these statistics requires climatological values, where :math:`c_f` is the forecast climatology value and :math:`c_o` is the observation climatology value. These statistics are the 1st and 2nd moments of the scalar anomalies. The moments are defined as: .. math:: - \text{FABAR} = \text{Mean}(f - c) = \bar{f - c} = \frac{1}{n} \sum_{i=1}^n (f_i - c) + \text{FABAR} = \text{Mean}(f - c_f) = \frac{1}{n} \sum_{i=1}^n (f_i - {c_f}_i) + + \text{OABAR} = \text{Mean}(o - c_o) = \frac{1}{n} \sum_{i=1}^n (o_i - {c_o}_i) - \text{OABAR} = \text{Mean}(o - c) = \bar{o - c} = \frac{1}{n} \sum_{i=1}^n (o_i - c) + \text{FOABAR} = \text{Mean}[(f - c_f)(o - c_o)] = \frac{1}{n} \sum_{i=1}^n (f_i - {c_f}_i)(o_i - {c_o}_i) - \text{FOABAR} = \text{Mean}[(f - c)(o - c)] = \bar{(f - c)(o - c)} = \frac{1}{n} \sum_{i=1}^n (f_i - c)(o_i - c) + \text{FFABAR} = \text{Mean}[(f - c_f)^2] = \frac{1}{n} \sum_{i=1}^n (f_i - {c_f}_i)^2 - \text{FFABAR} = \text{Mean}[(f - c)^2] = \bar{(f - c)}^2 = \frac{1}{n} \sum_{i=1}^n (f_i - c)^2 + \text{OOABAR} = \text{Mean}[(o - c_o)^2] = \frac{1}{n} \sum_{i=1}^n (o_i - {c_o}_i)^2 - \text{OOABAR} = \text{Mean}[(o - c)^2] = \bar{(o - c)}^2 = \frac{1}{n} \sum_{i=1}^n (o_i - c)^2 + \text{MAE} = \text{Mean}(|(f - c_f) - (o - c_o)|) = \frac{1}{n} \sum_{i=1}^n |(f_i - {c_f}_i) - (o_i - {c_o}_i)| Vector L1 and L2 Values ----------------------- -Called "UFBAR", "VFBAR", "UOBAR", "VOBAR", "UVFOBAR", "UVFFBAR", "UVOOBAR" in VL1L2 output :numref:`table_PS_format_info_VL1L2` +Called "UFBAR", "VFBAR", "UOBAR", "VOBAR", "UVFOBAR", "UVFFBAR", and "UVOOBAR" in VL1L2 output :numref:`table_PS_format_info_VL1L2` -These statistics are the moments for wind vector values, where **u** is the E-W wind component and **v** is the N-S wind component ( :math:`u_f` is the forecast E-W wind component; :math:`u_o` is the observed E-W wind component; :math:`v_f` is the forecast N-S wind component; and :math:`v_o` is the observed N-S wind component). The following measures are computed: +These statistics are the moments for wind vector values, where :math:`u` is the E-W wind component and :math:`v` is the N-S wind component ( :math:`u_f` is the forecast E-W wind component; :math:`u_o` is the observed E-W wind component; :math:`v_f` is the forecast N-S wind component; and :math:`v_o` is the observed N-S wind component). The following measures are computed: .. math:: - \text{UFBAR} = \text{Mean}(u_f) = \bar{u}_f = \frac{1}{n} \sum_{i=1}^n u_{fi} + \text{UFBAR} = \text{Mean}(u_f) = \frac{1}{n} \sum_{i=1}^n u_{fi} - \text{VFBAR} = \text{Mean}(v_f) = \bar{v}_f = \frac{1}{n} \sum_{i=1}^n v_{fi} + \text{VFBAR} = \text{Mean}(v_f) = \frac{1}{n} \sum_{i=1}^n v_{fi} - \text{UOBAR} = \text{Mean}(u_o) = \bar{u}_o = \frac{1}{n} \sum_{i=1}^n u_{oi} + \text{UOBAR} = \text{Mean}(u_o) = \frac{1}{n} \sum_{i=1}^n u_{oi} - \text{VOBAR} = \text{Mean}(v_o) = \bar{v}_o = \frac{1}{n} \sum_{i=1}^n v_{oi} + \text{VOBAR} = \text{Mean}(v_o) = \frac{1}{n} \sum_{i=1}^n v_{oi} \text{UVFOBAR} = \text{Mean}(u_f u_o + v_f v_o) = \frac{1}{n} \sum_{i=1}^n (u_{fi} u_{oi} + v_{fi} v_{oi}) @@ -710,25 +714,27 @@ These statistics are the moments for wind vector values, where **u** is the E-W Vector Anomaly L1 and L2 Values ------------------------------- -Called "UFABAR", "VFABAR", "UOABAR", "VOABAR", "UVFOABAR", "UVFFABAR", "UVOOABAR" in VAL1L2 output :numref:`table_PS_format_info_VAL1L2` +Called "UFABAR", "VFABAR", "UOABAR", "VOABAR", "UVFOABAR", "UVFFABAR", and "UVOOABAR" in VAL1L2 output :numref:`table_PS_format_info_VAL1L2` -These statistics require climatological values for the wind vector components, :math:`u_c \text{ and } v_c`. The measures are defined below: +These statistics require climatological values for the wind vector components, where :math:`{u_c}_f` and :math:`{v_c}_f` are the forecast climatology vectors and :math:`{u_c}_o` and :math:`{v_c}_o` are the observation climatology vectors. The measures are defined below: .. math:: - \text{UFABAR} = \text{Mean}(u_f - u_c) = \frac{1}{n} \sum_{i=1}^n (u_{fi} - u_c) + \text{UFABAR} = \text{Mean}(u_f - {u_c}_f) = \frac{1}{n} \sum_{i=1}^n ({u_f}_i - {{u_c}_f}_i) - \text{VFBAR} = \text{Mean}(v_f - v_c) = \frac{1}{n} \sum_{i=1}^n (v_{fi} - v_c) + \text{VFBAR} = \text{Mean}(v_f - {v_c}_f) = \frac{1}{n} \sum_{i=1}^n ({v_f}_i - {{v_c}_f}_i) - \text{UOABAR} = \text{Mean}(u_o - u_c) = \frac{1}{n} \sum_{i=1}^n (u_{oi} - u_c) + \text{UOABAR} = \text{Mean}(u_o - {u_c}_o) = \frac{1}{n} \sum_{i=1}^n ({u_o}_i - {{u_c}_o}_i) - \text{VOABAR} = \text{Mean}(v_o - v_c) = \frac{1}{n} \sum_{i=1}^n (v_{oi} - v_c) + \text{VOABAR} = \text{Mean}(v_o - {v_c}_o) = \frac{1}{n} \sum_{i=1}^n ({v_o}_i - {{v_c}_o}_i) - \text{UVFOABAR} &= \text{Mean}[(u_f - u_c)(u_o - u_c) + (v_f - v_c)(v_o - v_c)] \\ - &= \frac{1}{n} \sum_{i=1}^n (u_{fi} - u_c) + (u_{oi} - u_c) + (v_{fi} - v_c)(v_{oi} - v_c) + \text{UVFOABAR} &= \text{Mean}[(u_f - {u_c}_f)(u_o - {u_c}_o) + (v_f - {v_c}_f)(v_o - {v_c}_o)] \\ + &= \frac{1}{n} \sum_{i=1}^n ({u_f}_i - {{u_c}_f}_i) + ({u_o}_i - {{u_c}_o}_i) + ({v_f}_i - {{v_c}_f}_i)({v_o}_i - {{v_c}_o}_i) - \text{UVFFABAR} = \text{Mean}[(u_f - u_c)^2 + (v_f - v_c)^2] = \frac{1}{n} \sum_{i=1}^n ((u_{fi} - u_c)^2 + (v_{fi} - v_c)^2) + \text{UVFFABAR} &= \text{Mean}[(u_f - {u_c}_f)^2 + (v_f - {v_c}_f)^2] \\ + &= \frac{1}{n} \sum_{i=1}^n (({u_f}_i - {{u_c}_f}_i)^2 + ({v_f}_i - {{v_c}_f}_i)^2) - \text{UVOOABAR} = \text{Mean}[(u_o - u_c)^2 + (v_o - v_c)^2] = \frac{1}{n} \sum_{i=1}^n ((u_{oi} - u_c)^2 + (v_{oi} - v_c)^2) + \text{UVOOABAR} &= \text{Mean}[(u_o - {u_c}_o)^2 + (v_o - {v_c}_o)^2] \\ + &= \frac{1}{n} \sum_{i=1}^n (({u_o}_i - {{u_c}_o}_i)^2 + ({v_o}_i - {{v_c}_o}_i)^2) Gradient Values --------------- diff --git a/docs/Users_Guide/point-stat.rst b/docs/Users_Guide/point-stat.rst index 6c9849511e..70e3847b79 100644 --- a/docs/Users_Guide/point-stat.rst +++ b/docs/Users_Guide/point-stat.rst @@ -1204,7 +1204,7 @@ The first set of header columns are common to all of the output files generated - Mean(o²) * - 31 - MAE - - Mean Absolute Error + - Mean(\|f-o\|) .. _table_PS_format_info_SAL1L2: @@ -1223,25 +1223,25 @@ The first set of header columns are common to all of the output files generated - Scalar Anomaly L1L2 line type * - 25 - TOTAL - - Total number of matched triplets of forecast (f), observation (o), and climatological value (c) + - Total number of matched pairs of forecast (f), observation (o), forecast climatology (cf), and observation climatology (co) * - 26 - FABAR - - Mean(f-c) + - Mean(f-cf) * - 27 - OABAR - - Mean(o-c) + - Mean(o-co) * - 28 - FOABAR - - Mean((f-c)*(o-c)) + - Mean((f-cf)*(o-co)) * - 29 - FFABAR - - Mean((f-c)²) + - Mean((f-cf)²) * - 30 - OOABAR - - Mean((o-c)²) + - Mean((o-co)²) * - 31 - MAE - - Mean Absolute Error + - Mean(\|(f-cf)-(o-co)\|) .. _table_PS_format_info_VL1L2: @@ -1318,28 +1318,28 @@ The first set of header columns are common to all of the output files generated - Vector Anomaly L1L2 line type * - 25 - TOTAL - - Total number of matched triplets of forecast winds (uf, vf), observation winds (uo, vo), and climatological winds (uc, vc) + - Total number of matched pairs of forecast winds (uf, vf), observation winds (uo, vo), forecast climatology winds (ucf, vcf), and observation climatology winds (uco, vco) * - 26 - UFABAR - - Mean(uf-uc) + - Mean(uf-ucf) * - 27 - VFABAR - - Mean(vf-vc) + - Mean(vf-vcf) * - 28 - UOABAR - - Mean(uo-uc) + - Mean(uo-uco) * - 29 - VOABAR - - Mean(vo-vc) + - Mean(vo-vco) * - 30 - UVFOABAR - - Mean((uf-uc)*(uo-uc)+(vf-vc)*(vo-vc)) + - Mean((uf-ucf)*(uo-uco)+(vf-vcf)*(vo-vco)) * - 31 - UVFFABAR - - Mean((uf-uc)²+(vf-vc)²) + - Mean((uf-ucf)²+(vf-vcf)²) * - 32 - UVOOABAR - - Mean((uo-uc)²+(vo-vc)²) + - Mean((uo-uco)²+(vo-vco)²) * - 33 - FA_SPEED_BAR - Mean forecast wind speed anomaly @@ -1348,7 +1348,7 @@ The first set of header columns are common to all of the output files generated - Mean observed wind speed anomaly * - 35 - TOTAL_DIR - - Total number of matched triplets for which the forecast, observation, and climatological wind directions are well-defined (i.e. non-zero vectors) + - Total number of matched pairs for which the forecast, observation, forecast climatology, and observation climatology wind directions are well-defined (i.e. non-zero vectors) * - 36 - DIRA_ME - Mean wind direction anomaly difference, from -180 to 180 degrees diff --git a/src/libcode/vx_stat_out/stat_columns.cc b/src/libcode/vx_stat_out/stat_columns.cc index 330a74d827..35a2fe7ba0 100644 --- a/src/libcode/vx_stat_out/stat_columns.cc +++ b/src/libcode/vx_stat_out/stat_columns.cc @@ -2927,7 +2927,7 @@ void write_sl1l2_cols(const SL1L2Info &sl1l2_info, sl1l2_info.oobar); at.set_entry(r, c+6, // MAE - sl1l2_info.mae); + sl1l2_info.smae); return; } @@ -2963,7 +2963,7 @@ void write_sal1l2_cols(const SL1L2Info &sl1l2_info, sl1l2_info.ooabar); at.set_entry(r, c+6, // MAE - sl1l2_info.mae); + sl1l2_info.samae); return; } diff --git a/src/libcode/vx_statistics/compute_stats.cc b/src/libcode/vx_statistics/compute_stats.cc index 40c4e82589..bbc9e0ac1a 100644 --- a/src/libcode/vx_statistics/compute_stats.cc +++ b/src/libcode/vx_statistics/compute_stats.cc @@ -101,7 +101,7 @@ void compute_cntinfo(const SL1L2Info &s, bool aflag, CNTInfo &cnt_info) { cnt_info.me2.v = cnt_info.me.v * cnt_info.me.v; // Compute mean absolute error - cnt_info.mae.v = s.mae; + cnt_info.mae.v = s.smae; // Compute mean squared error cnt_info.mse.v = ffbar + oobar - 2.0*fobar; @@ -1111,7 +1111,7 @@ void compute_sl1l2_mean(const SL1L2Info *sl1l2_info, int n, sl1l2_mean.obar += sl1l2_info[i].obar; sl1l2_mean.ffbar += sl1l2_info[i].ffbar; sl1l2_mean.oobar += sl1l2_info[i].oobar; - sl1l2_mean.mae += sl1l2_info[i].mae; + sl1l2_mean.smae += sl1l2_info[i].smae; } if(sl1l2_info[i].sacount > 0) { @@ -1121,6 +1121,7 @@ void compute_sl1l2_mean(const SL1L2Info *sl1l2_info, int n, sl1l2_mean.oabar += sl1l2_info[i].oabar; sl1l2_mean.ffabar += sl1l2_info[i].ffabar; sl1l2_mean.ooabar += sl1l2_info[i].ooabar; + sl1l2_mean.samae += sl1l2_info[i].samae; } } // end for i @@ -1130,13 +1131,14 @@ void compute_sl1l2_mean(const SL1L2Info *sl1l2_info, int n, sl1l2_mean.obar /= n_sl1l2; sl1l2_mean.ffbar /= n_sl1l2; sl1l2_mean.oobar /= n_sl1l2; - sl1l2_mean.mae /= n_sl1l2; + sl1l2_mean.smae /= n_sl1l2; } if(sl1l2_mean.sacount > 0) { sl1l2_mean.fabar /= n_sal1l2; sl1l2_mean.oabar /= n_sal1l2; sl1l2_mean.ffabar /= n_sal1l2; sl1l2_mean.ooabar /= n_sal1l2; + sl1l2_mean.samae /= n_sal1l2; } return; diff --git a/src/libcode/vx_statistics/met_stats.cc b/src/libcode/vx_statistics/met_stats.cc index 9312867e49..4c679aed83 100644 --- a/src/libcode/vx_statistics/met_stats.cc +++ b/src/libcode/vx_statistics/met_stats.cc @@ -1124,11 +1124,11 @@ SL1L2Info & SL1L2Info::operator+=(const SL1L2Info &c) { s_info.ffbar = (ffbar*scount + c.ffbar*c.scount)/s_info.scount; s_info.oobar = (oobar*scount + c.oobar*c.scount)/s_info.scount; - if(is_bad_data(mae) || is_bad_data(c.mae)) { - s_info.mae = bad_data_double; + if(is_bad_data(smae) || is_bad_data(c.smae)) { + s_info.smae = bad_data_double; } else { - s_info.mae = (mae*scount + c.mae*c.scount)/s_info.scount; + s_info.smae = (smae*scount + c.smae*c.scount)/s_info.scount; } } @@ -1141,11 +1141,11 @@ SL1L2Info & SL1L2Info::operator+=(const SL1L2Info &c) { s_info.ffabar = (ffabar*sacount + c.ffabar*c.sacount)/s_info.sacount; s_info.ooabar = (ooabar*sacount + c.ooabar*c.sacount)/s_info.sacount; - if(is_bad_data(mae) || is_bad_data(c.mae)) { - s_info.mae = bad_data_double; + if(is_bad_data(samae) || is_bad_data(c.samae)) { + s_info.samae = bad_data_double; } else { - s_info.mae = (mae*sacount + c.mae*c.sacount)/s_info.sacount; + s_info.samae = (samae*sacount + c.samae*c.sacount)/s_info.sacount; } } @@ -1170,15 +1170,15 @@ void SL1L2Info::zero_out() { // SL1L2 Quantities fbar = obar = 0.0; fobar = ffbar = oobar = 0.0; + smae = 0.0; scount = 0; // SAL1L2 Quantities fabar = oabar = 0.0; foabar = ffabar = ooabar = 0.0; + samae = 0.0; sacount = 0; - mae = 0.0; - return; } @@ -1211,6 +1211,7 @@ void SL1L2Info::assign(const SL1L2Info &c) { fobar = c.fobar; ffbar = c.ffbar; oobar = c.oobar; + smae = c.smae; scount = c.scount; // SAL1L2 Quantities @@ -1219,10 +1220,9 @@ void SL1L2Info::assign(const SL1L2Info &c) { foabar = c.foabar; ffabar = c.ffabar; ooabar = c.ooabar; + samae = c.samae; sacount = c.sacount; - mae = c.mae; - return; } @@ -1272,7 +1272,7 @@ void SL1L2Info::set(const PairDataPoint &pd_all) { fobar += wgt*f*o; ffbar += wgt*f*f; oobar += wgt*o*o; - mae += wgt*fabs(f-o); + smae += wgt*fabs(f-o); scount++; // SAL1L2 sums @@ -1282,6 +1282,7 @@ void SL1L2Info::set(const PairDataPoint &pd_all) { foabar += wgt*(f-fc)*(o-oc); ffabar += wgt*(f-fc)*(f-fc); ooabar += wgt*(o-oc)*(o-oc); + samae += wgt*fabs((f-fc)-(o-oc)); sacount++; } } diff --git a/src/libcode/vx_statistics/met_stats.h b/src/libcode/vx_statistics/met_stats.h index b053266c33..f3bef1a90c 100644 --- a/src/libcode/vx_statistics/met_stats.h +++ b/src/libcode/vx_statistics/met_stats.h @@ -224,17 +224,16 @@ class SL1L2Info { double fbar, obar; double fobar; double ffbar, oobar; + double smae; int scount; // SAL1L2 Quantities double fabar, oabar; double foabar; double ffabar, ooabar; + double samae; int sacount; - // Mean absolute error - double mae; - // Compute sums void set(const PairDataPoint &); diff --git a/src/tools/core/series_analysis/series_analysis.cc b/src/tools/core/series_analysis/series_analysis.cc index 9373c0e040..5622908e0d 100644 --- a/src/tools/core/series_analysis/series_analysis.cc +++ b/src/tools/core/series_analysis/series_analysis.cc @@ -92,17 +92,18 @@ static void do_cnt (int, const PairDataPoint *); static void do_sl1l2 (int, const PairDataPoint *); static void do_pct (int, const PairDataPoint *); -static void store_stat_fho (int, const ConcatString &, const CTSInfo &); -static void store_stat_ctc (int, const ConcatString &, const CTSInfo &); -static void store_stat_cts (int, const ConcatString &, const CTSInfo &); -static void store_stat_mctc (int, const ConcatString &, const MCTSInfo &); -static void store_stat_mcts (int, const ConcatString &, const MCTSInfo &); -static void store_stat_cnt (int, const ConcatString &, const CNTInfo &); -static void store_stat_sl1l2(int, const ConcatString &, const SL1L2Info &); -static void store_stat_pct (int, const ConcatString &, const PCTInfo &); -static void store_stat_pstd (int, const ConcatString &, const PCTInfo &); -static void store_stat_pjc (int, const ConcatString &, const PCTInfo &); -static void store_stat_prc (int, const ConcatString &, const PCTInfo &); +static void store_stat_fho (int, const ConcatString &, const CTSInfo &); +static void store_stat_ctc (int, const ConcatString &, const CTSInfo &); +static void store_stat_cts (int, const ConcatString &, const CTSInfo &); +static void store_stat_mctc (int, const ConcatString &, const MCTSInfo &); +static void store_stat_mcts (int, const ConcatString &, const MCTSInfo &); +static void store_stat_cnt (int, const ConcatString &, const CNTInfo &); +static void store_stat_sl1l2 (int, const ConcatString &, const SL1L2Info &); +static void store_stat_sal1l2(int, const ConcatString &, const SL1L2Info &); +static void store_stat_pct (int, const ConcatString &, const PCTInfo &); +static void store_stat_pstd (int, const ConcatString &, const PCTInfo &); +static void store_stat_pjc (int, const ConcatString &, const PCTInfo &); +static void store_stat_prc (int, const ConcatString &, const PCTInfo &); static void setup_nc_file(const VarInfo *, const VarInfo *); static void add_nc_var(const ConcatString &, const ConcatString &, @@ -860,8 +861,8 @@ void process_scores() { // Compute partial sums if(!conf_info.fcst_info[0]->is_prob() && - (conf_info.output_stats[STATLineType::sl1l2].n() > 0 || - conf_info.output_stats[STATLineType::sal1l2].n() > 0)) { + (conf_info.output_stats[STATLineType::sl1l2].n() + + conf_info.output_stats[STATLineType::sal1l2].n()) > 0) { do_sl1l2(i_point+i, &pd_ptr[i]); } @@ -1128,6 +1129,11 @@ void do_sl1l2(int n, const PairDataPoint *pd_ptr) { for(j=0; j " << "unsupported column name requested \"" << c @@ -1822,6 +1822,62 @@ void store_stat_sl1l2(int n, const ConcatString &col, //////////////////////////////////////////////////////////////////////// +void store_stat_sal1l2(int n, const ConcatString &col, + const SL1L2Info &s_info) { + double v; + + // Set the column name to all upper case + ConcatString c = to_upper(col); + + // Get the column value + if(c == "TOTAL") { v = (double) s_info.sacount; } + else if(c == "FABAR") { v = s_info.fabar; } + else if(c == "OABAR") { v = s_info.oabar; } + else if(c == "FOABAR") { v = s_info.foabar; } + else if(c == "FFABAR") { v = s_info.ffabar; } + else if(c == "OOABAR") { v = s_info.ooabar; } + else if(c == "MAE") { v = s_info.samae; } + else { + mlog << Error << "\nstore_stat_sal1l2() -> " + << "unsupported column name requested \"" << c + << "\"\n\n"; + exit(1); + } + + // Construct the NetCDF variable name + ConcatString var_name("series_sal1l2_"); + var_name << c; + + // Append threshold information, if supplied + if(s_info.fthresh.get_type() != thresh_na || + s_info.othresh.get_type() != thresh_na) { + var_name << "_fcst" << s_info.fthresh.get_abbr_str() + << "_" << setlogic_to_abbr(conf_info.cnt_logic) + << "_obs" << s_info.othresh.get_abbr_str(); + } + + // Add map for this variable name + if(stat_data.count(var_name) == 0) { + + // Build key + ConcatString lty_stat("SAL1L2_"); + lty_stat << c; + + // Add new map entry + add_nc_var(var_name, c, stat_long_name[lty_stat], + s_info.fthresh.get_str(), + s_info.othresh.get_str(), + bad_data_double); + } + + // Store the statistic value + put_nc_val(n, var_name, (float) v); + + return; +} + +//////////////////////////////////////////////////////////////////////// + void store_stat_pct(int n, const ConcatString &col, const PCTInfo &pct_info) { int i = 0; diff --git a/src/tools/core/stat_analysis/aggr_stat_line.cc b/src/tools/core/stat_analysis/aggr_stat_line.cc index 75a9f60417..441cbe5e07 100644 --- a/src/tools/core/stat_analysis/aggr_stat_line.cc +++ b/src/tools/core/stat_analysis/aggr_stat_line.cc @@ -3999,12 +3999,6 @@ void mpr_to_cnt(STATAnalysisJob &job, const AggrMPRInfo &info, void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, int i_thresh, SL1L2Info &s_info) { - int i; - int scount, sacount; - double f, o, fc, oc; - double f_sum, o_sum, ff_sum, oo_sum, fo_sum; - double fa_sum, oa_sum, ffa_sum, ooa_sum, foa_sum; - double abs_err_sum; PairDataPoint pd_thr; // @@ -4034,31 +4028,41 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, // // Initialize counts // - scount = sacount = 0; - f_sum = o_sum = ff_sum = oo_sum = fo_sum = 0.0; - fa_sum = oa_sum = ffa_sum = ooa_sum = foa_sum = 0.0; - abs_err_sum = 0.0; + int scount = 0; + int sacount = 0; + double f_sum = 0.0; + double o_sum = 0.0; + double ff_sum = 0.0; + double oo_sum = 0.0; + double fo_sum = 0.0; + double smae_sum = 0.0; + double fa_sum = 0.0; + double oa_sum = 0.0; + double ffa_sum = 0.0; + double ooa_sum = 0.0; + double foa_sum = 0.0; + double samae_sum = 0.0; // // Update the partial sums // - for(i=0; i