panda grup oleh semacam dalam kelompok

166

Saya ingin mengelompokkan dataframe saya dengan dua kolom dan kemudian mengurutkan hasil agregat dalam grup.

In [167]:
df

Out[167]:
count   job source
0   2   sales   A
1   4   sales   B
2   6   sales   C
3   3   sales   D
4   7   sales   E
5   5   market  A
6   3   market  B
7   2   market  C
8   4   market  D
9   1   market  E

In [168]:
df.groupby(['job','source']).agg({'count':sum})

Out[168]:
            count
job     source  
market  A   5
        B   3
        C   2
        D   4
        E   1
sales   A   2
        B   4
        C   6
        D   3
        E   7

Sekarang saya ingin mengurutkan kolom hitung dalam urutan menurun di masing-masing grup. Dan kemudian hanya mengambil tiga baris teratas. Untuk mendapatkan sesuatu seperti:

            count
job     source  
market  A   5
        D   4
        B   3
sales   E   7
        C   6
        B   4
JoeDanger
sumber

Jawaban:

147

Apa yang ingin Anda lakukan sebenarnya adalah sebuah groupby (berdasarkan hasil dari group pertama): mengurutkan dan mengambil tiga elemen pertama per grup.

Mulai dari hasil group pertama oleh:

In [60]: df_agg = df.groupby(['job','source']).agg({'count':sum})

Kami mengelompokkan berdasarkan tingkat indeks pertama:

In [63]: g = df_agg['count'].groupby(level=0, group_keys=False)

Lalu kami ingin mengurutkan ('memesan') masing-masing grup dan mengambil tiga elemen pertama:

In [64]: res = g.apply(lambda x: x.order(ascending=False).head(3))

Namun, untuk ini, ada fungsi pintas untuk melakukan ini nlargest,:

In [65]: g.nlargest(3)
Out[65]:
job     source
market  A         5
        D         4
        B         3
sales   E         7
        C         6
        B         4
dtype: int64
Joris
sumber
Apakah akan ada cara untuk meringkas segala sesuatu yang tidak terkandung dalam tiga hasil teratas per grup dan menambahkannya ke grup sumber yang disebut "lain" untuk setiap pekerjaan?
JoeDanger
31
ordersudah ditinggalkan penggunaan sort_valuesbukannya
zthomas.nc
Terima kasih atas jawabannya. Untuk langkah selanjutnya, apakah akan ada cara untuk menetapkan urutan penyortiran berdasarkan nilai-nilai di kolom groupby? Misalnya, mengurutkan naik jika nilainya 'Beli' dan mengurutkan turun jika nilainya adalah 'Jual'.
Bowen Liu
174

Anda juga bisa melakukannya dalam sekali jalan, dengan melakukan sortir terlebih dahulu dan menggunakan head untuk mengambil 3 pertama dari setiap grup.

In[34]: df.sort_values(['job','count'],ascending=False).groupby('job').head(3)

Out[35]: 
   count     job source
4      7   sales      E
2      6   sales      C
1      4   sales      B
5      5  market      A
8      4  market      D
6      3  market      B
tvashtar
sumber
14
Apakah groupbyjaminan bahwa pesanan tetap terjaga?
toto_tico
52
Tampaknya memang demikian; dari dokumentasi groupby : groupby mempertahankan urutan baris dalam setiap grup
toto_tico
10
toto_tico- Itu benar, namun perlu berhati-hati dalam menafsirkan pernyataan itu. Urutan baris DALAM KELOMPOK TUNGGAL dipertahankan, namun groupby memiliki semacam = Pernyataan benar secara default yang berarti kelompok itu sendiri mungkin telah diurutkan pada kunci. Dengan kata lain jika kerangka data saya memiliki kunci (pada input) 3 2 2 1, .. grup dengan objek akan menampilkan 3 grup dalam urutan 1 2 3 (diurutkan). Gunakan sort = False untuk memastikan pesanan grup dan urutan baris dipertahankan.
user2103050
4
kepala (3) memberi lebih dari 3 hasil?
Nabin
27

Berikut adalah contoh lain dari mengambil 3 teratas pada urutan diurutkan, dan mengurutkan dalam grup:

In [43]: import pandas as pd                                                                                                                                                       

In [44]:  df = pd.DataFrame({"name":["Foo", "Foo", "Baar", "Foo", "Baar", "Foo", "Baar", "Baar"], "count_1":[5,10,12,15,20,25,30,35], "count_2" :[100,150,100,25,250,300,400,500]})

In [45]: df                                                                                                                                                                        
Out[45]: 
   count_1  count_2  name
0        5      100   Foo
1       10      150   Foo
2       12      100  Baar
3       15       25   Foo
4       20      250  Baar
5       25      300   Foo
6       30      400  Baar
7       35      500  Baar


### Top 3 on sorted order:
In [46]: df.groupby(["name"])["count_1"].nlargest(3)                                                                                                                               
Out[46]: 
name   
Baar  7    35
      6    30
      4    20
Foo   5    25
      3    15
      1    10
dtype: int64


### Sorting within groups based on column "count_1":
In [48]: df.groupby(["name"]).apply(lambda x: x.sort_values(["count_1"], ascending = False)).reset_index(drop=True)
Out[48]: 
   count_1  count_2  name
0       35      500  Baar
1       30      400  Baar
2       20      250  Baar
3       12      100  Baar
4       25      300   Foo
5       15       25   Foo
6       10      150   Foo
7        5      100   Foo
Surya
sumber
9

Coba ini sebagai gantinya

cara sederhana untuk melakukan 'groupby' dan menyortir dalam urutan menurun

df.groupby(['companyName'])['overallRating'].sum().sort_values(ascending=False).head(20)
SSCSWAPNIL
sumber
8

Jika Anda tidak perlu menjumlahkan kolom, maka gunakan jawaban @ tvashtar. Jika Anda perlu menjumlahkan, maka Anda dapat menggunakan jawaban @ joris 'atau yang ini sangat mirip dengannya.

df.groupby(['job']).apply(lambda x: (x.groupby('source')
                                      .sum()
                                      .sort_values('count', ascending=False))
                                     .head(3))
Ted Petrou
sumber