Compute mode statistic using zero-copy processing.
804{
805 std::vector<double> mode_values(num_windows);
806 constexpr double tolerance = 1e-10;
807
808 std::vector<size_t> indices(num_windows);
809 std::iota(indices.begin(), indices.end(), 0);
810
811 std::for_each(std::execution::par_unseq, indices.begin(), indices.end(),
812 [&](size_t i) {
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);
816
817 if (window.empty()) {
818 mode_values[i] = 0.0;
819 return;
820 }
821
822 std::map<int64_t, std::pair<double, size_t>> frequency_map;
823
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 };
828 } else {
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);
831 }
832 }
833
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; });
836
837 mode_values[i] = max_count_it != frequency_map.end() ? max_count_it->second.first : window[0];
838 });
839
840 return mode_values;
841}