İtertools.groupby () nasıl kullanılır?


507

Python'un itertools.groupby()işlevini nasıl kullanacağınız hakkında anlaşılır bir açıklama bulamadım . Ne yapmaya çalışıyorum:

  • Bir liste alın - bu durumda, nesnelleştirilmiş bir lxmlöğenin alt öğeleri
  • Bazı kriterlere göre gruplara ayırın
  • Daha sonra bu grupların her birini ayrı ayrı yineleyin.

Ben inceledim belgeleri ve örnekler ; ama sıkıntı numaraları basit listesinin ötesinde bunları uygulamak için çalışıyoruz yaşadım.

Peki, nasıl kullanırım itertools.groupby()? Kullanmam gereken başka bir teknik var mı? İyi "önkoşul" okumaya işaret edenler de takdir edilecektir.


bunun için yararlı bir durum leetcode.com/problems/string-compression
ShawnLee

Yanıtlar:


656

ÖNEMLİ NOT: Önce verilerinizi sıralamanız gerekir .


Alamadığım kısım örnek yapıda

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

kgeçerli gruplama anahtarıdır ve gbu gruplama anahtarı tarafından tanımlanan grup üzerinden yineleme yapmak için kullanabileceğiniz bir yineleyicidir. Başka bir deyişle, groupbyyineleyicinin kendisi yineleyicileri döndürür.

İşte daha net değişken adları kullanarak buna bir örnek:

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

Bu size çıktı verecektir:

Bir ayı bir hayvandır.
Ördek bir hayvandır.

Bir kaktüs bir bitkidir.

Sürat teknesi bir araçtır.
Okul otobüsü bir araçtır.

Bu örnekte, thingsher bir gruptaki ilk öğenin ikinci öğenin ait olduğu grup olduğu grupların bir listesidir.

groupby()(1) grubunun veri ve (2) ile bir grup olarak işlevi: işlevi iki değer alır.

Burada, her bir gruptaki ilk öğeyi gruplama anahtarı olarak kullanmayı lambda x: x[0]söyler groupby().

Yukarıdaki forifadede, groupbyher bir benzersiz anahtar için bir kez olmak üzere üç (anahtar, grup yineleyici) çifti döndürür. Bu gruptaki her bir öğeyi yinelemek için döndürülen yineleyiciyi kullanabilirsiniz.

Bir liste kavrayışı kullanarak aynı verilerle biraz farklı bir örnek:

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

Bu size çıktı verecektir:

hayvanlar: ayı ve ördek.
bitkiler: kaktüs.
araçlar: sürat teknesi ve okul otobüsü.


1
Grupları önceden belirtmenin ve ardından sıralama gerektirmemenin bir yolu var mı?
John Salvatier

2
itertools genellikle benim için tıklar, ancak bunun için bir 'blok' da vardı. Örneklerini takdir ettim - dokümanlardan çok daha net. Bence itertools ya tıklama ya da tıklama eğilimindedir ve benzer sorunlara çarptıysanız kavramak çok daha kolaydır. Henüz vahşi doğada buna ihtiyacım yok.
Profane

3
@Julian python belgeleri çoğu şey için harika görünüyor, ancak yineleyiciler, jeneratörler ve kiraz söz konusu olduğunda, dokümanlar çoğunlukla beni gizliyor. Django'nun belgeleri iki kat şaşırtıcı.
Marc Maxmeister

6
Sıralama için +1 - Verilerimi gruplandırana kadar ne demek istediğinizi anlamadım.
Cody

4
@DavidCrook partiye çok geç ama birine yardım edebilir. Muhtemelen diziniz sıralanmadığı groupby(sorted(my_collection, key=lambda x: x[0]), lambda x: x[0]))için varsayım altında deneyin my_collection = [("animal", "bear"), ("plant", "cactus"), ("animal", "duck")]ve gruplamak istiyorsunuzanimal or plant
Robin Nemeth

72

Python belgelerindeki örnek oldukça basittir:

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

Yani sizin durumunuzda, veriler düğümlerin bir listesidir, keyfunckriterler fonksiyonunuzun mantığının gittiği ve ardından groupby()verileri grupladığı yerdir .

