Как я могу использовать itertools.groupby ()?

Я не смог найти понятного объяснения того, как на самом деле использовать itertools.groupby()функцию Python . То, что я пытаюсь сделать, это:

  • Возьмите список - в этом случае, дети объективированного lxmlэлемента
  • Разделите его на группы по некоторым критериям
  • Затем выполните итерацию по каждой из этих групп отдельно.

Я просмотрел документацию и примеры , но у меня возникли проблемы при попытке применить их за пределы простого списка цифр.

Итак, как мне использовать itertools.groupby()? Есть ли другая техника, которую я должен использовать? Указатели на хорошее «предварительное» чтение также приветствуются.

3.08.2008 18:27:09
один полезный случай для этого был бы leetcode.com/problems/string-compression
ShawnLee 8.08.2019 01:02:19
13 ОТВЕТОВ
РЕШЕНИЕ

ВАЖНОЕ ПРИМЕЧАНИЕ: сначала нужно отсортировать данные .


Часть, которую я не получил, это то, что в примере конструкции

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 + "."

Это даст вам вывод:

животные: медведь и утка.
растения: кактус.
транспортные средства: скоростной катер и школьный автобус.

643
24.10.2018 22:30:28
Есть ли способ указать группы заранее и затем не требовать сортировки?
John Salvatier 10.05.2011 19:39:42
itertools обычно щелкает для меня, но у меня также был «блок» для этого. Я оценил ваши примеры - намного понятнее, чем документы. Я думаю, что itertools склонны либо щелкать, либо нет, и их гораздо легче понять, если вы столкнулись с подобными проблемами. Еще не понадобился этот в дикой природе.
Profane 21.08.2011 20:30:53
@Julian документы на python отлично подходят для большинства вещей, но когда дело доходит до итераторов, генераторов и cherrypy, документы в основном меня озадачивают. Документы Джанго вдвойне сбивают с толку.
Marc Maxmeister 1.10.2012 18:19:51
+1 за сортировку - я не понял, что вы имели в виду, пока не сгруппировал свои данные.
Cody 24.04.2014 02:25:57
@DavidCrook очень поздно на вечеринку, но может кому-то помочь. Вероятно, это потому, что ваш массив не отсортирован, попробуйте groupby(sorted(my_collection, key=lambda x: x[0]), lambda x: x[0]))в предположении, что my_collection = [("animal", "bear"), ("plant", "cactus"), ("animal", "duck")]вы хотите сгруппироватьanimal or plant
Robin Nemeth 14.12.2017 15:12:24

Пример на документации по 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Метод на самом деле просто перебирает список и всякий раз, когда ключ меняется, он создает новую группу.

71
24.01.2020 13:41:47
Итак, вы читали keyfuncи говорили: «Да, я точно знаю, что это такое, потому что эта документация довольно проста». Невероятно!
Jarad 7.04.2017 19:22:10
Я полагаю, что большинство людей уже знают об этом "прямолинейном", но бесполезном примере, поскольку в нем не говорится, какие "данные" и "keyfunc" использовать !! Но я думаю, вы тоже не знаете, иначе вы бы помогли людям, разъяснив это, а не просто скопировав это. Или ты?
Apostolos 28.03.2018 19:14:07

itertools.groupby это инструмент для группировки предметов.

Из документов мы узнаем, что это может сделать:

# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B

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

groupby объекты дают пары ключей-групп, где группа является генератором.

Характеристики

  • А. Группируйте последовательные элементы вместе
  • 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
>>> # keyfunc = lambda s: s.islower()                      # equivalent
>>> def keyfunc(s):
...     """Return a True if a string is lowercase, else False."""   
...     return s.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), keyfunc)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']

Пользы

Примечание. Некоторые из последних примеров взяты из PyCon (выступления) Виктора Террона (на испанском языке) «Кунг-фу на рассвете с Itertools». Смотрите также groupbyисходный код, написанный на C.

* Функция, в которой все элементы передаются и сравниваются, влияя на результат. Другие объекты с основными функциями включают в себя sorted(), max()и min().


отклик

# OP: Yes, you can use `groupby`, e.g. 
[do_something(list(g)) for _, g in groupby(lxml_elements, criteria_func)]
65
22.10.2019 12:13:28
Технически, документы, вероятно, должны сказать [''.join(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D.
Mateen Ulhaq 24.10.2018 22:55:42
Да. Таким образом, большинство строк документации itertools «сокращены». Поскольку все itertools являются итераторами, они должны быть приведены к встроенному ( list(), tuple()) или использованы в цикле / понимании для отображения содержимого. Это избыточности, которые автор, скорее всего, исключил для экономии места.
pylang 25.10.2018 00:13:02

Отличный трюк с groupby заключается в запуске кодирования длины в одну строку:

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

даст вам список из двух кортежей, где первый элемент - это символ, а второй - количество повторений.

Изменить: Обратите внимание, что это то, что отличает семантику itertools.groupbySQL GROUP BY: itertools не (и вообще не может) заранее сортировать итератор, поэтому группы с одинаковым «ключом» не объединяются.

39
8.08.2017 10:01:05

Другой пример:

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)

Еще один пример группового - когда ключи не отсортированы. В следующем примере элементы в xx сгруппированы по значениям в yy. В этом случае сначала выводится один набор нулей, затем набор единиц, а затем снова набор нулей.

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]
27
21.01.2013 16:54:08
Это интересно, но разве itertools.islice не будет лучше для разбиения итерируемого? Он возвращает объект, который повторяется как генератор, но он использует C-код.
trojjer 4.12.2013 10:37:36
@trojjer islice было бы лучше, если бы группы были одинакового размера.
woodm1979 17.12.2013 17:48:13
Я хочу получить: [0, 1, 2], [1, 2, 3], [2, 3, 4] ...
GilbertS 22.02.2020 11:26:57

