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

paulmthompson / WhiskerToolbox / 15939366926

28 Jun 2025 01:59AM UTC coverage: 71.74% (+0.1%) from 71.62%
15939366926

push

github

paulmthompson
convert mask data over to strong typed time

208 of 218 new or added lines in 16 files covered. (95.41%)

42 existing lines in 3 files now uncovered.

11340 of 15807 relevant lines covered (71.74%)

1133.35 hits per line

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

92.44
/src/WhiskerToolbox/DataManager/utils/DataAggregation/DataAggregation.cpp
1
#include "DataAggregation.hpp"
2

3
#include "AnalogTimeSeries/utils/statistics.hpp"
4

5
#include <algorithm>
6
#include <cmath>
7
#include <optional>
8
#include <stdexcept>
9

10
namespace DataAggregation {
11

12
int64_t calculateOverlapDuration(Interval const & a, Interval const & b) {
11✔
13
    const int64_t overlap_start = std::max(a.start, b.start);
11✔
14
    const int64_t overlap_end = std::min(a.end, b.end);
11✔
15

16
    if (overlap_start <= overlap_end) {
11✔
17
        return overlap_end - overlap_start + 1;
9✔
18
    }
19
    return 0;
2✔
20
}
21

22
int findOverlappingIntervalIndex(Interval const & target_interval,
18✔
23
                                 std::vector<Interval> const & reference_intervals,
24
                                 OverlapStrategy strategy) {
25
    std::vector<int> overlapping_indices;
18✔
26

27
    // Find all overlapping intervals using existing is_overlapping function
28
    for (size_t i = 0; i < reference_intervals.size(); ++i) {
63✔
29
        if (is_overlapping(target_interval, reference_intervals[i])) {
45✔
30
            overlapping_indices.push_back(static_cast<int>(i));
24✔
31
        }
32
    }
33

34
    if (overlapping_indices.empty()) {
18✔
35
        return -1;// No overlap found
1✔
36
    }
37

38
    switch (strategy) {
17✔
39
        case OverlapStrategy::First:
12✔
40
            return overlapping_indices.front();
12✔
41

42
        case OverlapStrategy::Last:
1✔
43
            return overlapping_indices.back();
1✔
44

45
        case OverlapStrategy::MaxOverlap: {
4✔
46
            int best_index = overlapping_indices[0];
4✔
47
            int64_t max_overlap = calculateOverlapDuration(target_interval, reference_intervals[best_index]);
4✔
48

49
            for (size_t i = 1; i < overlapping_indices.size(); ++i) {
6✔
50
                const int current_index = overlapping_indices[i];
2✔
51
                const int64_t current_overlap = calculateOverlapDuration(target_interval, reference_intervals[current_index]);
2✔
52

53
                if (current_overlap > max_overlap) {
2✔
54
                    max_overlap = current_overlap;
1✔
55
                    best_index = current_index;
1✔
56
                }
57
            }
58
            return best_index;
4✔
59
        }
60

61
        default:
×
62
            throw std::invalid_argument("Unknown overlap strategy");
×
63
    }
64
}
18✔
65

66
double applyTransformation(Interval const & interval,
115✔
67
                           TransformationConfig const & config,
68
                           std::map<std::string, std::vector<Interval>> const & reference_intervals,
69
                           std::map<std::string, std::shared_ptr<AnalogTimeSeries>> const & reference_analog,
70
                           std::map<std::string, std::shared_ptr<PointData>> const & reference_points) {
71
    switch (config.type) {
115✔
72
        case TransformationType::IntervalStart:
22✔
73
            return static_cast<double>(interval.start);
22✔
74

75
        case TransformationType::IntervalEnd:
13✔
76
            return static_cast<double>(interval.end);
13✔
77

78
        case TransformationType::IntervalDuration:
10✔
79
            return static_cast<double>(interval.end - interval.start + 1);
10✔
80

81
        case TransformationType::IntervalID: {
19✔
82
            auto it = reference_intervals.find(config.reference_data_key);
19✔
83
            if (it == reference_intervals.end()) {
19✔
84
                return std::nan("");// Reference data not found
1✔
85
            }
86

87
            const int overlap_index = findOverlappingIntervalIndex(interval, it->second, config.overlap_strategy);
18✔
88
            if (overlap_index == -1) {
18✔
89
                return std::nan("");// No overlap found
1✔
90
            }
91

92
            return static_cast<double>(overlap_index);// 0-based indexing
17✔
93
        }
94

95
        case TransformationType::IntervalCount: {
9✔
96
            auto it = reference_intervals.find(config.reference_data_key);
9✔
97
            if (it == reference_intervals.end()) {
9✔
98
                return std::nan("");// Reference data not found
1✔
99
            }
100

101
            // Count all overlapping intervals
102
            int count = 0;
8✔
103
            for (auto const & ref_interval: it->second) {
29✔
104
                if (is_overlapping(interval, ref_interval)) {
21✔
105
                    count++;
10✔
106
                }
107
            }
108

109
            return static_cast<double>(count);
8✔
110
        }
111

112
        case TransformationType::AnalogMean: {
14✔
113
            auto it = reference_analog.find(config.reference_data_key);
14✔
114
            if (it == reference_analog.end() || !it->second) {
14✔
115
                return std::nan("");// Reference data not found
2✔
116
            }
117

118
            return calculate_mean_in_time_range(*it->second, TimeFrameIndex(interval.start), TimeFrameIndex(interval.end));
12✔
119
        }
120

121
        case TransformationType::AnalogMin: {
8✔
122
            auto it = reference_analog.find(config.reference_data_key);
8✔
123
            if (it == reference_analog.end() || !it->second) {
8✔
124
                return std::nan("");// Reference data not found
×
125
            }
126

127
            return calculate_min_in_time_range(*it->second, TimeFrameIndex(interval.start), TimeFrameIndex(interval.end));
8✔
128
        }
129

130
        case TransformationType::AnalogMax: {
7✔
131
            auto it = reference_analog.find(config.reference_data_key);
7✔
132
            if (it == reference_analog.end() || !it->second) {
7✔
133
                return std::nan("");// Reference data not found
×
134
            }
135

136
            return calculate_max_in_time_range(*it->second, TimeFrameIndex(interval.start), TimeFrameIndex(interval.end));
7✔
137
        }
138

139
        case TransformationType::AnalogStdDev: {
3✔
140
            auto it = reference_analog.find(config.reference_data_key);
3✔
141
            if (it == reference_analog.end() || !it->second) {
3✔
142
                return std::nan("");// Reference data not found
×
143
            }
144

145
            return calculate_std_dev_in_time_range(*it->second, TimeFrameIndex(interval.start), TimeFrameIndex(interval.end));
3✔
146
        }
147

148
        case TransformationType::PointMeanX: {
6✔
149
            auto it = reference_points.find(config.reference_data_key);
6✔
150
            if (it == reference_points.end() || !it->second) {
6✔
151
                return std::nan("");// Reference data not found
2✔
152
            }
153

154
            double sum_x = 0.0;
4✔
155
            int count = 0;
4✔
156

157
            // Collect all X coordinates within the interval
158
            for (int64_t t = interval.start; t <= interval.end; ++t) {
15✔
159
                const auto& points = it->second->getPointsAtTime(TimeFrameIndex(t));
11✔
160
                for (const auto& point : points) {
21✔
161
                    sum_x += point.x;
10✔
162
                    count++;
10✔
163
                }
164
            }
165

166
            if (count == 0) {
4✔
167
                return std::nan("");// No points found in interval
1✔
168
            }
169

170
            return sum_x / static_cast<double>(count);
3✔
171
        }
172

173
        case TransformationType::PointMeanY: {
4✔
174
            auto it = reference_points.find(config.reference_data_key);
4✔
175
            if (it == reference_points.end() || !it->second) {
4✔
UNCOV
176
                return std::nan("");// Reference data not found
×
177
            }
178

179
            double sum_y = 0.0;
4✔
180
            int count = 0;
4✔
181

182
            // Collect all Y coordinates within the interval
183
            for (int64_t t = interval.start; t <= interval.end; ++t) {
15✔
184
                const auto& points = it->second->getPointsAtTime(TimeFrameIndex(static_cast<int>(t)));
11✔
185
                for (const auto& point : points) {
21✔
186
                    sum_y += point.y;
10✔
187
                    count++;
10✔
188
                }
189
            }
190

191
            if (count == 0) {
4✔
192
                return std::nan("");// No points found in interval
1✔
193
            }
194

195
            return sum_y / static_cast<double>(count);
3✔
196
        }
197

UNCOV
198
        default:
×
UNCOV
199
            throw std::invalid_argument("Unknown transformation type");
×
200
    }
201
}
202

203
std::vector<std::vector<double>> aggregateData(
31✔
204
        std::vector<Interval> const & row_intervals,
205
        std::vector<TransformationConfig> const & transformations,
206
        std::map<std::string, std::vector<Interval>> const & reference_intervals,
207
        std::map<std::string, std::shared_ptr<AnalogTimeSeries>> const & reference_analog,
208
        std::map<std::string, std::shared_ptr<PointData>> const & reference_points) {
209

210
    std::vector<std::vector<double>> result;
31✔
211
    result.reserve(row_intervals.size());
31✔
212

213
    for (auto const & interval: row_intervals) {
76✔
214
        std::vector<double> row;
45✔
215
        row.reserve(transformations.size());
45✔
216

217
        for (auto const & transformation: transformations) {
160✔
218
            const double value = applyTransformation(interval, transformation, reference_intervals, reference_analog, reference_points);
115✔
219
            row.push_back(value);
115✔
220
        }
221

222
        result.push_back(std::move(row));
45✔
223
    }
45✔
224

225
    return result;
31✔
UNCOV
226
}
×
227

228
}// namespace DataAggregation
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