我需要找到python列表的子集,例如:
a = [[1,2,100],[1,3,2100],[2,3,200],[3,4,1600]]
假设每个元素的第一个元素表示start_time,第二个元素表示end_time,我的查询格式为(start,end)。生成的子集应该使得子集中每个元素的start_time和end_time应该在start和end之间。
最快的方法是什么(或者我应该保存数据以获得更好的运行时间的任何结构)?
答案 0 :(得分:2)
您可以使用范围树来存储积分。将(start_time,end_time)对视为(x,y)坐标。然后查询(开始,结束)成为在广场[开始,结束] x [开始,结束]中找到点的问题。
可以在O(n log n)时间内计算两个维度上的范围树,并在O(log n)时间内对它们进行查询。
不幸的是,我不知道任何优秀的Python实现(可能Python Quadtree除外),因此您可能需要自己动手。但是,它肯定会比任何线性搜索解决方案都快。
如果您不想使用或编写范围树,请考虑使用NumPy代替更快的线性搜索:
arr = np.array(a)
xa, ya, val = arr.T
pts = (xa >= start) & (ya <= end)
print arr[pts]
答案 1 :(得分:1)
>>> start, end = 0, 5
>>> result = [i for i in a if start <= i[0] and end >= i[1]]
>>> print result
... [[1, 2, 100], [1, 3, 2100], [2, 3, 200], [3, 4, 1600]]
>>> start, end = 2, 3
>>> result = [i for i in a if start <= i[0] and end >= i[1]]
>>> print result
... [[2, 3, 200]]
列表理解。如果您希望它不包含,请移除=
。
答案 2 :(得分:1)
使用bisect
module演示的算法可以为您提供最快的搜索时间,但我们必须创建一些已排序的索引。
您必须将开始时间和结束时间都存储在列表中,其中包含a
列表中条目的索引:
starttimes = [(l[0], i) for i,l in enumerate(a)]
starttimes.sort()
endtimes = [(l[1], i) for i, l in enumerate(a)]
endtimes.sort()
然后,您可以根据bisect
和bisect.bisect_left
函数创建专门的bisect.bisect_right
函数:
def bisect_timeseries_start(starttimes, start):
while lo < hi:
mid = (lo+hi)//2
if starttimes[mid][0] < start: lo = mid+1
else: hi = mid
return starttimes[lo][1]
def bisect_timeseries_end(endtimes, end):
while lo < hi:
mid = (lo+hi)//2
if end < endtimes[mid][0]: hi = mid
else: lo = mid+1
return endtimes[lo][1]
现在您可以使用以下函数找到开始和结束索引:
startindex = bisect.bisect_timeseries_start(starttimes, start)
endindex = bisect.bisect_timeseries_end(endtimes, end)
现在可以轻松返回匹配范围:
startendrange = a[startindex:endindex]
每次搜索都有O(lg n)
费用,其中n
是列表的长度。将这些操作组合成一个封装时间序列列表a
和索引的类很容易。