• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 15685147280

16 Jun 2025 03:31PM UTC coverage: 67.52% (+0.9%) from 66.58%
15685147280

push

github

paulmthompson
remove some unnecessary analog time series functions

11 of 11 new or added lines in 1 file covered. (100.0%)

72 existing lines in 6 files now uncovered.

8860 of 13122 relevant lines covered (67.52%)

1182.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.17
/src/WhiskerToolbox/DataManager/AnalogTimeSeries/utils/statistics.cpp
1
#include "statistics.hpp"
2

3
#include "AnalogTimeSeries/Analog_Time_Series.hpp"
4

5
#include <algorithm>
6
#include <cmath>
7
#include <limits>
8
#include <numeric>
9

10
// ========== Mean ==========
11

12
float calculate_mean_impl(std::vector<float> const & data, size_t start, size_t end) {
28✔
13
    if (data.empty() || start >= end || start >= data.size() || end > data.size()) {
28✔
14
        return std::numeric_limits<float>::quiet_NaN();
3✔
15
    }
16
    return calculate_mean_impl(data.begin() + start, data.begin() + end);
25✔
17
}
18

19
float calculate_mean(std::span<const float> data_span) {
26✔
20
    return calculate_mean_impl(data_span.begin(), data_span.end());
26✔
21
}
22

23
float calculate_mean(AnalogTimeSeries const & series) {
19✔
24
    auto const & data = series.getAnalogTimeSeries();
19✔
25
    return calculate_mean_impl(data, 0, data.size());
19✔
26
}
27

28
float calculate_mean(AnalogTimeSeries const & series, int64_t start, int64_t end) {
3✔
29
    auto const & data = series.getAnalogTimeSeries();
3✔
30
    if (start < 0 || end < 0 || start >= end) {
3✔
31
        return std::numeric_limits<float>::quiet_NaN();
×
32
    }
33
    return calculate_mean_impl(data, static_cast<size_t>(start), static_cast<size_t>(end));
3✔
34
}
35

36
float calculate_mean_in_time_range(AnalogTimeSeries const & series, TimeFrameIndex start_time, TimeFrameIndex end_time) {
22✔
37

38
    auto data_span = series.getDataInTimeFrameIndexRange(start_time, end_time);
22✔
39
    return calculate_mean(data_span);
44✔
40
}
41

42
// ========== Standard Deviation ==========
43

44
float calculate_std_dev_impl(std::vector<float> const & data, size_t start, size_t end) {
39✔
45
    if (data.empty() || start >= end || start >= data.size() || end > data.size()) {
39✔
46
        return std::numeric_limits<float>::quiet_NaN();
4✔
47
    }
48
    return calculate_std_dev_impl(data.begin() + start, data.begin() + end);
35✔
49
}
50

51
float calculate_std_dev(std::span<const float> data_span) {
23✔
52
    return calculate_std_dev_impl(data_span.begin(), data_span.end());
23✔
53
}
54

55
float calculate_std_dev(AnalogTimeSeries const & series) {
30✔
56
    auto const & data = series.getAnalogTimeSeries();
30✔
57
    return calculate_std_dev_impl(data, 0, data.size());
30✔
58
}
59

60
float calculate_std_dev(AnalogTimeSeries const & series, int64_t start, int64_t end) {
3✔
61
    auto const & data = series.getAnalogTimeSeries();
3✔
62
    if (start < 0 || end < 0 || start >= end) {
3✔
UNCOV
63
        return std::numeric_limits<float>::quiet_NaN();
×
64
    }
65
    return calculate_std_dev_impl(data, static_cast<size_t>(start), static_cast<size_t>(end));
3✔
66
}
67

68
float calculate_std_dev_in_time_range(AnalogTimeSeries const & series, TimeFrameIndex start_time, TimeFrameIndex end_time) {
18✔
69
    auto data_span = series.getDataInTimeFrameIndexRange(start_time, end_time);
18✔
70
    return calculate_std_dev(data_span);
36✔
71
}
72

