python groupby的行为?

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

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

从文档:“返回的组本身就是一个迭代器,它与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]} 

groupby迭代器返回分组函数结果的元组和一个绑定到groupby运算符正在处理的相同“外部”迭代器的新迭代器。 当你将dict()到由groupby返回的迭代器而不消耗这个“内部”迭代器时, groupby将不得不为你提前“外部”迭代器。 你必须认识到, groupby函数不会作用于一个序列,它会将任何这样的序列转换为一个迭代器。

也许这个比喻用一些比喻和手工来解释更好。 请随着我们形成一个桶线。

想象迭代器就像一个人从井里抽水一样。 他有无限数量的水桶可供使用,但水井可能是有限的。 每次你问这个人一桶水,他都会从水井里抽出一个新桶,然后把它传给你。

groupby情况下,你插入另一个人到你的萌芽桶链。 这个人根本没有立即通过水桶。 他把你给的指示的结果加上另外一个人,每当你要求一个桶时,他们就会把你的桶通过这个桶,通过这个桶送给任何问题的人,只要它们和指示相同。 如果指令的结果发生变化,那么groupby bucket passer将会停止传递这些桶。 这么well给了groupby ,把它传递给每个组的人, group A group B等等。

在你的例子中,水被编号,但是从井中只能抽出1000个水桶。 下面是当你将groupby人传递给dict()调用时发生的情况:

  1. 你的dict()调用要求groupby提供一个桶。 现在, groupby从井里的人那里要求一桶水,记住所给出的指示的结果,抓住水桶。 为了dict()他将通过指示的结果( False )加上一个新的人, group A 。 结果被存储为关键字,并且想要拉桶的group A被存储为值。 然而,这个人还没有要求水桶,因为没有人要求

  2. 你的dict()调用要求groupby换另一个桶。 groupby有这些指示,并去寻找结果改变的下一个桶。 它仍然坚持到第一桶,没有人要求,所以它扔掉了这个桶。 相反,它要求从井下一个桶,并使用他的指示。 结果和以前一样,所以它也抛出这个新的桶! 更多的水stream过地板,所以去下一个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可以获得的所有存储桶。 group B返回groupbygroupby只持有一个桶,999号桶,该桶的指令结果与group B正在查找的结果相同。 所以这个桶group B给了list() ,然后耸了耸肩,因为没有更多的桶,因为groupby告诉了他。

  6. 然后,您要求dict()为与关键字False关联的人员,即人员group A 现在, groupby已经无事可做了,井水也干了,他站在一个有999个水桶的水坑里,周围有数字。 你的第二个list()什么都没有。

这个故事的寓意是什么? 在和groupby谈话时,立即要求所有水桶,因为如果你不这样做的话,他们会全部泄漏! 迭代者就像幻想中的扫帚一样,不加理解地努力地移动水,如果你不知道如何控制,你最好希望你用完水。

这里是代码,将做你所期望的(用less一点水来防止洪水):

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

你错过的是,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文档。