ПРЕДУПРЕЖДЕНИЕ:

Список синтаксиса (groupby (...)) не будет работать так, как вы хотите. Кажется, он уничтожает внутренние объекты итератора, поэтому

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

будет производить:

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

Вместо списка (groupby (...)) попробуйте [(k, list (g)) для k, g в groupby (...)], или, если вы часто используете этот синтаксис,

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

и получить доступ к функциональности groupby, избегая этих надоедливых (для маленьких данных) итераторов вместе.

21
28.12.2014 19:26:52
Многие ответы относятся к камню преткновения, который вы должны отсортировать перед группировкой, чтобы получить ожидаемые результаты. Я только что столкнулся с этим ответом, который объясняет странное поведение, которого я не видел раньше. Я не видел раньше, потому что только сейчас я пытался составить список (groupby (range (10))), как говорит @singular. До этого я всегда использовал «рекомендуемый» подход «ручной» итерации по объектам groupby, а не позволяя списку () конструктор «автоматически» сделать это
The Red Pea 11.09.2014 05:13:14

Я хотел бы привести еще один пример, где не работает groupby без сортировки. Адаптировано из примера Джеймса Сулака

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.

есть две группы с транспортным средством, тогда как можно ожидать только одну группу

9
19.10.2013 04:09:53
Сначала нужно отсортировать данные, используя в качестве ключа функцию, по которой вы группируете. Это упоминается в двух постах выше, но не выделяется.
mbatchkarov 25.06.2013 15:19:47
Я выполнял диктовку, чтобы сохранить подитераторы по ключу, пока не понял, что это так просто, как dict (groupby (итератор, ключ)). Сладкий.
trojjer 4.12.2013 12:00:29
Если подумать и после экспериментов, вызов dict, обернутый вокруг groupby, измотает групповые итераторы. Черт.
trojjer 4.12.2013 13:57:53
Какой смысл этого ответа? Как это основывается на первоначальном ответе ?
codeforester 12.04.2020 01:39:10

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

Как видите, есть два и два, но они попали в отдельные группы. Именно тогда я понял, что вам нужно отсортировать список, переданный функции 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 не будет работать !

7
11.11.2015 22:55:48
На самом деле это работает. Вы можете подумать, что это поведение нарушено, но в некоторых случаях это полезно. См. Ответы на этот вопрос для примера: stackoverflow.com/questions/1553275/…
Denis Otkidach 15.10.2009 16:29:52

Сортировка и групповой

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'}
6
20.08.2018 11:51:28

Как использовать itertools.groupby () в Python?

Вы можете использовать groupby, чтобы группировать вещи для повторения. Вы даете groupby итерируемую и необязательную ключевую функцию / вызываемую функцию, с помощью которой проверяются элементы, когда они выходят из итерируемой, и она возвращает итератор, который дает двукратный набор результата вызываемой клавиши и фактических элементов в еще один повторяемый. Из справки:

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

Вот пример группирования с использованием сопрограммы для группировки по счетчику, он использует вызываемый ключ (в данном случае 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])]
5
27.07.2015 18:37:13

Может пригодиться один полезный пример:

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)

1
18.06.2017 17:16:54

Эта базовая реализация помогла мне понять эту функцию. Надеюсь, это поможет и другим:

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
1
5.04.2020 19:54:11

Вы можете написать собственную групповую функцию:

           def groupby(data):
                kv = {}
                for k,v in data:
                    if k not in kv:
                         kv[k]=[v]
                    else:
                        kv[k].append(v)
           return kv

     Run on ipython:
       In [10]: data = [('a', 1), ('b',2),('a',2)]

        In [11]: groupby(data)
        Out[11]: {'a': [1, 2], 'b': [2]}
0
10.10.2018 18:06:35
переизобретать колесо не очень хорошая идея, также вопрос состоит в том, чтобы объяснить itertools groupby, а не писать собственные
user2678074 16.05.2019 08:39:47
@ user2678074 Вы правы. Это что-то, если вы хотите написать свой собственный с точки зрения обучения.
Sky 16.05.2019 11:17:42
Также лучше использовать defaultdict (список), чтобы он был еще короче
Mickey Perlstein 12.08.2019 19:24:38
@MickeyPerlstein и быстрее.
funnydman 26.02.2020 12:51:11