簡體   English   中英

如何使用 itertools.groupby()?

[英]How do I use itertools.groupby()?

我還沒有找到關於如何實際使用 Python 的itertools.groupby() function 的可以理解的解釋。我正在嘗試做的是:

  • 拿一個列表——在本例中,是一個對象化的lxml元素的子元素
  • 根據一些標准將其分組
  • 然后分別迭代這些組中的每一個。

我已經查看了文檔,但我在嘗試將它們應用到一個簡單的數字列表之外時遇到了麻煩。

那么,我該如何使用itertools.groupby()呢? 我應該使用另一種技術嗎? 指向良好的“先決條件”閱讀的指針也將不勝感激。

重要提示:您必須先對數據進行排序


我沒有得到的部分是在示例構造中

groups = []
uniquekeys = []
for k, g in groupby(data, keyfunc):
   groups.append(list(g))    # Store group iterator as a list
   uniquekeys.append(k)

k是當前分組鍵, g是一個迭代器,您可以使用它來迭代由該分組鍵定義的組。 換句話說, groupby迭代器本身返回迭代器。

這是一個示例,使用更清晰的變量名稱:

from itertools import groupby

things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]

for key, group in groupby(things, lambda x: x[0]):
    for thing in group:
        print("A %s is a %s." % (thing[1], key))
    print("")
    

這將為您提供輸出:

熊是一種動物。
鴨子是一種動物。

仙人掌是一種植物。

快艇是一種交通工具。
校車是交通工具。

在這個例子中, things是一個元組列表,其中每個元組中的第一項是第二項所屬的組。

groupby()函數有兩個參數:(1)要分組的數據和(2)要分組的函數。

這里, lambda x: x[0]告訴groupby()使用每個元組中的第一項作為分組鍵。

在上面for語句中, groupby返回三個(鍵,組迭代器)對 - 每個唯一鍵一次。 您可以使用返回的迭代器來迭代該組中的每個單獨項目。

這是一個稍微不同的示例,使用列表推導,使用相同的數據:

for key, group in groupby(things, lambda x: x[0]):
    listOfThings = " and ".join([thing[1] for thing in group])
    print(key + "s:  " + listOfThings + ".")

這將為您提供輸出:

動物:熊和鴨。
植物:仙人掌。
交通工具:快艇和校車。

itertools.groupby是用於對項目進行分組的工具。

docs中,我們進一步收集了它可能會做什么:

# [k for k, g in groupby('AAAABBBCCDAABBB')] --> ABCDAB

# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D

groupby對象產生鍵組對,其中組是生成器。

特征

  • A. 將連續的項目組合在一起
  • B. 給定一個排序的可迭代項,對所有出現的項目進行分組
  • C. 指定如何使用按鍵功能對項目進行分組*

比較

# Define a printer for comparing outputs
>>> def print_groupby(iterable, keyfunc=None):
...    for k, g in it.groupby(iterable, keyfunc):
...        print("key: '{}'--> group: {}".format(k, list(g)))
# Feature A: group consecutive occurrences
>>> print_groupby("BCAACACAADBBB")
key: 'B'--> group: ['B']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A', 'A']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A']
key: 'C'--> group: ['C']
key: 'A'--> group: ['A', 'A']
key: 'D'--> group: ['D']
key: 'B'--> group: ['B', 'B', 'B']

# Feature B: group all occurrences
>>> print_groupby(sorted("BCAACACAADBBB"))
key: 'A'--> group: ['A', 'A', 'A', 'A', 'A']
key: 'B'--> group: ['B', 'B', 'B', 'B']
key: 'C'--> group: ['C', 'C', 'C']
key: 'D'--> group: ['D']

# Feature C: group by a key function
>>> # islower = lambda s: s.islower()                      # equivalent
>>> def islower(s):
...     """Return True if a string is lowercase, else False."""   
...     return s.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), keyfunc=islower)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']

用途

注意:后面的幾個例子來自 Víctor Terrón 的 PyCon (談話) (西班牙語) ,“Kung Fu at Dawn with Itertools”。 另請參閱用 C 編寫的groupby 源代碼

* 一個函數,所有項目都通過並比較,影響結果。 其他具有關鍵功能的對象包括sorted()max()min()


回復

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]

Python 文檔中的示例非常簡單:

groups = []
uniquekeys = []
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

因此,在您的情況下, data 是節點列表, keyfunc是您的條件函數的邏輯所在,然后groupby()對數據進行分組。

在調用groupby之前,您必須小心按條件對數據進行排序,否則它將不起作用。 groupby方法實際上只是遍歷一個列表,每當鍵更改時,它都會創建一個新組。

