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

Я не смог найти понятное объяснение того, как на самом деле использовать Python . Я пытаюсь сделать вот что:--4-->

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

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

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

11 ответов


как сказал Себастьян,сначала нужно отсортировать данные. Это важно.

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

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 iterator сам возвращает итераторы.

вот пример этого, используя более четкую переменную имена:

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

это даст вам выход:

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


вы можете показать нам свой код?

пример в документах Python довольно прост:

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

Итак, в вашем случае данные-это список узлов, keyfunc - это логика вашей функции критериев, а затем groupby() группы данных.

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


neato трюк с groupby, чтобы запустить кодировку длины в одной строке:

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

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

Edit: обратите внимание, что это то, что отделяет 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 является итератором (суб-итератором, как его называет документация).

это полезно для chunking генератора:

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. В этом случае сначала выводится один набор нулей, за которым следует набор единиц, а затем снова набор ноли.

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]

предупреждение:

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

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

будет:

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

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

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

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


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 объекты дают пары ключ-группа, где группа является генератором.

особенности

  • A. группируйте последовательные элементы вместе
  • B. группируйте все вхождения элемента, учитывая отсортированную итерацию
  • C. Определить как группировать элементы с помощью ключевой функции

сравнения

# Define a printer for comparing outputs
>>> def print_groupby(iterable, key=None):
...    for k, g in it.groupby(iterable, key):
...        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
>>> key = lambda x: x.islower()
>>> print_groupby(sorted("bCAaCacAADBbB"), key)
key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']

использует

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


ответ

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

Я хотел бы привести еще один пример, когда 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.

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


@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 не работает!


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

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

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

сортировка и 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'}

один полезный пример, с которым я столкнулся, может быть полезен:

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)