async def test_cluster_within_one_datablock_gives_NEW(): sa = SmartAlert(threshold=0, timeout=600) d0 = DataBlock(ts=np.arange(11),zs=[0,0,0,1,1,1,1,1,0,0]) await sa.put(d0) assert await get(sa) == ("NEW",DataBlock(id=1,ts=range(3,9),zs=[1,1,1,1,1])) with pytest.raises(asyncio.TimeoutError): await get(sa)
async def test_two_clusters_in_one_block(): sa = SmartAlert(threshold=0, timeout=600) d0 = DataBlock(ts=np.arange(0, 11),zs=[0,1,1,1,0,0,2,2,2,0]) await sa.put(d0) assert await get(sa) == ("NEW",DataBlock(id=1,ts=[6,7,8,9], zs=[2,2,2])) assert await get(sa) == ("NEW",DataBlock(id=2,ts=[1,2,3,4], zs=[1,1,1])) with pytest.raises(asyncio.TimeoutError): await get(sa)
async def test_cluster_rewritten_by_second_data_gives_DEL(): sa = SmartAlert(threshold=0, timeout=600) d0 = DataBlock(ts=np.arange(11),zs=[0,0,0,1,1,1,1,1,0,0]) d1 = DataBlock(ts=np.arange(11),zs=[0,0,0,0,0,0,0,0,0,0]) await sa.put(d0) await sa.put(d1) assert await get(sa) == ("NEW",DataBlock(id=1,ts=range(3,9),zs=[1,1,1,1,1])) assert await get(sa) == ("DEL",DataBlock(id=1,ts=range(3,9),zs=[1,1,1,1,1])) with pytest.raises(asyncio.TimeoutError): await get(sa)
async def test_cluster_within_two_datablock_gives_UPD(): sa = SmartAlert(threshold=0, timeout=600) d0=DataBlock(ts=np.arange(0, 11),zs=[0,0,0,0,0,1,1,1,1,1]) d1=DataBlock(ts=np.arange(10,21),zs=[2,2,2,2,2,0,0,0,0,0]) await sa.put(d0) await sa.put(d1) assert await get(sa) == ("NEW",DataBlock(id=1,ts=range(5,11),zs=[1,1,1,1,1])) assert await get(sa) == ("UPD",DataBlock(id=1,ts=range(5,16),zs=[1,1,1,1,1,2,2,2,2,2])) with pytest.raises(asyncio.TimeoutError): await get(sa)
async def get(self) -> DataBlock: T0, T1 = await self.regions.get() ts = self.db.slice_ts(T0, T1) tc = 0.5 * (ts[:-1] + ts[1:]) zs = self.db.at(tc) ids = list(self.db.clients.keys()) return DataBlock(id=ids, ts=ts, zs=zs)
def find_clusters(data: DataBlock, thr: float): idx = np.nonzero(data.zs > thr)[0] #find the borders between groups borders = np.where(np.diff(idx, prepend=-np.inf, append=np.inf) > 1)[0] #return each group res = [] for i0, i1 in zip(borders[:-1], borders[1:]): g = idx[slice(i0, i1)] gt = np.append(g, g[-1] + 1) res += [DataBlock(data.ts[gt], data.zs[g], id=data.id)] return res
async def get(self) -> DataBlock: """Calculate the significance and return it in a :snap.DataBlock: """ tw0, tw1 = self.ana.time_window time_start = self.t0 + tw1 - tw0 + self.delay await timing.wait_until(time_start, self.tChunk_min) t1 = timing.now() - self.delay #define time regions ts = np.arange(self.t0 - tw0, t1 - tw1, self.dt) #calculate significance zs = self.ana(self.data, ts) #drop obsolete data t_last = ts[-1] + self.dt self.drop_tail(t_last + tw0) return DataBlock(np.append(ts, t_last), zs)
async def test_two_clusters_merge_by_z_order(): sa = SmartAlert(threshold=0, timeout=600) d0 = DataBlock(ts=np.arange(0, 11),zs=[0,1,1,1,0,0,2,2,2,0]) d1 = DataBlock(ts=np.arange(0, 11),zs=[0,0,0,2,2,2,2,0,1,1]) await sa.put(d0) await sa.put(d1) assert await get(sa) == ("NEW",DataBlock(id=1,ts=[6,7,8,9], zs=[2,2,2])) assert await get(sa) == ("NEW",DataBlock(id=2,ts=[1,2,3,4], zs=[1,1,1])) assert await get(sa) == ("UPD",DataBlock(id=1,ts=[3,4,5,6,7], zs=[2,2,2,2])) assert await get(sa) == ("DEL",DataBlock(id=2,ts=[1,2,3,4], zs=[1,1,1])) assert await get(sa) == ("NEW",DataBlock(id=3,ts=[8,9,10], zs=[1,1])) with pytest.raises(asyncio.TimeoutError): await get(sa)
async def put(self, data: DataBlock): t_drop = data.T1() - self.timeout self.db.put(data) await self.regions.put(data.T0(), data.T1()) self.db.drop_tail(t_drop)
def random_DataBlock(ts, **kwargs): return DataBlock(ts, zs=np.random.normal(size=len(ts) - 1), **kwargs)
def make_DataBlock(zs, ids: str = 'c_{n}'): return DataBlock(zs=zs, ts=np.linspace(0, 10, zs.shape[0] + 1), id=[ids.format(n=n) for n in range(zs.shape[1])])
async def test_below_threshold_yields_no_alerts(): sa = SmartAlert(threshold=5, timeout=600) data=DataBlock(ts=np.arange(11),zs=[3]*10) await sa.put(data) with pytest.raises(asyncio.TimeoutError): await get(sa)