73
float calculate_std_dev_approximate(AnalogTimeSeries const & series,
19✔
74
                                    float sample_percentage,
75
                                    size_t min_sample_threshold) {
76
    auto const & data = series.getAnalogTimeSeries();
19✔
77
    if (data.empty()) {
19✔
78
        return std::numeric_limits<float>::quiet_NaN();
1✔
79
    }
80

81
    size_t const data_size = data.size();
18✔
82
    size_t const target_sample_size = static_cast<size_t>(data_size * sample_percentage / 100.0f);
18✔
83

84
    // Fall back to exact calculation if sample would be too small
85
    if (target_sample_size < min_sample_threshold) {
18✔
86
        return calculate_std_dev(series);
16✔
87
    }
88

89
    // Use systematic sampling for better cache performance
90
    size_t const step_size = data_size / target_sample_size;
2✔
91
    if (step_size == 0) {
2✔
UNCOV
92
        return calculate_std_dev(series);
×
93
    }
94

95
    // Calculate mean of sampled data
96
    float sum = 0.0f;
2✔
97
    size_t sample_count = 0;
2✔
98
    for (size_t i = 0; i < data_size; i += step_size) {
1,102✔
99
        sum += data[i];
1,100✔
100
        ++sample_count;
1,100✔
101
    }
102
    float const mean = sum / static_cast<float>(sample_count);
2✔
103

104
    // Calculate variance of sampled data
105
    float variance_sum = 0.0f;
2✔
106
    for (size_t i = 0; i < data_size; i += step_size) {
1,102✔
107
        float const diff = data[i] - mean;
1,100✔
108
        variance_sum += diff * diff;
1,100✔
109
    }
110

111
    return std::sqrt(variance_sum / static_cast<float>(sample_count));
2✔
112
}
113

114
float calculate_std_dev_adaptive(AnalogTimeSeries const & series,
5✔
115
                                 size_t initial_sample_size,
116
                                 size_t max_sample_size,
117
                                 float convergence_tolerance) {
118
    auto const & data = series.getAnalogTimeSeries();
5✔
119
    if (data.empty()) {
5✔
120
        return std::numeric_limits<float>::quiet_NaN();
1✔
121
    }
122

123
    size_t const data_size = data.size();
4✔
124
    if (data_size <= max_sample_size) {
4✔
125
        return calculate_std_dev(series);
2✔
126
    }
127

128
    size_t current_sample_size = std::min(initial_sample_size, data_size);
2✔
129
    float previous_std_dev = 0.0f;
2✔
130
    bool first_iteration = true;
2✔
131

132
    while (current_sample_size <= max_sample_size) {
4✔
133
        // Use systematic sampling
134
        size_t const step_size = data_size / current_sample_size;
4✔
135
        if (step_size == 0) break;
4✔
136

137
        // Calculate mean of current sample
138
        float sum = 0.0f;
4✔
139
        size_t actual_sample_count = 0;
4✔
140
        for (size_t i = 0; i < data_size; i += step_size) {
1,804✔
141
            sum += data[i];
1,800✔
142
            ++actual_sample_count;
1,800✔
143
        }
144
        float const mean = sum / static_cast<float>(actual_sample_count);
4✔
145

146
        // Calculate standard deviation of current sample
147
        float variance_sum = 0.0f;
4✔
148
        for (size_t i = 0; i < data_size; i += step_size) {
1,804✔
149
            float const diff = data[i] - mean;
1,800✔
150
            variance_sum += diff * diff;
1,800✔
151
        }
152
        float const current_std_dev = std::sqrt(variance_sum / static_cast<float>(actual_sample_count));
4✔
153

154
        // Check for convergence (skip first iteration)
155
        if (!first_iteration) {
4✔
156
            float const relative_change = std::abs(current_std_dev - previous_std_dev) /
2✔
157
                                          std::max(current_std_dev, previous_std_dev);
2✔
158
            if (relative_change < convergence_tolerance) {
2✔
159
                return current_std_dev;
2✔
160
            }
161
        }
162

163
        previous_std_dev = current_std_dev;
2✔
164
        first_iteration = false;
2✔
165

166
        // Increase sample size for next iteration (double it)
167
        current_sample_size = std::min(current_sample_size * 2, max_sample_size);
2✔
168
    }
169

UNCOV
170
    return previous_std_dev;
×
171
}
172

