Bagaimana cara mengelompokkan baris data ke dalam daftar di grup panda dengan?

274

Saya memiliki bingkai data panda dfseperti:

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

Saya ingin mengelompokkan berdasarkan kolom pertama dan mendapatkan kolom kedua sebagai daftar di baris :

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

Apakah mungkin untuk melakukan sesuatu seperti ini menggunakan panda groupby?

Abhishek Thakur
sumber

Jawaban:

394

Anda dapat melakukan ini dengan menggunakan groupbyuntuk mengelompokkan pada kolom minat dan kemudian apply listke setiap grup:

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]
EdChum
sumber
7
Ini membutuhkan banyak waktu jika dataset sangat besar, katakanlah 10 juta baris. Apakah ada cara yang lebih cepat untuk melakukan ini? Jumlah uniques di 'a' sekitar 500k
Abhishek Thakur
6
groupby sangat lambat dan haus akan memori, yang bisa Anda lakukan adalah mengurutkan berdasarkan kolom A, kemudian menemukan idxmin dan idxmax (mungkin menyimpan ini dalam dict) dan menggunakan ini untuk mengiris dataframe Anda akan lebih cepat saya pikir
EdChum
1
Ketika saya mencoba solusi ini dengan masalah saya (memiliki beberapa kolom ke groupBy dan ke grup), itu tidak berfungsi - panda mengirim 'Function tidak mengurangi'. Kemudian saya menggunakan tuplejawaban kedua di sini: stackoverflow.com/questions/19530568/… . Lihat jawaban kedua di stackoverflow.com/questions/27439023/… untuk penjelasan.
Andarin
Solusi ini bagus, tetapi apakah ada cara untuk menyimpan set daftar, artinya dapatkah saya menghapus duplikat dan kemudian menyimpannya?
Sriram Arvind Lakshmanakumar
1
@PoeteMaudit Maaf, saya tidak mengerti apa yang Anda minta dan mengajukan pertanyaan dalam komentar adalah bentuk yang buruk di SO. Apakah Anda bertanya bagaimana menggabungkan beberapa kolom menjadi satu daftar?
EdChum
47

Jika kinerja penting, turun ke level numpy:

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

Tes:

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
BM
sumber
8
Bagaimana kita bisa menggunakan ini jika kita mengelompokkan dengan dua atau lebih kunci misalnya dengan .groupby([df.index.month, df.index.day])bukan hanya .groupby('a')?
ru111
25

Cara praktis untuk mencapai ini adalah:

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

Lihatlah penulisan Agregasi Khusus: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-using-py-py

Anamika Modi
sumber
5
lambda args: f(args)setara denganf
BallpointBen
6
Sebenarnya, agg(list)sudah cukup. Lihat juga di sini .
cs95
!! Saya hanya googling untuk beberapa sintaks dan menyadari notebook saya sendiri direferensikan untuk solusi lol. Terima kasih telah menghubungkan ini. Hanya untuk menambahkan, karena 'daftar' bukan fungsi seri, Anda harus menggunakannya dengan menerapkan df.groupby('a').apply(list)atau menggunakannya dengan agg sebagai bagian dari dikt df.groupby('a').agg({'b':list}). Anda juga dapat menggunakannya dengan lambda (yang saya sarankan) karena Anda dapat melakukan lebih banyak dengan itu. Contoh: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})yang memungkinkan Anda menerapkan fungsi seri ke col c dan unik lalu fungsi daftar ke col b.
Akshay Sehgal
21

Seperti yang Anda katakan groupbymetode pd.DataFrameobjek dapat melakukan pekerjaan.

Contoh

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

yang memberikan dan deskripsi indeks bijaksana kelompok.

Untuk mendapatkan elemen grup tunggal, Anda dapat melakukannya, misalnya

 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
Acorbe
sumber
21

Untuk mengatasi ini untuk beberapa kolom bingkai data:

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]

Jawaban ini terinspirasi dari jawaban Anamika Modi . Terima kasih!

Markus Dutschke
sumber
12

Gunakan salah satu berikut groupbydan aggresep.

# 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

Untuk menggabungkan beberapa kolom sebagai daftar, gunakan salah satu dari yang berikut:

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]

Untuk mengelompokkan grup hanya satu kolom, konversi grup dengan SeriesGroupByobjek, lalu panggil SeriesGroupBy.agg. Menggunakan,

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
cs95
sumber
Apakah metode di atas dijamin untuk menjaga ketertiban? artinya elemen dari baris yang sama (tetapi kolom yang berbeda, b dan c dalam kode Anda di atas) akan memiliki indeks yang sama dalam daftar yang dihasilkan?
Kai
@ Kai oh, pertanyaan bagus. Iya dan tidak. GroupBy mengurutkan output dengan nilai-nilai kunci kerapu. Namun jenisnya umumnya stabil sehingga pemesanan relatif per kelompok dipertahankan. Untuk menonaktifkan sepenuhnya perilaku penyortiran, gunakan groupby(..., sort=False). Di sini, tidak ada bedanya karena saya mengelompokkan pada kolom A yang sudah diurutkan.
cs95
maaf, saya tidak mengerti jawaban anda. Bisakah Anda jelaskan lebih detail. Saya pikir ini pantas untuk pertanyaan sendiri ..
Kai
1
Ini jawaban yang sangat bagus! Apakah ada cara untuk membuat nilai-nilai daftar ini unik? sesuatu seperti .agg (pd.Series.tolist.unique) mungkin?
Federico Gentile
1
@FedericoGentile Anda dapat menggunakan lambda. Ini salah satu caranya:df.groupby('a')['b'].agg(lambda x: list(set(x)))
cs95
7

Jika mencari daftar unik saat mengelompokkan beberapa kolom ini mungkin dapat membantu:

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

Mari kita gunakan df.groupbydengan daftar dan Serieskonstruktor

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
YOBEN_S
sumber
2

Sudah waktunya untuk menggunakan, aggbukan apply.

Kapan

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

Jika Anda ingin beberapa kolom ditumpuk ke dalam daftar, hasilkan pd.DataFrame

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

Jika Anda ingin satu kolom dalam daftar, hasilkan ps.Series

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

Catatan, hasilnya pd.DataFramesekitar 10x lebih lambat dari yang dihasilkan ps.Seriesketika Anda hanya mengagregasi satu kolom, gunakan dalam kasus multicolumns.

Mithril
sumber
0

Di sini saya telah mengelompokkan elemen dengan "|" sebagai pemisah

    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]
Ganesh Kharad
sumber
0

Cara termudah yang saya lihat tidak mencapai sebagian besar hal yang sama setidaknya untuk satu kolom yang mirip dengan jawaban Anamika hanya dengan sintaks tuple untuk fungsi agregat.

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
Metrd
sumber