pandas dataframe groupby datatime luna (Programare, Python, Pandas, Datetime, Pandas Groupby)

atomh33ls a intrebat.

Luați în considerare un fișier 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

Pot să citesc acest fișier și să reformez coloana de date în format datatime:

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

Am încercat să grupez datele în funcție de lună. Se pare că ar trebui să existe o modalitate evidentă de a accesa luna și de a grupa după aceasta. Dar se pare că nu reușesc să o fac. Știe cineva cum?

Ceea ce încerc în prezent este o re-indexare după dată:

b.index=b['date']

Pot să accesez luna în felul următor:

b.index.month

Cu toate acestea, nu reușesc să găsesc o funcție care să grupeze după lună.

Comentarii

  • Dacă aveți dificultăți în aplicarea oricărui răspuns, vă rugăm să țineți cont de faptul că în această întrebare (și, prin urmare, în răspunsuri) valoarea Datetime este atribuită indexului din Dataframe. Un mic sfat/reamintire ar putea fi următorul: dacă aveți o coloană Datetime, puteți de fapt să accesați valoarea unică Yeay/Month/Day/Hour/Minute doar făcând my_df.my_column.dt.month –  > Por Federico Dorato.
4 răspunsuri
atomh33ls

A reușit să o facă:

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

Sau

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

Comentarii

    55

  • Cred că modalitățile mai pandonice sunt fie să folosești resample (atunci când oferă funcționalitatea de care aveți nevoie) sau să folosiți un TimeGrouper: df.groupby(pd.TimeGrouper(freq='M')) –  > Por Karl D..
  • pentru a obține suma sau media DataFrame-ului rezultat, df.groupby(pd.TimeGrouper(freq='M')).sum() sau df.groupby(pd.TimeGrouper(freq='M')).mean() –  > Por Alexandre.
  • pd.TimeGrouper a fost eliminat în favoarea lui pd.Grouper, , care este puțin mai flexibil, dar care necesită în continuare freq și level argumente. –  > Por BallpointBen.
  • prima metodă nu pare să funcționeze. Se afișează eroarea „Series object has no attribute ‘month'” pentru o serie creată prin metoda to_datetime. –  > Por ely.
  • @ely Răspunsul se bazează implicit pe rândurile din întrebarea originală în care b primește un index după ce a fost citit din CSV. Adăugați b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p') după linia b = pd.read_csv('b.dat'). [Am editat și eu răspunsul chiar acum] –  > Por goodside.
PandasRocks

(actualizare: 2018)

Rețineți că pd.Timegrouper este depreciat și va fi eliminat. Utilizați în schimb:

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

Comentarii

  • Găsiți documentația Grouper aici și specificațiile de frecvență (freq=...) aici. Câteva exemple sunt freq=D pentru zile, , freq=B pentru zile lucrătoare, , freq=W pentru săptămâni sau chiar freq=Q pentru trimestre. –  > Por Kim.
  • Mi s-a părut util să folosesc „key” pentru a evita să trebuiască să reindexez df, după cum urmează: df.groupby(pd.Grouper(key=’your_date_column’, freq=’M’)) – –  > Por Edward.
  • Funcționează acest lucru dacă grupați după două coloane, dintre care doar una este coloana de valoare dată-timp? –  > Por exlo.
jpp

O soluție care evită MultiIndex este de a crea un nou fișier datetime coloană care să seteze day = 1. Apoi grupați după această coloană.

Normalizați ziua lunii

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)

Apoi utilizați groupby în mod normal:

g = df.groupby('YearMonth')

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

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

Comparație cu pd.Grouper

Avantajul subtil al acestei soluții este că, spre deosebire de pd.Grouper, , indicele de grupare este normalizat în funcție de începutul fiecărei luni și nu la sfârșitul lunii, și, prin urmare, puteți extrage cu ușurință grupurile prin intermediul get_group:

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

Calcularea ultimei zile a lunii octombrie este puțin mai greoaie. pd.Grouper, , începând cu v0.23, acceptă un convention dar acesta este aplicabil numai pentru un parametru PeriodIndex grupator.

Comparație cu conversia șirurilor de caractere

O alternativă la ideea de mai sus este convertirea într-un șir de caractere, de exemplu, convertirea datetime 2017-10-XX în șir de caractere '2017-10'. Cu toate acestea, acest lucru nu este recomandat, deoarece se pierd toate avantajele de eficiență ale unui datetime serie (stocată intern ca date numerice într-un bloc de memorie contiguă) față de un object serie de șiruri de caractere (stocate ca matrice de pointeri).

Comentarii

  • Consultați acest răspuns pentru modul corect de utilizare a decalajelor atunci când există deja valori day=1 : stackoverflow.com/a/45831333/9987623. –  > Por AlexK.
  • @AlexK, are pd.tseries.offsets are un avantaj față de pd.tseries.MonthBegin ? –  > Por jpp.
  • îmi pare rău, nu știu suficient pentru a le deosebi. Am adăugat doar comentariul pentru că dvs. df['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1) codul de mai sus schimbă orice dată care este deja prima zi a lunii în prima zi a lunii precedente. –  > Por AlexK.
  • @AlexK, Bună observație, am actualizat răspunsul în consecință. –  > Por jpp.
tsando

Soluție ușor alternativă la cea a lui @jpp, dar care scoate un YearMonth șir:

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