173
float calculate_std_dev_approximate_in_time_range(AnalogTimeSeries const & series,
3✔
174
                                                  TimeFrameIndex start_time, 
175
                                                  TimeFrameIndex end_time,
176
                                                  float sample_percentage,
177
                                                  size_t min_sample_threshold) {
178
    auto data_span = series.getDataInTimeFrameIndexRange(start_time, end_time);
3✔
179
    if (data_span.empty()) {
3✔
180
        return std::numeric_limits<float>::quiet_NaN();
1✔
181
    }
182

183
    size_t const data_size = data_span.size();
2✔
184
    size_t const target_sample_size = static_cast<size_t>(data_size * sample_percentage / 100.0f);
2✔
185

186
    // Fall back to exact calculation if sample would be too small
187
    if (target_sample_size < min_sample_threshold) {
2✔
188
        return calculate_std_dev(data_span);
1✔
189
    }
190

191
    // Use systematic sampling for better cache performance
192
    size_t const step_size = data_size / target_sample_size;
1✔
193
    if (step_size == 0) {
1✔
UNCOV
194
        return calculate_std_dev(data_span);
×
195
    }
196

197
    // Calculate mean of sampled data
198
    float sum = 0.0f;
1✔
199
    size_t sample_count = 0;
1✔
200
    for (size_t i = 0; i < data_size; i += step_size) {
27✔
201
        sum += data_span[i];
26✔
202
        ++sample_count;
26✔
203
    }
204
    float const mean = sum / static_cast<float>(sample_count);
1✔
205

206
    // Calculate variance of sampled data
207
    float variance_sum = 0.0f;
1✔
208
    for (size_t i = 0; i < data_size; i += step_size) {
27✔
209
        float const diff = data_span[i] - mean;
26✔
210
        variance_sum += diff * diff;
26✔
211
    }
212

213
    return std::sqrt(variance_sum / static_cast<float>(sample_count));
1✔
214
}
215

216
// ========== Minimum ==========
217

218
float calculate_min_impl(std::vector<float> const & data, size_t start, size_t end) {
16✔
219
    if (data.empty() || start >= end || start >= data.size() || end > data.size()) {
16✔
220
        return std::numeric_limits<float>::quiet_NaN();
3✔
221
    }
222
    return calculate_min_impl(data.begin() + start, data.begin() + end);
13✔
223
}
224

225
float calculate_min(std::span<const float> data_span) {
24✔
226
    return calculate_min_impl(data_span.begin(), data_span.end());
24✔
227
}
228

229
float calculate_min(AnalogTimeSeries const & series) {
7✔
230
    auto const & data = series.getAnalogTimeSeries();
7✔
231
    return calculate_min_impl(data, 0, data.size());
7✔
232
}
233

234
float calculate_min(AnalogTimeSeries const & series, int64_t start, int64_t end) {
3✔
235
    auto const & data = series.getAnalogTimeSeries();
3✔
236
    if (start < 0 || end < 0 || start >= end) {
3✔
UNCOV
237
        return std::numeric_limits<float>::quiet_NaN();
×
238
    }
239
    return calculate_min_impl(data, static_cast<size_t>(start), static_cast<size_t>(end));
3✔
240
}
241

242
float calculate_min_in_time_range(AnalogTimeSeries const & series, TimeFrameIndex start_time, TimeFrameIndex end_time) {
20✔
243
    auto data_span = series.getDataInTimeFrameIndexRange(start_time, end_time);
20✔
244
    return calculate_min(data_span);
40✔
245
}
246

247
// ========== Maximum ==========
248

249
float calculate_max_impl(std::vector<float> const & data, size_t start, size_t end) {
16✔
250
    if (data.empty() || start >= end || start >= data.size() || end > data.size()) {
16✔
251
        return std::numeric_limits<float>::quiet_NaN();
3✔
252
    }
253
    return calculate_max_impl(data.begin() + start, data.begin() + end);
13✔
254
}
255

256
float calculate_max(std::span<const float> data_span) {
23✔
257
    return calculate_max_impl(data_span.begin(), data_span.end());
23✔
258
}
259

260
float calculate_max(AnalogTimeSeries const & series) {
7✔
261
    auto const & data = series.getAnalogTimeSeries();
7✔
262
    return calculate_max_impl(data, 0, data.size());
7✔
263
}
264

265
float calculate_max(AnalogTimeSeries const & series, int64_t start, int64_t end) {
3✔
266
    auto const & data = series.getAnalogTimeSeries();
3✔
267
    if (start < 0 || end < 0 || start >= end) {
3✔
UNCOV
268
        return std::numeric_limits<float>::quiet_NaN();
×
269
    }
270
    return calculate_max_impl(data, static_cast<size_t>(start), static_cast<size_t>(end));
3✔
271
}
272

273
float calculate_max_in_time_range(AnalogTimeSeries const & series, TimeFrameIndex start_time, TimeFrameIndex end_time) {
19✔
274
    auto data_span = series.getDataInTimeFrameIndexRange(start_time, end_time);
19✔
275
    return calculate_max(data_span);
38✔
276
}
277

278

279

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc