pandalar veri çerçeve grubu tarih ve aya göre


98

Bir csv dosyası düşünün:

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

Bunu içinde okuyabilir ve tarih sütununu datetime biçiminde yeniden biçimlendirebilirim:

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

Verileri aylara göre gruplandırmaya çalışıyorum. Aya erişmenin ve buna göre gruplandırmanın açık bir yolu olmalı gibi görünüyor. Ama bunu yapacak gibi görünmüyorum. Nasıl olduğunu bilen var mı?

Şu anda denediğim şey tarihe göre yeniden indeksleniyor:

b.index=b['date']

Aya şöyle erişebilirim:

b.index.month

Ancak aylara göre bir araya toplayacak bir işlev bulamıyorum.


Cevaplardan herhangi birini uygulamakta zorlanıyorsanız, lütfen bu soruda (ve dolayısıyla cevaplarda) Datetime değerinin Dataframe dizinine atandığını unutmayın. Hızlı bir ipucu / hatırlatıcı şunlar olabilir: Bir Tarih / Saat sütununuz varsa, aslında tek Yeay / Ay / Gün / Saat / Dakika değerine yalnızca şunu yaparak erişebilirsinizmy_df.my_column.dt.month
Federico Dorato

Yanıtlar:


181

Yapmayı başardı:

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

Veya

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

54
Daha pandonic yolları ya kullanımına olduğunu düşünüyorum resample(ihtiyacınız işlevsellik sağlar olduğunda) veya kullanmak TimeGrouper:df.groupby(pd.TimeGrouper(freq='M'))
Karl D.

10
DataFrame sonucunu veya ortalamasını almak için, df.groupby(pd.TimeGrouper(freq='M')).sum()veyadf.groupby(pd.TimeGrouper(freq='M')).mean()
Alexandre

9
pd.TimeGrouperlehine kullanımdan kaldırıldı pd.Grouper, bu biraz daha esnek ama yine de alıyor freqve leveltartışıyor.
BallpointBen

ilk yöntem işe yaramıyor gibi görünüyor. Bu, üzerinden oluşturulan bir Seri için 'Series nesnesinin' ay 'özniteliği yok' 'hatasını verir to_datetime.
ely

1
@ely Cevap, CSV'den okunduktan bsonra bir indeks verildiği orijinal sorudaki satırlara dayanıyor . Satırdan b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')sonra ekleyin b = pd.read_csv('b.dat'). [Yanıtı şimdi de düzenledim.]
goodside

77

(güncelleme: 2018)

Bunun pd.Timegrouperamortismana tabi olduğunu ve kaldırılacağını unutmayın. Bunun yerine kullanın:

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

2
Gruplandırıcısı docs bulun burada ve frekans özelliklerine ( freq=...) burada . Bazı örnekler şunlardır freq=Diçin gün , freq=Biçin iş günü , freq=Wiçin hafta hatta freq=Qiçin dörtte .
Kim

3
Aşağıdaki gibi, df'yi yeniden dizinlemek zorunda kalmamak için 'key' kullanmayı yararlı buldum: df.groupby (pd.Grouper (key = 'your_date_column', freq = 'M'))
Edward

14

MultiIndex'i engelleyen bir çözüm, yeni bir datetimesütun ayarı day = 1. oluşturmaktır . Sonra bu sütuna göre gruplandırın.

Ayın gününü normalleştir

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)

O zaman groupbynormal olarak kullanın :

g = df.groupby('YearMonth')

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

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

İle karşılaştırıldığında pd.Grouper

Bu çözümün hafif yararı pd.Grouper, gruplama endeksinin sondan ziyade her ayın başına normalleştirilmesidir ve bu nedenle grupları şu yolla kolayca çıkarabilirsiniz get_group:

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

Ekim ayının son gününü hesaplamak biraz daha zahmetlidir. pd.Grouper, v0.23'ten itibaren bir conventionparametreyi destekler , ancak bu yalnızca bir gruplayıcı için geçerlidir PeriodIndex.

Dize dönüştürme ile karşılaştırma

Yukarıdaki fikre bir alternatif, bir dizeye dönüştürmek, örneğin tarih saati 2017-10-XXdizeye dönüştürmektir '2017-10'. Ancak, bir dizi diziye datetimekarşı object(bir dizi işaretçi olarak saklanan) bir dizinin tüm verimlilik avantajlarını ( bitişik bir bellek bloğunda dahili olarak sayısal veriler olarak depolanır) kaybettiğiniz için bu önerilmez .


Zaten gün = 1 değerleri olduğunda ofsetleri kullanmanın doğru yolu için bu yanıta bakın: stackoverflow.com/a/45831333/9987623 .
AlexK

@AlexK, pd.tseries.offsetsüzerinde bir avantajı var pd.tseries.MonthBeginmı?
jpp

üzgünüm, bunları birbirinden ayıracak kadar bilmiyorum. Yorumu ekledim çünkü df['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1)yukarıdaki kodunuz zaten ayın ilkinden önceki ayın ilkine kadar olan herhangi bir tarihi değiştiriyor.
AlexK

@AlexK, İyi nokta, cevabı buna göre güncelledi.
jpp

8

@ Jpp'ye biraz alternatif çözüm ancak bir YearMonthdize çıktı:

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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.