pandas dataframe groupby datetime mes

90

Considere un archivo 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

Puedo leer esto y reformatear la columna de fecha en formato de fecha y hora:

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

He intentado agrupar los datos por mes. Parece que debería haber una forma obvia de acceder al mes y agrupar por eso. Pero parece que no puedo hacerlo. ¿Alguien sabe cómo?

Lo que estoy intentando actualmente es volver a indexar por la fecha:

b.index=b['date']

Puedo acceder al mes así:

b.index.month

Sin embargo, parece que no puedo encontrar una función para agrupar por mes.

atomh33ls
fuente

Respuestas:

174

Conseguido hacerlo:

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+
atomh33ls
fuente
52
Creo que las formas más pandónicas son usar resample(cuando proporciona la funcionalidad que necesita) o usar TimeGrouper:df.groupby(pd.TimeGrouper(freq='M'))
Karl D.
10
para obtener la suma o el promedio de DataFrame del resultado, df.groupby(pd.TimeGrouper(freq='M')).sum()odf.groupby(pd.TimeGrouper(freq='M')).mean()
Alexandre
9
pd.TimeGrouperha quedado en desuso en favor de pd.Grouper, que es un poco más flexible, pero todavía toma freqy levelargumentos.
BallpointBen
el primer método no parece funcionar. Da el error, 'El objeto Serie no tiene atributo' mes '' para una Serie creada a través de to_datetime.
Ely
1
@ely La respuesta se basa implícitamente en las líneas de la pregunta original donde bse le da un índice después de leerse desde CSV. Agregue b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')después de la línea b = pd.read_csv('b.dat'). [Acabo de editar la respuesta también.]
Goodside
72

(actualización: 2018)

Tenga en cuenta que pd.Timegrouperestá depreciado y se eliminará. Utilice en su lugar:

 df.groupby(pd.Grouper(freq='M'))
PandasRocas
fuente
2
Encuentre los documentos de Grouper aquí y las especificaciones de frecuencia ( freq=...) aquí . Algunos ejemplos son freq=Dpara el día , freq=Bpara días laborables , freq=Wde semanas o incluso freq=Qde trimestres .
Kim
1
Encontré útil usar 'key' para evitar tener que reindexar el df, de la siguiente manera: df.groupby (pd.Grouper (key = 'your_date_column', freq = 'M'))
Edward
10

Una solución que evita MultiIndex es crear una nueva datetimeconfiguración de columna día = 1. Luego agrupe por esta columna. Ejemplo trivial a continuación.

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

# normalize day to beginning of month
df['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1)

# two alternative methods
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))

g = df.groupby('YearMonth')

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

# YearMonth
# 2017-10-01    15
# Name: Values, dtype: int64

El beneficio sutil de esta solución es, a diferencia de que pd.Grouperel índice de agrupamiento se normaliza al comienzo de cada mes en lugar de al final y, por lo tanto, puede extraer grupos fácilmente a través de get_group:

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

Calcular el último día de octubre es un poco más engorroso. pd.Grouper, a partir de la versión 0.23, admite un conventionparámetro, pero esto solo se aplica a un PeriodIndexagrupador.

jpp
fuente
8

Solución ligeramente alternativa a @ jpp's pero generando una YearMonthcadena:

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