pandas dataframe group โดย datetime month


98

พิจารณาไฟล์ csv:

string,date,number
a string,2/5/11 9:16am,1.0
a string,3/5/11 10:44pm,2.0
a string,4/22/11 12:07pm,3.0
a string,4/22/11 12:10pm,4.0
a string,4/29/11 11:59am,1.0
a string,5/2/11 1:41pm,2.0
a string,5/2/11 2:02pm,3.0
a string,5/2/11 2:56pm,4.0
a string,5/2/11 3:00pm,5.0
a string,5/2/14 3:02pm,6.0
a string,5/2/14 3:18pm,7.0

ฉันสามารถอ่านสิ่งนี้ได้และฟอร์แมตคอลัมน์วันที่ใหม่เป็นรูปแบบวันที่และเวลา:

b=pd.read_csv('b.dat')
b['date']=pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')

ฉันพยายามจัดกลุ่มข้อมูลตามเดือน ดูเหมือนว่าควรมีวิธีที่ชัดเจนในการเข้าถึงเดือนและจัดกลุ่มตามนั้น แต่ดูเหมือนจะทำไม่ได้ ไม่มีใครรู้วิธี?

สิ่งที่ฉันกำลังพยายามคือการจัดทำดัชนีใหม่ตามวันที่:

b.index=b['date']

ฉันสามารถเข้าถึงเดือนได้ดังนี้:

b.index.month

อย่างไรก็ตามฉันไม่สามารถหาฟังก์ชั่นที่จะรวมกันเป็นรายเดือนได้


หากคุณกำลังดิ้นรนกับการใช้คำตอบใด ๆ โปรดจำไว้ว่าในคำถามนี้ (และดังนั้นในคำตอบ) ค่า Datetime จะถูกกำหนดให้กับดัชนีของ Dataframe เคล็ดลับ / การแจ้งเตือนสั้น ๆ อาจเป็นดังต่อไปนี้: หากคุณมีคอลัมน์ Datetime คุณสามารถเข้าถึงค่า Yeay / Month / Day / Hour / Minute เดียวได้เพียงแค่ทำmy_df.my_column.dt.month
Federico Dorato

คำตอบ:


179

จัดการได้:

b = pd.read_csv('b.dat')
b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')
b.groupby(by=[b.index.month, b.index.year])

หรือ

b.groupby(pd.Grouper(freq='M'))  # update for v0.21+

54
ฉันคิดว่าจะใช้วิธี pandonic มากขึ้นresample(เมื่อมีฟังก์ชันที่คุณต้องการ) หรือใช้ a TimeGrouper:df.groupby(pd.TimeGrouper(freq='M'))
Karl D.

10
เพื่อรับผลรวม DataFrame หรือค่าเฉลี่ยdf.groupby(pd.TimeGrouper(freq='M')).sum()หรือdf.groupby(pd.TimeGrouper(freq='M')).mean()
Alexandre

9
pd.TimeGrouperได้รับการสนับสนุนpd.Grouperซึ่งมีความยืดหยุ่นมากกว่าเล็กน้อย แต่ยังคงใช้เวลาfreqและlevelข้อโต้แย้ง
BallpointBen

วิธีแรกดูเหมือนจะไม่ได้ผล มันทำให้ข้อผิดพลาด 'วัตถุซีรี่ส์ไม่มีแอตทริบิวต์ 'เดือน'' to_datetimeสำหรับซีรีส์ที่สร้างผ่าน
ely

1
@ely คำตอบโดยปริยายขึ้นอยู่กับบรรทัดในคำถามเดิมซึ่งbจะได้รับดัชนีหลังจากอ่านจาก CSV เพิ่มb.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')หลังบรรทัดb = pd.read_csv('b.dat'). [ฉันได้แก้ไขคำตอบแล้วในตอนนี้ด้วย]
goodside

77

(ปรับปรุง: 2018)

โปรดทราบว่าpd.Timegrouperเป็นค่าเสื่อมราคาและจะถูกลบออก ใช้แทน:

 df.groupby(pd.Grouper(freq='M'))

2
ค้นหาเอกสารกะรังที่นี่และข้อกำหนดความถี่ ( freq=...) ที่นี่ ตัวอย่างบางส่วนfreq=Dสำหรับวัน , freq=Bสำหรับวันทำการ , freq=Wสำหรับสัปดาห์ที่ผ่านมาหรือแม้กระทั่งfreq=Qสำหรับไตรมาส
Kim

