MLpp
LinearRegression.hpp
1 #pragma once
2 /* (C) 2020 Roman Werpachowski. */
3 #include <string>
4 #include <Eigen/Core>
5 #include "dll.hpp"
6 #include "Crossvalidation.hpp"
7 
8 namespace ml
9 {
16  namespace LinearRegression {
17 
29  struct Result
30  {
31  unsigned int n;
32  unsigned int dof;
33  double rss;
34  double tss;
37  double var_y() const {
38  if (dof) {
39  return rss / static_cast<double>(dof);
40  }
41  else {
42  return std::numeric_limits<double>::quiet_NaN();
43  }
44  }
45 
51  double r2() const {
52  return 1 - rss / tss;
53  }
54 
59  double adjusted_r2() const {
60  if (dof) {
61  return 1 - (rss / static_cast<double>(dof)) / (tss / static_cast<double>(n - 1));
62  }
63  else {
64  return std::numeric_limits<double>::quiet_NaN();
65  }
66  }
67  };
68 
76  struct UnivariateOLSResult: public Result
77  {
78  double slope;
79  double intercept;
80  double var_slope;
81  double var_intercept;
85  DLL_DECLSPEC std::string to_string() const;
86 
91  DLL_DECLSPEC Eigen::VectorXd predict(Eigen::Ref<const Eigen::VectorXd> x) const;
92 
97  double predict(double x) const
98  {
99  return x * slope + intercept;
100  }
101  };
102 
108  {
109  Eigen::VectorXd beta;
110  Eigen::MatrixXd cov;
113  DLL_DECLSPEC std::string to_string() const;
114 
120  DLL_DECLSPEC Eigen::VectorXd predict(Eigen::Ref<const Eigen::MatrixXd> X) const;
121 
127  DLL_DECLSPEC double predict_single(Eigen::Ref<const Eigen::VectorXd> x) const;
128  };
129 
139  {
140  Eigen::VectorXd beta;
141  double effective_dof;
148  DLL_DECLSPEC Eigen::VectorXd predict(Eigen::Ref<const Eigen::MatrixXd> X) const;
149 
155  DLL_DECLSPEC double predict_single(Eigen::Ref<const Eigen::VectorXd> x) const;
156  };
157 
160  {
161  Eigen::MatrixXd cov;
164  DLL_DECLSPEC std::string to_string() const;
165 
167  };
168 
171  {
173  DLL_DECLSPEC std::string to_string() const;
174 
176  };
177 
185  DLL_DECLSPEC UnivariateOLSResult univariate(Eigen::Ref<const Eigen::VectorXd> x, Eigen::Ref<const Eigen::VectorXd> y);
186 
196  DLL_DECLSPEC UnivariateOLSResult univariate(double x0, double dx, Eigen::Ref<const Eigen::VectorXd> y);
197 
205  DLL_DECLSPEC UnivariateOLSResult univariate_without_intercept(Eigen::Ref<const Eigen::VectorXd> x, Eigen::Ref<const Eigen::VectorXd> y);
206 
219  DLL_DECLSPEC MultivariateOLSResult multivariate(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y);
220 
240  template <bool DoStandardise> RidgeRegressionResult ridge(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
241 
245  template <> DLL_DECLSPEC RidgeRegressionResult ridge<true>(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
246 
250  template <> DLL_DECLSPEC RidgeRegressionResult ridge<false>(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
251 
255  inline RidgeRegressionResult ridge(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda, bool do_standardise)
256  {
257  if (do_standardise) {
258  return ridge<true>(X, y, lambda);
259  } else {
260  return ridge<false>(X, y, lambda);
261  }
262  }
263 
285  template <bool DoStandardise> LassoRegressionResult lasso(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
286 
290  template <> DLL_DECLSPEC LassoRegressionResult lasso<true>(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
291 
295  template <> DLL_DECLSPEC LassoRegressionResult lasso<false>(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda);
296 
300  inline LassoRegressionResult lasso(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, double lambda, bool do_standardise)
301  {
302  if (do_standardise) {
303  return lasso<true>(X, y, lambda);
304  } else {
305  return lasso<false>(X, y, lambda);
306  }
307  }
308 
322  template <class Regression> double press(Eigen::Ref<const Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> y, Regression regression)
323  {
324  const auto trainer = [&regression](const Eigen::Ref<const Eigen::MatrixXd> X, const Eigen::Ref<const Eigen::VectorXd> y) -> auto {
325  return regression(X, y);
326  };
327  const auto tester = [](const decltype(regression(X, y))& result, const Eigen::Ref<const Eigen::MatrixXd> X, const Eigen::Ref<const Eigen::VectorXd> y) -> double {
328  return (y - result.predict(X)).squaredNorm() / static_cast<double>(y.size());
329  };
330  return Crossvalidation::leave_one_out(X, y, trainer, tester) * static_cast<double>(y.size());
331  }
332 
343  template <bool WithIntercept> double press_univariate(Eigen::Ref<const Eigen::VectorXd> x, Eigen::Ref<const Eigen::VectorXd> y)
344  {
345  const auto trainer = [](const Eigen::Ref<const Eigen::VectorXd> x, const Eigen::Ref<const Eigen::VectorXd> y) -> UnivariateOLSResult {
346  if (WithIntercept) {
347  return univariate(x, y);
348  } else {
349  return univariate_without_intercept(x, y);
350  }
351  };
352  const auto tester = [](const UnivariateOLSResult& result, const Eigen::Ref<const Eigen::VectorXd> x, const Eigen::Ref<const Eigen::VectorXd> y) -> double {
353  return (y - result.predict(x)).squaredNorm() / static_cast<double>(y.size());
354  };
355  return Crossvalidation::leave_one_out_scalar(x, y, trainer, tester) * static_cast<double>(y.size());
356  }
357 
358 
359 
365  DLL_DECLSPEC Eigen::MatrixXd add_ones(Eigen::Ref<const Eigen::MatrixXd> X);
366 
374  DLL_DECLSPEC void standardise(Eigen::Ref<Eigen::MatrixXd> X);
375 
389  DLL_DECLSPEC void standardise(Eigen::Ref<Eigen::MatrixXd> X, Eigen::VectorXd& means, Eigen::VectorXd& standard_deviations);
390 
401  DLL_DECLSPEC void unstandardise(Eigen::Ref<Eigen::MatrixXd> X, Eigen::Ref<const Eigen::VectorXd> means, Eigen::Ref<const Eigen::VectorXd> standard_deviations);
402 
412  Eigen::VectorXd calculate_XXt_beta(const Eigen::Ref<const Eigen::MatrixXd> X, const Eigen::Ref<const Eigen::VectorXd> y, Eigen::Ref<Eigen::MatrixXd> XXt, Eigen::LDLT<Eigen::MatrixXd>& xxt_decomp, const Eigen::Ref<const Eigen::VectorXd> lambda);
413  }
414 }
ml::LinearRegression::ridge< true >
RidgeRegressionResult ridge< true >(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate ridge regression with intercept, standardising X inputs internally.
ml::Crossvalidation::leave_one_out_scalar
double leave_one_out_scalar(const Eigen::Ref< const Eigen::VectorXd > x, Eigen::Ref< const Eigen::VectorXd > y, Trainer train_func, Tester test_func)
Calculates model test error using leave-one-out cross-validation (scalar X version).
Definition: Crossvalidation.hpp:257
ml::LinearRegression::UnivariateOLSResult::intercept
double intercept
Definition: LinearRegression.hpp:79
ml::LinearRegression::lasso
LassoRegressionResult lasso(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate Lasso regression with intercept.
ml::LinearRegression::MultivariateOLSResult::cov
Eigen::MatrixXd cov
Definition: LinearRegression.hpp:110
ml::LinearRegression::MultivariateOLSResult::beta
Eigen::VectorXd beta
Definition: LinearRegression.hpp:109
ml
Definition: BallTree.hpp:10
ml::LinearRegression::MultivariateOLSResult
Result of multivariate Ordinary Least Squares regression.
Definition: LinearRegression.hpp:107
ml::LinearRegression::Result::r2
double r2() const
R2 coefficient.
Definition: LinearRegression.hpp:51
ml::LinearRegression::Result::var_y
double var_y() const
Estimated variance of observations Y, equal to rss / dof.
Definition: LinearRegression.hpp:37
ml::LinearRegression::unstandardise
void unstandardise(Eigen::Ref< Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > means, Eigen::Ref< const Eigen::VectorXd > standard_deviations)
Reverses the outcome of standardise().
dll.hpp
ml::LinearRegression::Result::tss
double tss
Definition: LinearRegression.hpp:34
ml::LinearRegression::LassoRegressionResult::to_string
std::string to_string() const
Formats the result as string.
ml::LinearRegression::UnivariateOLSResult::var_intercept
double var_intercept
Definition: LinearRegression.hpp:81
ml::LinearRegression::multivariate
MultivariateOLSResult multivariate(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y)
Carries out multivariate linear regression.
ml::LinearRegression::MultivariateOLSResult::predict
Eigen::VectorXd predict(Eigen::Ref< const Eigen::MatrixXd > X) const
Predicts Y given X.
ml::LinearRegression::univariate
UnivariateOLSResult univariate(Eigen::Ref< const Eigen::VectorXd > x, Eigen::Ref< const Eigen::VectorXd > y)
Carries out univariate (aka simple) linear regression with intercept.
ml::LinearRegression::press
double press(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, Regression regression)
Calculates the PRESS statistic (Predicted Residual Error Sum of Squares).
Definition: LinearRegression.hpp:322
ml::LinearRegression::LassoRegressionResult
Result of a multivariate Lasso regression with intercept.
Definition: LinearRegression.hpp:170
ml::LinearRegression::univariate_without_intercept
UnivariateOLSResult univariate_without_intercept(Eigen::Ref< const Eigen::VectorXd > x, Eigen::Ref< const Eigen::VectorXd > y)
Carries out univariate (aka simple) linear regression without intercept.
ml::LinearRegression::RegularisedRegressionResult
Result of a multivariate regularised regression with intercept.
Definition: LinearRegression.hpp:138
ml::LinearRegression::UnivariateOLSResult::to_string
std::string to_string() const
Formats the result as string.
ml::LinearRegression::MultivariateOLSResult::to_string
std::string to_string() const
Formats the result as string.
ml::LinearRegression::ridge< false >
RidgeRegressionResult ridge< false >(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate ridge regression with intercept, assuming standardised X inputs.
ml::Crossvalidation::leave_one_out
double leave_one_out(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, Trainer train_func, Tester test_func)
Calculates model test error using leave-one-out cross-validation.
Definition: Crossvalidation.hpp:209
ml::LinearRegression::Result::n
unsigned int n
Definition: LinearRegression.hpp:31
ml::LinearRegression::UnivariateOLSResult::var_slope
double var_slope
Definition: LinearRegression.hpp:80
ml::LinearRegression::add_ones
Eigen::MatrixXd add_ones(Eigen::Ref< const Eigen::MatrixXd > X)
Adds another row with 1s in every column to X.
ml::LinearRegression::RegularisedRegressionResult::predict
Eigen::VectorXd predict(Eigen::Ref< const Eigen::MatrixXd > X) const
Predicts Y given X.
ml::LinearRegression::RegularisedRegressionResult::predict_single
double predict_single(Eigen::Ref< const Eigen::VectorXd > x) const
Predicts Y given X.
ml::LinearRegression::Result::adjusted_r2
double adjusted_r2() const
Adjusted R2 coefficient.
Definition: LinearRegression.hpp:59
ml::LinearRegression::Result::dof
unsigned int dof
Definition: LinearRegression.hpp:32
ml::LinearRegression::UnivariateOLSResult::slope
double slope
Definition: LinearRegression.hpp:78
ml::LinearRegression::UnivariateOLSResult::cov_slope_intercept
double cov_slope_intercept
Definition: LinearRegression.hpp:82
ml::LinearRegression::RegularisedRegressionResult::effective_dof
double effective_dof
Definition: LinearRegression.hpp:141
ml::LinearRegression::Result::rss
double rss
Definition: LinearRegression.hpp:33
ml::LinearRegression::RidgeRegressionResult::to_string
std::string to_string() const
Formats the result as string.
ml::LinearRegression::RegularisedRegressionResult::beta
Eigen::VectorXd beta
Definition: LinearRegression.hpp:140
ml::LinearRegression::UnivariateOLSResult::predict
Eigen::VectorXd predict(Eigen::Ref< const Eigen::VectorXd > x) const
Predicts Y given X.
ml::LinearRegression::UnivariateOLSResult::predict
double predict(double x) const
Predicts Y given X.
Definition: LinearRegression.hpp:97
ml::LinearRegression::standardise
void standardise(Eigen::Ref< Eigen::MatrixXd > X)
Standardises independent variables.
ml::LinearRegression::ridge
RidgeRegressionResult ridge(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate ridge regression with intercept.
ml::LinearRegression::UnivariateOLSResult
Result of 1D Ordinary Least Squares regression (with or without intercept).
Definition: LinearRegression.hpp:76
ml::LinearRegression::Result
Result of linear regression.
Definition: LinearRegression.hpp:29
ml::LinearRegression::press_univariate
double press_univariate(Eigen::Ref< const Eigen::VectorXd > x, Eigen::Ref< const Eigen::VectorXd > y)
Calculates the PRESS statistic (Predicted Residual Error Sum of Squares) for univariate regression.
Definition: LinearRegression.hpp:343
ml::LinearRegression::RidgeRegressionResult::cov
Eigen::MatrixXd cov
Definition: LinearRegression.hpp:161
ml::LinearRegression::lasso< true >
LassoRegressionResult lasso< true >(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate Lasso regression with intercept, standardising X inputs internally.
ml::LinearRegression::lasso< false >
LassoRegressionResult lasso< false >(Eigen::Ref< const Eigen::MatrixXd > X, Eigen::Ref< const Eigen::VectorXd > y, double lambda)
Carries out multivariate Lasso regression with intercept, assuming standardised X inputs.
ml::LinearRegression::MultivariateOLSResult::predict_single
double predict_single(Eigen::Ref< const Eigen::VectorXd > x) const
Predicts Y given X.
ml::LinearRegression::RidgeRegressionResult
Result of a multivariate ridge regression with intercept.
Definition: LinearRegression.hpp:159