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()
调用时发生的情况:
-
你的
dict()
调用要求groupby
提供一个桶。 现在,groupby
从井里的人那里要求一桶水,记住所给出的指示的结果,抓住水桶。 为了dict()
他将通过指示的结果(False
)加上一个新的人,group A
。 结果被存储为关键字,并且想要拉桶的group A
被存储为值。 然而,这个人还没有要求水桶,因为没有人要求 。 -
你的
dict()
调用要求groupby
换另一个桶。groupby
有这些指示,并去寻找结果改变的下一个桶。 它仍然坚持到第一桶,没有人要求,所以它扔掉了这个桶。 相反,它要求从井下一个桶,并使用他的指示。 结果和以前一样,所以它也抛出这个新的桶! 更多的水stream过地板,所以去下一个499桶。 只有当501号桶通过时,结果才会改变,所以现在groupby
发现另一个人给(group B
)指示,以及新的结果True
,把这两个字传递给dict()
。 -
你的
dict()
调用存储True
作为一个键,而人员group B
作为值存储。group B
什么也不做,没有人要求水。 -
你的
dict()
要求另一个桶。groupby
溢出更多的水,直到它拿着999号桶,井边的人耸耸肩,说现在井里是空的。groupby
告诉dict()
井是空的,没有更多的水桶来了,他可以请不要问。 它仍然拥有999号桶,因为它从来不需要为井下的下一个桶提供空间。 -
现在你来了,问
dict()
与关键字True
相关的事情,这是人group B
。 您将group B
传递给list()
,因此会向group B
询问group B
group B
可以获得的所有存储桶。group B
返回groupby
,groupby
只持有一个桶,999号桶,该桶的指令结果与group B
正在查找的结果相同。 所以这个桶group B
给了list()
,然后耸了耸肩,因为没有更多的桶,因为groupby
告诉了他。 -
然后,您要求
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文档。