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++]);
299 const std::vector<std::vector<double>>& X,
300 const std::vector<double>& y,
304 std::size_t n = X.size();
306 throw std::invalid_argument(
"statcpp::cross_validate_linear: X and y must have same size");
310 std::vector<double> fold_errors(k);
312 for (std::size_t fold = 0; fold < k; ++fold) {
314 std::vector<std::size_t> test_idx = folds[fold];
315 std::vector<std::size_t> train_idx;
316 for (std::size_t f = 0; f < k; ++f) {
318 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
323 std::vector<std::vector<double>> X_train(train_idx.size());
324 std::vector<double> y_train(train_idx.size());
325 for (std::size_t i = 0; i < train_idx.size(); ++i) {
326 X_train[i] = X[train_idx[i]];
327 y_train[i] = y[train_idx[i]];
331 std::vector<std::vector<double>> X_test(test_idx.size());
332 std::vector<double> y_test(test_idx.size());
333 for (std::size_t i = 0; i < test_idx.size(); ++i) {
334 X_test[i] = X[test_idx[i]];
335 y_test[i] = y[test_idx[i]];
344 for (std::size_t i = 0; i < test_idx.size(); ++i) {
345 double pred =
predict(model, X_test[i]);
346 double err = y_test[i] - pred;
349 fold_errors[fold] =
mse /
static_cast<double>(test_idx.size());
351 fold_errors[fold] = std::numeric_limits<double>::infinity();
355 double mean_error =
statcpp::mean(fold_errors.begin(), fold_errors.end());
357 / std::sqrt(
static_cast<double>(k));
359 return {mean_error, se_error, fold_errors, k};
372 const std::vector<std::vector<double>>& X,
373 const std::vector<double>& y)
399 const std::vector<std::vector<double>>& X,
400 std::vector<std::vector<double>>& X_scaled,
401 std::vector<double>& X_mean,
402 std::vector<double>& X_std,
403 std::size_t n, std::size_t p)
405 for (std::size_t j = 0; j < p; ++j) {
407 for (std::size_t i = 0; i < n; ++i) {
410 X_mean[j] =
sum /
static_cast<double>(n);
413 for (std::size_t i = 0; i < n; ++i) {
414 double d = X[i][j] - X_mean[j];
417 X_std[j] = std::sqrt(ss /
static_cast<double>(n));
418 if (X_std[j] < 1e-10) X_std[j] = 1.0;
420 for (std::size_t i = 0; i < n; ++i) {
421 X_scaled[i][j] = (X[i][j] - X_mean[j]) / X_std[j];
430 const std::vector<double>&
beta,
431 const std::vector<double>& X_mean,
432 const std::vector<double>& X_std,
435 std::vector<double> coefficients(p + 1);
437 coefficients[0] = y_mean;
438 for (std::size_t j = 0; j < p; ++j) {
439 coefficients[j + 1] =
beta[j] / X_std[j];
440 coefficients[0] -= coefficients[j + 1] * X_mean[j];
443 coefficients[0] = y_mean;
444 for (std::size_t j = 0; j < p; ++j) {
445 coefficients[j + 1] =
beta[j];
469 const std::vector<std::vector<double>>& X,
470 const std::vector<double>& y,
473 std::size_t max_iter = 1000,
480 throw std::invalid_argument(
"statcpp::ridge_regression: lambda must be non-negative");
483 std::size_t n = X.size();
485 throw std::invalid_argument(
"statcpp::ridge_regression: empty data");
488 throw std::invalid_argument(
"statcpp::ridge_regression: X and y must have same size");
491 std::size_t p = X[0].size();
494 std::vector<double> X_mean(p, 0.0);
495 std::vector<double> X_std(p, 1.0);
498 std::vector<std::vector<double>> X_scaled = X;
499 std::vector<double> y_centered(n);
505 for (std::size_t i = 0; i < n; ++i) {
506 y_centered[i] = y[i] - y_mean;
511 std::vector<double>
beta(p, 0.0);
512 std::vector<double> residuals = y_centered;
514 std::size_t iter = 0;
515 bool converged =
false;
517 for (iter = 0; iter < max_iter; ++iter) {
518 double max_change = 0.0;
520 for (std::size_t j = 0; j < p; ++j) {
522 for (std::size_t i = 0; i < n; ++i) {
523 residuals[i] += X_scaled[i][j] *
beta[j];
529 for (std::size_t i = 0; i < n; ++i) {
530 xr += X_scaled[i][j] * residuals[i];
531 xx += X_scaled[i][j] * X_scaled[i][j];
535 double beta_new = xr / (xx + lambda);
536 double change = std::abs(beta_new -
beta[j]);
537 max_change = std::max(max_change, change);
542 for (std::size_t i = 0; i < n; ++i) {
543 residuals[i] -= X_scaled[i][j] *
beta[j];
547 if (max_change < tol) {
559 for (std::size_t i = 0; i < n; ++i) {
560 double pred = coefficients[0];
561 for (std::size_t j = 0; j < p; ++j) {
562 pred += coefficients[j + 1] * X[i][j];
564 double err = y[i] - pred;
567 mse /=
static_cast<double>(n);
569 return {coefficients, lambda,
mse, iter, converged};
588 const std::vector<std::vector<double>>& X,
589 const std::vector<double>& y,
592 std::size_t max_iter = 1000,
599 throw std::invalid_argument(
"statcpp::lasso_regression: lambda must be non-negative");
602 std::size_t n = X.size();
604 throw std::invalid_argument(
"statcpp::lasso_regression: empty data");
607 throw std::invalid_argument(
"statcpp::lasso_regression: X and y must have same size");
610 std::size_t p = X[0].size();
613 std::vector<double> X_mean(p, 0.0);
614 std::vector<double> X_std(p, 1.0);
617 std::vector<std::vector<double>> X_scaled = X;
618 std::vector<double> y_centered(n);
624 for (std::size_t i = 0; i < n; ++i) {
625 y_centered[i] = y[i] - y_mean;
629 std::vector<double>
beta(p, 0.0);
630 std::vector<double> residuals = y_centered;
633 auto soft_threshold = [](
double x,
double t) ->
double {
634 if (x > t)
return x - t;
635 if (x < -t)
return x + t;
639 std::size_t iter = 0;
640 bool converged =
false;
642 for (iter = 0; iter < max_iter; ++iter) {
643 double max_change = 0.0;
645 for (std::size_t j = 0; j < p; ++j) {
647 for (std::size_t i = 0; i < n; ++i) {
648 residuals[i] += X_scaled[i][j] *
beta[j];
654 for (std::size_t i = 0; i < n; ++i) {
655 xr += X_scaled[i][j] * residuals[i];
656 xx += X_scaled[i][j] * X_scaled[i][j];
660 double beta_new = soft_threshold(xr, lambda) / xx;
661 double change = std::abs(beta_new -
beta[j]);
662 max_change = std::max(max_change, change);
667 for (std::size_t i = 0; i < n; ++i) {
668 residuals[i] -= X_scaled[i][j] *
beta[j];
672 if (max_change < tol) {
684 for (std::size_t i = 0; i < n; ++i) {
685 double pred = coefficients[0];
686 for (std::size_t j = 0; j < p; ++j) {
687 pred += coefficients[j + 1] * X[i][j];
689 double err = y[i] - pred;
692 mse /=
static_cast<double>(n);
694 return {coefficients, lambda,
mse, iter, converged};
714 const std::vector<std::vector<double>>& X,
715 const std::vector<double>& y,
719 std::size_t max_iter = 1000,
726 throw std::invalid_argument(
"statcpp::elastic_net_regression: lambda must be non-negative");
728 if (alpha < 0.0 || alpha > 1.0) {
729 throw std::invalid_argument(
"statcpp::elastic_net_regression: alpha must be in [0, 1]");
732 std::size_t n = X.size();
734 throw std::invalid_argument(
"statcpp::elastic_net_regression: empty data");
737 throw std::invalid_argument(
"statcpp::elastic_net_regression: X and y must have same size");
740 std::size_t p = X[0].size();
743 std::vector<double> X_mean(p, 0.0);
744 std::vector<double> X_std(p, 1.0);
747 std::vector<std::vector<double>> X_scaled = X;
748 std::vector<double> y_centered(n);
754 for (std::size_t i = 0; i < n; ++i) {
755 y_centered[i] = y[i] - y_mean;
759 std::vector<double>
beta(p, 0.0);
760 std::vector<double> residuals = y_centered;
762 auto soft_threshold = [](
double x,
double t) ->
double {
763 if (x > t)
return x - t;
764 if (x < -t)
return x + t;
768 double lambda1 = alpha * lambda;
769 double lambda2 = (1.0 - alpha) * lambda;
771 std::size_t iter = 0;
772 bool converged =
false;
774 for (iter = 0; iter < max_iter; ++iter) {
775 double max_change = 0.0;
777 for (std::size_t j = 0; j < p; ++j) {
778 for (std::size_t i = 0; i < n; ++i) {
779 residuals[i] += X_scaled[i][j] *
beta[j];
784 for (std::size_t i = 0; i < n; ++i) {
785 xr += X_scaled[i][j] * residuals[i];
786 xx += X_scaled[i][j] * X_scaled[i][j];
790 double beta_new = soft_threshold(xr, lambda1) / (xx + lambda2);
791 double change = std::abs(beta_new -
beta[j]);
792 max_change = std::max(max_change, change);
796 for (std::size_t i = 0; i < n; ++i) {
797 residuals[i] -= X_scaled[i][j] *
beta[j];
801 if (max_change < tol) {
813 for (std::size_t i = 0; i < n; ++i) {
814 double pred = coefficients[0];
815 for (std::size_t j = 0; j < p; ++j) {
816 pred += coefficients[j + 1] * X[i][j];
818 double err = y[i] - pred;
821 mse /=
static_cast<double>(n);
823 return {coefficients, lambda,
mse, iter, converged};
842inline std::pair<double, std::vector<double>>
cv_ridge(
843 const std::vector<std::vector<double>>& X,
844 const std::vector<double>& y,
845 const std::vector<double>& lambda_grid,
849 std::vector<double> cv_errors(lambda_grid.size());
851 for (std::size_t l = 0; l < lambda_grid.size(); ++l) {
852 double lambda = lambda_grid[l];
855 double total_error = 0.0;
856 for (std::size_t fold = 0; fold < k; ++fold) {
857 std::vector<std::size_t> test_idx = folds[fold];
858 std::vector<std::size_t> train_idx;
859 for (std::size_t f = 0; f < k; ++f) {
861 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
865 std::vector<std::vector<double>> X_train(train_idx.size());
866 std::vector<double> y_train(train_idx.size());
867 for (std::size_t i = 0; i < train_idx.size(); ++i) {
868 X_train[i] = X[train_idx[i]];
869 y_train[i] = y[train_idx[i]];
876 for (std::size_t i : test_idx) {
877 double pred = model.coefficients[0];
878 for (std::size_t j = 0; j < X[i].size(); ++j) {
879 pred += model.coefficients[j + 1] * X[i][j];
881 double err = y[i] - pred;
884 total_error +=
mse /
static_cast<double>(test_idx.size());
886 total_error += std::numeric_limits<double>::infinity();
889 cv_errors[l] = total_error /
static_cast<double>(k);
893 auto min_it = std::min_element(cv_errors.begin(), cv_errors.end());
894 double best_lambda = lambda_grid[std::distance(cv_errors.begin(), min_it)];
896 return {best_lambda, cv_errors};
911inline std::pair<double, std::vector<double>>
cv_lasso(
912 const std::vector<std::vector<double>>& X,
913 const std::vector<double>& y,
914 const std::vector<double>& lambda_grid,
918 std::vector<double> cv_errors(lambda_grid.size());
920 for (std::size_t l = 0; l < lambda_grid.size(); ++l) {
921 double lambda = lambda_grid[l];
924 double total_error = 0.0;
925 for (std::size_t fold = 0; fold < k; ++fold) {
926 std::vector<std::size_t> test_idx = folds[fold];
927 std::vector<std::size_t> train_idx;
928 for (std::size_t f = 0; f < k; ++f) {
930 train_idx.insert(train_idx.end(), folds[f].begin(), folds[f].end());
934 std::vector<std::vector<double>> X_train(train_idx.size());
935 std::vector<double> y_train(train_idx.size());
936 for (std::size_t i = 0; i < train_idx.size(); ++i) {
937 X_train[i] = X[train_idx[i]];
938 y_train[i] = y[train_idx[i]];
945 for (std::size_t i : test_idx) {
946 double pred = model.coefficients[0];
947 for (std::size_t j = 0; j < X[i].size(); ++j) {
948 pred += model.coefficients[j + 1] * X[i][j];
950 double err = y[i] - pred;
953 total_error +=
mse /
static_cast<double>(test_idx.size());
955 total_error += std::numeric_limits<double>::infinity();
958 cv_errors[l] = total_error /
static_cast<double>(k);
961 auto min_it = std::min_element(cv_errors.begin(), cv_errors.end());
962 double best_lambda = lambda_grid[std::distance(cv_errors.begin(), min_it)];
964 return {best_lambda, cv_errors};
980 const std::vector<std::vector<double>>& X,
981 const std::vector<double>& y,
982 std::size_t n_lambda = 100,
983 double lambda_min_ratio = 0.0001)
985 std::size_t n = X.size();
986 std::size_t p = X[0].size();
992 double lambda_max = 0.0;
993 for (std::size_t j = 0; j < p; ++j) {
995 for (std::size_t i = 0; i < n; ++i) {
996 xy += X[i][j] * (y[i] - y_mean);
998 lambda_max = std::max(lambda_max, std::abs(xy) /
static_cast<double>(n));
1001 if (lambda_max <= 0.0) {
1002 throw std::invalid_argument(
1003 "statcpp::generate_lambda_grid: lambda_max must be positive (data may be constant)");
1006 if (n_lambda <= 1) {
1007 return {lambda_max};
1010 double lambda_min = lambda_max * lambda_min_ratio;
1013 std::vector<double> grid(n_lambda);
1014 double log_max = std::log(lambda_max);
1015 double log_min = std::log(lambda_min);
1016 double step = (log_max - log_min) /
static_cast<double>(n_lambda - 1);
1018 for (std::size_t i = 0; i < n_lambda; ++i) {
1019 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.
cv_result cross_validate_linear(const std::vector< std::vector< double > > &X, const std::vector< double > &y, std::size_t k=5, bool shuffle=true)
Perform k-fold cross-validation for multiple regression model.
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, bool shuffle=true)
Select optimal lambda for Lasso regression using cross-validation.
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)
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)
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, bool shuffle=true)
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.