itertools.groupby()を使用するにはどうすればよいですか?


507

Pythonのitertools.groupby()関数を実際に使用する方法について、わかりやすい説明を見つけることができませんでした。私がやろうとしているのはこれです:

  • リストを取る-この場合、オブジェクト化されたlxml要素の子
  • いくつかの基準に基づいてグループに分けます
  • その後、これらの各グループを個別に繰り返します。

私が確認しましたドキュメント、および例を、私は数字の単純なリストを超えてそれらを適用しようとするトラブルがありました。

では、どのように使用しitertools.groupby()ますか?使用する必要がある別のテクニックはありますか?適切な「前提条件」の読み方へのポインタもいただければ幸いです。


回答:


656

重要な注意:最初にデータソートする必要があります


私が得られなかった部分は、例の構造の中にあります

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は、はタプルのリストで、各タプルの最初のアイテムは2番目のアイテムが属するグループです。

このgroupby()関数は、(1)グループ化するデータと(2)グループ化する関数の2つの引数を取ります。

ここでlambda x: x[0]groupby()、各タプルの最初の項目をグループ化キーとして使用するように指示しています。

上記のforステートメントでは、groupbyは、3つの(キー、グループイテレータ)ペアを返します-一意のキーごとに1回。返されたイテレータを使用して、そのグループ内の個々のアイテムを反復できます。

以下は、リスト内包表記を使用した、同じデータのわずかに異なる例です。

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

これはあなたに出力を与えます:

動物:クマとアヒル。
植物:サボテン。
車両:スピードボートとスクールバス。


1
グループを事前に指定して、並べ替えを必要としない方法はありますか?
John Salvatier、

2
itertoolsは通常私のためにクリックしますが、私もこれのための「ブロック」を持っていました。私はあなたの例に感謝します-ドキュメントよりはるかに明確です。itertoolsはクリックするかしないかのどちらかである傾向があり、同様の問題に遭遇した場合に把握しやすいと思います。これはまだ実際には必要ありません。
Profane 2011

3
@Julian pythonのドキュメントはほとんどのものにとって素晴らしいように見えますが、イテレータ、ジェネレータ、cherrypyに関しては、ドキュメントはほとんど私を不思議に思っています。Djangoのドキュメントは二重に不可解です。
Marc Maxmeister、2012年

6
並べ替えの+1-データをグループ化するまで、あなたの意味がわかりませんでした。
コーディ

4
@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

72

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メソッドは実際にはリストを反復処理し、キーが変更されるたびに新しいグループを作成します。


46
それで、あなたは読んでkeyfunc、「ええ、私はそれが何であるかを正確に知っています。なぜなら、このドキュメントは非常に単純だからです。」?信じられない!
Jarad 2017

5
私はほとんどの人がこの「まっすぐな」しかしすでに役に立たない例について知っていると信じています。しかし、あなたもどちらも知らないと思います。さもなければ、それをコピーして貼り付けるだけでなく、それを明確にすることで人々を助けるでしょう。それともあなたは?
Apostolos

69

itertools.groupby アイテムをグループ化するためのツールです。

docsからそれが何をする可能性があるかをさらに収集します:

# [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, 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']

用途

注:後者の例のいくつかは、VíctorTerrónのPyCon (talk) (スペイン語)、「Kerung with Dawn 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)]

1
技術的には、ドキュメントはおそらく言うべき[''.join(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC Dです。
Mateen Ulhaq 2018年

1
はい。ほとんどのitertools docstringは、このように「要約」されています。すべてのitertoolsはイテレーターであるため、コンテンツを表示するには、組み込みツール(list()tuple())にキャストするか、ループ/理解で消費する必要があります。これらは、スペースを節約するために著者が除外した可能性のある冗長性です。
pylang 2018年

39

groupbyの巧妙なトリックは、長さエンコーディングを1行で実行することです。

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

最初の要素がcharで2番目が繰り返しの数である2タプルのリストが表示されます。

編集:これがitertools.groupbySQLのGROUP BYセマンティクスとは別のものであることに注意してください:itertoolsはイテレーターを事前にソートしない(そして一般にできない)ため、同じ「キー」を持つグループはマージされません。


27

もう一つの例:

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]

それは興味深いですが、itertools.isliceはイテラブルをチャンクするのに適していませんか?ジェネレータのように反復するオブジェクトを返しますが、Cコードを使用します。
trojjer 2013

@trojjer isliceは、グループのサイズが一貫している場合に適しています。
woodm1979 2013

取得したいもの:[0、1、2]、[1、2、3]、[2、3、4] ...
GilbertS

21

警告:

構文リスト(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機能にアクセスできます。


3
回答の多くは、予期される結果を得るためにgroupbyの前に並べ替える必要がある障害について言及しています。私はこの答えに出会ったばかりで、これまで見たことのない奇妙な行動を説明しています。@singularが言うように、今はlist(groupby(range(10))をリストしようとしただけだったので、今まで見たことはありませんでした。 list()コンストラクターに「自動的に」実行させる
The Red Pea

9

並べ替えのない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.

ビークルを持つ2つのグループがありますが、1つのグループのみを期待できます


5
最初に、グループ化する関数をキーとして使用して、データをソートする必要があります。これは上記の2つの投稿で言及されていますが、強調されていません。
mbatchkarov 2013年

これがdict(groupby(iterator、key))と同じくらい単純であることがわかるまで、私はdict内包表記を行って、サブイテレーターをキーで保持していました。甘い。
trojjer 2013

考え直して、実験後、groupbyの周りにラップされたdict呼び出しは、グループのサブイテレーターを使い果たします。くそー。
trojjer 2013

この答えのポイントは何ですか?元の答えにどのように基づいていますか?
codeforester

7

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

ご覧のとおり、2つのoと2つの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関数機能しません


7
実際に動作します。この動作は壊れていると考えるかもしれませんが、場合によっては役立ちます。例えばこの質問への回答を参照してください:stackoverflow.com/questions/1553275/...
デニスOtkidach

6

並べ替えとグループ化

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'}

5

Pythonのitertools.groupby()を使用するにはどうすればよいですか?

groupbyを使用して、反復するものをグループ化できます。iterableと、アイテムがiterableから出てくるときにチェックするオプションのキー関数/ callable をgroupbyに与え、キーcallableの結果と実際のアイテムの2タプルを与えるイテレータを返します別の反復可能。ヘルプから:

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

1

私が遭遇した1つの便利な例が役に立つかもしれません:

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

この基本的な実装は、この機能を理解するのに役立ちました。それが他の人にも役立つことを願っています:

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

0

独自のgroupby関数を書くことができます:

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

1
ホイールを再発明することは素晴らしいアイデアではありません。また、質問はitertools groupbyを説明することであり、独自に作成することではありません
user2678074

1
@ user2678074その通りです。学習の観点から独自に記述したい場合は、これが役立ちます。
スカイ

2
また、defaultdict(list)を使用すると、さらに短くなります
Mickey Perlstein

@MickeyPerlstein以上。
funnydman
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.