Kelompok panda dengan jumlah kumulatif

93

Saya ingin menambahkan kolom jumlah kumulatif ke dataframe Pandas saya sehingga:

name | day       | no
-----|-----------|----
Jack | Monday    | 10
Jack | Tuesday   | 20
Jack | Tuesday   | 10
Jack | Wednesday | 50
Jill | Monday    | 40
Jill | Wednesday | 110

menjadi:

Jack | Monday     | 10  | 10
Jack | Tuesday    | 30  | 40
Jack | Wednesday  | 50  | 90
Jill | Monday     | 40  | 40
Jill | Wednesday  | 110 | 150

Saya mencoba berbagai kombo df.groupbydan df.agg(lambda x: cumsum(x))tidak berhasil.

kc2819
sumber
Apakah Anda benar-benar yakin ingin agregasi selama hari kerja? Itu kehilangan indeks, dan juga jumlah kumulatif kurang masuk akal jika ada beberapa minggu. Jawaban oleh dmitry-andreev dan @vjayky menghitung cumsum selama urutan hari untuk setiap nama sebagai gantinya. Pikirkan bagaimana ini bisa diperpanjang jika ada kolom tanggal juga, yang entri dapat diurutkan sebelum pengelompokan dan agregasi.
Elias Hasle

Jawaban:

89

Ini harus dilakukan, perlu groupby()dua kali:

df.groupby(['name', 'day']).sum() \
  .groupby(level=0).cumsum().reset_index()

Penjelasan:

print(df)
   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110

# sum per name/day
print( df.groupby(['name', 'day']).sum() )
                 no
name day           
Jack Monday      10
     Tuesday     30
     Wednesday   50
Jill Monday      40
      Wednesday  110

# cumulative sum per name/day
print( df.groupby(['name', 'day']).sum() \
         .groupby(level=0).cumsum() )
                 no
name day           
Jack Monday      10
     Tuesday     40
     Wednesday   90
Jill Monday      40
     Wednesday  150

Kerangka data yang dihasilkan dari jumlah pertama diindeks oleh 'name'dan oleh 'day'. Anda dapat melihatnya dengan mencetak

df.groupby(['name', 'day']).sum().index 

Saat menghitung jumlah kumulatif, Anda ingin melakukannya dengan 'name', sesuai dengan indeks pertama (level 0).

Terakhir, gunakan reset_indexagar nama diulang.

df.groupby(['name', 'day']).sum().groupby(level=0).cumsum().reset_index()

   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   40
2  Jack  Wednesday   90
3  Jill     Monday   40
4  Jill  Wednesday  150
CT Zhu
sumber
3
Terima kasih atas jawabannya. Saya memang memiliki beberapa pertanyaan: 1. Bisakah Anda jelaskan apa artinya 'level = [0]'? 2. Juga, seperti yang Anda lihat, Anda memiliki nomor baris dalam bingkai data Anda sebelumnya dan nomor baris ini hilang setelah Anda melakukan penjumlahan kumulatif. Apakah ada cara untuk mendapatkannya kembali?
pengguna3694373
5
1), Nomor indeks harus pergi, karena cumsums berasal dari beberapa baris, seperti angka ke-2, 40, adalah 10 + 20 + 10, nilai indeks manakah yang harus didapat? 1, 2 atau 3? Jadi, mari terus gunakan namedan daysebagai multiIndex, yang lebih masuk akal ( reset_index()untuk mendapatkan intindeks, jika diinginkan). 2), level=[0]caranya groupbyadalah untuk mengoperasikan tingkat pertama MultiIndex, yaitu kolom name.
CT Zhu
Terima kasih CT. Saya memahaminya nanti dan mencoba reset_index () untuk menyelesaikan masalah saya. Terimakasih untuk penjelasan detilnya!
pengguna3694373
4
Ada bug halus: groupby()default pertama untuk menyortir kunci, jadi jika Anda menambahkan baris Jack-Thursday di bagian bawah dataset input Anda akan mendapatkan hasil yang tidak diharapkan. Dan karena groupby()dapat bekerja dengan nama level, saya menemukan df.groupby(['name', 'day'], sort=False).sum().groupby(by='name').cumsum().reset_index()lebih sedikit rahasia.
Nickolay
Bagaimana Anda mengganti nama kolom?
Jonathan Lam
47

Ini berfungsi di panda 0.16.2

In[23]: print df
        name          day   no
