Vespucci  1.0.0
linleastsq.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright (C) 2014-2016 Wright State University - All Rights Reserved
3  Daniel P. Foose - Maintainer/Lead Developer
4 
5  This file is part of Vespucci.
6 
7  Vespucci is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Vespucci is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Vespucci. If not, see <http://www.gnu.org/licenses/>.
19 *******************************************************************************/
35 arma::uword Vespucci::Math::LinLeastSq::IModPoly(const arma::vec &spectrum,
36  const arma::vec &abscissa,
37  arma::vec &baseline,
38  arma::vec &corrected,
39  double &err,
40  const arma::uword poly_order,
41  const arma::uword max_it,
42  const double threshold)
43 {
44  if (poly_order == 0)
45  throw std::invalid_argument("Polynomial order must be 1 (linear) or greater.");
46  if (threshold >= 1 || threshold <= 0)
47  throw std::invalid_argument("Threshold value must be between 0 and 1.");
48  if (spectrum.n_rows != abscissa.n_rows)
49  throw std::invalid_argument("Spectrum and abscissa must be the same size.");
50 
51  arma::uword i = 1;
52  bool no_max_it = (max_it == 0);
53  arma::mat X;
54  arma::vec coefs, fit;
55  double dev;
56 
57  //perform first regresion (on spectrum without removed major peaks)
58  X = Vandermonde(abscissa, poly_order);
60  fit = Vespucci::Math::LinLeastSq::CalcPoly(coefs, abscissa);
61  dev = Vespucci::Math::LinLeastSq::CalcDev(spectrum, fit);
62 
63  double prev_dev = dev; //used in while loop critera
64 
65  //arma::arma::find major peak areas to remove and remove them
66  arma::uvec non_peak_ind = NonPeakInd(spectrum, dev);
67  arma::vec new_abscissa = abscissa(non_peak_ind);
68 
69  arma::vec prev_fit = spectrum(non_peak_ind);; //not a fit here, but in the loop.
70 
71  X = Vespucci::Math::LinLeastSq::Vandermonde(new_abscissa, poly_order);
72 
73  arma::uword rows = new_abscissa.n_rows;
74  do{ //always perform at least one regression on the "shrunken" spectrum
75  //Polynomial Fitting
77  fit = Vespucci::Math::LinLeastSq::CalcPoly(coefs, new_abscissa);
78  //Residual and dev calc (residual calculted inside CalcDev)
79  dev = Vespucci::Math::LinLeastSq::CalcDev(prev_fit, fit);
80  //error criteria
81  err = Vespucci::Math::LinLeastSq::CalcErr(dev, prev_dev);
82  //Reconstruction of model input
83  fit += dev * arma::ones(rows);
84  //if a value in the previous fit is lower than this fit, take the previous
85  arma::uvec ind = arma::find (prev_fit < fit);
86  fit(ind) = prev_fit(ind);
87  prev_fit = fit;
88  prev_dev = dev;
89  ++i;
90  }while (err > threshold && (no_max_it || i <= max_it));
91  //calculate fit for all values in original abscissa
92  baseline = Vespucci::Math::LinLeastSq::CalcPoly(coefs, abscissa);
93  corrected = spectrum - baseline;
94  return i;
95 }
96 
103 double Vespucci::Math::LinLeastSq::CalcDev(const arma::vec &spectrum, const arma::vec &fit)
104 {
105  using namespace arma;
106  arma::vec R = spectrum - fit;
107  double R_avg = mean(R);
108  arma::vec centered = R - R_avg*arma::ones(R.n_rows);
109  centered = arma::pow(centered, 2.0);
110  return std::sqrt(sum(centered)/centered.n_rows);
111 }
112 
119 arma::uvec Vespucci::Math::LinLeastSq::NonPeakInd(const arma::vec &spectrum, const double dev)
120 {
121  using namespace arma;
122  arma::vec SUM = spectrum + dev * arma::ones(spectrum.n_rows);
123  return arma::find(spectrum <= SUM);
124 }
125 
132 arma::vec Vespucci::Math::LinLeastSq::CalcPoly(const arma::vec &coefs, const arma::vec &x)
133 {
134  arma::vec y = coefs(0) + x*coefs(1); //0th and 1st power of x
135  //this loop only used for powers where pow(x, power) needs to be calculated
136  if (coefs.n_rows > 1){
137  for (arma::uword i = 2; i < coefs.n_rows; ++i)
138  y += coefs(i) * arma::pow(x, i);
139  }
140  return y;
141 }
142 
149 arma::mat Vespucci::Math::LinLeastSq::OrdinaryLeastSquares(const arma::mat &X, const arma::mat &y)
150 {
151  arma::mat Q, R;
152  arma::qr(Q, R, X);
153  return arma::solve(R, Q.t()) * y;
154 }
155 
162 arma::mat Vespucci::Math::LinLeastSq::Vandermonde(const arma::vec &x, const int poly_order)
163 {
164  arma::mat X(x.n_rows, poly_order + 1);
165  X.col(0) = arma::ones(x.n_rows); //faster than pow(X, 0)
166  X.col(1) = x;
167  for (arma::uword i = 2; i < X.n_cols; ++i)
168  X.col(i) = arma::pow(x, i);
169  return X;
170 }
171 
178 double Vespucci::Math::LinLeastSq::CalcErr(const double &dev, const double &prev_dev)
179 {
180  return std::abs( (dev - prev_dev) / dev );
181 }
182 
183 
185  const arma::vec &y,
186  arma::vec &fit,
187  std::map<std::string, double> &stats)
188 {
189  double n = X.n_rows;
190  double dof = n - 2; //for linear fit
191  double y_bar = arma::mean(y);
192 
193  arma::vec coefs = OrdinaryLeastSquares(X, y);
194  fit = Vespucci::Math::LinLeastSq::CalcPoly(coefs, X.col(1));
195 
196  arma::vec residuals = y - fit;
197 
198  arma::vec centered = y - y_bar;
199  double residual_sumsq = arma::as_scalar(arma::sum(arma::pow(residuals, 2.0)));
200  double total_sumsq = arma::as_scalar(arma::sum(arma::pow(centered, 2.0)));
201  double regression_sumsq = arma::as_scalar(arma::sum(arma::pow((fit - y_bar), 2.0)));
202  double R_squared = 1.0 - (residual_sumsq/total_sumsq);
203  double adj_R_squared = 1 - (1 - R_squared)*(n - 1)/dof; //p==1 for deg-1 polynomial
204 
205  arma::mat var_hat = (residual_sumsq / dof) * inv(X.t() * X);
206 
207  std::string param_name;
208  for(arma::uword i = 0; i < coefs.n_rows; ++i){
209  param_name = "a" + std::to_string(i);
210  stats[param_name] = coefs(i);
211  stats["s_" + param_name] = var_hat(i,i);
212  }
213  stats["R squared"] = R_squared;
214  stats["Adjusted R squared"] = adj_R_squared;
215  stats["s(y)"] = std::sqrt(regression_sumsq / n);
216  stats["F"] =(regression_sumsq) / (residual_sumsq/dof);
217  stats["Degrees of Freedom"] = dof;
218  stats["SSreg"] = regression_sumsq;
219  stats["SSres"] = residual_sumsq;
220  stats["Norm of residuals"] = std::sqrt(residual_sumsq);
221  return coefs;
222 }
223 
232 arma::vec Vespucci::Math::LinLeastSq::FitGaussian(const arma::vec &x, const arma::vec &y, arma::vec &fit, std::map<std::string, double> &stats)
233 {
234  arma::vec log_x = arma::log(x);
235  arma::vec log_y = arma::log(y);
236  arma::mat vdm = Vespucci::Math::LinLeastSq::Vandermonde(log_x, 2);
237  arma::vec fit_coefs = Vespucci::Math::LinLeastSq::OrdinaryLeastSquares(vdm, log_y, fit, stats);
238  fit = arma::exp(fit);
239  stats["A"] = std::exp(stats["a_0"] - (0.5*stats["a_1"]/stats["a_2"]));
240  stats["b"] = -0.5 * stats["a_1"] / stats["a_2"];
241  if (stats["a_2"] >= 0){
242  return arma::zeros(3);
243  }
244  stats["c"] = std::sqrt(-1.0/(2.0*stats["a_2"]));
245  stats["s_A"] = stats["A"] * std::sqrt(std::pow((0.5*std::sqrt( std::pow(stats["s_a_1"]/stats["a_1"], 2.0)) +
246  std::pow(stats["s_a_2"]/stats["a_2"], 2.0)), 2.0)
247  + std::pow(stats["s_a_0"], 2.0));
248  stats["s_b"] = 0.5 * std::sqrt(std::pow(stats["s_a_1"]/stats["s_a"], 2.0)
249  + std::pow(stats["s_a_2"]/stats["a_2"], 2.0));
250 
251  stats["s_c"] = std::sqrt(0.5)*0.5*stats["s_a_2"]/stats["a_2"];
252  return fit_coefs;
253 }
double CalcErr(const double &dev, const double &prev_dev)
Vespucci::Math::LinLeastSq::CalcErr Calculate the error criterion.
Definition: linleastsq.cpp:178
VESPUCCI_EXPORT arma::uword IModPoly(const arma::vec &spectrum, const arma::vec &abscissa, arma::vec &baseline, arma::vec &corrected, double &err, const arma::uword poly_order, const arma::uword max_it, const double threshold)
Vespucci::Math::LinLeastSq::IModPoly Perform the Vancouver Raman Algorithm to correct baseline...
Definition: linleastsq.cpp:35
VESPUCCI_EXPORT arma::vec FitGaussian(const arma::vec &x, const arma::vec &y, arma::vec &fit, std::map< std::string, double > &stats)
Vespucci::Math::LinLeastSq::FitGaussian.
Definition: linleastsq.cpp:232
arma::uvec NonPeakInd(const arma::vec &spectrum, const double dev)
Vespucci::Math::LinLeastSq::NonPeakInd.
Definition: linleastsq.cpp:119
arma::vec CalcPoly(const arma::vec &coefs, const arma::vec &x)
Vespucci::Math::LinLeastSq::CalcPoly Calculate the values of a polynomial.
Definition: linleastsq.cpp:132
double CalcDev(const arma::vec &spectrum, const arma::vec &fit)
Vespucci::Math::LinLeastSq::CalcDev.
Definition: linleastsq.cpp:103
VESPUCCI_EXPORT arma::mat Vandermonde(const arma::vec &x, const int poly_order)
Vespucci::Math::LinLeastSq::Vandermonde Build a Vandermonde matrix for OLS.
Definition: linleastsq.cpp:162
VESPUCCI_EXPORT arma::mat OrdinaryLeastSquares(const arma::mat &X, const arma::mat &y)
Vespucci::Math::LinLeastSq::OrdinaryLeastSquares Perform Squares.
Definition: linleastsq.cpp:149