python groupby的行为?

时间:2011-06-04 10:05:33

标签: python dictionary iterator group-by

>>from itertools import groupby
>>keyfunc = lambda x : x > 500
>>obj = dict(groupby(range(1000), keyfunc))
>>list(obj[True])
[999]
>>list(obj[False])
[]
对于条件(x> 500),

范围(1000)显然是默认排序的。 我期望条件(x> 500)将0到999之间的数字分组在 dict 中。但由此产生的字典只有999。
其他数字在哪里? 任何人都可以解释这里发生了什么吗?

3 个答案:

答案 0 :(得分:22)

来自docs

  

返回的组本身就是一个迭代器,它与groupby()共享底层的iterable。由于源是共享的,因此当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]}

答案 1 :(得分:9)

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

也许用一些比喻和手工操作可以更好地解释这一点。请跟随我们形成一条铲斗线。

想象一下,迭代器是一个人从井中抽水桶的人。他有无限数量的桶可供使用,但井可能是有限的。每当你向这个人要一桶水时,他都会从水井中抽出一个新桶并传递给你。

groupby案例中,您将另一个人插入到您的新手链中。这个人根本没有立即通过水桶。每次你要求一个水桶时,他都会通过你给你的指示结果加上另一个的人,然后他会通过groupby人向你传递水桶给任何人,只要它们与指令的结果相同。如果指令的结果发生变化,groupby存储桶传递将停止传递这些存储桶。因此,well会向groupby提供存档,并将其传递给每个群组的人group Agroup B,等等。

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

  1. 您的dict()电话会向groupby询问存储桶。现在,groupby要求井中人员提供一个水桶,记住所给出的指示的结果,并坚持到水桶。要dict(),他会传递说明的结果(False)加上新人group A。结果存储为密钥,并且想要提取存储桶的group A人员存储为值。然而,这个人还要求存储桶,因为没有人要求它

  2. 您的dict()来电询问groupby另一个存储桶。 groupby有这些说明,并寻找结果发生变化的下一个桶。它仍然坚持第一个桶,没有人要求它,所以扔掉这个桶。相反,它要求井中的下一个桶并使用他的指令。结果和以前一样,所以它也抛弃了这个新桶!更多的水流过地板,所以下一个499桶。只有当数量为501的存储桶被传递时,结果才会发生变化,所以现在groupby找到另一个人(新人group B)和新结果True发送指令,传递这两个到dict()

  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()什么都没有。

  7. 这个故事的寓意?与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]
    

答案 2 :(得分:4)

你缺少的是,groupby-function迭代你给定的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文档