From 0862b3005ee58fbb8728dffc50745bc4476d6ffa Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 8 Aug 2024 13:33:52 -0600 Subject: [PATCH 1/3] 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. --- 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 | 107 +++++++++++++----- .../core/stat_analysis/aggr_stat_line.cc | 40 +++---- .../core/stat_analysis/parse_stat_line.cc | 4 +- 7 files changed, 124 insertions(+), 67 deletions(-) diff --git a/src/libcode/vx_stat_out/stat_columns.cc b/src/libcode/vx_stat_out/stat_columns.cc index 330a74d82..35a2fe7ba 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 40c4e8258..bbc9e0ac1 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 9312867e4..4c679aed8 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 b053266c3..f3bef1a90 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 9373c0e04..7eb77fee5 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,61 @@ void store_stat_sl1l2(int n, const ConcatString &col, //////////////////////////////////////////////////////////////////////// +void store_stat_sal1l2(int n, const ConcatString &col, + const SL1L2Info &s_info) { + double v; + ConcatString lty_stat, var_name; + + // 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 + var_name << cs_erase << "series_sal1l2_" << 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 + lty_stat << "SAL1L2_" << 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 75a9f6041..4f9be0b1d 100644 --- a/src/tools/core/stat_analysis/aggr_stat_line.cc +++ b/src/tools/core/stat_analysis/aggr_stat_line.cc @@ -4002,9 +4002,8 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &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; + double f_sum, o_sum, ff_sum, oo_sum, fo_sum, smae_sum; + double fa_sum, oa_sum, ffa_sum, ooa_sum, foa_sum, samae_sum; PairDataPoint pd_thr; // @@ -4035,9 +4034,8 @@ 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; + f_sum = o_sum = ff_sum = oo_sum = fo_sum = smae_sum = 0.0; + fa_sum = oa_sum = ffa_sum = ooa_sum = foa_sum = samae_sum = 0.0; // // Update the partial sums @@ -4052,13 +4050,13 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, fc = pd_thr.fcmn_na[i]; oc = pd_thr.ocmn_na[i]; - f_sum += f; - o_sum += o; - ff_sum += f*f; - oo_sum += o*o; - fo_sum += f*o; - abs_err_sum += fabs(f-o); - scount += 1; + f_sum += f; + o_sum += o; + ff_sum += f*f; + oo_sum += o*o; + fo_sum += f*o; + smae_sum += fabs(f-o); + scount += 1; // // Check for valid climo data @@ -4070,6 +4068,7 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, ffa_sum += (f-fc)*(f-fc); ooa_sum += (o-oc)*(o-oc); foa_sum += (f-oc)*(o-oc); + samae_sum += fabs((f-fc)-(o-oc)); sacount += 1; } } // end for @@ -4081,16 +4080,17 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, s_info.fobar = fo_sum/scount; s_info.ffbar = ff_sum/scount; s_info.oobar = oo_sum/scount; - s_info.mae = abs_err_sum/scount; + s_info.smae = smae_sum/scount; } if(sacount != 0) { - s_info.sacount = sacount; - s_info.fabar = fa_sum/sacount; - s_info.oabar = oa_sum/sacount; - s_info.foabar = foa_sum/sacount; - s_info.ffabar = ffa_sum/sacount; - s_info.ooabar = ooa_sum/sacount; + s_info.sacount = sacount; + s_info.fabar = fa_sum/sacount; + s_info.oabar = oa_sum/sacount; + s_info.foabar = foa_sum/sacount; + s_info.ffabar = ffa_sum/sacount; + s_info.ooabar = ooa_sum/sacount; + s_info.samae = samae_sum/scount; } return; diff --git a/src/tools/core/stat_analysis/parse_stat_line.cc b/src/tools/core/stat_analysis/parse_stat_line.cc index 27161ec1a..503e3c212 100644 --- a/src/tools/core/stat_analysis/parse_stat_line.cc +++ b/src/tools/core/stat_analysis/parse_stat_line.cc @@ -200,7 +200,7 @@ void parse_sl1l2_line(STATLine &l, SL1L2Info &s_info) { s_info.fobar = atof(l.get_item("FOBAR")); s_info.ffbar = atof(l.get_item("FFBAR")); s_info.oobar = atof(l.get_item("OOBAR")); - s_info.mae = atof(l.get_item("MAE")); + s_info.smae = atof(l.get_item("MAE")); return; } @@ -217,7 +217,7 @@ void parse_sal1l2_line(STATLine &l, SL1L2Info &s_info) { s_info.foabar = atof(l.get_item("FOABAR")); s_info.ffabar = atof(l.get_item("FFABAR")); s_info.ooabar = atof(l.get_item("OOABAR")); - s_info.mae = atof(l.get_item("MAE")); + s_info.samae = atof(l.get_item("MAE")); return; } From 9324fce393281fd5a525b0b537466bae325fc2e1 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 8 Aug 2024 16:30:51 -0600 Subject: [PATCH 2/3] Per #2924, update the User's Guide climatology details and equations. --- docs/Users_Guide/appendixC.rst | 74 ++++++++++++++++++--------------- docs/Users_Guide/point-stat.rst | 34 +++++++-------- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/docs/Users_Guide/appendixC.rst b/docs/Users_Guide/appendixC.rst index 15c3ab5c2..a6bbb0fe5 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 6c9849511..70e3847b7 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 From 3d3fbbb9c0907633c98c365fa0f7e8467e33f640 Mon Sep 17 00:00:00 2001 From: John Halley Gotway Date: Thu, 8 Aug 2024 17:21:23 -0600 Subject: [PATCH 3/3] Per #2924, some changes to aggr_stat_line.cc and series_analysis.cc to satisfy some SonarQube code smells. --- .../core/series_analysis/series_analysis.cc | 7 ++-- .../core/stat_analysis/aggr_stat_line.cc | 32 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/tools/core/series_analysis/series_analysis.cc b/src/tools/core/series_analysis/series_analysis.cc index 7eb77fee5..5622908e0 100644 --- a/src/tools/core/series_analysis/series_analysis.cc +++ b/src/tools/core/series_analysis/series_analysis.cc @@ -1825,7 +1825,6 @@ void store_stat_sl1l2(int n, const ConcatString &col, void store_stat_sal1l2(int n, const ConcatString &col, const SL1L2Info &s_info) { double v; - ConcatString lty_stat, var_name; // Set the column name to all upper case ConcatString c = to_upper(col); @@ -1846,7 +1845,8 @@ void store_stat_sal1l2(int n, const ConcatString &col, } // Construct the NetCDF variable name - var_name << cs_erase << "series_sal1l2_" << c; + ConcatString var_name("series_sal1l2_"); + var_name << c; // Append threshold information, if supplied if(s_info.fthresh.get_type() != thresh_na || @@ -1860,7 +1860,8 @@ void store_stat_sal1l2(int n, const ConcatString &col, if(stat_data.count(var_name) == 0) { // Build key - lty_stat << "SAL1L2_" << c; + ConcatString lty_stat("SAL1L2_"); + lty_stat << c; // Add new map entry add_nc_var(var_name, c, stat_long_name[lty_stat], diff --git a/src/tools/core/stat_analysis/aggr_stat_line.cc b/src/tools/core/stat_analysis/aggr_stat_line.cc index 4f9be0b1d..441cbe5e0 100644 --- a/src/tools/core/stat_analysis/aggr_stat_line.cc +++ b/src/tools/core/stat_analysis/aggr_stat_line.cc @@ -3999,11 +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, smae_sum; - double fa_sum, oa_sum, ffa_sum, ooa_sum, foa_sum, samae_sum; PairDataPoint pd_thr; // @@ -4033,22 +4028,33 @@ void mpr_to_psum(STATAnalysisJob &job, const AggrMPRInfo &info, // // Initialize counts // - scount = sacount = 0; - f_sum = o_sum = ff_sum = oo_sum = fo_sum = smae_sum = 0.0; - fa_sum = oa_sum = ffa_sum = ooa_sum = foa_sum = samae_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