9#ifndef CMP_STATISTICS_H
10#define CMP_STATISTICS_H
20#include <unsupported/Eigen/FFT>
59 std::default_random_engine
rng_;
74 KFold(Eigen::Index n_splits, Eigen::Index n_obs,
bool shuffle =
false,
unsigned int random_state = 42)
81 throw std::invalid_argument(
"Number of observations must be at least 2.");
83 if(n_splits < 2 || n_splits > n_obs) {
84 throw std::invalid_argument(
"Number of splits must be between 2 and the number of observations.");
89 for(Eigen::Index i = 0; i <
nObs_; ++i) {
132 std::pair<Eigen::VectorXs, Eigen::VectorXs>
operator*()
const {
143 Eigen::Index train_idx = 0;
145 if(i < start || i >=
end) {
150 return {train_indices, test_indices};
185 std::pair<Eigen::VectorXs, Eigen::VectorXs>
operator()(Eigen::Index split)
const {
186 if(split < 0 || split >=
nSplits_) {
187 throw std::out_of_range(
"Split index out of range.");
233 std::default_random_engine
rng_;
236 std::uniform_int_distribution<size_t>
dist_;
253 Bootstrap(
size_t n_obs,
size_t n_obs_resample,
bool with_replacement =
true,
unsigned int random_state = 42)
261 throw std::invalid_argument(
"Number of observations must be at least 1.");
263 if(n_obs_resample < 1) {
264 throw std::invalid_argument(
"Number of resampled observations must be at least 1.");
267 throw std::invalid_argument(
"Number of resampled observations cannot be greater than number of observations when sampling without replacement.");
272 for(
size_t i = 0; i <
nObs_; ++i) {
293 std::shuffle(shuffled_indices.data(), shuffled_indices.data() + shuffled_indices.size(),
rng_);
294 sample_indices = shuffled_indices.head(
nObsResample_).eval();
297 return sample_indices;
313Eigen::VectorXd mean(
const Eigen::Ref<const Eigen::MatrixXd>& data);
327Eigen::MatrixXd covariance(
const Eigen::Ref<const Eigen::MatrixXd>& data);
337double quantile(
const Eigen::Ref<const Eigen::VectorXd>& data,
double quantile);
355Eigen::VectorXd
interQuantileRange(
const Eigen::Ref<const Eigen::MatrixXd>& data,
double lowerQuantile,
double upperQuantile);
370Eigen::MatrixXd
pearsonCorrelation(
const Eigen::Ref<const Eigen::MatrixXd>& data1,
const Eigen::Ref<const Eigen::MatrixXd>& data2);
382Eigen::MatrixXd
laggedCorrelation(
const Eigen::Ref<const Eigen::MatrixXd>& data1,
const Eigen::Ref<const Eigen::MatrixXd>& data2,
int lag);
393std::vector<Eigen::MatrixXd>
laggedCorrelation(
const Eigen::Ref<const Eigen::MatrixXd>& data1,
const Eigen::Ref<const Eigen::MatrixXd>& data2,
int minLag,
int maxLag);
403std::pair<Eigen::VectorXd, double>
selfCorrelationLength(
const Eigen::Ref<const Eigen::MatrixXd>& data);
418 const Eigen::Ref<const Eigen::MatrixXd>& data) {
419 size_t nPoints = data.rows();
420 size_t nFeatures = data.cols();
422 while(nFFT < 2 * nPoints) nFFT <<= 1;
424 Eigen::FFT<double> fft;
427 std::vector<std::vector<std::complex<double>>> X(nFeatures, std::vector<std::complex<double>>(nFFT));
428 for(
size_t i = 0; i < nFeatures; ++i) {
429 Eigen::VectorXd x = data.col(i);
430 x.array() -= x.mean();
432 std::vector<double> x_pad(nFFT, 0.0);
433 for(
size_t t = 0; t < nPoints; ++t) x_pad[t] = x[t];
435 fft.fwd(X[i], x_pad);
439 std::vector<Eigen::MatrixXd> corr(nPoints, Eigen::MatrixXd::Zero(nFeatures, nFeatures));
441 for(
size_t i = 0; i < nFeatures; ++i) {
442 double var_i = data.col(i).array().square().sum() / nPoints;
443 for(
size_t j = 0; j < nFeatures; ++j) {
444 double var_j = data.col(j).array().square().sum() / nPoints;
447 std::vector<std::complex<double>> R_fft(nFFT);
448 for(
size_t k = 0; k < nFFT; ++k)
449 R_fft[k] = X[i][k] * std::conj(X[j][k]);
452 std::vector<double> r_pad(nFFT, 0.0);
453 fft.inv(r_pad, R_fft);
456 for(
size_t lag = 0; lag < nPoints; ++lag)
457 corr[lag](i, j) = r_pad[lag] / (nPoints * std::sqrt(var_i * var_j));
501 size_t n = data.rows();
502 if(n < 2)
throw std::invalid_argument(
"Need at least 2 points to compute pairwise distances.");
505 size_t num_pairs = n * (n - 1) / 2;
513 double local_sum = 0.0;
516 #pragma omp for schedule(dynamic)
517 for(
size_t i = 0; i < n - 1; ++i) {
519 size_t offset = i * n - i * (i + 1) / 2 - i - 1;
521 for(
size_t j = i + 1; j < n; ++j) {
523 double dist = (data.row(i) - data.row(j)).norm();
534 mean_val /=
static_cast<double>(num_pairs);
544 if(!
is_computed)
throw std::runtime_error(
"Distances not yet computed.");
559 if(!
is_computed)
throw std::runtime_error(
"Distances not yet computed.");
560 if(q < 0.0 || q > 1.0)
throw std::invalid_argument(
"Quantile must be in [0, 1].");
562 size_t idx =
static_cast<size_t>(q * (
distances.size() - 1));
589 if(!
is_computed)
throw std::runtime_error(
"Distances not yet computed.");
590 if(r_thresholds.empty())
return {};
592 size_t k = r_thresholds.size();
596 std::vector<std::pair<double, size_t>> sorted_r(k);
597 for(
size_t i = 0; i < k; ++i) {
598 sorted_r[i] = {r_thresholds[i], i};
600 std::sort(sorted_r.begin(), sorted_r.end(),
601 [](
const auto & a,
const auto & b) {
602 return a.first < b.first;
606 std::vector<double> r_vals(k);
607 for(
size_t i = 0; i < k; ++i) r_vals[i] = sorted_r[i].first;
610 std::vector<size_t> global_counts(k + 1, 0);
616 std::vector<size_t> local_counts(k + 1, 0);
618 #pragma omp for schedule(static)
619 for(
size_t i = 0; i < m; ++i) {
623 auto it = std::upper_bound(r_vals.begin(), r_vals.end(), d);
624 size_t idx = std::distance(r_vals.begin(), it);
633 for(
size_t j = 0; j <= k; ++j) {
634 global_counts[j] += local_counts[j];
641 std::vector<double> C_r(k, 0.0);
642 size_t cumulative_sum = 0;
644 for(
size_t j = 0; j < k; ++j) {
645 cumulative_sum += global_counts[j];
648 size_t original_idx = sorted_r[j].second;
649 C_r[original_idx] =
static_cast<double>(cumulative_sum) /
static_cast<double>(m);
Bootstrap resampling index generator.
Definition statistics.h:221
size_t nObsResample_
Target number of observations to resample .
Definition statistics.h:227
Eigen::VectorXs operator()()
Executes the bootstrap sampling process.
Definition statistics.h:281
bool withReplacement_
Flag indicating whether to sample with replacement.
Definition statistics.h:242
Bootstrap(size_t n_obs, size_t n_obs_resample, bool with_replacement=true, unsigned int random_state=42)
Constructor for the Bootstrap resampler.
Definition statistics.h:253
std::uniform_int_distribution< size_t > dist_
Uniform integer distribution for sampling with replacement.
Definition statistics.h:236
Eigen::VectorXs indices_
Internal array storing the base indices for sampling.
Definition statistics.h:239
unsigned int rngState_
Seed for the random number generator.
Definition statistics.h:230
size_t nObs_
Original number of observations .
Definition statistics.h:224
std::default_random_engine rng_
Random number engine.
Definition statistics.h:233
Iterator class to traverse folds for K-Fold cross-validation.
Definition statistics.h:102
KFoldIterator(const KFold &kf, Eigen::Index split)
Constructs a KFoldIterator.
Definition statistics.h:116
Eigen::Index currentSplit_
The current active fold index .
Definition statistics.h:105
std::pair< Eigen::VectorXs, Eigen::VectorXs > operator*() const
Dereferences the iterator to compute training and test index sets for the current fold.
Definition statistics.h:132
KFoldIterator & operator++()
Increments the iterator to point to the next fold.
Definition statistics.h:157
const KFold & parent_
Reference to the parent KFold object to access index arrays and sizes.
Definition statistics.h:108
bool operator!=(const KFoldIterator &other) const
Checks inequality with another iterator (used for loop termination).
Definition statistics.h:124
K-Fold cross-validation partition generator.
Definition statistics.h:44
std::pair< Eigen::VectorXs, Eigen::VectorXs > operator()(Eigen::Index split) const
Directly accesses the train/test indices for a specific split without iterating.
Definition statistics.h:185
KFold(Eigen::Index n_splits, Eigen::Index n_obs, bool shuffle=false, unsigned int random_state=42)
Constructor for the KFold partitioner.
Definition statistics.h:74
bool shuffle_
Flag indicating if the dataset indices should be shuffled before splitting.
Definition statistics.h:53
KFoldIterator begin() const
Gets the iterator pointing to the first fold.
Definition statistics.h:167
Eigen::Index nSplits_
Number of folds/splits .
Definition statistics.h:47
Eigen::VectorXs indices_
Internal array storing the (potentially shuffled) sequence of indices.
Definition statistics.h:62
std::default_random_engine rng_
Random number engine used for shuffling.
Definition statistics.h:59
KFoldIterator end() const
Gets the iterator pointing to the end of the folds.
Definition statistics.h:175
Eigen::Index nObs_
Total number of observations in the dataset.
Definition statistics.h:50
size_t nFolds() const
Gets the total number of splits/folds .
Definition statistics.h:196
unsigned int rngState_
Seed for the random number generator.
Definition statistics.h:56
Computes and stores pairwise spatial distances between high-dimensional data points.
Definition statistics.h:473
double quantile(double q)
Fast quantile retrieval without executing a full sort.
Definition statistics.h:558
double mean_val
The globally computed mean distance across all pairs.
Definition statistics.h:479
std::vector< double > distances
1D flattened array of the strictly upper triangular distance matrix.
Definition statistics.h:476
void compute(const Eigen::MatrixXd &data)
Computes the upper triangle of the pairwise Euclidean distance matrix.
Definition statistics.h:500
std::vector< double > correlation_integral(const std::vector< double > &r_thresholds) const
Computes the spatial Correlation Integral for a grid of radial thresholds.
Definition statistics.h:588
PairwiseDistanceStats()
Default constructor.
Definition statistics.h:486
double mean() const
Retrieves the mean pairwise distance .
Definition statistics.h:543
bool is_computed
Flag indicating if the distances have been computed and the class is ready to query.
Definition statistics.h:482
Matrix< size_t, Eigen::Dynamic, 1 > VectorXs
Definition cmp_defines.h:20
Definition statistics.h:27
double quantile(const Eigen::Ref< const Eigen::VectorXd > &data, double quantile)
Computes a specific quantile of a 1D vector of data.
Definition statistics.cpp:21
std::vector< Eigen::MatrixXd > selfCrossCorrelationFFT(const Eigen::Ref< const Eigen::MatrixXd > &data)
Computes the complete auto- and cross-correlation functions across all lags efficiently using the Fas...
Definition statistics.h:417
Eigen::MatrixXd laggedCorrelation(const Eigen::Ref< const Eigen::MatrixXd > &data1, const Eigen::Ref< const Eigen::MatrixXd > &data2, int lag)
Computes the cross-correlation matrix between two datasets with a specified temporal lag.
Definition statistics.cpp:104
std::pair< Eigen::VectorXd, double > selfCorrelationLength(const Eigen::Ref< const Eigen::MatrixXd > &data)
Computes the self-correlation (autocorrelation) length and the effective sample size.
Definition statistics.cpp:155
Eigen::MatrixXd pearsonCorrelation(const Eigen::Ref< const Eigen::MatrixXd > &data1, const Eigen::Ref< const Eigen::MatrixXd > &data2)
Computes the Pearson correlation matrix between two datasets.
Definition statistics.cpp:69
Eigen::VectorXd interQuantileRange(const Eigen::Ref< const Eigen::MatrixXd > &data, double lowerQuantile, double upperQuantile)
Computes the interquartile range (IQR) column-wise for a dataset.
Definition statistics.cpp:45