groupby 的一個技巧是在一行中運行長度編碼:

[(c,len(list(cgen))) for c,cgen in groupby(some_string)]

會給你一個 2 元組列表,其中第一個元素是 char,第二個是重復次數。

編輯:請注意,這是將itertools.groupby與 SQL GROUP BY語義分開的原因: itertools 不會(通常也不能)提前對迭代器進行排序,因此不會合並具有相同“鍵”的組。

另一個例子:

for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
    print key, list(igroup)

結果是

0 [0, 1, 2, 3, 4]
1 [5, 6, 7, 8, 9]
2 [10, 11]

請注意, igroup是一個迭代器(文檔稱之為子迭代器)。

這對於分塊生成器很有用:

def chunker(items, chunk_size):
    '''Group items in chunks of chunk_size'''
    for _key, group in itertools.groupby(enumerate(items), lambda x: x[0] // chunk_size):
        yield (g[1] for g in group)

with open('file.txt') as fobj:
    for chunk in chunker(fobj):
        process(chunk)

groupby的另一個示例 - 當鍵未排序時。 在以下示例中, xx中的項目按yy中的值分組。 在這種情況下,首先輸出一組零,然后輸出一組 1,然后再輸出一組零。

xx = range(10)
yy = [0, 0, 0, 1, 1, 1, 0, 0, 0, 0]
for group in itertools.groupby(iter(xx), lambda x: yy[x]):
    print group[0], list(group[1])

產生:

0 [0, 1, 2]
1 [3, 4, 5]
0 [6, 7, 8, 9]

警告:

語法 list(groupby(...)) 不會按您想要的方式工作。 它似乎破壞了內部迭代器對象,所以使用

for x in list(groupby(range(10))):
    print(list(x[1]))

將產生:

[]
[]
[]
[]
[]
[]
[]
[]
[]
[9]

取而代之的是 list(groupby(...)),嘗試 [(k, list(g)) for k,g in groupby(...)],或者如果您經常使用該語法,

def groupbylist(*args, **kwargs):
    return [(k, list(g)) for k, g in groupby(*args, **kwargs)]

並訪問 groupby 功能,同時避免那些討厭的(對於小數據)迭代器。

我想舉另一個例子,沒有排序的 groupby 不起作用。 改編自 James Sulak 的示例

from itertools import groupby

things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]

for key, group in groupby(things, lambda x: x[0]):
    for thing in group:
        print "A %s is a %s." % (thing[1], key)
    print " "

輸出是

A bear is a vehicle.

A duck is a animal.
A cactus is a animal.

A speed boat is a vehicle.
A school bus is a vehicle.

有兩組有車輛,而一個可以預期只有一組

@CaptSolo,我嘗試了您的示例,但是沒有用。

