9
9
10
10
from time_stream .period import Period
11
11
from time_stream .base import TimeSeries
12
- from time_stream .aggregation import AggregationFunction , Max , Mean , Min
12
+ from time_stream .aggregation import AggregationFunction , Max , Mean , Min , MeanSum , Sum
13
13
14
14
15
15
def generate_time_series (resolution : Period , periodicity : Period , length : int , missing_data : bool = False ) -> TimeSeries :
@@ -129,31 +129,31 @@ def setUp(self):
129
129
self .mock_ts .time_name = "timestamp"
130
130
131
131
@parameterized .expand ([
132
- ("mean" , Mean ), ("min" , Min ), ("max" , Max )
132
+ ("mean" , Mean ), ("min" , Min ), ("max" , Max ), ( "mean_sum" , MeanSum ), ( "sum" , Sum )
133
133
])
134
134
def test_get_with_string (self , get_input , expected ):
135
135
"""Test AggregationFunction.get() with string input."""
136
136
agg = AggregationFunction .get (get_input )
137
137
self .assertIsInstance (agg , expected )
138
138
139
139
@parameterized .expand ([
140
- (Mean , Mean ), (Min , Min ), (Max , Max )
140
+ (Mean , Mean ), (Min , Min ), (Max , Max ), ( MeanSum , MeanSum ), ( Sum , Sum )
141
141
])
142
142
def test_get_with_class (self , get_input , expected ):
143
143
"""Test AggregationFunction.get() with class input."""
144
144
agg = AggregationFunction .get (get_input )
145
145
self .assertIsInstance (agg , expected )
146
146
147
147
@parameterized .expand ([
148
- (Mean (), Mean ), (Min (), Min ), (Max (), Max )
148
+ (Mean (), Mean ), (Min (), Min ), (Max (), Max ), ( MeanSum (), MeanSum ), ( Sum (), Sum )
149
149
])
150
150
def test_get_with_instance (self , get_input , expected ):
151
151
"""Test AggregationFunction.get() with instance input."""
152
152
agg = AggregationFunction .get (get_input )
153
153
self .assertIsInstance (agg , expected )
154
154
155
155
@parameterized .expand ([
156
- "Mean" , "MIN" , "mAx" , "123"
156
+ "Mean" , "MIN" , "mAx" , "123" , "meansum" , "sUm"
157
157
])
158
158
def test_get_with_invalid_string (self , get_input ):
159
159
"""Test AggregationFunction.get() with invalid string."""
@@ -232,7 +232,14 @@ class TestSimpleAggregations(unittest.TestCase):
232
232
233
233
("hourly_to_daily_min" , ts_PT1H_2days , Min , P1D , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )],
234
234
[24 , 24 ], {"value" : [0 , 24 ]}, [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )]),
235
+
236
+ ("hourly_to_daily_mean_sum" , ts_PT1H_2days , MeanSum , P1D , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )],
237
+ [24 , 24 ], {"value" : [276 , 852 ]}, None ),
238
+
239
+ ("hourly_to_daily_sum" , ts_PT1H_2days , Sum , P1D , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )],
240
+ [24 , 24 ], {"value" : [276 , 852 ]}, None ),
235
241
])
242
+
236
243
def test_microsecond_to_microsecond (
237
244
self , _ , input_ts , aggregator , target_period , column , timestamps , counts , values , timestamps_of
238
245
):
@@ -251,6 +258,12 @@ def test_microsecond_to_microsecond(
251
258
252
259
("hourly_to_monthly_min" , ts_PT1H_2month , Min , P1M , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 2 , 1 )],
253
260
[744 , 672 ], {"value" : [0 , 744 ]}, [datetime (2025 , 1 , 1 ), datetime (2025 , 2 , 1 )]),
261
+
262
+ ("hourly_to_monthly_mean_sum" , ts_PT1H_2month , MeanSum , P1M , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 2 , 1 )],
263
+ [744 , 672 ], {"value" : [276396 , 725424 ]}, None ),
264
+
265
+ ("hourly_to_monthly_sum" , ts_PT1H_2month , Sum , P1M , "value" , [datetime (2025 , 1 , 1 ), datetime (2025 , 2 , 1 )],
266
+ [744 , 672 ], {"value" : [276396 , 725424 ]}, None ),
254
267
])
255
268
def test_microsecond_to_month (
256
269
self , _ , input_ts , aggregator , target_period , column , timestamps , counts , values , timestamps_of
@@ -269,6 +282,12 @@ def test_microsecond_to_month(
269
282
270
283
("monthly_to_yearly_min" , ts_P1M_2years , Min , P1Y , "value" , [datetime (2025 , 1 , 1 ), datetime (2026 , 1 , 1 )],
271
284
[12 , 12 ], {"value" : [0 , 12 ]}, [datetime (2025 , 1 , 1 ), datetime (2026 , 1 , 1 )]),
285
+
286
+ ("monthly_to_yearly_mean_sum" , ts_P1M_2years , MeanSum , P1Y , "value" , [datetime (2025 , 1 , 1 ), datetime (2026 , 1 , 1 )],
287
+ [12 , 12 ], {"value" : [66 , 210 ]}, None ),
288
+
289
+ ("monthly_to_yearly_sum" , ts_P1M_2years , Sum , P1Y , "value" , [datetime (2025 , 1 , 1 ), datetime (2026 , 1 , 1 )],
290
+ [12 , 12 ], {"value" : [66 , 210 ]}, None ),
272
291
])
273
292
def test_month_to_month (
274
293
self , _ , input_ts , aggregator , target_period , column , timestamps , counts , values , timestamps_of
@@ -292,6 +311,14 @@ def test_month_to_month(
292
311
[datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )], [24 , 24 ],
293
312
{"value" : [0 , 24 ], "value_plus1" : [1 , 25 ], "value_times2" : [0 , 48 ]},
294
313
[datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )]),
314
+
315
+ ("multi_column_mean_sum" , ts_PT1H_2days , MeanSum , P1D , ["value" , "value_plus1" , "value_times2" ],
316
+ [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )], [24 , 24 ],
317
+ {"value" : [276 , 852 ], "value_plus1" : [300 , 876 ], "value_times2" : [552 , 1704 ]}, None ),
318
+
319
+ ("multi_column_sum" , ts_PT1H_2days , Sum , P1D , ["value" , "value_plus1" , "value_times2" ],
320
+ [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )], [24 , 24 ],
321
+ {"value" : [276 , 852 ], "value_plus1" : [300 , 876 ], "value_times2" : [552 , 1704 ]}, None ),
295
322
])
296
323
def test_multi_column (
297
324
self , _ , input_ts , aggregator , target_period , column , timestamps , counts , values , timestamps_of
@@ -492,6 +519,27 @@ def test_missing_criteria_available(self, _, valid, criteria):
492
519
assert_frame_equal (result .df , expected_df , check_dtype = False , check_column_order = False , check_exact = False )
493
520
494
521
522
+ class TestMeanSumWithMissingData (unittest .TestCase ):
523
+ """Tests the MeanSum aggregation with time series that has missing data."""
524
+ def setUp (self ):
525
+ self .input_ts = ts_PT1H_2days_missing
526
+ self .target_period = P1D
527
+ self .column = "value"
528
+ self .timestamps = [datetime (2025 , 1 , 1 ), datetime (2025 , 1 , 2 )]
529
+ self .expected_counts = [24 , 24 ]
530
+ self .actual_counts = [20 , 21 ]
531
+ self .values = {"value" : [280.8 , 853.71 ]}
532
+
533
+ def test_mean_sum_with_missing_data (self ):
534
+ """Test MeanSum aggregation with time series that has missing data."""
535
+ expected_df = generate_expected_df (
536
+ self .timestamps , MeanSum , self .column , self .values , self .expected_counts ,
537
+ self .actual_counts
538
+ )
539
+ result = MeanSum ().apply (self .input_ts , self .target_period , self .column )
540
+ assert_frame_equal (result .df , expected_df , check_dtype = False , check_column_order = False , check_exact = False )
541
+
542
+
495
543
class TestPaddedAggregations (unittest .TestCase ):
496
544
"""Tests that aggregations work as expected with padded time series."""
497
545
0 commit comments