【问题标题】:itertools.groupby() not grouping correctlyitertools.groupby() 没有正确分组
【发布时间】:2011-12-28 08:03:24
【问题描述】:

我有这些数据:

self.data = [(1, 1, 5.0),
             (1, 2, 3.0),
             (1, 3, 4.0),
             (2, 1, 4.0),
             (2, 2, 2.0)]

当我运行这段代码时:

for mid, group in itertools.groupby(self.data, key=operator.itemgetter(0)):

对于list(group),我得到:

[(1, 1, 5.0),
 (1, 2, 3.0),
 (1, 3, 4.0)]

这就是我想要的。

但如果我使用 1 而不是 0

for mid, group in itertools.groupby(self.data, key=operator.itemgetter(1)):

按元组中的第二个数字分组,我只得到:

[(1, 1, 5.0)]

即使有其他元组在第 1(第 2)个位置有“1”。

【问题讨论】:

    标签: python itertools


    【解决方案1】:

    itertools.groupby 将具有相同键的连续项收集在一起。 如果您希望所有项目具有相同的键,则必须先对 self.data 进行排序。

    for mid, group in itertools.groupby(
        sorted(self.data,key=operator.itemgetter(1)), key=operator.itemgetter(1)):
    

    【讨论】:

    • 我之前在零位上排序。所以我只是在进行 groupby 之前再次排序并且它有效。 self.data.sort(key=operator.itemgetter(1))
    • 不需要排序;你想使用 dictionary 代替:grouped = {} 然后for v in self.data: grouped.setdefault(v[1], []).append(v)。排序是一个 O(NlogN) 操作,使用字典对值进行分组可以让您在 O(N) 时间内完成任务。
    【解决方案2】:

    没有排序的变体(通过字典)。在性能方面应该更好。

    def full_group_by(l, key=lambda x: x):
        d = defaultdict(list)
        for item in l:
            d[key(item)].append(item)
        return d.items()
    

    【讨论】:

    • 回来发同样的东西,我没看你的答案!这显然是要走的路:)
    • 不幸的是,所有的键都必须是可散列的,所以如果这些是用于示例列表,它就不起作用,不像itertools.groupby...
    • @Jeronimo:您会尝试找到密钥的可散列反射;说tuple() 用于列表键或frozenset(d.items()) 用于字典等。如果这真的不可能,那么您将不得不退回到排序的O(NlogN)价格。使用字典进行分组可以让您在线性 (O(N)) 时间内完成任务。
    【解决方案3】:

    下面“修复”了 Python 的 itertools.groupby 的几个烦恼。

    def groupby2(l, key=lambda x:x, val=lambda x:x, agg=lambda x:x, sort=True):
        if sort:
            l = sorted(l, key=key)
        return ((k, agg((val(x) for x in v))) \
            for k,v in itertools.groupby(l, key=key))
    

    具体来说,

    1. 不需要您对数据进行排序。
    2. 不要求您必须仅使用key 作为命名参数。
    3. 输出是tuple(key, grouped_values) 的干净生成器,其中值由第三个参数指定。
    4. 能够轻松应用 sum 或 avg 等聚合函数。

    示例用法

    import itertools
    from operator import itemgetter
    from statistics import *
    
    t = [('a',1), ('b',2), ('a',3)]
    for k,v in groupby2(t, itemgetter(0), itemgetter(1), sum):
      print(k, v)
    

    打印出来,

    a 4
    b 2
    

    Play with this code

    【讨论】:

    • 为什么会有这些“烦恼”? groupby() 允许您将 *连续匹配值 组合成组,它从未打算在整个系列中分组,这需要读取输入迭代中的每个值。 itertools 模块的核心用例是尽可能避免使用迭代器的所有值。
    • 请注意,排序是有代价的:将 N 个项目排序成一个排序序列需要 O(NlogN) 时间。另一方面,使用字典进行分组需要线性时间 (O(N))。您的“实用功能”消除了避免支付排序成本的选项。并且由于您没有使用仅关键字参数,任何阅读您的 group2() 调用的人每次都必须参考文档以找出所有参数做。
    • 您的t 最好使用from collections import defaultdictsummed = defaultdict(int)for k, v in t: summed[k] += vfor k, v in summed: print(k, v) 处理。这更加不言而喻代码实现了什么,并且在线性时间内完成,不需要排序。
    • @MartijnPieters 该示例仅用于演示。当然有更有效的方法来做到这一点。
    • 另见:more_itertools.groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None)keyfunc 与您的key 相似,valuefunc 与您的val 相似,reducefunc 与您的agg 相似。
    猜你喜欢
    • 1970-01-01
    • 2019-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多