Dataframe satırlarını panda groupby'de listeye nasıl gruplayabilirim?


274

Panda veri çerçevesi dfgibi:

a b
A 1
A 2
B 5
B 5
B 4
C 6

İlk sütuna göre gruplandırmak ve ikinci sütunu satırlar halinde listeler olarak almak istiyorum :

A [1,2]
B [5,5,4]
C [6]

Pandalar groupby kullanarak böyle bir şey yapmak mümkün mü?

Yanıtlar:


394

Bunu, groupbyilgili sütunda gruplamak için ve ardından apply listher gruba kullanarak yapabilirsiniz:

In [1]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6]})
        df

Out[1]: 
   a  b
0  A  1
1  A  2
2  B  5
3  B  5
4  B  4
5  C  6

In [2]: df.groupby('a')['b'].apply(list)
Out[2]: 
a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

In [3]: df1 = df.groupby('a')['b'].apply(list).reset_index(name='new')
        df1
Out[3]: 
   a        new
0  A     [1, 2]
1  B  [5, 5, 4]
2  C        [6]

7
Veri kümesi çok büyükse bu çok zaman alır, örneğin 10 milyon satır. Bunu yapmanın daha hızlı bir yolu var mı? 'A' daki benzersizlerin sayısı 500k civarındadır
Abhishek Thakur

6
groupby kötü bir şekilde yavaş ve hafıza aç, yapabileceğiniz şey A sütununa göre sıralamak, sonra idxmin ve idxmax bulmak (muhtemelen bunu bir dikte depolamak) ve bunu veri çerçevenizi dilimlemek için kullanın sanırım
EdChum

1
Benim sorunum (groupBy ve grup için birden çok sütun olması) ile bu çözümü denediğimde, işe yaramadı - 'İşlev azalmaz' gönderilen pandalar. Sonra tupleburada ikinci cevabı izledim : stackoverflow.com/questions/19530568/… . Açıklama için stackoverflow.com/questions/27439023/… adresindeki ikinci cevaba bakınız .
Andarin

Bu çözüm iyi, ancak liste kümesini saklamanın bir yolu var, yani kopyaları kaldırabilir ve sonra saklayabilir miyim?
Sriram Arvind Lakshmanakumar

1
@PoeteMaudit Maalesef, ne soruyorsun ve yorumlarda soru sormanın SO'daki kötü form olduğunu anlamıyorum. Birden çok sütunu tek bir listede nasıl birleştireceğinizi mi soruyorsunuz?
EdChum

47

Performans önemliyse, numpy seviyesine inin:

import numpy as np

df = pd.DataFrame({'a': np.random.randint(0, 60, 600), 'b': [1, 2, 5, 5, 4, 6]*100})

def f(df):
         keys, values = df.sort_values('a').values.T
         ukeys, index = np.unique(keys, True)
         arrays = np.split(values, index[1:])
         df2 = pd.DataFrame({'a':ukeys, 'b':[list(a) for a in arrays]})
         return df2

Testler:

In [301]: %timeit f(df)
1000 loops, best of 3: 1.64 ms per loop

In [302]: %timeit df.groupby('a')['b'].apply(list)
100 loops, best of 3: 5.26 ms per loop

8
Örneğin .groupby([df.index.month, df.index.day]), sadece yerine iki veya daha fazla tuşa göre gruplandırıyorsak bunu nasıl kullanabiliriz .groupby('a')?
ru111

25

Bunu başarmanın kullanışlı bir yolu:

df.groupby('a').agg({'b':lambda x: list(x)})

Özel Toplamalar yazmaya bakın: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-using-py


5
lambda args: f(args)eşdeğerf
BallpointBen

6
Aslında, agg(list)yeter. Ayrıca buraya bakınız .
cs95

!! Ben sadece bazı sözdizimi için googling ve kendi dizüstü çözüm lol için başvurdu fark etti. Bunu bağladığınız için teşekkürler. Eklemek gerekirse, 'list' bir dizi işlevi olmadığından, ya uygula df.groupby('a').apply(list)ya da bir dikte parçası olarak agg ile kullanmanız gerekecektir df.groupby('a').agg({'b':list}). Onunla çok daha fazlasını yapabileceğiniz için lambda (da tavsiye ederim) ile de kullanabilirsiniz. Örnek: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})col c öğesine bir seri işlevi ve col b öğesine benzersiz bir liste işlevi uygulamanızı sağlar.
Akshay Sehgal

21

Dediğiniz gibi groupby, bir pd.DataFramenesnenin yöntemi işi yapabilir.

Misal

 L = ['A','A','B','B','B','C']
 N = [1,2,5,5,4,6]

 import pandas as pd
 df = pd.DataFrame(zip(L,N),columns = list('LN'))


 groups = df.groupby(df.L)

 groups.groups
      {'A': [0, 1], 'B': [2, 3, 4], 'C': [5]}

ve grupların indeks bazında tanımını yapar.

Tek grup öğelerini almak için, örneğin şunları yapabilirsiniz:

 groups.get_group('A')

     L  N
  0  A  1
  1  A  2

  groups.get_group('B')

     L  N
  2  B  5
  3  B  5
  4  B  4

