在python中,什么';识别n个事件t个时间间隔的集群最有效的方法是什么?
例如,如果我有以下事件数据,并且希望找到至少包含两个事件的集群,这些事件彼此之间的间隔不超过1分钟,其中id_1、id_2和id_3都相同。作为参考,除了日期时间戳之外,我还有历元时间戳(以微秒为单位)在python中,什么';识别n个事件t个时间间隔的集群最有效的方法是什么?,python,Python,例如,如果我有以下事件数据,并且希望找到至少包含两个事件的集群,这些事件彼此之间的间隔不超过1分钟,其中id_1、id_2和id_3都相同。作为参考,除了日期时间戳之外,我还有历元时间戳(以微秒为单位) event_id timestamp id_1 id_2 id_3 9442823 Jun 15, 2015 10:22 PM PDT A 1 34567 9442821 Jun 15, 2015 10:22 PM PDT A 2 1234
event_id timestamp id_1 id_2 id_3
9442823 Jun 15, 2015 10:22 PM PDT A 1 34567
9442821 Jun 15, 2015 10:22 PM PDT A 2 12345
9442817 Jun 15, 2015 10:22 PM PDT A 3 34567
9442814 Jun 15, 2015 10:22 PM PDT A 1 12345
9442813 Jun 15, 2015 10:22 PM PDT A 2 34567
9442810 Jun 15, 2015 10:22 PM PDT A 3 12345
9442805 Jun 15, 2015 10:22 PM PDT A 1 34567
9442876 Jun 15, 2015 10:23 PM PDT A 2 12345
9442866 Jun 15, 2015 10:23 PM PDT A 3 34567
9442858 Jun 15, 2015 10:23 PM PDT A 1 12345
9442845 Jun 15, 2015 10:23 PM PDT C 2 34567
9442840 Jun 15, 2015 10:23 PM PDT C 3 12345
9442839 Jun 15, 2015 10:23 PM PDT C 1 34567
9442838 Jun 15, 2015 10:23 PM PDT C 2 12345
9442907 Jun 15, 2015 10:24 PM PDT C 3 34567
9442886 Jun 15, 2015 10:24 PM PDT C 1 12345
9442949 Jun 15, 2015 10:25 PM PDT C 2 34567
9442934 Jun 15, 2015 10:25 PM PDT C 3 12345
对于找到的每个集群,我想返回一组(id_1、id_2、id_3、[事件id列表]、集群的最小时间戳、集群的最大时间戳)。此外,如果有一个包含(例如)6个事件的集群,我只想返回一个包含所有事件的结果,而不是每个包含3个事件的分组返回一个结果。如果我正确理解了您的问题,您可以使用scikit learn的DBSCAN聚类算法和自定义距离(或度量)函数。如果id_1、id_2或id_3中的任何一个不同,则距离函数应返回一个非常大的数字。否则就是应该返回时差 但使用这种方法时,簇的数量由算法决定,而不是作为算法的输入。如果您决定将集群数量作为输入传递,那么k-means是您可能需要研究的集群算法。在纯python中,使用一个“滑动窗口”,它包含1分钟范围内的所有事件 前提很简单:维护一个作为子序列的事件队列 在总列表中,按顺序排列。“窗口”(队列)应该是您关心的所有事件。在这种情况下,这取决于60秒的时间间隔要求 在处理事件时,将一个事件添加到队列末尾。如果队列中的第一个事件距离新添加的最后一个事件的时间超过60秒,请通过从队列前面删除第一个事件向前滑动窗口 这是蟒蛇3:
import collections
import operator
import itertools
from datetime import datetime
#### FROM HERE: vvv is just faking events. Delete or replace.
class Event(collections.namedtuple('Event', 'event_id timestamp id_1 id_2 id_3 epoch_ts')):
def __str__(self):
return ('{e.event_id} {e.timestamp} {e.id_1} {e.id_2} {e.id_3}'
.format(e=self))
def get_events():
event_list = map(operator.methodcaller('strip'), '''
9442823 Jun 15, 2015 10:22 PM PDT A 1 34567
9442821 Jun 15, 2015 10:22 PM PDT A 2 12345
9442817 Jun 15, 2015 10:22 PM PDT A 3 34567
9442814 Jun 15, 2015 10:22 PM PDT A 1 12345
9442813 Jun 15, 2015 10:22 PM PDT A 2 34567
9442810 Jun 15, 2015 10:22 PM PDT A 3 12345
9442805 Jun 15, 2015 10:22 PM PDT A 1 34567
9442876 Jun 15, 2015 10:23 PM PDT A 2 12345
9442866 Jun 15, 2015 10:23 PM PDT A 3 34567
9442858 Jun 15, 2015 10:23 PM PDT A 1 12345
9442845 Jun 15, 2015 10:23 PM PDT C 2 34567
9442840 Jun 15, 2015 10:23 PM PDT C 3 12345
9442839 Jun 15, 2015 10:23 PM PDT C 1 34567
9442838 Jun 15, 2015 10:23 PM PDT C 2 12345
9442907 Jun 15, 2015 10:24 PM PDT C 3 34567
9442886 Jun 15, 2015 10:24 PM PDT C 1 12345
9442949 Jun 15, 2015 10:25 PM PDT C 2 34567
9442934 Jun 15, 2015 10:25 PM PDT C 3 12345
'''.strip().splitlines())
for line in event_list:
idev, *rest = line.split()
ts = rest[:6]
id1, id2, id3 = rest[6:]
id2 = int(id2) # faster when sorting (see find_clustered_events)
id3 = int(id3) # faster when sorting (see find_clustered_events)
ts_str = ' '.join(ts)
dt = datetime.strptime(ts_str.replace('PDT', '-0700'), '%b %d, %Y %I:%M %p %z')
epoch = dt.timestamp()
ev = Event(idev, ts_str, id1, id2, id3, epoch)
yield ev
#### TO HERE: ^^^ was just faking up your events. Delete or replace.
def add_cluster(key, group):
'''Do whatever you want with the clusters. I'll print them.'''
print('Cluster:', key)
print(' ','\n '.join(map(str, group)), sep='')
def find_clustered_events(events, cluster=3, gap_secs=60):
'''Call add_cluster on clusters of events within a maximum time gap.
Args:
events (iterable): series of events, in chronological order
cluster (int): minimum number of events in a cluster
gap_secs (float): maximum time-gap from start to end of cluster
Returns:
None.
'''
window = collections.deque()
evkey = lambda e: (e.id_1, e.id_2, e.id_3)
for ev in events:
window.append(ev)
t0 = window[0].epoch_ts
tn = window[-1].epoch_ts
if tn - t0 < gap_secs:
continue
window.pop()
for k, g in itertools.groupby(sorted(window, key=evkey), key=evkey):
group = tuple(g)
if len(group) >= cluster:
add_cluster(k, group)
window.append(ev)
window.popleft()
# Call find_clustered with event generator, cluster args.
# Note that your data doesn't have any 3-clusters without time seconds. :-(
find_clustered_events(get_events(), cluster=2)
请注意:上面的代码并不试图跟踪集群中已有的事件。例如,如果您有一个每15秒发生一次的事件类型,您将有一个如下的序列:
$ python test.py
Cluster: ('A', 1, 34567)
9442823 Jun 15, 2015 10:22 PM PDT A 1 34567
9442805 Jun 15, 2015 10:22 PM PDT A 1 34567
Cluster: ('A', 2, 12345)
9442821 Jun 15, 2015 10:22 PM PDT A 2 12345
9442876 Jun 15, 2015 10:23 PM PDT A 2 12345
Cluster: ('A', 3, 34567)
9442817 Jun 15, 2015 10:22 PM PDT A 3 34567
9442866 Jun 15, 2015 10:23 PM PDT A 3 34567
Cluster: ('A', 1, 12345)
9442814 Jun 15, 2015 10:22 PM PDT A 1 12345
9442858 Jun 15, 2015 10:23 PM PDT A 1 12345
Cluster: ('C', 2, 34567)
9442845 Jun 15, 2015 10:23 PM PDT C 2 34567
9442949 Jun 15, 2015 10:25 PM PDT C 2 34567
event1 t=0:00
event2 t=0:15
event3 t=0:30
event4 t=0:45
event5 t=1:00
您将得到重叠的簇:
event1, event2, event3 (t=0:00 .. 0:30)
event2, event3, event4 (t=0:15 .. 0:45)
event3, event4, event5 (t=0:30 .. 1:00)
从技术上讲,这些都是有效的集群,每个集群略有不同。但是,如果希望事件仅出现在单个集群中,则可能希望从窗口中删除以前聚集的事件
或者,如果集群和重复的机会很低,那么在add_cluster()
函数中实现重复检查可能会提高性能,从而减少主循环所做的工作
最后一点注意:这会进行大量排序。而且排序效率不高,因为每次出现新事件时都会重复排序。如果数据集很大,性能可能会很差。如果事件键相对较少(也就是说,如果id1,2,3值趋向于反复重复),则最好为每个不同的键(id1+id2+id3)动态创建单独的deque,并将事件分派到适当的deque,应用相同的窗口逻辑,然后检查deque的长度
另一方面,如果您正在处理web服务器日志之类的东西,请求者总是在更改,那么可能会导致所有无用的数据出现内存问题。因此,这是内存与时间的权衡。因此,这可能是一个csv?数据当前在熊猫数据框中,但我不认为这是数据格式。