0      Jack       Monday    10
1      Jack      Tuesday    20
2      Jack      Tuesday    10
3      Jack    Wednesday    50
4      Jill       Monday    40
5      Jill    Wednesday   110
In[24]: df['no_cumulative'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
In[25]: print df
        name          day   no  no_cumulative
0      Jack       Monday    10             10
1      Jack      Tuesday    20             30
2      Jack      Tuesday    10             40
3      Jack    Wednesday    50             90
4      Jill       Monday    40             40
5      Jill    Wednesday   110            150
Dmitry Andreev
sumber
Menunjukkan cara menambahkannya kembali ke df sangat membantu. Saya mencoba menggunakan transformasi, tetapi itu tidak berfungsi dengan baik dengan cumsum ().
zerovector
2
Perhatikan bahwa jawaban ini (tampaknya setara dengan solusi sederhana oleh @vjayky ) tidak digabungkan dengan namedan daysebelum menghitung jumlah kumulatif oleh name(catatan: ada 2 baris untuk Jack + Tuesday di hasil). Inilah yang membuatnya lebih sederhana dari jawaban CT Zhu .
Nickolay
39

Modifikasi jawaban @ Dmitry. Ini lebih sederhana dan berfungsi di pandas 0.19.0:

print(df) 

 name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110

df['no_csum'] = df.groupby(['name'])['no'].cumsum()

print(df)
   name        day   no  no_csum
0  Jack     Monday   10       10
1  Jack    Tuesday   20       30
2  Jack    Tuesday   10       40
3  Jack  Wednesday   50       90
4  Jill     Monday   40       40
5  Jill  Wednesday  110      150
vjayky.dll
sumber
2
Ini tampaknya menjadi solusi paling sederhana jika Anda tidak memerlukan agregasi dua langkah , seperti yang diminta dalam pertanyaan.
Nickolay
Satu-satunya bagian yang tidak terlalu saya sukai adalah ia mengubah tipe int saya menjadi float.
Chris Farr
Ini harus menjadi jawaban yang diterima untuk cumsum di bagian grup. @ChrisFarr Sepertinya tidak berubah menjadi mengambang lagi bagi saya pada panda 1.0.3.
Louis Yang
8

kamu harus menggunakan

df['cum_no'] = df.no.cumsum()

http://pandas.pydata.org/pandas-docs/version/0.19.2/generated/pandas.DataFrame.cumsum.html

Cara lain untuk melakukannya

import pandas as pd
df = pd.DataFrame({'C1' : ['a','a','a','b','b'],
           'C2' : [1,2,3,4,5]})
df['cumsum'] = df.groupby(by=['C1'])['C2'].transform(lambda x: x.cumsum())
df

masukkan deskripsi gambar di sini

sushmit
sumber
3
Ini menghitung total berjalan global, bukan jumlah terpisah untuk setiap grup secara terpisah. Jadi Jill-Monday diberi nilai 130 ( 90, sebagai jumlah dari semua nilai Jack, + 40, nilai untuk Jill-Monday).
Nickolay
@Nickolay baru saja menambahkan jawaban lain, beri tahu saya jika berfungsi
sushmit
Saya tidak yakin apakah itu menghitung total lari global sesuai contoh baris 3 saya mendapat nilai 4
sushmit
Mengapa saya menggunakan lambda x: x.cumsum () di sini, bukan pandas.series.cumsum ()?
Jinhua Wang
7

Selain df.groupby(by=['name','day']).sum().groupby(level=[0]).cumsum() (lihat di atas), Anda juga bisa melakukan adf.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum()

  • df.groupby(by=['name','day']).sum() sebenarnya hanya memindahkan kedua kolom ke MultiIndex
  • as_index=False berarti Anda tidak perlu memanggil reset_index sesudahnya
Christoph
sumber
Terima kasih telah memposting ini, ini membantu saya memahami apa yang terjadi di sini! Perhatikan bahwa groupby().sum()tidak hanya memindahkan kedua kolom ke MultiIndex - ini juga merangkum dua nilai untuk Jack + Tuesday. Dan as_index=Falsetampaknya tidak berpengaruh apa pun dalam kasus ini, karena indeks sudah disetel sebelum groupby. Dan karena groupby().cumsum()nukes nama / hari dari kolom bingkai data, Anda harus menambahkan kolom numerik yang dihasilkan ke bingkai data asli (seperti saran vjayky dan Dmitry), atau memindahkan nama / hari ke indeks, dan reset_index sesudahnya.
Nickolay
0

data.csv:

name,day,no
Jack,Monday,10
Jack,Tuesday,20
Jack,Tuesday,10
Jack,Wednesday,50
Jill,Monday,40
Jill,Wednesday,110

Kode:

import numpy as np
import pandas as pd

df = pd.read_csv('data.csv')
print(df)
df = df.groupby(['name', 'day'])['no'].sum().reset_index()
print(df)
df['cumsum'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
print(df)

Keluaran:

   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   20
2  Jack    Tuesday   10
3  Jack  Wednesday   50
4  Jill     Monday   40
5  Jill  Wednesday  110
   name        day   no
0  Jack     Monday   10
1  Jack    Tuesday   30
2  Jack  Wednesday   50
3  Jill     Monday   40
4  Jill  Wednesday  110
   name        day   no  cumsum
0  Jack     Monday   10      10
1  Jack    Tuesday   30      40
2  Jack  Wednesday   50      90
3  Jill     Monday   40      40
4  Jill  Wednesday  110     150
Aaj Kaal
sumber