From d0054da758c77570352ebd2cb34418c9c8a1a1d0 Mon Sep 17 00:00:00 2001 From: Malte Schaaf Date: Wed, 14 Jan 2026 09:15:00 +0100 Subject: [PATCH 1/3] Fix: Use bisect_left for calculating min_index in resampling function - Changed from `bisect` to `bisect_left` to ensure the correct lower bound index is calculated for relevant samples. - This ensures that the minimum relevant timestamp is included in the range of relevant samples. - Improves accuracy when determining the slice of the buffer for resampling. Signed-off-by: Malte Schaaf --- src/frequenz/sdk/timeseries/_resampling/_resampler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frequenz/sdk/timeseries/_resampling/_resampler.py b/src/frequenz/sdk/timeseries/_resampling/_resampler.py index 81471fc0d..d6d971b36 100644 --- a/src/frequenz/sdk/timeseries/_resampling/_resampler.py +++ b/src/frequenz/sdk/timeseries/_resampling/_resampler.py @@ -9,7 +9,7 @@ import itertools import logging import math -from bisect import bisect +from bisect import bisect, bisect_left from collections import deque from datetime import datetime, timedelta, timezone from typing import assert_never @@ -437,7 +437,7 @@ def resample(self, timestamp: datetime) -> Sample[Quantity]: ) minimum_relevant_timestamp = timestamp - period * conf.max_data_age_in_periods - min_index = bisect( + min_index = bisect_left( self._buffer, minimum_relevant_timestamp, key=lambda s: s[0], From ce0329671f04702d60c8078436b28067a563ded9 Mon Sep 17 00:00:00 2001 From: Malte Schaaf Date: Wed, 14 Jan 2026 09:18:12 +0100 Subject: [PATCH 2/3] Change default value of max_data_age_in_periods in ResamplerConfig - Updated the default value of `max_data_age_in_periods` from 3.0 to 1.0. - The previous default caused the resampler to always include the past 3 periods, which is not ideal as a default behavior. - The new default ensures that only the most recent period is resampled unless explicitly configured. Signed-off-by: Malte Schaaf --- src/frequenz/sdk/timeseries/_resampling/_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frequenz/sdk/timeseries/_resampling/_config.py b/src/frequenz/sdk/timeseries/_resampling/_config.py index 047892803..f265e792e 100644 --- a/src/frequenz/sdk/timeseries/_resampling/_config.py +++ b/src/frequenz/sdk/timeseries/_resampling/_config.py @@ -94,7 +94,7 @@ class ResamplerConfig: It must be a positive time span. """ - max_data_age_in_periods: float = 3.0 + max_data_age_in_periods: float = 1.0 """The maximum age a sample can have to be considered *relevant* for resampling. Expressed in number of periods, where period is the `resampling_period` @@ -102,7 +102,7 @@ class ResamplerConfig: the *input sampling period* if we are upsampling (input period bigger than the resampling period). - It must be bigger than 1.0. + It must be bigger or equal than 1.0. Example: If `resampling_period` is 3 seconds, the input sampling period is From 83c677425549e294366817259c6ff44d185db74d Mon Sep 17 00:00:00 2001 From: Malte Schaaf Date: Wed, 14 Jan 2026 09:23:36 +0100 Subject: [PATCH 3/3] Fix: Adjust timestamp handling in resampling to represent the start of the period - Changed `sample_time` in the `_ResamplingHelper`'s `resampling` function to `timestamp - conf.resampling_period`. - This ensures that the timestamp of each resampled data point represents the beginning of the resampling period instead of the end. - Improves clarity and consistency for interpreting resampled data timestamps. Signed-off-by: Malte Schaaf --- src/frequenz/sdk/timeseries/_resampling/_resampler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frequenz/sdk/timeseries/_resampling/_resampler.py b/src/frequenz/sdk/timeseries/_resampling/_resampler.py index d6d971b36..e7182fd90 100644 --- a/src/frequenz/sdk/timeseries/_resampling/_resampler.py +++ b/src/frequenz/sdk/timeseries/_resampling/_resampler.py @@ -458,7 +458,8 @@ def resample(self, timestamp: datetime) -> Sample[Quantity]: if relevant_samples else None ) - return Sample(timestamp, None if value is None else Quantity(value)) + sample_time = timestamp - conf.resampling_period + return Sample(sample_time, None if value is None else Quantity(value)) def _log_no_relevant_samples( self, minimum_relevant_timestamp: datetime, timestamp: datetime