7#include "unsupported/Eigen/FFT"
11std::vector<double>
compute_dynamic_range_energy(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
13 std::vector<double> dr_values(num_windows);
15 std::vector<size_t> indices(num_windows);
16 std::iota(indices.begin(), indices.end(), 0);
18 MayaFlux::Parallel::for_each(MayaFlux::Parallel::par_unseq, indices.begin(), indices.end(),
20 const size_t start_idx = i * hop_size;
21 const size_t end_idx = std::min(start_idx + window_size, data.size());
22 auto window = data.subspan(start_idx, end_idx - start_idx);
24 double min_val = std::numeric_limits<double>::max();
25 double max_val = std::numeric_limits<double>::lowest();
27 for (double sample : window) {
28 double abs_sample = std::abs(sample);
29 min_val = std::min(min_val, abs_sample);
30 max_val = std::max(max_val, abs_sample);
35 dr_values[i] = 20.0 * std::log10(max_val / min_val);
41std::vector<double>
compute_zero_crossing_energy(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
43 std::vector<double> zcr_values(num_windows);
45 std::vector<size_t> indices(num_windows);
46 std::iota(indices.begin(), indices.end(), 0);
48 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
50 const size_t start_idx = i * hop_size;
51 const size_t end_idx = std::min(start_idx + window_size, data.size());
52 auto window = data.subspan(start_idx, end_idx - start_idx);
54 int zero_crossings = 0;
55 for (size_t j = 1; j < window.size(); ++j) {
56 if ((window[j] >= 0.0) != (window[j - 1] >= 0.0)) {
60 zcr_values[i] =
static_cast<double>(
zero_crossings) /
static_cast<double>(window.size() - 1);
66std::vector<double>
compute_power_energy(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
68 std::vector<double> power_values(num_windows);
70 std::vector<size_t> indices(num_windows);
71 std::iota(indices.begin(), indices.end(), 0);
73 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
75 const size_t start_idx = i * hop_size;
76 const size_t end_idx = std::min(start_idx + window_size, data.size());
77 auto window = data.subspan(start_idx, end_idx - start_idx);
79 double sum_squares = 0.0;
80 for (double sample : window) {
81 sum_squares += sample * sample;
83 power_values[i] = sum_squares;
89std::vector<double>
compute_peak_energy(std::span<const double> data,
const uint32_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
91 std::vector<double> peak_values(num_windows);
93 std::vector<size_t> indices(num_windows);
94 std::iota(indices.begin(), indices.end(), 0);
96 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
98 const size_t start_idx = i * hop_size;
99 const size_t end_idx = std::min(start_idx + window_size, data.size());
100 auto window = data.subspan(start_idx, end_idx - start_idx);
102 double max_val = 0.0;
103 for (double sample : window) {
104 max_val = std::max(max_val, std::abs(sample));
106 peak_values[i] = max_val;
112std::vector<double>
compute_rms_energy(std::span<const double> data,
const uint32_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
114 std::vector<double> rms_values(num_windows);
116 std::vector<size_t> indices(num_windows);
117 std::iota(indices.begin(), indices.end(), 0);
119 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
121 const size_t start_idx = i * hop_size;
122 const size_t end_idx = std::min(start_idx + window_size, data.size());
123 auto window = data.subspan(start_idx, end_idx - start_idx);
125 double sum_squares = 0.0;
126 for (double sample : window) {
127 sum_squares += sample * sample;
129 rms_values[i] = std::sqrt(sum_squares /
static_cast<double>(window.size()));
135std::vector<double>
compute_spectral_energy(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
137 std::vector<double> spectral_energy(num_windows);
139 std::vector<size_t> indices(num_windows);
140 std::iota(indices.begin(), indices.end(), 0);
142 Eigen::VectorXd hanning_window(window_size);
143 for (uint32_t i = 0; i < window_size; ++i) {
144 hanning_window(i) = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (window_size - 1)));
147 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
149 const size_t start_idx = i * hop_size;
150 const size_t end_idx = std::min(start_idx + window_size, data.size());
151 auto window = data.subspan(start_idx, end_idx - start_idx);
153 Eigen::VectorXd windowed_data = Eigen::VectorXd::Zero(window_size);
154 const size_t actual_size = window.size();
156 for (int j = 0; j < actual_size; ++j) {
157 windowed_data(j) = window[j] * hanning_window(j);
160 Eigen::FFT<double> fft;
161 Eigen::VectorXcd fft_result;
162 fft.fwd(fft_result, windowed_data);
165 for (
int j = 0; j < fft_result.size(); ++j) {
166 energy += std::norm(fft_result(j));
169 spectral_energy[i] = energy /
static_cast<double>(window_size);
172 return spectral_energy;
175std::vector<double>
compute_harmonic_energy(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
177 std::vector<double> harmonic_energy(num_windows);
179 std::vector<size_t> indices(num_windows);
180 std::iota(indices.begin(), indices.end(), 0);
182 Eigen::VectorXd hanning_window(window_size);
183 for (uint32_t i = 0; i < window_size; ++i) {
184 hanning_window(i) = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (window_size - 1)));
187 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
189 const size_t start_idx = i * hop_size;
190 const size_t end_idx = std::min(start_idx + window_size, data.size());
191 auto window = data.subspan(start_idx, end_idx - start_idx);
193 Eigen::VectorXd windowed_data = Eigen::VectorXd::Zero(window_size);
194 const size_t actual_size = window.size();
196 for (int j = 0; j < actual_size; ++j) {
197 windowed_data(j) = window[j] * hanning_window(j);
200 Eigen::FFT<double> fft;
201 Eigen::VectorXcd fft_result;
202 fft.fwd(fft_result, windowed_data);
204 const int harmonic_bins = std::max(1,
static_cast<int>(fft_result.size() / 8));
207 for (
int j = 1; j < harmonic_bins; ++j) {
208 energy += std::norm(fft_result(j));
211 harmonic_energy[i] = energy /
static_cast<double>(harmonic_bins);
214 return harmonic_energy;
218 std::span<const double> data,
221 std::vector<size_t> positions;
222 positions.reserve(data.size() / 4);
224 for (
size_t i = 1; i < data.size(); ++i) {
225 bool current_above = (data[i] >= threshold);
226 bool previous_above = (data[i - 1] >= threshold);
228 if (current_above != previous_above) {
229 positions.push_back(i);
233 positions.shrink_to_fit();
238 std::span<const double> data,
242 if (data.size() < 3) {
246 std::vector<size_t> positions;
247 positions.reserve(data.size() / 100);
249 size_t last_peak = 0;
251 for (
size_t i = 1; i < data.size() - 1; ++i) {
252 double abs_val = std::abs(data[i]);
254 if (abs_val > threshold && abs_val >= std::abs(data[i - 1]) && abs_val >= std::abs(data[i + 1])) {
256 if (positions.empty() || (i - last_peak) >= min_distance) {
257 positions.push_back(i);
263 positions.shrink_to_fit();
268 std::span<const double> data,
269 uint32_t window_size,
273 const size_t num_windows = (data.size() - window_size) / hop_size + 1;
275 if (num_windows < 2) {
279 std::vector<double> flux_values(num_windows - 1);
281 Eigen::VectorXd hanning_window(window_size);
282 for (uint32_t i = 0; i < window_size; ++i) {
283 hanning_window(i) = 0.5 * (1.0 - std::cos(2.0 * M_PI * i / (window_size - 1)));
286 Eigen::VectorXcd prev_spectrum;
288 for (
size_t i = 0; i < num_windows; ++i) {
289 const size_t start_idx = i * hop_size;
290 const size_t end_idx = std::min(start_idx + window_size, data.size());
291 auto window = data.subspan(start_idx, end_idx - start_idx);
293 Eigen::VectorXd windowed_data = Eigen::VectorXd::Zero(window_size);
294 for (
size_t j = 0; j < window.size(); ++j) {
295 windowed_data(j) = window[j] * hanning_window(j);
298 Eigen::FFT<double> fft;
299 Eigen::VectorXcd spectrum;
300 fft.fwd(spectrum, windowed_data);
304 for (
int j = 0; j < spectrum.size(); ++j) {
305 double curr_mag = std::abs(spectrum(j));
306 double prev_mag = std::abs(prev_spectrum(j));
307 double diff = curr_mag - prev_mag;
312 flux_values[i - 1] = flux;
315 prev_spectrum = spectrum;
318 std::vector<size_t> positions;
320 double max_flux = *std::max_element(flux_values.begin(), flux_values.end());
321 if (max_flux > 0.0) {
322 for (
size_t i = 0; i < flux_values.size(); ++i) {
323 flux_values[i] /= max_flux;
327 for (
size_t i = 1; i < flux_values.size() - 1; ++i) {
328 if (flux_values[i] > threshold && flux_values[i] > flux_values[i - 1] && flux_values[i] >= flux_values[i + 1]) {
330 size_t sample_pos = (i + 1) * hop_size;
331 positions.push_back(sample_pos);
338std::vector<double>
compute_mean_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
340 std::vector<double> mean_values(num_windows);
342 std::vector<size_t> indices(num_windows);
343 std::iota(indices.begin(), indices.end(), 0);
345 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
347 const size_t start_idx = i * hop_size;
348 const size_t end_idx = std::min(start_idx + window_size, data.size());
349 auto window = data.subspan(start_idx, end_idx - start_idx);
352 for (double sample : window) {
355 mean_values[i] = sum /
static_cast<double>(window.size());
361std::vector<double>
compute_variance_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
bool sample_variance)
363 std::vector<double> variance_values(num_windows);
365 std::vector<size_t> indices(num_windows);
366 std::iota(indices.begin(), indices.end(), 0);
368 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
370 const size_t start_idx = i * hop_size;
371 const size_t end_idx = std::min(start_idx + window_size, data.size());
372 auto window = data.subspan(start_idx, end_idx - start_idx);
374 if (window.size() <= 1) {
375 variance_values[i] = 0.0;
380 for (
double sample : window) {
383 double mean = sum /
static_cast<double>(window.size());
385 double sum_sq_diff = 0.0;
386 for (
double sample : window) {
387 double diff = sample -
mean;
388 sum_sq_diff += diff * diff;
391 double divisor = sample_variance ?
static_cast<double>(window.size() - 1) :
static_cast<double>(window.size());
393 variance_values[i] = sum_sq_diff / divisor;
396 return variance_values;
399std::vector<double>
compute_std_dev_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
bool sample_variance)
403 std::vector<double> std_dev_values(num_windows);
404 std::transform(std::execution::par_unseq, variance_values.begin(), variance_values.end(), std_dev_values.begin(),
405 [](
double variance) { return std::sqrt(variance); });
407 return std_dev_values;
410std::vector<double>
compute_skewness_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
412 std::vector<double> skewness_values(num_windows);
414 std::vector<size_t> indices(num_windows);
415 std::iota(indices.begin(), indices.end(), 0);
417 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
419 const size_t start_idx = i * hop_size;
420 const size_t end_idx = std::min(start_idx + window_size, data.size());
421 auto window = data.subspan(start_idx, end_idx - start_idx);
423 if (window.size() < 2) {
424 skewness_values[i] = 0.0;
429 for (
double sample : window) {
432 double mean = sum /
static_cast<double>(window.size());
434 double sum_sq_diff = 0.0;
435 double sum_cube_diff = 0.0;
436 for (
double sample : window) {
437 double diff = sample -
mean;
438 double sq_diff = diff * diff;
439 sum_sq_diff += sq_diff;
440 sum_cube_diff += sq_diff * diff;
443 double variance = sum_sq_diff /
static_cast<double>(window.size());
444 if (variance <= 0.0) {
445 skewness_values[i] = 0.0;
449 double std_dev = std::sqrt(variance);
450 double third_moment = sum_cube_diff /
static_cast<double>(window.size());
455 return skewness_values;
458std::vector<double>
compute_kurtosis_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
460 std::vector<double> kurtosis_values(num_windows);
462 std::vector<size_t> indices(num_windows);
463 std::iota(indices.begin(), indices.end(), 0);
465 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
467 const size_t start_idx = i * hop_size;
468 const size_t end_idx = std::min(start_idx + window_size, data.size());
469 auto window = data.subspan(start_idx, end_idx - start_idx);
471 if (window.size() < 2) {
472 kurtosis_values[i] = 0.0;
477 for (
double sample : window) {
480 double mean = sum /
static_cast<double>(window.size());
482 double sum_sq_diff = 0.0;
483 double sum_fourth_diff = 0.0;
484 for (
double sample : window) {
485 double diff = sample -
mean;
486 double sq_diff = diff * diff;
487 sum_sq_diff += sq_diff;
488 sum_fourth_diff += sq_diff * sq_diff;
491 double variance = sum_sq_diff /
static_cast<double>(window.size());
492 if (variance <= 0.0) {
493 kurtosis_values[i] = 0.0;
497 double fourth_moment = sum_fourth_diff /
static_cast<double>(window.size());
499 kurtosis_values[i] = (fourth_moment / (variance * variance)) - 3.0;
502 return kurtosis_values;
505std::vector<double>
compute_median_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
507 std::vector<double> median_values(num_windows);
509 std::vector<size_t> indices(num_windows);
510 std::iota(indices.begin(), indices.end(), 0);
512 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
514 const size_t start_idx = i * hop_size;
515 const size_t end_idx = std::min(start_idx + window_size, data.size());
516 auto window = data.subspan(start_idx, end_idx - start_idx);
518 if (window.empty()) {
519 median_values[i] = 0.0;
523 std::vector<double> sorted_window(window.begin(), window.end());
524 std::ranges::sort(sorted_window);
526 size_t n = sorted_window.size();
528 median_values[i] = (sorted_window[n / 2 - 1] + sorted_window[n / 2]) / 2.0;
530 median_values[i] = sorted_window[n / 2];
534 return median_values;
537std::vector<double>
compute_percentile_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
double percentile)
539 std::vector<double> percentile_values(num_windows);
541 if (percentile < 0.0)
543 if (percentile > 100.0)
546 std::vector<size_t> indices(num_windows);
547 std::iota(indices.begin(), indices.end(), 0);
549 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
551 const size_t start_idx = i * hop_size;
552 const size_t end_idx = std::min(start_idx + window_size, data.size());
553 auto window = data.subspan(start_idx, end_idx - start_idx);
555 if (window.empty()) {
556 percentile_values[i] = 0.0;
560 std::vector<double> sorted_window(window.begin(), window.end());
561 std::ranges::sort(sorted_window);
563 if (percentile == 0.0) {
564 percentile_values[i] = sorted_window.front();
567 if (percentile == 100.0) {
568 percentile_values[i] = sorted_window.back();
572 double index = (percentile / 100.0) *
static_cast<double>(sorted_window.size() - 1);
573 auto lower_idx =
static_cast<size_t>(std::floor(index));
574 auto upper_idx =
static_cast<size_t>(std::ceil(index));
576 if (lower_idx == upper_idx) {
577 percentile_values[i] = sorted_window[lower_idx];
579 double weight = index -
static_cast<double>(lower_idx);
580 percentile_values[i] = sorted_window[lower_idx] * (1.0 - weight) + sorted_window[upper_idx] * weight;
584 return percentile_values;
587std::vector<double>
compute_entropy_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
size_t num_bins)
589 std::vector<double> entropy_values(num_windows);
591 std::vector<size_t> indices(num_windows);
592 std::iota(indices.begin(), indices.end(), 0);
594 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
596 const size_t start_idx = i * hop_size;
597 const size_t end_idx = std::min(start_idx + window_size, data.size());
598 auto window = data.subspan(start_idx, end_idx - start_idx);
600 if (window.empty()) {
601 entropy_values[i] = 0.0;
605 size_t bins = num_bins;
607 bins =
static_cast<size_t>(std::ceil(std::log2(window.size()) + 1));
608 bins = std::max(bins,
static_cast<size_t>(1));
609 bins = std::min(bins, window.size());
612 auto [min_it, max_it] = std::ranges::minmax_element(window);
613 double min_val = *min_it;
614 double max_val = *max_it;
616 if (max_val <= min_val) {
617 entropy_values[i] = 0.0;
621 double bin_width = (max_val - min_val) /
static_cast<double>(bins);
623 std::vector<size_t> bin_counts(bins, 0);
624 for (
double value : window) {
625 auto bin_idx =
static_cast<size_t>((value - min_val) / bin_width);
628 bin_counts[bin_idx]++;
631 double entropy = 0.0;
632 size_t total_count = window.size();
634 for (
size_t count : bin_counts) {
636 double probability =
static_cast<double>(count) /
static_cast<double>(total_count);
637 entropy -= probability * std::log2(probability);
641 entropy_values[i] = entropy;
644 return entropy_values;
647std::vector<double>
compute_min_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
649 std::vector<double> min_values(num_windows);
651 std::vector<size_t> indices(num_windows);
652 std::iota(indices.begin(), indices.end(), 0);
654 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
656 const size_t start_idx = i * hop_size;
657 const size_t end_idx = std::min(start_idx + window_size, data.size());
658 auto window = data.subspan(start_idx, end_idx - start_idx);
660 min_values[i] = *std::ranges::min_element(window);
666std::vector<double>
compute_max_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
668 std::vector<double> max_values(num_windows);
670 std::vector<size_t> indices(num_windows);
671 std::iota(indices.begin(), indices.end(), 0);
673 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
675 const size_t start_idx = i * hop_size;
676 const size_t end_idx = std::min(start_idx + window_size, data.size());
677 auto window = data.subspan(start_idx, end_idx - start_idx);
679 max_values[i] = *std::ranges::max_element(window);
685std::vector<double>
compute_range_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
687 std::vector<double> range_values(num_windows);
689 std::vector<size_t> indices(num_windows);
690 std::iota(indices.begin(), indices.end(), 0);
692 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
694 const size_t start_idx = i * hop_size;
695 const size_t end_idx = std::min(start_idx + window_size, data.size());
696 auto window = data.subspan(start_idx, end_idx - start_idx);
698 auto [min_it, max_it] = std::ranges::minmax_element(window);
699 range_values[i] = *max_it - *min_it;
705std::vector<double>
compute_sum_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
707 std::vector<double> sum_values(num_windows);
709 std::vector<size_t> indices(num_windows);
710 std::iota(indices.begin(), indices.end(), 0);
712 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
714 const size_t start_idx = i * hop_size;
715 const size_t end_idx = std::min(start_idx + window_size, data.size());
716 auto window = data.subspan(start_idx, end_idx - start_idx);
718 sum_values[i] = std::accumulate(window.begin(), window.end(), 0.0);
724std::vector<double>
compute_count_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
726 std::vector<double> count_values(num_windows);
728 std::vector<size_t> indices(num_windows);
729 std::iota(indices.begin(), indices.end(), 0);
731 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
733 const size_t start_idx = i * hop_size;
734 const size_t end_idx = std::min(start_idx + window_size, data.size());
736 count_values[i] = static_cast<double>(end_idx - start_idx);
742std::vector<double>
compute_mad_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
744 std::vector<double> mad_values(num_windows);
746 std::vector<size_t> indices(num_windows);
747 std::iota(indices.begin(), indices.end(), 0);
749 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
751 const size_t start_idx = i * hop_size;
752 const size_t end_idx = std::min(start_idx + window_size, data.size());
753 auto window = data.subspan(start_idx, end_idx - start_idx);
755 if (window.empty()) {
760 std::vector<double> sorted_window(window.begin(), window.end());
761 std::ranges::sort(sorted_window);
764 size_t n = sorted_window.size();
766 median = (sorted_window[n / 2 - 1] + sorted_window[n / 2]) / 2.0;
768 median = sorted_window[n / 2];
771 std::vector<double> abs_deviations;
772 abs_deviations.reserve(window.size());
773 for (
double val : window) {
774 abs_deviations.push_back(std::abs(val - median));
777 std::ranges::sort(abs_deviations);
778 size_t mad_n = abs_deviations.size();
779 if (mad_n % 2 == 0) {
780 mad_values[i] = (abs_deviations[mad_n / 2 - 1] + abs_deviations[mad_n / 2]) / 2.0;
782 mad_values[i] = abs_deviations[mad_n / 2];
789std::vector<double>
compute_cv_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
bool sample_variance)
794 std::vector<double> cv_values(num_windows);
795 std::transform(std::execution::par_unseq, mean_vals.begin(), mean_vals.end(), std_vals.begin(), cv_values.begin(),
797 return (std::abs(mean) > 1e-15) ? std_dev / mean : 0.0;
803std::vector<double>
compute_mode_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size)
805 std::vector<double> mode_values(num_windows);
806 constexpr double tolerance = 1e-10;
808 std::vector<size_t> indices(num_windows);
809 std::iota(indices.begin(), indices.end(), 0);
811 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
813 const size_t start_idx = i * hop_size;
814 const size_t end_idx = std::min(start_idx + window_size, data.size());
815 auto window = data.subspan(start_idx, end_idx - start_idx);
817 if (window.empty()) {
818 mode_values[i] = 0.0;
822 std::map<int64_t, std::pair<double, size_t>> frequency_map;
824 for (
double value : window) {
825 auto bucket =
static_cast<int64_t
>(std::round(value / tolerance));
826 if (frequency_map.find(bucket) == frequency_map.end()) {
827 frequency_map[bucket] = { value, 1 };
829 frequency_map[bucket].second++;
830 frequency_map[bucket].first = (frequency_map[bucket].first *
static_cast<double>(frequency_map[bucket].second - 1) + value) /
static_cast<double>(frequency_map[bucket].second);
834 auto max_count_it = std::ranges::max_element(frequency_map,
835 [](
const auto& a,
const auto& b) {
return a.second.second < b.second.second; });
837 mode_values[i] = max_count_it != frequency_map.end() ? max_count_it->second.first : window[0];
843std::vector<double>
compute_zscore_statistic(std::span<const double> data,
const size_t num_windows,
const uint32_t hop_size,
const uint32_t window_size,
bool sample_variance)
845 std::vector<double> zscore_values(num_windows);
847 std::vector<size_t> indices(num_windows);
848 std::iota(indices.begin(), indices.end(), 0);
850 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
852 const size_t start_idx = i * hop_size;
853 const size_t end_idx = std::min(start_idx + window_size, data.size());
854 auto window = data.subspan(start_idx, end_idx - start_idx);
856 if (window.empty()) {
857 zscore_values[i] = 0.0;
862 for (
double sample : window) {
865 double mean = sum /
static_cast<double>(window.size());
867 double sum_sq_diff = 0.0;
868 for (
double sample : window) {
869 double diff = sample -
mean;
870 sum_sq_diff += diff * diff;
873 double divisor = sample_variance ?
static_cast<double>(window.size() - 1) :
static_cast<double>(window.size());
875 double variance = sum_sq_diff / divisor;
876 double std_dev = std::sqrt(variance);
879 double sum_zscore = 0.0;
880 for (
double val : window) {
883 zscore_values[i] = sum_zscore /
static_cast<double>(window.size());
885 zscore_values[i] = 0.0;
889 return zscore_values;
std::vector< double > compute_zscore_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, bool sample_variance)
Compute z-score statistic using zero-copy processing.
std::vector< double > compute_entropy_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, size_t num_bins)
Compute entropy statistic using zero-copy processing.
std::vector< double > compute_skewness_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute skewness statistic using zero-copy processing.
std::vector< double > compute_dynamic_range_energy(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute dynamic range energy using zero-copy processing.
std::vector< double > compute_variance_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, bool sample_variance)
Compute variance statistic using zero-copy processing.
std::vector< double > compute_mad_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute MAD (Median Absolute Deviation) statistic using zero-copy processing.
std::vector< size_t > find_onset_positions(std::span< const double > data, uint32_t window_size, uint32_t hop_size, double threshold)
Find onset positions using spectral flux.
std::vector< double > compute_harmonic_energy(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute harmonic energy using low-frequency FFT analysis.
std::vector< double > compute_peak_energy(std::span< const double > data, const uint32_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute peak energy using zero-copy processing.
std::vector< double > compute_power_energy(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute power energy using zero-copy processing.
std::vector< double > compute_std_dev_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, bool sample_variance)
Compute standard deviation statistic using zero-copy processing.
std::vector< double > compute_sum_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute sum statistic using zero-copy processing.
std::vector< double > compute_percentile_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, double percentile)
Compute percentile statistic using zero-copy processing.
std::vector< size_t > find_peak_positions(std::span< const double > data, double threshold, size_t min_distance)
Find actual peak positions in the signal.
std::vector< double > compute_mode_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute mode statistic using zero-copy processing.
std::vector< double > compute_rms_energy(std::span< const double > data, const uint32_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute RMS energy using zero-copy processing.
std::vector< double > compute_min_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute min statistic using zero-copy processing.
std::vector< double > compute_mean_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute mean statistic using zero-copy processing.
std::vector< double > compute_range_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute range statistic using zero-copy processing.
std::vector< double > compute_zero_crossing_energy(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute zero-crossing energy using zero-copy processing.
std::vector< double > compute_cv_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size, bool sample_variance)
Compute CV (Coefficient of Variation) statistic using zero-copy processing.
std::vector< size_t > find_zero_crossing_positions(std::span< const double > data, double threshold)
Find actual zero-crossing positions in the signal.
std::vector< double > compute_median_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute median statistic using zero-copy processing.
std::vector< double > compute_spectral_energy(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute spectral energy using FFT-based analysis.
std::vector< double > compute_kurtosis_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute kurtosis statistic using zero-copy processing.
std::vector< double > compute_max_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute max statistic using zero-copy processing.
std::vector< double > compute_count_statistic(std::span< const double > data, const size_t num_windows, const uint32_t hop_size, const uint32_t window_size)
Compute count statistic using zero-copy processing.
std::vector< size_t > zero_crossings(const std::vector< double > &data, double threshold)
Detect zero crossings in single-channel signal.
double std_dev(const std::vector< double > &data)
Calculate standard deviation of single-channel data.
double mean(const std::vector< double > &data)
Calculate mean of single-channel data.