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]

Reply via email to