21

Bunu bir veri çerçevesinin birkaç sütunu için çözmek için:

In [5]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6],'c'
   ...: :[3,3,3,4,4,4]})

In [6]: df
Out[6]: 
   a  b  c
0  A  1  3
1  A  2  3
2  B  5  3
3  B  5  4
4  B  4  4
5  C  6  4

In [7]: df.groupby('a').agg(lambda x: list(x))
Out[7]: 
           b          c
a                      
A     [1, 2]     [3, 3]
B  [5, 5, 4]  [3, 4, 4]
C        [6]        [4]

Bu cevap Anamika Modi'nin cevabından ilham aldı . Teşekkür ederim!


12

Aşağıdakilerden herhangi birini groupbyve aggtarifleri kullanın .

# Setup
df = pd.DataFrame({
  'a': ['A', 'A', 'B', 'B', 'B', 'C'],
  'b': [1, 2, 5, 5, 4, 6],
  'c': ['x', 'y', 'z', 'x', 'y', 'z']
})
df

   a  b  c
0  A  1  x
1  A  2  y
2  B  5  z
3  B  5  x
4  B  4  y
5  C  6  z

Birden çok sütunu liste olarak toplamak için aşağıdakilerden birini kullanın:

df.groupby('a').agg(list)
df.groupby('a').agg(pd.Series.tolist)

           b          c
a                      
A     [1, 2]     [x, y]
B  [5, 5, 4]  [z, x, y]
C        [6]        [z]

Yalnızca tek bir sütunu grup olarak listelemek için, groupby'yi bir SeriesGroupBynesneye dönüştürün ve arayın SeriesGroupBy.agg. Kullanım,

df.groupby('a').agg({'b': list})  # 4.42 ms 
df.groupby('a')['b'].agg(list)    # 2.76 ms - faster

a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

yukarıdaki yöntemlerin düzeni koruma garantisi var mı? aynı satırdaki öğelerin (ancak yukarıdaki kodunuzdaki farklı sütunlar, b ve c ) sonuç listelerinde aynı dizine sahip olacağı anlamına mı geliyor?
Kai

@Kai oh, güzel soru. Evet ve hayır. GroupBy çıktıyı orfoz anahtar değerlerine göre sıralar. Bununla birlikte, sıralama genel olarak stabildir, bu nedenle grup başına nispi sıralama korunur. Sıralama davranışını tamamen devre dışı bırakmak için tuşunu kullanın groupby(..., sort=False). Burada, zaten sıralanmış A sütununda gruplama yaptığım için hiçbir fark yaratmaz.
cs95

üzgünüm, cevabınızı anlamıyorum. Daha ayrıntılı açıklayabilir misiniz? Bence bu kendi sorusunu hak ediyor ..
Kai

1
Bu çok iyi bir cevap! Listenin değerlerini benzersiz yapmanın bir yolu var mı? .agg (pd.Series.tolist.unique) gibi bir şey olabilir mi?
Federico Gentile

1
@FedericoGentile bir lambda kullanabilirsiniz. İşte bir yolu:df.groupby('a')['b'].agg(lambda x: list(set(x)))
cs95

7

Birden çok sütunu gruplarken benzersiz bir liste arıyorsanız, bu muhtemelen yardımcı olabilir:

df.groupby('a').agg(lambda x: list(set(x))).reset_index()

2

df.groupbyListe ve Seriesyapıcı ile birlikte kullanalım

pd.Series({x : y.b.tolist() for x , y in df.groupby('a')})
Out[664]: 
A       [1, 2]
B    [5, 5, 4]
C          [6]
dtype: object

2

Kullanma zamanı aggyerine apply.

Ne zaman

df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6], 'c': [1,2,5,5,4,6]})

Listede birden çok sütun yığını olmasını istiyorsanız, pd.DataFrame

df.groupby('a')[['b', 'c']].agg(list)
# or 
df.groupby('a').agg(list)

Listede tek bir sütun istiyorsanız, ps.Series

df.groupby('a')['b'].agg(list)
#or
df.groupby('a')['b'].apply(list)

Not sonuç pd.DataFramedaha yavaş sonuç daha 10x hakkındadır ps.Seriessadece agrega tek sütun, multicolumns durumda kullandığınızda.


0

Burada öğeleri "|" ile gruplandırdım ayırıcı olarak

    import pandas as pd

    df = pd.read_csv('input.csv')

    df
    Out[1]:
      Area  Keywords
    0  A  1
    1  A  2
    2  B  5
    3  B  5
    4  B  4
    5  C  6

    df.dropna(inplace =  True)
    df['Area']=df['Area'].apply(lambda x:x.lower().strip())
    print df.columns
    df_op = df.groupby('Area').agg({"Keywords":lambda x : "|".join(x)})

    df_op.to_csv('output.csv')
    Out[2]:
    df_op
    Area  Keywords

    A       [1| 2]
    B    [5| 5| 4]
    C          [6]

0

Gördüğüm en kolay yol, Anamika'nın sadece toplama işlevi için söz dizimi ile cevabına benzeyen bir sütun için aynı şeyin çoğunu elde edemiyorum .

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
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.