def _update(self, t): """ Ingress a new data point and update the matrix profile and matrix profile indices without egressing the oldest data point """ self._n = self._T.shape[0] l = self._n - self._m + 1 T_new = np.append(self._T, t) QT_new = np.empty(self._QT.shape[0] + 1) S = T_new[l:] t_drop = T_new[l - 1] if np.isfinite(t): self._T_isfinite = np.append(self._T_isfinite, True) else: self._T_isfinite = np.append(self._T_isfinite, False) t = 0 T_new[-1] = 0 S[-1] = 0 self._T_subseq_isfinite = np.append( self._T_subseq_isfinite, np.all(self._T_isfinite[-self._m:])) QT_new[1:] = self._QT[:l] - T_new[:l] * t_drop + T_new[self._m:] * t QT_new[0] = np.sum(T_new[:self._m] * S[:self._m]) Q_squared = np.sum(S * S) self._T_squared = np.append( self._T_squared, np.sum(T_new[-self._m:] * T_new[-self._m:])) D = core._mass_absolute(Q_squared, self._T_squared, QT_new) D[~self._T_subseq_isfinite] = np.inf if np.any(~self._T_isfinite[-self._m:]): D[:] = np.inf core.apply_exclusion_zone(D, D.shape[0] - 1, self._excl_zone) update_idx = np.argwhere(D[:l] < self._P[:l]).flatten() self._I[update_idx] = l self._P[update_idx] = D[update_idx] I_last = np.argmin(D) if np.isinf(D[I_last]): I_new = np.append(self._I, -1) P_new = np.append(self._P, np.inf) else: I_new = np.append(self._I, I_last) P_new = np.append(self._P, D[I_last]) left_I_new = np.append(self._left_I, I_last) left_P_new = np.append(self._left_P, D[I_last]) self._T = T_new self._P = P_new self._I = I_new self._left_I = left_I_new self._left_P = left_P_new self._QT = QT_new
def test_apply_exclusion_zone(): T = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=float) left = np.empty(T.shape) right = np.empty(T.shape) exclusion_zone = 2 for i in range(T.shape[0]): left[:] = T[:] naive.apply_exclusion_zone(left, i, exclusion_zone) right[:] = T[:] core.apply_exclusion_zone(right, i, exclusion_zone) naive.replace_inf(left) naive.replace_inf(right) npt.assert_array_equal(left, right)
def test_apply_exclusion_zone(): T = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=float) ref = np.empty(T.shape) comp = np.empty(T.shape) exclusion_zone = 2 for i in range(T.shape[0]): ref[:] = T[:] naive.apply_exclusion_zone(ref, i, exclusion_zone) comp[:] = T[:] core.apply_exclusion_zone(comp, i, exclusion_zone) naive.replace_inf(ref) naive.replace_inf(comp) npt.assert_array_equal(ref, comp)
def test_apply_exclusion_zone_bool(): T = np.ones(10, dtype=bool) ref = np.empty(T.shape, dtype=bool) comp = np.empty(T.shape, dtype=bool) exclusion_zone = 2 for i in range(T.shape[0]): ref[:] = T[:] naive.apply_exclusion_zone(ref, i, exclusion_zone, False) comp[:] = T[:] core.apply_exclusion_zone(comp, i, exclusion_zone, False) naive.replace_inf(ref) naive.replace_inf(comp) npt.assert_array_equal(ref, comp)
def test_apply_exclusion_zone(): T = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=float) left = np.empty(T.shape) right = np.empty(T.shape) exclusion_zone = 2 for i in range(T.shape[0]): left[:] = T[:] for j in range(max(i - exclusion_zone, 0), min(i + exclusion_zone + 1, T.shape[0])): left[j] = np.inf right[:] = T[:] core.apply_exclusion_zone(right, i, exclusion_zone) utils.replace_inf(left) utils.replace_inf(right) npt.assert_array_equal(left, right)
def test_apply_exclusion_zone_multidimensional(): T = np.array( [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]], dtype=np.float64, ) ref = np.empty(T.shape, dtype=np.float64) comp = np.empty(T.shape, dtype=np.float64) exclusion_zone = 2 for i in range(T.shape[1]): ref[:, :] = T[:, :] naive.apply_exclusion_zone(ref, i, exclusion_zone, np.inf) comp[:, :] = T[:, :] core.apply_exclusion_zone(comp, i, exclusion_zone, np.inf) naive.replace_inf(ref) naive.replace_inf(comp) npt.assert_array_equal(ref, comp)
def _update(self, t): """ Ingress a new data point and update the matrix profile and matrix profile indices without egressing the oldest data point """ self._n = self._T.shape[0] l = self._n - self._m + 1 T_new = np.append(self._T, t) QT_new = np.empty(self._QT.shape[0] + 1) S = T_new[l:] t_drop = T_new[l - 1] if np.isfinite(t): self._T_isfinite = np.append(self._T_isfinite, True) else: self._T_isfinite = np.append(self._T_isfinite, False) t = 0 T_new[-1] = 0 S[-1] = 0 T_subseq_isfinite = np.all(core.rolling_window(self._T_isfinite, self._m), axis=1) for j in range(l, 0, -1): QT_new[j] = (self._QT[j - 1] - T_new[j - 1] * t_drop + T_new[j + self._m - 1] * t) QT_new[0] = 0 for j in range(self._m): QT_new[0] = QT_new[0] + T_new[j] * S[j] Q_squared = np.sum(S * S) T_squared = np.sum(core.rolling_window(T_new * T_new, self._m), axis=1) D = core._mass_absolute(Q_squared, T_squared, QT_new) D[~T_subseq_isfinite] = np.inf if np.any(~self._T_isfinite[-self._m:]): D[:] = np.inf core.apply_exclusion_zone(D, D.shape[0] - 1, self._excl_zone) for j in range(l): if D[j] < self._P[j]: self._I[j] = l self._P[j] = D[j] I_last = np.argmin(D) if np.isinf(D[I_last]): I_new = np.append(self._I, -1) P_new = np.append(self._P, np.inf) else: I_new = np.append(self._I, I_last) P_new = np.append(self._P, D[I_last]) left_I_new = np.append(self._left_I, I_last) left_P_new = np.append(self._left_P, D[I_last]) self._T = T_new self._P = P_new self._I = I_new self._left_I = left_I_new self._left_P = left_P_new self._QT = QT_new
def _update_egress(self, t): """ Ingress a new data point, egress the oldest data point, and update the matrix profile and matrix profile indices """ self._n = self._T.shape[0] l = self._n - self._m + 1 - 1 # Subtract 1 due to egress self._T[:] = np.roll(self._T, -1) self._T[-1] = t self._n_appended += 1 self._QT[:] = np.roll(self._QT, -1) S = self._T[l:] t_drop = self._T[l - 1] self._T_isfinite[:] = np.roll(self._T_isfinite, -1) self._I[:] = np.roll(self._I, -1) self._P[:] = np.roll(self._P, -1) self._left_I[:] = np.roll(self._left_I, -1) self._left_P[:] = np.roll(self._left_P, -1) if np.isfinite(t): self._T_isfinite[-1] = True else: self._T_isfinite[-1] = False t = 0 self._T[-1] = 0 S[-1] = 0 T_subseq_isfinite = np.all(core.rolling_window(self._T_isfinite, self._m), axis=1) for j in range(l, 0, -1): self._QT_new[j] = (self._QT[j - 1] - self._T[j - 1] * t_drop + self._T[j + self._m - 1] * t) self._QT_new[0] = 0 for j in range(self._m): self._QT_new[0] = self._QT_new[0] + self._T[j] * S[j] Q_squared = np.sum(S * S) T_squared = np.sum(core.rolling_window(self._T * self._T, self._m), axis=1) D = core._mass_absolute(Q_squared, T_squared, self._QT_new) D[~T_subseq_isfinite] = np.inf if np.any(~self._T_isfinite[-self._m:]): D[:] = np.inf core.apply_exclusion_zone(D, D.shape[0] - 1, self._excl_zone) for j in range(D.shape[0]): if D[j] < self._P[j]: self._I[j] = D.shape[0] + self._n_appended - 1 self._P[j] = D[j] I_last = np.argmin(D) if np.isinf(D[I_last]): self._I[-1] = -1 self._P[-1] = np.inf else: self._I[-1] = I_last + self._n_appended self._P[-1] = D[I_last] self._left_I[-1] = I_last + self._n_appended self._left_P[-1] = D[I_last] self._QT[:] = self._QT_new
def update(self, t): """ Append a single new data point, `t`, to the existing time series `T` and update the matrix profile and matrix profile indices. Parameters ---------- t : float A single new data point to be appended to `T` Notes ----- `DOI: 10.1007/s10618-017-0519-9 \ <https://www.cs.ucr.edu/~eamonn/MP_journal.pdf>`__ See Table V Note that line 11 is missing an important `sqrt` operation! """ n = self._T.shape[0] l = n - self._m + 1 T_new = np.append(self._T, t) QT_new = np.empty(self._QT.shape[0] + 1) S = T_new[l:] t_drop = T_new[l - 1] if np.isinf(t) or np.isnan(t): self._illegal = np.append(self._illegal, True) t = 0 T_new[-1] = 0 S[-1] = 0 else: self._illegal = np.append(self._illegal, False) if np.any(self._illegal[-self._m:]): μ_Q = np.inf σ_Q = np.nan else: μ_Q, σ_Q = core.compute_mean_std(S, self._m) μ_Q = μ_Q[0] σ_Q = σ_Q[0] M_T_new = np.append(self._M_T, μ_Q) Σ_T_new = np.append(self._Σ_T, σ_Q) for j in range(l, 0, -1): QT_new[j] = (self._QT[j - 1] - T_new[j - 1] * t_drop + T_new[j + self._m - 1] * t) QT_new[0] = 0 for j in range(self._m): QT_new[0] = QT_new[0] + T_new[j] * S[j] D = core.calculate_distance_profile(self._m, QT_new, μ_Q, σ_Q, M_T_new, Σ_T_new) if np.any(self._illegal[-self._m:]): D[:] = np.inf core.apply_exclusion_zone(D, D.shape[0] - 1, self._excl_zone) for j in range(l): if D[j] < self._P[j]: self._I[j] = l self._P[j] = D[j] I_last = np.argmin(D) if np.isinf(D[I_last]): I_new = np.append(self._I, -1) P_new = np.append(self._P, np.inf) else: I_new = np.append(self._I, I_last) P_new = np.append(self._P, D[I_last]) left_I_new = np.append(self._left_I, I_last) left_P_new = np.append(self._left_P, D[I_last]) self._T = T_new self._P = P_new self._I = I_new self._left_I = left_I_new self._left_P = left_P_new self._QT = QT_new self._M_T = M_T_new self._Σ_T = Σ_T_new
def _update_egress(self, t): """ Ingress a new data point, egress the oldest data point, and update the matrix profile and matrix profile indices """ self._n = self._T.shape[0] l = self._n - self._m + 1 - 1 # Subtract 1 due to egress self._T[:-1] = self._T[1:] self._T[-1] = t self._n_appended += 1 self._QT[:-1] = self._QT[1:] S = self._T[l:] t_drop = self._T[l - 1] self._T_isfinite[:-1] = self._T_isfinite[1:] self._T_subseq_isfinite[:-1] = self._T_subseq_isfinite[1:] self._T_squared[:-1] = self._T_squared[1:] self._I[:-1] = self._I[1:] self._P[:-1] = self._P[1:] self._left_I[:-1] = self._left_I[1:] self._left_P[:-1] = self._left_P[1:] if np.isfinite(t): self._T_isfinite[-1] = True else: self._T_isfinite[-1] = False t = 0 self._T[-1] = 0 S[-1] = 0 self._T_subseq_isfinite[-1] = np.all(self._T_isfinite[-self._m:]) self._QT_new[ 1:] = self._QT[:l] - self._T[:l] * t_drop + self._T[self._m:] * t self._QT_new[0] = np.sum(self._T[:self._m] * S[:self._m]) Q_squared = np.sum(S * S) self._T_squared[-1] = np.sum(self._T[-self._m:] * self._T[-self._m:]) D = core._mass_absolute(Q_squared, self._T_squared, self._QT_new) D[~self._T_subseq_isfinite] = np.inf if np.any(~self._T_isfinite[-self._m:]): D[:] = np.inf core.apply_exclusion_zone(D, D.shape[0] - 1, self._excl_zone) update_idx = np.argwhere(D < self._P).flatten() self._I[update_idx] = D.shape[ 0] + self._n_appended - 1 # D.shape[0] is base-1 self._P[update_idx] = D[update_idx] I_last = np.argmin(D) if np.isinf(D[I_last]): self._I[-1] = -1 self._P[-1] = np.inf else: self._I[-1] = I_last + self._n_appended self._P[-1] = D[I_last] self._left_I[-1] = I_last + self._n_appended self._left_P[-1] = D[I_last] self._QT[:] = self._QT_new