3
ฉันพบว่ามีประโยชน์ในการใช้ 'คีย์' เพื่อหลีกเลี่ยงการทำดัชนี df ซ้ำดังนี้: df.groupby (pd.Grouper (key = 'your_date_column', freq = 'M'))
เอ็ดเวิร์ด

14

วิธีแก้ปัญหาวิธีหนึ่งที่หลีกเลี่ยง MultiIndex คือการสร้างdatetimeคอลัมน์การตั้งค่าวันใหม่ = 1 จากนั้นจัดกลุ่มตามคอลัมน์นี้

ทำให้วันของเดือนเป็นปกติ

df = pd.DataFrame({'Date': pd.to_datetime(['2017-10-05', '2017-10-20', '2017-10-01', '2017-09-01']),
                   'Values': [5, 10, 15, 20]})

# normalize day to beginning of month, 4 alternative methods below
df['YearMonth'] = df['Date'] + pd.offsets.MonthEnd(-1) + pd.offsets.Day(1)
df['YearMonth'] = df['Date'] - pd.to_timedelta(df['Date'].dt.day-1, unit='D')
df['YearMonth'] = df['Date'].map(lambda dt: dt.replace(day=1))
df['YearMonth'] = df['Date'].dt.normalize().map(pd.tseries.offsets.MonthBegin().rollback)

จากนั้นใช้groupbyตามปกติ:

g = df.groupby('YearMonth')

res = g['Values'].sum()

# YearMonth
# 2017-09-01    20
# 2017-10-01    30
# Name: Values, dtype: int64

เปรียบเทียบกับ pd.Grouper

ประโยชน์ที่ลึกซึ้งของโซลูชันนี้คือไม่เหมือนกับpd.Grouperดัชนีปลากะรังจะถูกทำให้เป็นมาตรฐานในช่วงต้นเดือนของแต่ละเดือนแทนที่จะเป็นจุดสิ้นสุดดังนั้นคุณจึงสามารถแยกกลุ่มได้อย่างง่ายดายผ่านทางget_group:

some_group = g.get_group('2017-10-01')

การคำนวณวันสุดท้ายของเดือนตุลาคมจะยุ่งยากกว่าเล็กน้อย pd.Grouperณ v0.23 รองรับconventionพารามิเตอร์ แต่ใช้ได้กับPeriodIndexปลากะรังเท่านั้น

เปรียบเทียบกับการแปลงสตริง

ทางเลือกในการคิดดังกล่าวข้างต้นคือการแปลงเป็นสตริงเช่นแปลง datetime สตริง2017-10-XX '2017-10'อย่างไรก็ตามไม่แนะนำให้ทำเช่นนี้เนื่องจากคุณสูญเสียประโยชน์ด้านประสิทธิภาพทั้งหมดของdatetimeชุดข้อมูล (เก็บไว้ภายในเป็นข้อมูลตัวเลขในบล็อกหน่วยความจำที่อยู่ติดกัน) เทียบกับobjectชุดของสตริง (จัดเก็บเป็นอาร์เรย์ของพอยน์เตอร์)


ดูคำตอบนี้สำหรับวิธีการที่เหมาะสมที่จะใช้ชดเชยเมื่อมีอยู่แล้ววัน = 1 ค่า: stackoverflow.com/a/45831333/9987623
AlexK

@ AlexK pd.tseries.offsetsมีข้อได้เปรียบกว่าpd.tseries.MonthBeginหรือไม่?
jpp

ขอโทษนะฉันไม่รู้ว่าจะแยกสิ่งเหล่านั้นออกไปได้ดีพอ ฉันเพิ่งเพิ่มความคิดเห็นเนื่องจากdf['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1)รหัสของคุณด้านบนเปลี่ยนวันที่ใด ๆ ที่เป็นวันแรกของเดือนเป็นวันแรกของเดือนก่อนหน้า
AlexK

@AlexK จุดดีมีการปรับปรุงคำตอบตามนี้
jpp

8

ทางเลือกอื่นในการแก้ปัญหาเล็กน้อยสำหรับ @ jpp แต่ส่งออกYearMonthสตริง:

df['YearMonth'] = pd.to_datetime(df['Date']).apply(lambda x: '{year}-{month}'.format(year=x.year, month=x.month))

res = df.groupby('YearMonth')['Values'].sum()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.