Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将数据点分组为系列_Python_Algorithm_Series - Fatal编程技术网

Python 将数据点分组为系列

Python 将数据点分组为系列,python,algorithm,series,Python,Algorithm,Series,我在列表中有一系列数据点元组,格式如下: points = [(1, 'a'), (2, 'b'), (2, 'a'), (3, 'd'), (4, 'c')] 每个元组中的第一个项是一个整数,它们保证被排序。每个元组中的第二个值是任意字符串 我需要按序列中的第一个值将它们分组到列表中。因此,假设间隔为3,则上述列表将分为: [['a', 'b', 'a', 'd'], ['c']] 我编写了以下函数,它在小数据集上运行良好。然而,对于大的输入来说,这是不够的。有没有关于如何重写/优化/缩小

我在列表中有一系列数据点元组,格式如下:

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

一种方法是不保证速度:

将元组列表分为两个列表: [1,2,2,3,4]和['a','b','a','d','c']

因为第一个列表是排序的,所以您可以一直迭代它,直到找到超出范围的元素。然后,您知道开始和结束元素的索引,这样您就可以从第二个数组中切掉字符串。继续,直到你完成了所有的间歇

我不确定传统Python列表的效率如何,但如果数据集足够大,可以尝试使用NumPy数组,它可以非常快速地进行切片。

您的代码是On2。这里有一个解决方案:

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']]
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]]

根据您的代码,我假设我先前的评论是正确的。这里的问题似乎是性能在^2上-您重复列表理解,它多次迭代所有项


我说,使用一个简单的for循环。如果当前项与前一项属于同一组,请将其添加到现有的内部列表[[a],[b]]->[[a],[b,c]]。如果没有,则将其添加到新的内部列表中,可能首先添加空的填充列表。

在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()

为了完整起见,这里有一个使用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)]

使用迭代器进行惰性计算怎么样

这应等同于您的初始解决方案:

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']]
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]]

下面是一种使用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

我不确定我是否理解你的分组。你是说,在区间3中,分组将包括关键范围1..3、4..6、7..9等?懒惰评估会解决你的性能问题吗?itertools.groupby几乎可以做你想做的事情,直到现在还不清楚:如果点[0][0]中有5,那么键的范围会是5..7、8..10等等吗?传统的Python列表是一个数组,所以订阅和切片应该非常有效。我的回答很好。如果你算出关键函数,它实际上变得相当可读,我想我实际上需要知道一个组何时为空,所以不能这样假设。好的,试试这个。由于itertools.groupby至少在2.6中是用C实现的,因此很难用Python实现它,因此我认为字典查找速度会更快。如果有疑问,当然要进行基准测试。您的dict版本是唯一正确打印空组的版本。它也很快。接受这个答案链表会导致这个Ologn问题,但我不知道如何在python中有效地实现它。这个版本速度更快,可读性也很强。但它不会为空间隔存储空组。参见Nicholas Riley的答案和评论此版本速度很快,但它不会为空间隔存储空组。参见Nicholas Riley的回答和评论,除此之外,它返回的是一个生成器,而不是设计的列表。然后,生成器可以根据需要使用列表具体化为一个列表,输出的不同之处是什么?或者问题在于假设块总是从1开始?在这种情况下,很容易修改为从点剥离第一个标记项对,计算出该标记块的第一个端点,并将该标记项粘贴到第一个块中。哎呀。。我的错。它确实给出了正确的输出。我正在删除我以前的评论并更新此解决方案。而且,这似乎比defaultdict版本稍微快一点