我在列表中有一系列数据点(元组),格式如下:
points = [(1, 'a'), (2, 'b'), (2, 'a'), (3, 'd'), (4, 'c')]
每个元组中的第一项是一个整数,它们可以保证排序。每个元组中的第二个值是一个任意字符串。
我需要按照系列中的第一个值将它们分组到列表中。因此,如果间隔为3,则上面的列表将被分解为:
[['a', 'b', 'a', 'd'], ['c']]
我编写了以下函数,它可以在小数据集上正常工作。但是,它对于大量投入是有利的。有关如何重写/优化/缩小这一点的任何提示,以便我可以处理大型数据集吗?
def split_series(points, interval):
series = []
start = points[0][0]
finish = points[-1][0]
marker = start
next = start + interval
while marker <= finish:
series.append([point[1] for point in points if marker <= point[0] < next])
marker = next
next += interval
return series
答案 0 :(得分:2)
一种方法(没有速度承诺):
将元组列表分成两个列表:
[1,2,2,3,4]
和['a','b','a','d','c']
由于第一个列表已排序,您可以继续迭代它,直到您到达范围之外的元素。然后,您知道开始和结束元素的索引,这样您就可以将字符串从第二个数组中分割出来。继续,直到你有所有的间隔。
我不确定传统Python列表的效率如何,但如果你的数据集足够大,你可以尝试使用NumPy数组,它会很快切片。
答案 1 :(得分:2)
您的代码是O(n 2 )。这是一个O(n)解决方案:
def split_series(points, interval):
series = []
current_group = []
marker = points[0][0]
for value, data in points:
if value >= marker + interval:
series.append(current_group)
current_group = []
marker += interval
current_group.append(data)
if current_group:
series.append(current_group)
return series
points = [(1, 'a'), (2, 'b'), (2, 'a'), (3, 'd'), (4, 'c')]
print split_series(points, 3) # Prints [['a', 'b', 'a', 'd'], ['c']]
答案 2 :(得分:2)
为了完整性,这是一个itertools.groupby
的解决方案,但字典解决方案可能会更快(更不用说更容易阅读)。
import itertools
import operator
def split_series(points, interval):
start = points[0][0]
return [[v for k, v in grouper] for group, grouper in
itertools.groupby((((n - start) // interval, val)
for n, val in points), operator.itemgetter(0))]
请注意,上述假设您在每个组中至少有一个项目,否则它会给您的脚本带来不同的结果,即:
>>> split_series([(1, 'a'), (2, 'b'), (6, 'a'), (6, 'd'), (11, 'c')], 3)
[['a', 'b'], ['a', 'd'], ['c']]
而不是
[['a', 'b'], ['a', 'd'], [], ['c']]
这是一个固定的字典解决方案。在某些时候,字典查找时间将开始占主导地位,但也许它对你来说足够快。
from collections import defaultdict
def split_series(points, interval):
offset = points[0][0]
maxval = (points[-1][0] - offset) // interval
vals = defaultdict(list)
for key, value in points:
vals[(key - offset) // interval].append(value)
return [vals[i] for i in xrange(maxval + 1)]
答案 3 :(得分:1)
从您的代码中,我假设我之前的评论是正确的。这里的问题似乎是性能为O(n ^ 2) - 你重复列表理解(迭代所有项目)多次。
我说,使用一个简单的for循环。如果当前项目与前一项目属于同一组,请将其添加到现有内部列表[[“a”],[“b”]] - &gt; [[“a”],[“b”,“c”]]。如果没有,请将其添加到新的内部列表,可能首先添加空填充列表。
答案 4 :(得分:1)
扩展Am的答案,使用defaultdict,并按键区间键除以正确分解它们。
from collections import defaultdict
def split_series(points, interval):
vals = defaultdict(list)
for key, value in points:
vals[(key-1)//interval].append(value)
return vals.values()
答案 5 :(得分:1)
这是一种使用xrange步骤行为的惰性方法:
def split_series(points, interval):
end_of_chunk = interval
chunk = []
for marker, item in points:
if marker > end_of_chunk:
for end_of_chunk in xrange(end_of_chunk, marker, interval):
yield chunk
chunk = []
end_of_chunk += interval
chunk.append(item)
yield chunk
答案 6 :(得分:0)
如何使用迭代器进行延迟评估?
这应该等同于您的初始解决方案:
from itertools import groupby
def split_series(points, interval):
"""
>>> points = [(1, 'a'), (2, 'b'), (2, 'a'), (3, 'd'), (4, 'c')]
>>> print list(split_series(points, 3))
[['a', 'b', 'a', 'd'], ['c']]
"""
def interval_key(t):
return (t[0] - points[0][0]) // interval
groups = groupby(points, interval_key)
for group in groups:
yield [v for _, v in group[1]]