Aramadan önce verileri ölçütlere göre sıralamaya dikkat edin, groupbyaksi takdirde çalışmaz. groupbyyöntemi aslında bir liste üzerinden yinelenir ve anahtar her değiştiğinde yeni bir grup oluşturur.


46
Yani okudunuz keyfuncve "evet, bunun tam olarak ne olduğunu biliyorum çünkü bu dokümantasyon oldukça basit." İnanılmaz!
Jarad

5
Çoğu insanın bu "basit" ama zaten işe yaramaz bir örnek bildiğine inanıyorum, çünkü ne tür bir 'veri' ve 'keyfunc' kullanacağını söylemiyor !! Ama sanırım siz de bilmiyorsunuz, aksi takdirde insanlara sadece kopyalayıp yapıştırarak değil açıklığa kavuşturarak yardım edersiniz. Yoksa sen mi?
Apostolos

69

itertools.groupby öğeleri gruplamak için bir araçtır.

Gönderen dokümanlar , bunun ne yapabilir ilerletmek toplamaktadır:

# [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 nesneler, grubun bir jeneratör olduğu anahtar grup çiftleri verir.

Özellikleri

  • A. Ardışık öğeleri birlikte gruplama
  • B. Sıralanabilir bir tekrarlanabilirlik göz önüne alındığında, bir öğenin tüm tekrarlamalarını gruplandırın
  • C. Tuş işlevi olan öğelerin nasıl gruplanacağını belirtin *

Karşılaştırmalar

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

Kullanımları

Not: İkinci örneklerden bazıları Víctor Terrón'un PyCon (konuşma) (İspanyolca) , "Itertools ile Şafakta Kung Fu" dan türemiştir. Ayrıca C ile yazılmış groupbykaynak koduna da bakınız .

* Tüm öğelerin geçtiği ve karşılaştırıldığı, sonucu etkileyen bir işlev. Temel işlevleri ile diğer amaçları içerir sorted(), max()ve min().


Tepki

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

1
Teknik olarak, dokümanlar muhtemelen söylemelidir [''.join(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D.
Mateen Ulhaq

1
Evet. Itertools öğretilerinin çoğu bu şekilde "kısaltılmış". Tüm itertooller yineleyiciler olduğundan, içeriği görüntülemek için bir yerleşkeye ( list(), tuple()) dökülmeli veya bir döngü / kavramada tüketilmelidir. Bunlar, yazarın yer tasarrufu sağlama olasılığı dışında kalan işten çıkarmalardır.
pylang

39

Groupby ile yapılan bir hile, uzunluk kodlamasını bir satırda çalıştırmaktır:

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

size ilk öğenin karakter ve ikincinin tekrar sayısı olduğu 2 tüpün bir listesini verecektir.

Düzenleme: Not bu itertools.groupbySQL GROUP BYanlambiliminden ayıran ne olduğunu : itertools yineleyici önceden sıralamak değil (ve genel olarak olamaz), böylece aynı "anahtar" olan gruplar birleştirilmez.


27

Başka bir örnek:

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

sonuç

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

İgroup bir yineleyici (belgelerin dediği gibi bir alt yineleyici) olduğunu unutmayın.

Bu bir jeneratörü parçalamak için kullanışlıdır:

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)

Başka bir groupby örneği - anahtarlar sıralanmadığında. Aşağıdaki örnekte, xx içindeki öğeler yy'deki değerlere göre gruplanmıştır. Bu durumda, önce bir grup sıfır, ardından bir grup verilir ve ardından bir grup sıfır gelir.

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

üretir:

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

Bu ilginç, ama itertools.islice tekrarlanabilir bir parça için daha iyi olmaz mıydı? Bir jeneratör gibi yinelenen bir nesne döndürür, ancak C kodu kullanır.
trojjer

@trojjer islice, gruplar tutarlı büyüklükteyse daha iyi olur.
woodm1979

Almak istiyorum: [0, 1, 2], [1, 2, 3], [2, 3, 4] ...
GilbertS

21

UYARI:

Sözdizimi listesi (groupby (...)) istediğiniz gibi çalışmaz. Dahili yineleyici nesnelerini yok ediyor gibi görünüyor, bu yüzden

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

üretecek:

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

Bunun yerine, listeden (groupby (...)), groupby (...)] 'de k, g için [(k, list (g)) deneyin veya bu sözdizimini sık sık kullanıyorsanız,

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

