This is an automated email from the ASF dual-hosted git repository.
Gerrrr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/otava.git
The following commit(s) were added to refs/heads/master by this push:
new cd5bc1e refactor(analysis): remove fill_missing method (#97) (#167)
cd5bc1e is described below
commit cd5bc1eedb2f4ecd851a76ef3211916774cb8afa
Author: adambernier <[email protected]>
AuthorDate: Tue Aug 11 19:19:25 2026 -0700
refactor(analysis): remove fill_missing method (#97) (#167)
---
otava/analysis.py | 20 --------------------
otava/series.py | 4 ----
tests/analysis_test.py | 13 -------------
tests/series_test.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 49 insertions(+), 39 deletions(-)
diff --git a/otava/analysis.py b/otava/analysis.py
index 21bb33c..54cfdb9 100644
--- a/otava/analysis.py
+++ b/otava/analysis.py
@@ -104,26 +104,6 @@ class TTestSignificanceTester(SignificanceTester):
return ChangePoint.from_candidate(candidate, stats)
-def fill_missing(data: Sequence[SupportsFloat]):
- """
- Forward-fills None occurrences with nearest previous non-None values.
- Initial None values are back-filled with the nearest future non-None value.
-
- TODO: Remove this.
- """
- prev = None
- for i in range(len(data)):
- if data[i] is None and prev is not None:
- data[i] = prev
- prev = data[i]
-
- prev = None
- for i in reversed(range(len(data))):
- if data[i] is None and prev is not None:
- data[i] = prev
- prev = data[i]
-
-
def merge(
change_points: TtestCPList, series: Sequence[SupportsFloat], max_pvalue:
float, min_magnitude: float
) -> TtestCPList:
diff --git a/otava/series.py b/otava/series.py
index 23f4af6..e3d9e82 100644
--- a/otava/series.py
+++ b/otava/series.py
@@ -24,7 +24,6 @@ from otava.analysis import (
TTestStats,
compute_change_points,
compute_change_points_orig,
- fill_missing,
)
from otava.change_point_divisive.base import (
ChangePoint,
@@ -166,7 +165,6 @@ class AnalyzedSeries:
weak_change_points = ChangePointsByMetric()
for metric in series.data.keys():
values = series.data[metric].copy()
- fill_missing(values)
if options.orig_edivisive:
change_points, _ = compute_change_points_orig(
values,
@@ -327,8 +325,6 @@ class AnalyzedSeries:
attributes=self.__series.attributes_at(cp.index),
)
)
- # TODO: Remove this. It should not be a requirement that metrics
have the same history.
- fill_missing(self.__series.data[metric])
r = ChangePointsByMetric.from_dict(result)
w = ChangePointsByMetric.from_dict(weak_change_points)
diff --git a/tests/analysis_test.py b/tests/analysis_test.py
index ef548a7..6c6df88 100644
--- a/tests/analysis_test.py
+++ b/tests/analysis_test.py
@@ -21,23 +21,10 @@ from otava.analysis import (
TTestSignificanceTester,
compute_change_points,
compute_change_points_orig,
- fill_missing,
)
from otava.change_point_divisive.base import CandidateChangePoint
-def test_fill_missing():
- list1 = [None, None, 1.0, 1.2, 0.5]
- list2 = [1.0, 1.2, None, None, 4.3]
- list3 = [1.0, 1.2, 0.5, None, None]
- fill_missing(list1)
- fill_missing(list2)
- fill_missing(list3)
- assert list1 == [1.0, 1.0, 1.0, 1.2, 0.5]
- assert list2 == [1.0, 1.2, 1.2, 1.2, 4.3]
- assert list3 == [1.0, 1.2, 0.5, 0.5, 0.5]
-
-
def test_single_series():
series = [
1.02,
diff --git a/tests/series_test.py b/tests/series_test.py
index 28ad2a9..cab900a 100644
--- a/tests/series_test.py
+++ b/tests/series_test.py
@@ -149,11 +149,11 @@ def test_div_by_zero():
def test_change_point_detection_performance():
- timestamps = range(0, 90) # 3 months of data
+ timestamps = range(90) # 3 months of data
series = [random() for x in timestamps]
start_time = time.process_time()
- for run in range(0, 10): # 10 series
+ for run in range(10): # 10 series
test = Series(
"test",
branch=None,
@@ -360,3 +360,50 @@ def test_orig_edivisive():
# assert len(change_points) == 2
# assert change_points[0].index == 4
# assert change_points[1].index == 6
+
+
+def test_series_sparse_commits():
+ """Verify series processes sparse commit sequences without artificial
padding."""
+ name = "sparse_commit_test"
+ branch = "main"
+ time_points = [1000, 1001, 1005, 1008]
+ metrics = {}
+ data = {"latency": [100.5, 101.0, 105.2, 98.7]}
+ # Match length of attributes list to len(time_points) == 4
+ attributes = {"env": ["ci", "ci", "ci", "ci"]}
+
+ series = Series(name, branch, time_points, metrics, data, attributes)
+
+ assert len(series.time) == 4
+ assert series.time == [1000, 1001, 1005, 1008]
+ assert series.data["latency"] == [100.5, 101.0, 105.2, 98.7]
+
+
+def test_series_irregular_timestamps():
+ """Verify change detection operates on unpadded time intervals."""
+ name = "irregular_ts_test"
+ branch = "main"
+ time_points = [1600000000, 1600000010, 1600000300, 1600003600]
+ metrics = {}
+ data = {"duration": [50.0, 51.0, 50.5, 95.0]}
+ attributes = {}
+
+ series = Series(name, branch, time_points, metrics, data, attributes)
+
+ assert len(series.time) == 4
+ assert series.data["duration"] == [50.0, 51.0, 50.5, 95.0]
+
+
+def test_series_raw_initialization():
+ """Verify Series initialization operates directly on raw unpadded input
data."""
+ name = "raw_init_test"
+ branch = "main"
+ time_points = [1, 2, 3]
+ metrics = {}
+ data = {"throughput": [10.0, 12.0, 11.5]}
+ attributes = {}
+
+ series = Series(name, branch, time_points, metrics, data, attributes)
+
+ assert len(series.time) == 3
+ assert series.data["throughput"] == [10.0, 12.0, 11.5]