pandas dataframe groupby datetime mese


98

Considera un file 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

Posso leggerlo e riformattare la colonna della data nel formato datetime:

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

Ho provato a raggruppare i dati per mese. Sembra che dovrebbe esserci un modo ovvio per accedere al mese e raggruppare in base a quello. Ma non riesco a farlo. Qualcuno sa come?

Quello che sto provando attualmente è la reindicizzazione entro la data:

b.index=b['date']

Posso accedere al mese in questo modo:

b.index.month

Tuttavia non riesco a trovare una funzione da raggruppare per mese.


Se hai difficoltà ad applicare una qualsiasi delle risposte, tieni presente che in questa domanda (e quindi nelle risposte) il valore Datetime è assegnato all'indice del Dataframe. Un rapido suggerimento / promemoria potrebbe essere il seguente: se hai una colonna Datetime, puoi effettivamente accedere al singolo valore Yeay / Month / Day / Hour / Minute semplicemente facendomy_df.my_column.dt.month
Federico Dorato

Risposte:


181

Riuscito a farlo:

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

O

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

54
Penso che i modi più pandonici siano usare resample(quando fornisce le funzionalità di cui hai bisogno) o usare un TimeGrouper:df.groupby(pd.TimeGrouper(freq='M'))
Karl D.

10
per ottenere il risultato DataFrame somma o media, df.groupby(pd.TimeGrouper(freq='M')).sum()oppuredf.groupby(pd.TimeGrouper(freq='M')).mean()
Alexandre

9
pd.TimeGrouperè stato sconsigliato a favore di pd.Grouper, che è un po 'più flessibile ma accetta ancora freqe levelargomentazioni.
Ballpoint

il primo metodo non sembra funzionare. Fornisce l'errore "L'oggetto serie non ha attributo" mese "" per una serie creata tramite to_datetime.
ely

1
@ely La risposta si basa implicitamente sulle righe della domanda originale in cui bviene fornito un indice dopo essere stata letta da CSV. Aggiungi b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')dopo la riga b = pd.read_csv('b.dat'). [Ho anche modificato la risposta proprio ora.]
goodside

77

(aggiornamento: 2018)

Nota che pd.Timegrouperè ammortizzato e verrà rimosso. Usa invece:

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

2
Trova i documenti di Grouper qui e le specifiche di frequenza ( freq=...) qui . Alcuni esempi sono freq=Dper giorni , freq=Bper giorni lavorativi , freq=Wper settimane o anche freq=Qper trimestri .
Kim

3
Ho trovato utile usare 'key' per evitare di dover reindicizzare il df, come segue: df.groupby (pd.Grouper (key = 'your_date_column', freq = 'M'))
Edward

14

Una soluzione che evita MultiIndex è creare una nuova datetimecolonna impostando giorno = 1. Quindi raggruppare per questa colonna.

Normalizza il giorno del mese

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)

Quindi usa groupby normalmente:

g = df.groupby('YearMonth')

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

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

Confronto con pd.Grouper

Il sottile vantaggio di questa soluzione è, a differenza pd.Grouper, che l'indice delle cernie viene normalizzato all'inizio di ogni mese anziché alla fine, e quindi puoi facilmente estrarre i gruppi tramite get_group:

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

Calcolare l'ultimo giorno di ottobre è leggermente più macchinoso. pd.Grouper, a partire dalla v0.23, supporta un conventionparametro, ma questo è applicabile solo per un PeriodIndexraggruppatore.

Confronto con la conversione di stringhe

Un'alternativa all'idea di cui sopra è convertire in una stringa, ad esempio convertire datetime 2017-10-XXin stringa '2017-10'. Tuttavia, ciò non è consigliato poiché si perdono tutti i vantaggi in termini di efficienza di una datetimeserie (archiviata internamente come dati numerici in un blocco di memoria contiguo) rispetto a una objectserie di stringhe (memorizzata come matrice di puntatori).


Vedi questa risposta per il modo corretto di utilizzare gli offset quando ci sono già valori day = 1: stackoverflow.com/a/45831333/9987623 .
AlexK

@AlexK, pd.tseries.offsetsha un vantaggio rispetto pd.tseries.MonthBegin?
jpp

scusa, non ne so abbastanza per distinguerli. Ho appena aggiunto il commento perché il tuo df['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1)codice sopra cambia qualsiasi data che è già il primo del mese alla prima del mese precedente.
AlexK

@AlexK, buon posto, ho aggiornato la risposta di conseguenza.
jpp

8

Soluzione leggermente alternativa a @ jpp ma che emette una YearMonthstringa:

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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.