ve sinir bozucu (küçük veri için) yineleyicilerden kaçınarak groupby işlevselliğine erişebilirsiniz.


3
Yanıtların çoğu, beklenen sonuçları elde etmek için gruptan önce sıralamanız gereken tökezleme bloğunu ifade eder. Daha önce görmediğim garip davranışı açıklayan bu cevapla daha yeni karşılaştım. Daha önce görmedim çünkü sadece şimdi @singular'ın dediği gibi listby (groupby (range (10)) listelemeye çalışıyordum. Bundan önce hep "groupby" nesneleri yerine "elle" yineleme "önerilen" yaklaşımını kullanmıştım list () yapıcısının "otomatik olarak" yapmasına izin vermek
Kızıl Bezelye

9

Sıralama olmadan groupby çalışmıyor başka bir örnek vermek istiyorum. James Sulak tarafından örnekten uyarlanmıştır

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

çıktı

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.

araçlı iki grup vardır, ancak sadece bir grup beklenebilir


5
Gruplandırdığınız işlevi anahtar olarak kullanarak önce verileri sıralamanız gerekir. Bu, yukarıda iki yazıda belirtilmiştir, ancak vurgulanmamıştır.
mbatchkarov

Bunun dict kadar basit olduğunu fark edene kadar (yineleyici, yineleyici) alt yineleyicileri anahtarla korumak için dik bir anlayış yapıyordum. Tatlı.
trojjer

İkinci düşüncelerde ve deneyden sonra, grubun etrafına sarılmış dikte çağrısı grup alt yineleyicilerini tüketecektir. Lanet olsun.
trojjer

Bu cevabın anlamı nedir? Nasıl üzerinde inşa ediyor orijinal cevap ?
codeforester

7

@CaptSolo, örneğini denedim, ama işe yaramadı.

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

Çıktı:

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

Gördüğünüz gibi, iki o ve iki e var, ama ayrı gruplara girdiler. O zamanlar groupby işlevine aktarılan listeyi sıralamanız gerektiğini fark ettim. Yani, doğru kullanım şöyle olacaktır:

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

Çıktı:

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

Sadece hatırlamak, eğer liste sıralı değilse, groupby işlevi çalışmaz !


7
Aslında işe yarıyor. Bu davranışı bozuk olarak düşünebilirsiniz, ancak bazı durumlarda yararlıdır. Bir örnek için bu sorunun yanıtlarına bakın: stackoverflow.com/questions/1553275/…
Denis Otkidach

6

Sıralama ve gruplandırma

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's itertools.groupby () nasıl kullanılır?

Yinelemek için bir şeyler gruplamak için groupby kullanabilirsiniz. Groupby öğesine bir yinelenebilir ve öğeleri yinelemeden çıktıklarında kontrol etmek için isteğe bağlı bir anahtar işlev / çağrılabilir veriyorsunuz ve çağrılabilir anahtarın ve öğedeki gerçek öğelerin iki parçasını veren bir yineleyici döndürüyor başka bir yinelenebilir. Yardımdan:

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

Burada bir sayıya göre gruplamak için bir eşdüzen kullanan bir groupby örneği var, coroutine.sendancak bu sayıyı birçok yineleme ve öğelerin gruplanmış bir alt yineleyicisi için sadece tükürmek için çağrılabilir bir anahtar (bu durumda ) kullanır:

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

baskılar

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

1

Karşılaştığım yararlı bir örnek yardımcı olabilir:

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)

Örnek giriş: 14445221

Örnek çıktı: (1,1) (3,4) (1,5) (2,2) (1,1)


1

Bu temel uygulama bu işlevi anlamama yardımcı oldu. Umarım başkalarına da yardımcı olur:

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

Kendi groupby fonksiyonunu yazabilirsiniz:

           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
yeniden icat tekerlek harika bir fikir değil, aynı zamanda kendi yazma değil, itertools groupby açıklamaktır
user2678074 16:19

1
@ user2678074 Haklısın. Bir öğrenme bakış açısı için kendi yazmak istiyorsanız bir şeydir.
Gökyüzü

2
Ayrıca daha iyi bir defaultdict (liste) kullanın, bu yüzden daha da kısa
Mickey Perlstein

@MickeyPerlstein ve daha hızlı.
funnydman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.