【问题标题】:python groupby behaviour?python groupby行为?
【发布时间】:2011-09-08 07:53:41
【问题描述】:
>>from itertools import groupby
>>keyfunc = lambda x : x > 500
>>obj = dict(groupby(range(1000), keyfunc))
>>list(obj[True])
[999]
>>list(obj[False])
[]

range(1000) 显然对于条件 (x > 500) 是默认排序的。
我期待从 0 到 999 的数字按条件 (x > 500) 分组在 dict 中。但结果字典只有 999。
其他数字在哪里? 谁能解释这里发生了什么?

【问题讨论】:

    标签: python dictionary iterator group-by


    【解决方案1】:

    来自docs

    返回的组本身就是一个迭代器,它与groupby() 共享底层迭代器。因为源是共享的,所以当groupby() 对象被推进时,之前的组不再可见。因此,如果以后需要该数据,则应将其存储为列表[.]

    您将迭代器存储在 obj 中并稍后实现它们。

    In [21]: dict((k, list(g)) for k, g in groupby(range(10), lambda x : x > 5))
    Out[21]: {False: [0, 1, 2, 3, 4, 5], True: [6, 7, 8, 9]}
    

    【讨论】:

      【解决方案2】:

      groupby 迭代器返回分组函数结果的元组和一个新迭代器,该迭代器与groupby 运算符正在处理的同一“外部”迭代器相关联。当您将dict() 应用于groupby 返回的迭代器而不使用此“内部”迭代器时,groupby 将不得不为您推进“外部”迭代器。您必须意识到groupby 函数不会作用于序列,它会将任何此类序列转换为您的迭代器。

      也许用一些比喻和挥手更好地解释这一点。请跟随我们形成一条桶线。

      将迭代器想象为一个从井中用桶装水的人。他有无限数量的桶可以使用,但井可能是有限的。每次你向这个人要一桶水,他都会从水井里拿出一桶新水递给你。

      groupby 案例中,您将另一个人插入到您萌芽的桶链中。这个人根本不会立即传递水桶。每次您请求存储桶时,他都会将您给出的指令的结果以及 另一个 人传递给您,然后他会通过 groupby 人将您的存储桶传递给正在请求的任何人,只要它们匹配指令的结果相同。如果指令的结果发生变化,groupby 桶传递者将停止传递这些桶。所以well 给了groupby 桶,groupby 将它传递给每个组的人,group Agroup B,等等。

      在您的示例中,水是有编号的,但只能从井中抽取 1000 个水桶。当您将 groupby 人传递给 dict() 调用时,会发生以下情况:

      1. 您的dict() 呼叫要求groupby 提供存储桶。现在,groupby 向井边的人要一个水桶,记住了指示的结果,抓住水桶。对于dict(),他将传递指令的结果 (False) 和一个新人 group A。结果存储为key,想要拉桶的group A人存储为value。然而,这个人还没有要水桶,因为没有人要它

      2. 您的dict() 呼叫要求groupby 提供另一个存储桶。 groupby 有这些说明,然后去寻找结果发生变化的下一个桶。它仍然抓着第一个桶,没有人要它,所以它扔掉这个桶。相反,它要求从井中取出下一个桶并使用他的指示。结果和以前一样,所以它也把这个新桶扔掉了!更多的水流过地板,接下来的 499 个水桶也是如此。只有当编号为 501 的桶被传递时结果才会改变,所以现在groupby 找到另一个人来指示(人group B),连同新的结果True,将这两个传递给@987654350 @。

      3. 您的 dict() 调用将 True 存储为键,将人员 group B 存储为值。 group B 什么都不做,没人要水。

      4. 您的dict() 请求另一个存储桶。 groupby 溢出更多的水,直到它拿着编号为 999 的桶,井边的人耸了耸肩,说现在井是空的。 groupby 告诉dict() 井是空的,没有更多的桶来了,请他不要再问了。它仍然拿着编号为 999 的桶,因为它永远不必为井中的下一个桶腾出空间。

      5. 现在你来,向dict() 询问与键True 相关联的事物,即人group B。您将group B 传递给list(),因此它将向group B 询问group B 可以获得的所有桶。 group B 回到 groupby,他只持有一个桶,编号为 999 的桶,该桶的指令结果与 group B 正在寻找的内容相匹配。所以这一桶group B给了list(),然后耸了耸肩,因为已经没有桶了,因为groupby告诉他的。

      6. 然后您向 dict() 询问与密钥 False 关联的人员,即人员 group A。到现在为止,groupby 已经没有什么可提供的了,井干了,他正站在一个有 999 桶水的水坑里,水里到处都是数字。你的第二个list() 什么也得不到。

      这个故事的寓意是什么?在与groupby 交谈时立即要求所有桶装水,因为如果你不这样做,他会把它们全部洒出来!迭代器就像幻想曲中的扫帚,在不知不觉中勤奋地移动水,如果你不知道如何控制它们,你最好希望自己没有水。

      这里的代码可以满足你的期望(用少一点的水来防止洪水):

      >>> from itertools import groupby
      >>> keyfunc = lambda x : x > 5
      >>> obj = dict((k, list(v)) for k, v in groupby(range(10), keyfunc))
      >>> obj(True)
      [0, 1, 2, 3, 4, 5]
      >>> obj(False)
      [6, 7, 8, 9]
      

      【讨论】:

      • 您是说“魔法师的学徒”吗?或者幻想曲也有带水的扫帚?
      • @ReblochonMasque 魔法师的学徒is one part from Fantasia.
      【解决方案3】:

      您缺少的是,groupby 函数迭代您给定的range(1000),从而返回 1000 个值。您只保存最后一个,在您的情况下为999。您要做的是,遍历返回值并将它们保存到您的字典中:

      dictionary = {}
      keyfunc = lambda x : x > 500
      for k, g in groupby(range(1000), keyfunc):
          dictionary[k] = list(g)
      

      所以你会得到预期的输出:

      {False: [0, 1, 2, ...], True: [501, 502, 503, ...]}
      

      有关详细信息,请参阅有关 itertools groupby 的 Python 文档。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-23
        • 1970-01-01
        • 2019-04-20
        • 1970-01-01
        • 2015-12-17
        • 2020-04-19
        相关资源
        最近更新 更多