กลุ่มหมีแพนด้าโดยผลรวมสะสม


94

ฉันต้องการเพิ่มคอลัมน์ผลรวมสะสมในดาต้าเฟรม Pandas ของฉันเพื่อให้:

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

กลายเป็น:

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

ฉันลองใช้คอมโบต่างๆdf.groupbyและdf.agg(lambda x: cumsum(x))ไม่มีประโยชน์


คุณแน่ใจจริงๆหรือว่าต้องการรวมในสัปดาห์วัน นั่นทำให้ดัชนีเสียไปและผลรวมสะสมก็สมเหตุสมผลน้อยกว่าหากมีเวลาหลายสัปดาห์ คำตอบโดย dmitry-andreev และ @vjayky คำนวณ cumsum ตามลำดับวันสำหรับแต่ละชื่อแทน ลองนึกดูว่าจะขยายได้อย่างไรหากมีคอลัมน์วันที่ด้วยเช่นกันซึ่งรายการต่างๆจะถูกจัดเรียงตามก่อนการจัดกลุ่มและการรวม
Elias Hasle

คำตอบ:


90

สิ่งนี้ควรทำต้องใช้groupby()สองครั้ง:

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

คำอธิบาย:

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

dataframe ที่เกิดจากการรวมแรกคือการจัดทำดัชนีโดยและ'name' 'day'คุณสามารถดูได้โดยการพิมพ์

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

เมื่อคำนวณผลรวมสะสมคุณต้องทำโดย'name'สอดคล้องกับดัชนีแรก (ระดับ 0)

สุดท้ายใช้reset_indexเพื่อให้มีชื่อซ้ำ

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

3
ขอบคุณสำหรับคำตอบ. ฉันมีข้อสงสัยบางประการ: 1. คุณช่วยอธิบายได้ไหมว่า 'level = [0]' หมายความว่าอะไร? 2. อย่างที่คุณเห็นคุณมีหมายเลขแถวในกรอบข้อมูลก่อนหน้านี้และหมายเลขแถวเหล่านี้จะหายไปเมื่อคุณทำผลรวมสะสม มีวิธีให้พวกเขากลับมาหรือไม่?
user3694373

5
1) หมายเลขดัชนีต้องไปเนื่องจาก cumsums มาจากหลายแถวเช่นหมายเลข 2, 40 คือ 10 + 20 + 10 ควรได้รับค่าดัชนีใด 1, 2 หรือ 3? ลองใช้ต่อไปnameและdayตามmultiIndexที่เหมาะสมกว่า ( reset_index()เพื่อรับintดัชนีถ้าต้องการ) 2) ที่level=[0]หมายถึงgroupbyคือการทำงานโดยระดับที่ 1 คือคอลัมน์MultiIndex name
CT Zhu

ขอบคุณ CT. ฉันเข้าใจในภายหลังและลอง reset_index () เพื่อแก้ปัญหาของฉัน ขอบคุณสำหรับคำอธิบายโดยละเอียด!
user3694373

4
มีข้อบกพร่องเล็กน้อย: groupby()ค่าเริ่มต้นแรกในการจัดเรียงคีย์ดังนั้นหากคุณเพิ่มแถว Jack-Thursday ที่ด้านล่างของชุดข้อมูลอินพุตคุณจะได้ผลลัพธ์ที่ไม่คาดคิด และเนื่องจากgroupby()สามารถทำงานกับชื่อระดับฉันพบว่าdf.groupby(['name', 'day'], sort=False).sum().groupby(by='name').cumsum().reset_index()มีความลับน้อยลง
Nickolay

คุณเปลี่ยนชื่อคอลัมน์ได้อย่างไร?
Jonathan Lam

47

สิ่งนี้ใช้ได้กับแพนด้า 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

การแสดงวิธีเพิ่มกลับไปที่ df นั้นมีประโยชน์มาก ฉันลองใช้การแปลงร่าง แต่มันก็ไม่ได้ดีกับ cumsum ()
zerovector

