Skip to content

Commit 28b4f9f

Browse files
Refactored to use ChangePointDetector and JacobianSignal
1 parent d8e9d14 commit 28b4f9f

5 files changed

Lines changed: 140 additions & 547 deletions

File tree

src/change_point_detector.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def _find_best_split(self, start: int, end: int, min_segment_length: int = 1) ->
104104
return best_k, reduction
105105

106106
def fit(self, max_change_point: int, min_fractional_reduction: float,
107-
min_segment_length: int = 1) -> None:
107+
min_subsequence_length: int = 1) -> None:
108108
"""
109109
Find change points based on the provided stopping criteria.
110110
@@ -126,7 +126,7 @@ def fit(self, max_change_point: int, min_fractional_reduction: float,
126126
# largest reduction first so the fixed-K budget is spent optimally.
127127
# Each entry: (-reduction, start, end, k) — negated for min-heap ordering.
128128
heap: List[Tuple[float, int, int, int]] = []
129-
k_init, red_init = self._find_best_split(0, self.length, min_segment_length)
129+
k_init, red_init = self._find_best_split(0, self.length, min_subsequence_length)
130130
if red_init > 0:
131131
heapq.heappush(heap, (-red_init, 0, self.length, k_init))
132132

@@ -135,7 +135,7 @@ def fit(self, max_change_point: int, min_fractional_reduction: float,
135135

136136
while heap and len(change_points) < max_change_point:
137137
neg_red, start, end, k = heapq.heappop(heap)
138-
if end - start < 2 * min_segment_length:
138+
if end - start < 2 * min_subsequence_length:
139139
continue # Skip segments that are too short to split into two min_segment_length pieces
140140
reduction = -neg_red
141141

@@ -146,8 +146,8 @@ def fit(self, max_change_point: int, min_fractional_reduction: float,
146146
total_abs_reduction += reduction
147147

148148
for seg_start, seg_end in ((start, k), (k, end)):
149-
if seg_end - seg_start >= 2 * min_segment_length:
150-
k_sub, red_sub = self._find_best_split(seg_start, seg_end, min_segment_length)
149+
if seg_end - seg_start >= 2 * min_subsequence_length:
150+
k_sub, red_sub = self._find_best_split(seg_start, seg_end, min_subsequence_length)
151151
if red_sub > 0:
152152
heapq.heappush(heap, (-red_sub, seg_start, seg_end, k_sub))
153153

src/jacobian_signal.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,36 @@ def _denan(self, jacobian_arr: np.ndarray) -> np.ndarray:
7171
"""Replace NaN entries in a Jacobian with zeros."""
7272
return np.where(np.isnan(jacobian_arr), 0.0, jacobian_arr)
7373

74-
def makeDetector(self, max_change_point: int,
74+
def fit(self, max_change_point: int,
7575
min_fractional_reduction: float,
76-
min_segment_length: int) -> ChangePointDetector:
77-
"""fit() step 4. signal_arr[k] is the signal for split index k+1
78-
(the time-grid index at which a new segment would begin).
76+
min_subsequence_length: int) -> ChangePointDetector:
77+
"""
78+
Fit a ChangePointDetector to the Jacobian signal.
79+
80+
Parameters
81+
----------
82+
max_change_point : int
83+
The maximum number of change points to detect.
84+
min_fractional_reduction : float
85+
The minimum fractional reduction in the adjusted sum of squares required to accept a change point.
86+
min_subsequence_length : int
87+
The minimum length of a subsequence between change points.
7988
80-
Returns a sorted (by time) list of accepted interior split indices.
89+
Returns
90+
-------
91+
ChangePointDetector
92+
A fitted ChangePointDetector object containing the detected change points and associated information.
8193
"""
8294
detector = ChangePointDetector(self.signal_arr)
8395
detector.fit(max_change_point=max_change_point,
8496
min_fractional_reduction=min_fractional_reduction,
85-
min_segment_length=min_segment_length)
97+
min_subsequence_length=min_subsequence_length)
8698
return detector
8799

88100
def plot(self,
89101
max_change_point: int,
90102
min_fractional_reduction: float,
91-
min_segment_length: int,
103+
min_subsequence_length: int,
92104
**plt_kwargs: Any) -> List[PlotOptions]:
93105
"""
94106
Two-panel plot.
@@ -108,9 +120,9 @@ def plot(self,
108120
Wraps the figure and the bottom axes. Call ``plt.show()`` or
109121
``po.fig.savefig(...)`` on the returned object as needed.
110122
"""
111-
detector = self.makeDetector(max_change_point=max_change_point,
123+
detector = self.fit(max_change_point=max_change_point,
112124
min_fractional_reduction=min_fractional_reduction,
113-
min_segment_length=min_segment_length)
125+
min_subsequence_length=min_subsequence_length)
114126
change_point_idxs = [i.splice_start for i in detector.subsequences[1:]]
115127
change_point_times = [self._timecourse_df.index[i+1]
116128
for i in change_point_idxs]

0 commit comments

Comments
 (0)