41inline double aic(
double log_likelihood, std::size_t k)
43 return -2.0 * log_likelihood + 2.0 *
static_cast<double>(k);
58 double sigma2 = model.
ss_residual /
static_cast<double>(n);
59 double n_d =
static_cast<double>(n);
62 double ll = -0.5 * n_d * (std::log(2.0 *
pi) + std::log(sigma2) + 1.0);
78 double sigma2 = model.
ss_residual /
static_cast<double>(n);
79 double n_d =
static_cast<double>(n);
81 double ll = -0.5 * n_d * (std::log(2.0 *
pi) + std::log(sigma2) + 1.0);
99inline double aicc(
double log_likelihood, std::size_t n, std::size_t k)
101 double n_d =
static_cast<double>(n);
102 double k_d =
static_cast<double>(k);
104 if (n_d <= k_d + 1.0) {
105 throw std::invalid_argument(
"statcpp::aicc: n must be greater than k + 1");
108 return aic(log_likelihood, k) + (2.0 * k_d * (k_d + 1.0)) / (n_d - k_d - 1.0);
122inline double bic(
double log_likelihood, std::size_t n, std::size_t k)
124 return -2.0 * log_likelihood +
static_cast<double>(k) * std::log(
static_cast<double>(n));
138 double sigma2 = model.
ss_residual /
static_cast<double>(n);
139 double n_d =
static_cast<double>(n);
141 double ll = -0.5 * n_d * (std::log(2.0 *
pi) + std::log(sigma2) + 1.0);
143 return bic(ll, n, 3);
157 double sigma2 = model.
ss_residual /
static_cast<double>(n);
158 double n_d =
static_cast<double>(n);
160 double ll = -0.5 * n_d * (std::log(2.0 *
pi) + std::log(sigma2) + 1.0);
163 return bic(ll, n, k);
182template <
typename IteratorX,
typename IteratorY>
184 IteratorY y_first, IteratorY y_last,
190 throw std::invalid_argument(
"statcpp::press_statistic: x and y must have same length");
193 double n_d =
static_cast<double>(n);
198 for (
auto it = x_first; it != x_last; ++it) {
199 double dx =
static_cast<double>(*it) - mean_x;
206 for (; it_x != x_last; ++it_x, ++it_y) {
207 double x_i =
static_cast<double>(*it_x);
208 double y_i =
static_cast<double>(*it_y);
210 double y_hat =
predict(model, x_i);
211 double residual = y_i - y_hat;
214 double dx = x_i - mean_x;
215 double h_ii = 1.0 / n_d + dx * dx / sxx;
218 double press_residual = residual / (1.0 - h_ii);
219 press += press_residual * press_residual;
251 std::size_t n, std::size_t k,
bool shuffle =
true)
254 throw std::invalid_argument(
"statcpp::create_cv_folds: k must be at least 2");
257 throw std::invalid_argument(
"statcpp::create_cv_folds: k cannot exceed n");
260 std::vector<std::size_t> indices(n);
261 std::iota(indices.begin(), indices.end(), 0);
267 std::vector<std::vector<std::size_t>> folds(k);
268 std::size_t fold_size = n / k;
269 std::size_t remainder = n % k;
271 std::size_t current = 0;
272 for (std::size_t i = 0; i < k; ++i) {
273 std::size_t this_fold_size = fold_size + (i < remainder ? 1 : 0);
274 for (std::size_t j = 0; j < this_fold_size; ++j) {
275 folds[i].push_back(indices[current++]);
295 const std::vector<std::vector<double>>& X,
296 const std::vector<double>& y,
299 std::size_t n = X.size();
301 throw std::invalid_argument(
"statcpp::cross_validate_linear: X and y must have same size");
305 std::vector<double> fold_errors(k);
307 for (std::size_t fold = 0; fold < k; ++fold) {
309 std::vector<std::size_t> test_idx = folds[fold];
310 std::vector<std::size_t> train_idx;
311 for (std::size_t f = 0; f < k; ++f) {
313 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
318 std::vector<std::vector<double>> X_train(train_idx.size());
319 std::vector<double> y_train(train_idx.size());
320 for (std::size_t i = 0; i < train_idx.size(); ++i) {
321 X_train[i] = X[train_idx[i]];
322 y_train[i] = y[train_idx[i]];
326 std::vector<std::vector<double>> X_test(test_idx.size());
327 std::vector<double> y_test(test_idx.size());
328 for (std::size_t i = 0; i < test_idx.size(); ++i) {
329 X_test[i] = X[test_idx[i]];
330 y_test[i] = y[test_idx[i]];
339 for (std::size_t i = 0; i < test_idx.size(); ++i) {
340 double pred =
predict(model, X_test[i]);
341 double err = y_test[i] - pred;
344 fold_errors[fold] =
mse /
static_cast<double>(test_idx.size());
346 fold_errors[fold] = std::numeric_limits<double>::infinity();
350 double mean_error =
statcpp::mean(fold_errors.begin(), fold_errors.end());
352 / std::sqrt(
static_cast<double>(k));
354 return {mean_error, se_error, fold_errors, k};
367 const std::vector<std::vector<double>>& X,
368 const std::vector<double>& y)
394 const std::vector<std::vector<double>>& X,
395 std::vector<std::vector<double>>& X_scaled,
396 std::vector<double>& X_mean,
397 std::vector<double>& X_std,
398 std::size_t n, std::size_t p)
400 for (std::size_t j = 0; j < p; ++j) {
402 for (std::size_t i = 0; i < n; ++i) {
405 X_mean[j] =
sum /
static_cast<double>(n);
408 for (std::size_t i = 0; i < n; ++i) {
409 double d = X[i][j] - X_mean[j];
412 X_std[j] = std::sqrt(ss /
static_cast<double>(n));
413 if (X_std[j] < 1e-10) X_std[j] = 1.0;
415 for (std::size_t i = 0; i < n; ++i) {
416 X_scaled[i][j] = (X[i][j] - X_mean[j]) / X_std[j];
425 const std::vector<double>&
beta,
426 const std::vector<double>& X_mean,
427 const std::vector<double>& X_std,
430 std::vector<double> coefficients(p + 1);
432 coefficients[0] = y_mean;
433 for (std::size_t j = 0; j < p; ++j) {
434 coefficients[j + 1] =
beta[j] / X_std[j];
435 coefficients[0] -= coefficients[j + 1] * X_mean[j];
438 coefficients[0] = y_mean;
439 for (std::size_t j = 0; j < p; ++j) {
440 coefficients[j + 1] =
beta[j];
464 const std::vector<std::vector<double>>& X,
465 const std::vector<double>& y,
468 std::size_t max_iter = 1000,
475 throw std::invalid_argument(
"statcpp::ridge_regression: lambda must be non-negative");
478 std::size_t n = X.size();
480 throw std::invalid_argument(
"statcpp::ridge_regression: empty data");
483 throw std::invalid_argument(
"statcpp::ridge_regression: X and y must have same size");
486 std::size_t p = X[0].size();
489 std::vector<double> X_mean(p, 0.0);
490 std::vector<double> X_std(p, 1.0);
493 std::vector<std::vector<double>> X_scaled = X;
494 std::vector<double> y_centered(n);
500 for (std::size_t i = 0; i < n; ++i) {
501 y_centered[i] = y[i] - y_mean;
506 std::vector<double>
beta(p, 0.0);
507 std::vector<double> residuals = y_centered;
509 std::size_t iter = 0;
510 bool converged =
false;
512 for (iter = 0; iter < max_iter; ++iter) {
513 double max_change = 0.0;
515 for (std::size_t j = 0; j < p; ++j) {
517 for (std::size_t i = 0; i < n; ++i) {
518 residuals[i] += X_scaled[i][j] *
beta[j];
524 for (std::size_t i = 0; i < n; ++i) {
525 xr += X_scaled[i][j] * residuals[i];
526 xx += X_scaled[i][j] * X_scaled[i][j];
530 double beta_new = xr / (xx + lambda);
531 double change = std::abs(beta_new -
beta[j]);
532 max_change = std::max(max_change, change);
537 for (std::size_t i = 0; i < n; ++i) {
538 residuals[i] -= X_scaled[i][j] *
beta[j];
542 if (max_change < tol) {
554 for (std::size_t i = 0; i < n; ++i) {
555 double pred = coefficients[0];
556 for (std::size_t j = 0; j < p; ++j) {
557 pred += coefficients[j + 1] * X[i][j];
559 double err = y[i] - pred;
562 mse /=
static_cast<double>(n);
564 return {coefficients, lambda,
mse, iter, converged};
583 const std::vector<std::vector<double>>& X,
584 const std::vector<double>& y,
587 std::size_t max_iter = 1000,
594 throw std::invalid_argument(
"statcpp::lasso_regression: lambda must be non-negative");
597 std::size_t n = X.size();
599 throw std::invalid_argument(
"statcpp::lasso_regression: empty data");
602 throw std::invalid_argument(
"statcpp::lasso_regression: X and y must have same size");
605 std::size_t p = X[0].size();
608 std::vector<double> X_mean(p, 0.0);
609 std::vector<double> X_std(p, 1.0);
612 std::vector<std::vector<double>> X_scaled = X;
613 std::vector<double> y_centered(n);
619 for (std::size_t i = 0; i < n; ++i) {
620 y_centered[i] = y[i] - y_mean;
624 std::vector<double>
beta(p, 0.0);
625 std::vector<double> residuals = y_centered;
628 auto soft_threshold = [](
double x,
double t) ->
double {
629 if (x > t)
return x - t;
630 if (x < -t)
return x + t;
634 std::size_t iter = 0;
635 bool converged =
false;
637 for (iter = 0; iter < max_iter; ++iter) {
638 double max_change = 0.0;
640 for (std::size_t j = 0; j < p; ++j) {
642 for (std::size_t i = 0; i < n; ++i) {
643 residuals[i] += X_scaled[i][j] *
beta[j];
649 for (std::size_t i = 0; i < n; ++i) {
650 xr += X_scaled[i][j] * residuals[i];
651 xx += X_scaled[i][j] * X_scaled[i][j];
655 double beta_new = soft_threshold(xr, lambda) / xx;
656 double change = std::abs(beta_new -
beta[j]);
657 max_change = std::max(max_change, change);
662 for (std::size_t i = 0; i < n; ++i) {
663 residuals[i] -= X_scaled[i][j] *
beta[j];
667 if (max_change < tol) {
679 for (std::size_t i = 0; i < n; ++i) {
680 double pred = coefficients[0];
681 for (std::size_t j = 0; j < p; ++j) {
682 pred += coefficients[j + 1] * X[i][j];
684 double err = y[i] - pred;
687 mse /=
static_cast<double>(n);
689 return {coefficients, lambda,
mse, iter, converged};
709 const std::vector<std::vector<double>>& X,
710 const std::vector<double>& y,
714 std::size_t max_iter = 1000,
721 throw std::invalid_argument(
"statcpp::elastic_net_regression: lambda must be non-negative");
723 if (alpha < 0.0 || alpha > 1.0) {
724 throw std::invalid_argument(
"statcpp::elastic_net_regression: alpha must be in [0, 1]");
727 std::size_t n = X.size();
729 throw std::invalid_argument(
"statcpp::elastic_net_regression: empty data");
732 throw std::invalid_argument(
"statcpp::elastic_net_regression: X and y must have same size");
735 std::size_t p = X[0].size();
738 std::vector<double> X_mean(p, 0.0);
739 std::vector<double> X_std(p, 1.0);
742 std::vector<std::vector<double>> X_scaled = X;
743 std::vector<double> y_centered(n);
749 for (std::size_t i = 0; i < n; ++i) {
750 y_centered[i] = y[i] - y_mean;
754 std::vector<double>
beta(p, 0.0);
755 std::vector<double> residuals = y_centered;
757 auto soft_threshold = [](
double x,
double t) ->
double {
758 if (x > t)
return x - t;
759 if (x < -t)
return x + t;
763 double lambda1 = alpha * lambda;
764 double lambda2 = (1.0 - alpha) * lambda;
766 std::size_t iter = 0;
767 bool converged =
false;
769 for (iter = 0; iter < max_iter; ++iter) {
770 double max_change = 0.0;
772 for (std::size_t j = 0; j < p; ++j) {
773 for (std::size_t i = 0; i < n; ++i) {
774 residuals[i] += X_scaled[i][j] *
beta[j];
779 for (std::size_t i = 0; i < n; ++i) {
780 xr += X_scaled[i][j] * residuals[i];
781 xx += X_scaled[i][j] * X_scaled[i][j];
785 double beta_new = soft_threshold(xr, lambda1) / (xx + lambda2);
786 double change = std::abs(beta_new -
beta[j]);
787 max_change = std::max(max_change, change);
791 for (std::size_t i = 0; i < n; ++i) {
792 residuals[i] -= X_scaled[i][j] *
beta[j];
796 if (max_change < tol) {
808 for (std::size_t i = 0; i < n; ++i) {
809 double pred = coefficients[0];
810 for (std::size_t j = 0; j < p; ++j) {
811 pred += coefficients[j + 1] * X[i][j];
813 double err = y[i] - pred;
816 mse /=
static_cast<double>(n);
818 return {coefficients, lambda,
mse, iter, converged};
837inline std::pair<double, std::vector<double>>
cv_ridge(
838 const std::vector<std::vector<double>>& X,
839 const std::vector<double>& y,
840 const std::vector<double>& lambda_grid,
843 std::vector<double> cv_errors(lambda_grid.size());
845 for (std::size_t l = 0; l < lambda_grid.size(); ++l) {
846 double lambda = lambda_grid[l];
849 double total_error = 0.0;
850 for (std::size_t fold = 0; fold < k; ++fold) {
851 std::vector<std::size_t> test_idx = folds[fold];
852 std::vector<std::size_t> train_idx;
853 for (std::size_t f = 0; f < k; ++f) {
855 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
859 std::vector<std::vector<double>> X_train(train_idx.size());
860 std::vector<double> y_train(train_idx.size());
861 for (std::size_t i = 0; i < train_idx.size(); ++i) {
862 X_train[i] = X[train_idx[i]];
863 y_train[i] = y[train_idx[i]];
870 for (std::size_t i : test_idx) {
871 double pred = model.coefficients[0];
872 for (std::size_t j = 0; j < X[i].size(); ++j) {
873 pred += model.coefficients[j + 1] * X[i][j];
875 double err = y[i] - pred;
878 total_error +=
mse /
static_cast<double>(test_idx.size());
880 total_error += std::numeric_limits<double>::infinity();
883 cv_errors[l] = total_error /
static_cast<double>(k);
887 auto min_it = std::min_element(cv_errors.begin(), cv_errors.end());
888 double best_lambda = lambda_grid[std::distance(cv_errors.begin(), min_it)];
890 return {best_lambda, cv_errors};
905inline std::pair<double, std::vector<double>>
cv_lasso(
906 const std::vector<std::vector<double>>& X,
907 const std::vector<double>& y,
908 const std::vector<double>& lambda_grid,
911 std::vector<double> cv_errors(lambda_grid.size());
913 for (std::size_t l = 0; l < lambda_grid.size(); ++l) {
914 double lambda = lambda_grid[l];
917 double total_error = 0.0;
918 for (std::size_t fold = 0; fold < k; ++fold) {
919 std::vector<std::size_t> test_idx = folds[fold];
920 std::vector<std::size_t> train_idx;
921 for (std::size_t f = 0; f < k; ++f) {
923 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
927 std::vector<std::vector<double>> X_train(train_idx.size());
928 std::vector<double> y_train(train_idx.size());
929 for (std::size_t i = 0; i < train_idx.size(); ++i) {
930 X_train[i] = X[train_idx[i]];
931 y_train[i] = y[train_idx[i]];
938 for (std::size_t i : test_idx) {
939 double pred = model.coefficients[0];
940 for (std::size_t j = 0; j < X[i].size(); ++j) {
941 pred += model.coefficients[j + 1] * X[i][j];
943 double err = y[i] - pred;
946 total_error +=
mse /
static_cast<double>(test_idx.size());
948 total_error += std::numeric_limits<double>::infinity();
951 cv_errors[l] = total_error /
static_cast<double>(k);
954 auto min_it = std::min_element(cv_errors.begin(), cv_errors.end());
955 double best_lambda = lambda_grid[std::distance(cv_errors.begin(), min_it)];
957 return {best_lambda, cv_errors};
973 const std::vector<std::vector<double>>& X,
974 const std::vector<double>& y,
975 std::size_t n_lambda = 100,
976 double lambda_min_ratio = 0.0001)
978 std::size_t n = X.size();
979 std::size_t p = X[0].size();
985 double lambda_max = 0.0;
986 for (std::size_t j = 0; j < p; ++j) {
988 for (std::size_t i = 0; i < n; ++i) {
989 xy += X[i][j] * (y[i] - y_mean);
991 lambda_max = std::max(lambda_max, std::abs(xy) /
static_cast<double>(n));
994 if (lambda_max <= 0.0) {
995 throw std::invalid_argument(
996 "statcpp::generate_lambda_grid: lambda_max must be positive (data may be constant)");
1000 return {lambda_max};
1003 double lambda_min = lambda_max * lambda_min_ratio;
1006 std::vector<double> grid(n_lambda);
1007 double log_max = std::log(lambda_max);
1008 double log_min = std::log(lambda_min);
1009 double step = (log_max - log_min) /
static_cast<double>(n_lambda - 1);
1011 for (std::size_t i = 0; i < n_lambda; ++i) {
1012 grid[i] = std::exp(log_max -
static_cast<double>(i) * step);
Basic statistical computation functions.
Dispersion and variance calculation functions.
Linear regression analysis.
void standardize_features(const std::vector< std::vector< double > > &X, std::vector< std::vector< double > > &X_scaled, std::vector< double > &X_mean, std::vector< double > &X_std, std::size_t n, std::size_t p)
特徴量の標準化 (平均0, 標準偏差1)
std::vector< double > rescale_coefficients(const std::vector< double > &beta, const std::vector< double > &X_mean, const std::vector< double > &X_std, double y_mean, std::size_t p, bool standardize)
標準化済み係数を元のスケールに逆変換する
void validate_no_intercept_column(const std::vector< std::vector< double > > &X, const char *func_name)
Check that X data does not contain an intercept column.
regularized_regression_result ridge_regression(const std::vector< std::vector< double > > &X, const std::vector< double > &y, double lambda, bool standardize=true, std::size_t max_iter=1000, double tol=1e-6)
Perform Ridge regression (L2 regularization)
double aicc(double log_likelihood, std::size_t n, std::size_t k)
Calculate AICc (corrected AIC)
double sample_stddev(Iterator first, Iterator last)
Sample standard deviation.
constexpr double pi
Pi constant.
cv_result loocv_linear(const std::vector< std::vector< double > > &X, const std::vector< double > &y)
Perform leave-one-out cross-validation.
regularized_regression_result lasso_regression(const std::vector< std::vector< double > > &X, const std::vector< double > &y, double lambda, bool standardize=true, std::size_t max_iter=1000, double tol=1e-6)
Perform Lasso regression (L1 regularization)
auto sum(Iterator first, Iterator last)
Sum.
multiple_regression_result multiple_linear_regression(const std::vector< std::vector< double > > &X, const std::vector< double > &y)
Perform multiple linear regression.
double predict(const simple_regression_result &model, double x)
Make prediction using simple regression model.
double beta(double a, double b)
Beta function.
std::vector< std::vector< double > > standardize(const std::vector< std::vector< double > > &data)
Z-score standardization.
double bic_linear(const simple_regression_result &model, std::size_t n)
Calculate BIC from simple regression model.
double mean(Iterator first, Iterator last)
Arithmetic mean.
std::vector< double > generate_lambda_grid(const std::vector< std::vector< double > > &X, const std::vector< double > &y, std::size_t n_lambda=100, double lambda_min_ratio=0.0001)
Automatically generate lambda grid for regularized regression.
double bic(double log_likelihood, std::size_t n, std::size_t k)
Calculate BIC (Bayesian Information Criterion)
double aic(double log_likelihood, std::size_t k)
Calculate AIC (Akaike Information Criterion)
std::pair< double, std::vector< double > > cv_lasso(const std::vector< std::vector< double > > &X, const std::vector< double > &y, const std::vector< double > &lambda_grid, std::size_t k=5)
Select optimal lambda for Lasso regression using cross-validation.
default_random_engine & get_random_engine()
Singleton accessor for global random engine.
std::vector< std::vector< std::size_t > > create_cv_folds(std::size_t n, std::size_t k, bool shuffle=true)
Generate indices for k-fold cross-validation.
double press_statistic(IteratorX x_first, IteratorX x_last, IteratorY y_first, IteratorY y_last, const simple_regression_result &model)
Calculate PRESS statistic.
double mse(Iterator1 first1, Iterator1 last1, Iterator2 first2)
Mean Squared Error (MSE)
cv_result cross_validate_linear(const std::vector< std::vector< double > > &X, const std::vector< double > &y, std::size_t k=5)
Perform k-fold cross-validation for multiple regression model.
std::pair< double, std::vector< double > > cv_ridge(const std::vector< std::vector< double > > &X, const std::vector< double > &y, const std::vector< double > &lambda_grid, std::size_t k=5)
Select optimal lambda for Ridge regression using cross-validation.
std::size_t count(Iterator first, Iterator last)
Data count.
double aic_linear(const simple_regression_result &model, std::size_t n)
Calculate AIC from simple regression model.
regularized_regression_result elastic_net_regression(const std::vector< std::vector< double > > &X, const std::vector< double > &y, double lambda, double alpha=0.5, bool standardize=true, std::size_t max_iter=1000, double tol=1e-6)
Perform Elastic Net regression (L1 + L2 regularization)
Random engine wrapper and utilities.
Structure to store cross-validation results.
std::vector< double > fold_errors
Structure to store multiple regression analysis results.
std::vector< double > coefficients
Regression coefficients (b0, b1, ..., bp)
double ss_residual
Residual sum of squares.
Structure to store regularized regression results.
std::vector< double > coefficients
Structure to store simple regression analysis results.
double ss_residual
Residual sum of squares.