2
โปรดทราบว่าคำตอบนี้ (ดูเหมือนจะเทียบเท่ากับวิธีแก้ปัญหาที่ง่ายกว่าโดย @vjayky ) จะไม่รวมโดยnameและdayก่อนการคำนวณผลรวมสะสมตามname(หมายเหตุ: มี 2 แถวสำหรับ Jack + Tuesday ในผลลัพธ์) นี่คือสิ่งที่ทำให้มันง่ายกว่าคำตอบโดย CT จู้
Nickolay

39

การปรับเปลี่ยนเป็นคำตอบของ @ Dmitry มันง่ายกว่าและใช้ได้กับแพนด้า 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

2
นี่ดูเหมือนจะเป็นวิธีแก้ปัญหาที่ง่ายที่สุดหากคุณไม่ต้องการการรวมสองขั้นตอนตามที่ร้องขอในคำถาม
Nickolay

ส่วนเดียวที่ฉันไม่ชอบเป็นพิเศษคือมันแปลง int dtype ของฉันให้เป็น float
Chris Farr

นี่ควรเป็นคำตอบที่ยอมรับสำหรับ cumsum ในส่วนของกลุ่ม @ChrisFarr ดูเหมือนว่าจะไม่เปลี่ยนเป็นลอยอีกต่อไปสำหรับฉันในฐานะแพนด้า 1.0.3
Louis Yang

8

คุณควรใช้

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

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

อีกวิธีหนึ่งในการทำ

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

ป้อนคำอธิบายภาพที่นี่


3
ซึ่งจะคำนวณผลรวมที่ทำงานทั่วโลกแทนที่จะเป็นผลรวมที่แยกกันสำหรับแต่ละกลุ่ม ดังนั้น Jill-Monday จึงได้รับการกำหนดค่าเป็น 130 ( 90ซึ่งเป็นผลรวมของค่าทั้งหมดของ Jack, + 40, ค่าสำหรับ Jill-Monday)
Nickolay

@ Nickolay เพิ่งเพิ่มคำตอบอื่นแจ้งให้เราทราบว่ามันใช้งานได้หรือไม่
sushmit

ฉันไม่แน่ใจว่ามันคำนวณผลรวมการวิ่งทั่วโลกตามตัวอย่างแถวที่ 3 ของฉันได้ค่าเป็น 4 หรือไม่
sushmit

เหตุใดฉันจึงใช้ lambda x: x.cumsum () ที่นี่แทนที่จะเป็น pandas.series.cumsum ()
Jinhua Wang

7

แทนที่จะเป็นdf.groupby(by=['name','day']).sum().groupby(level=[0]).cumsum() (ดูด้านบน) คุณสามารถทำไฟล์df.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum()

  • df.groupby(by=['name','day']).sum() เป็นเพียงการย้ายคอลัมน์ทั้งสองไปยัง MultiIndex
  • as_index=False หมายความว่าคุณไม่จำเป็นต้องเรียก reset_index ในภายหลัง

ขอบคุณที่โพสต์สิ่งนี้ช่วยให้ฉันเข้าใจว่าเกิดอะไรขึ้นที่นี่! โปรดทราบว่าgroupby().sum()ไม่ใช่แค่การย้ายทั้งสองคอลัมน์ไปยัง MultiIndex แต่ยังรวมค่าสองค่าสำหรับ Jack + Tuesday และas_index=Falseดูเหมือนจะไม่มีผลใด ๆ ในกรณีนี้เนื่องจากดัชนีถูกตั้งค่าไว้ก่อนหน้าgroupby. และเนื่องจากgroupby().cumsum()nukes ชื่อ / วันจากคอลัมน์ของ data frame คุณต้องเพิ่มคอลัมน์ตัวเลขที่เป็นผลลัพธ์ลงใน data frame เดิม (เช่น vjayky และ Dmitry ที่แนะนำ) หรือย้ายชื่อ / วันไปที่ดัชนีและ reset_index ในภายหลัง
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

รหัส:

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)

เอาท์พุต:

   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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.