2011-06-04 58 views
11
>>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)排序。
我期待的條件(x> 500)將從0到999的數字分組在字典中。但結果字典只有999.
其他數字在哪裏? 任何人都可以解釋這裏發生了什麼?python groupby的行爲?

回答

17

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

你缺少的東西是,在你給range(1000),在GROUPBY功能迭代並返回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, ...]} 

欲瞭解更多信息,請參閱Python文檔約itertools groupby

7

groupby迭代器返回分組函數結果的元組和一個綁定到運算符正在處理的相同「外部」迭代器的新迭代器。當您將dict()應用到由groupby返回的迭代器而不消耗此「內部」迭代器時,groupby將不得不爲您推進「外部」迭代器。你必須認識到groupby函數不會作用於一個序列,它會將任何這樣的序列轉換爲一個迭代器。

也許這可以用一些隱喻和handwaving更好地解釋。請隨着我們形成一個桶形線。

想象一下迭代器是一個人從井中抽水的人。他有無限數量的桶可供使用,但井可能是有限的。每次你向這個人詢問一桶水時,他都會從水井中抽出一個新水桶並傳給你。

groupby的情況下,您插入另一個人到您的萌芽桶鏈中。這個人根本沒有立即通過水桶。他向你傳遞了你給出的指示的結果,另外另一個人每次你要求一個桶時,只要它們與指令的結果相同,誰就會通過groupby人將桶傳遞給任何人。如果說明的結果發生變化,則groupby桶形傳送器將停止傳遞這些桶。所以well給水桶groupby,誰該傳遞給每個組的人,group Agroup B,等等。

在您的例子中,水被編號,但只能有1000桶從井裏抽出。這是發生了什麼,當你再經過groupby人到dict()電話:

  1. dict()電話詢問groupby一個水桶。現在,groupby要求井中人員提供一個桶,記住所給出的指示的結果,並保持在桶中。到dict()他會通過指示的結果(False)加上一個新的人,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 Blist(),因此會要求group B查詢全部的水桶group B可以得到。 group B追溯到groupby,他只持有一個存儲桶,存儲號碼爲999,此存儲桶的說明結果與group B正在查找的結果相匹配。所以這一桶group Blist(),然後聳了聳肩,因爲沒有更多的桶,因爲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] 
+0

你的意思是「魔法師的學徒」?或者也許幻想曲有掃帚也拿水? – 2017-12-31 01:42:54

+0

@ReblochonMasque魔法師的學徒[是幻想曲的一部分](https://en.wikipedia.org/wiki/Fantasia_%281940_film%29#Program)。 – 2017-12-31 15:01:50

+0

好的,謝謝@MartijnPieters,我不知道。 – 2017-12-31 15:17:53