from itertools import groupby 
[(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]

輸出:

[('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]

如您所見,有兩個 o 和兩個 e,但它們分為不同的組。 那時我意識到您需要對傳遞給 groupby 函數的列表進行排序。 因此,正確的用法是:

name = list('Pedro Manoel')
name.sort()
[(c,len(list(cs))) for c,cs in groupby(name)]

輸出:

[(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]

請記住,如果列表未排序,則 groupby 功能將不起作用

排序和分組

from itertools import groupby

val = [{'name': 'satyajit', 'address': 'btm', 'pin': 560076}, 
       {'name': 'Mukul', 'address': 'Silk board', 'pin': 560078},
       {'name': 'Preetam', 'address': 'btm', 'pin': 560076}]


for pin, list_data in groupby(sorted(val, key=lambda k: k['pin']),lambda x: x['pin']):
...     print pin
...     for rec in list_data:
...             print rec
... 
o/p:

560076
{'name': 'satyajit', 'pin': 560076, 'address': 'btm'}
{'name': 'Preetam', 'pin': 560076, 'address': 'btm'}
560078
{'name': 'Mukul', 'pin': 560078, 'address': 'Silk board'}

如何使用 Python 的 itertools.groupby()?

您可以使用 groupby 對要迭代的事物進行分組。 您給 groupby 一個可迭代對象和一個可選的函數/可調用項,通過該鍵函數/可調用項來檢查從可迭代項中出來的項目,並返回一個迭代器,該迭代器給出鍵可調用結果和實際項目的二元組另一個可迭代的。 從幫助:

groupby(iterable[, keyfunc]) -> create an iterator which returns
(key, sub-iterator) grouped by each value of key(value).

這是一個 groupby 使用協程按計數分組的示例,它使用一個可調用的鍵(在本例中為coroutine.send )只為多次迭代和一個分組的元素子迭代器吐出計數:

import itertools


def grouper(iterable, n):
    def coroutine(n):
        yield # queue up coroutine
        for i in itertools.count():
            for j in range(n):
                yield i
    groups = coroutine(n)
    next(groups) # queue up coroutine

    for c, objs in itertools.groupby(iterable, groups.send):
        yield c, list(objs)
    # or instead of materializing a list of objs, just:
    # return itertools.groupby(iterable, groups.send)

list(grouper(range(10), 3))

印刷

[(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]

可悲的是,我認為不建議使用itertools.groupby() 安全使用太難了,只需要幾行代碼就能寫出符合預期的東西。

def my_group_by(iterable, keyfunc):
    """Because itertools.groupby is tricky to use

    The stdlib method requires sorting in advance, and returns iterators not
    lists, and those iterators get consumed as you try to use them, throwing
    everything off if you try to look at something more than once.
    """
    ret = defaultdict(list)
    for k in iterable:
        ret[keyfunc(k)].append(k)
    return dict(ret)

像這樣使用它:

def first_letter(x):
    return x[0]

my_group_by('four score and seven years ago'.split(), first_letter)

要得到

{'f': ['four'], 's': ['score', 'seven'], 'a': ['and', 'ago'], 'y': ['years']}

這個基本實現幫助我理解了這個功能。 希望它也可以幫助其他人:

arr = [(1, "A"), (1, "B"), (1, "C"), (2, "D"), (2, "E"), (3, "F")]

for k,g in groupby(arr, lambda x: x[0]):
    print("--", k, "--")
    for tup in g:
        print(tup[1])  # tup[0] == k
-- 1 --
A
B
C
-- 2 --
D
E
-- 3 --
F

我遇到的一個有用的例子可能會有所幫助:

from itertools import groupby

#user input

myinput = input()

#creating empty list to store output

myoutput = []

for k,g in groupby(myinput):

    myoutput.append((len(list(g)),int(k)))

print(*myoutput)

樣本輸入:14445221

樣本輸出: (1,1) (3,4) (1,5) (2,2) (1,1)

from random import randint
from itertools import groupby

 l = [randint(1, 3) for _ in range(20)]

 d = {}
 for k, g in groupby(l, lambda x: x):
     if not d.get(k, None):
         d[k] = list(g)
     else:
         d[k] = d[k] + list(g)

上面的代碼顯示了如何使用 groupby 根據提供的 lambda 函數/鍵對列表進行分組。 唯一的問題是輸出沒有合並,這可以使用字典輕松解決。

例子:

l = [2, 1, 2, 3, 1, 3, 2, 1, 3, 3, 1, 3, 2, 3, 1, 2, 1, 3, 2, 3]

應用 groupby 后,結果將是:

for k, g in groupby(l, lambda x:x):
    print(k, list(g))

2 [2]
1 [1]
2 [2]
3 [3]
1 [1]
3 [3]
2 [2]
1 [1]
3 [3, 3]
1 [1]
3 [3]
2 [2]
3 [3]
1 [1]
2 [2]
1 [1]
3 [3]
2 [2]
3 [3]

一旦使用了如上所示的字典,就會得出以下結果,可以輕松地對其進行迭代:

{2: [2, 2, 2, 2, 2, 2], 1: [1, 1, 1, 1, 1, 1], 3: [3, 3, 3, 3, 3, 3, 3, 3]}

使用itertools.groupby識別的關鍵是,只要項目在 iterable 中是連續的,它們就會組合在一起 這就是排序有效的原因,因為基本上您正在重新排列集合,以便滿足callback(item)的所有項目現在按順序出現在已排序的集合中。

也就是說,您不需要對列表進行排序,您只需要一組鍵值對,其中值可以根據groupby產生的每個可迭代組增長。 即字典列表。

>>> things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
>>> coll = {}
>>> for k, g in itertools.groupby(things, lambda x: x[0]):
...     coll.setdefault(k, []).extend(i for _, i in g)
...
{'vehicle': ['bear', 'speed boat', 'school bus'], 'animal': ['duck', 'cactus']}

我一直無法找到有關如何實際使用 Python 的itertools.groupby()函數的可理解的解釋。 我想要做的是:

  • 拿一個列表 - 在這種情況下,一個對象化的lxml元素的子元素
  • 根據某些標准將其分組
  • 然后分別迭代這些組中的每一個。

我已經查看了文檔,但是在嘗試將它們應用到簡單的數字列表之外時遇到了麻煩。

那么,我如何使用itertools.groupby() 我應該使用另一種技術嗎? 指向良好的“先決條件”閱讀的指針也將不勝感激。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM