Extrayendo solo mes y año por separado de la columna Pandas Datetime

221

Tengo un Dataframe, df, con la siguiente columna:

df['ArrivalDate'] =
...
936   2012-12-31
938   2012-12-29
965   2012-12-31
966   2012-12-31
967   2012-12-31
968   2012-12-31
969   2012-12-31
970   2012-12-29
971   2012-12-31
972   2012-12-29
973   2012-12-29
...

Los elementos de la columna son pandas.tslib.Timestamp.

Solo quiero incluir el año y el mes. Pensé que habría una manera simple de hacerlo, pero no puedo entenderlo.

Esto es lo que he intentado:

df['ArrivalDate'].resample('M', how = 'mean')

Tuve el siguiente error:

Only valid with DatetimeIndex or PeriodIndex 

Entonces intenté:

df['ArrivalDate'].apply(lambda(x):x[:-2])

Tuve el siguiente error:

'Timestamp' object has no attribute '__getitem__' 

¿Alguna sugerencia?

Editar: lo descubrí.

df.index = df['ArrivalDate']

Entonces, puedo volver a muestrear otra columna usando el índice.

Pero todavía me gustaría un método para reconfigurar toda la columna. ¿Algunas ideas?

monkeybiz7
fuente
11
la mejor respuesta es claramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') como se muestra a continuación desde @ jaknap32
ihightower el
1
Ni siquiera tiene que hacer to_period: df.date_column.dt.month(o .year, o .day) funciona
elz
Posible duplicado del año de extracción de pandas
Code-Apprentice
2
@elphz: .dt.monthaunque pierde el año. Y .dt.to_period('M')cambia el tipo de datos a algo que ya no es datetime64. Terminé usando la respuesta de Juan sugiriendo .astype('datetime64[M]')truncar los valores.
Nickolay
¿Puedes cambiar la mejor respuesta?
Gonzalo García

Respuestas:

306

Si desea nuevas columnas que muestren año y mes por separado, puede hacer esto:

df['year'] = pd.DatetimeIndex(df['ArrivalDate']).year
df['month'] = pd.DatetimeIndex(df['ArrivalDate']).month

o...

df['year'] = df['ArrivalDate'].dt.year
df['month'] = df['ArrivalDate'].dt.month

Luego puede combinarlos o trabajar con ellos tal como son.

KieranPC
fuente
77
¿Hay alguna manera de hacer esto en una sola línea? Quiero evitar atravesar la misma columna varias veces.
Fixxxer
2
Algunos rápida evaluación comparativa con timeitsugiere que el DatetimeIndexenfoque es mucho más rápido que cualquiera de los dos .map/.applyo .dt.
Snorfalorpagus
2
la mejor respuesta es claramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') como se muestra a continuación desde @ jaknap32
ihightower el
¿Qué hace realmente pd.Datetimeindex?
JOHN
A veces hago esto: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca
229

La mejor manera encontrada !!

el df['date_column']tiene que estar en formato de fecha y hora.

df['month_year'] = df['date_column'].dt.to_period('M')

También puede usarlo Dpara Día, 2Mdurante 2 meses, etc. para diferentes intervalos de muestreo, y en caso de que uno tenga datos de series de tiempo con sello de tiempo, podemos elegir intervalos de muestreo granulares como 45Min45 minutos, 15Minpara muestreo de 15 minutos, etc.

kabrapankaj32
fuente
8
Tenga en cuenta que la columna resultante ya no es del tipo datetime64d. Usando df.my_date_column.astype('datetime64[M]'), como en la respuesta de @ Juan, se convierte en fechas que representan el primer día de cada mes.
Nickolay
3
Me sorprende que esto sea todo el camino hasta aquí.
Tim
154

Puedes acceder directamente a yearmonth atributos y , o solicitar un datetime.datetime:

In [15]: t = pandas.tslib.Timestamp.now()

In [16]: t
Out[16]: Timestamp('2014-08-05 14:49:39.643701', tz=None)

In [17]: t.to_pydatetime() #datetime method is deprecated
Out[17]: datetime.datetime(2014, 8, 5, 14, 49, 39, 643701)

In [18]: t.day
Out[18]: 5

In [19]: t.month
Out[19]: 8

In [20]: t.year
Out[20]: 2014

Una forma de combinar año y mes es hacer un número entero que los codifique, como: 201408para agosto de 2014. A lo largo de una columna completa, puede hacer esto como:

df['YearMonth'] = df['ArrivalDate'].map(lambda x: 100*x.year + x.month)

o muchas variantes de los mismos.

Sin embargo, no soy un gran fanático de hacer esto, ya que hace que la alineación de la fecha y la aritmética sean dolorosas más tarde y especialmente dolorosas para otros que se encuentran con su código o datos sin esta misma convención. Una mejor manera es elegir una convención del día del mes, como el último día de la semana no festivo en los EE. UU., O el primer día, etc., y dejar los datos en un formato de fecha / hora con la convención de fecha elegida.

El calendarmódulo es útil para obtener el valor numérico de ciertos días, como el último día de la semana. Entonces podrías hacer algo como:

import calendar
import datetime
df['AdjustedDateToEndOfMonth'] = df['ArrivalDate'].map(
    lambda x: datetime.datetime(
        x.year,
        x.month,
        max(calendar.monthcalendar(x.year, x.month)[-1][:5])
    )
)

Si está buscando una manera de resolver el problema más simple de simplemente formatear la columna de fecha y hora en alguna representación en cadena, para eso puede usar la strftimefunción de la datetime.datetimeclase, de esta manera:

In [5]: df
Out[5]: 
            date_time
0 2014-10-17 22:00:03

In [6]: df.date_time
Out[6]: 
0   2014-10-17 22:00:03
Name: date_time, dtype: datetime64[ns]

In [7]: df.date_time.map(lambda x: x.strftime('%Y-%m-%d'))
Out[7]: 
0    2014-10-17
Name: date_time, dtype: object
ely
fuente
44
El rendimiento puede ser malo, por lo que siempre es bueno hacer el mejor uso posible de las funciones auxiliares, las operaciones vectorizadas y las pandastécnicas de división, aplicación y combinación. Mis sugerencias anteriores no deben tomarse como un aval de que son los enfoques más eficaces para su caso, solo que son elecciones Pythonic estilísticamente válidas para una variedad de casos.
ely
La respuesta a continuación por @KieranPC es mucho más rápida
Ben
2
la mejor respuesta es claramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') como se muestra a continuación desde @ jaknap32
ihightower el
2
Se supone que debes multiplicar por 100 pulgadas df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month).
Git Gud
1
@ zthomas.nc Creo que funcionan mejor como dos respuestas separadas, ya que ofrecen dos formas muy diferentes de resolverlo.
ely
34

Si desea el par único de mes año, el uso de aplicar es bastante elegante.

df['mnth_yr'] = df['date_column'].apply(lambda x: x.strftime('%B-%Y')) 

Salidas mes-año en una columna.

No olvides cambiar primero el formato a fecha-hora antes, generalmente lo olvido.

df['date_column'] = pd.to_datetime(df['date_column'])
kabrapankaj32
fuente
También puede evitar la función lambda:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
Rishabh
13

Extrayendo el año decir de ['2018-03-04']

df['Year'] = pd.DatetimeIndex(df['date']).year  

El df ['Año'] crea una nueva columna. Mientras que si quieres extraer el mes solo usa .month

Douglas
fuente
1
Gracias, ha sido realmente útil date_1 = pd.DatetimeIndex (df ['date']) --year = date_1.year # Por años-- --month = date_1.month # Por meses-- --dy = date_1. día # Por días--
Edwin Torres
7

Primero puede convertir sus cadenas de fecha con pandas.to_datetime , que le da acceso a todas las facilidades nudosas de datetime y timedelta . Por ejemplo:

df['ArrivalDate'] = pandas.to_datetime(df['ArrivalDate'])
df['Month'] = df['ArrivalDate'].values.astype('datetime64[M]')
Juan A. Navarro
fuente
Esto funcionó muy bien para mí, ya que estaba buscando una funcionalidad análoga a la de pyspark trunc. ¿Hay alguna documentación para la astype('datetime64[M]')convención?
h1-the-swan
6

Gracias a jaknap32 , quería agregar los resultados según el año y el mes, así que funcionó:

df_join['YearMonth'] = df_join['timestamp'].apply(lambda x:x.strftime('%Y%m'))

La salida fue ordenada:

0    201108
1    201108
2    201108
Subespacio
fuente
6

La solución de @KieranPC es el enfoque correcto para Pandas, pero no es fácilmente extensible para atributos arbitrarios. Para esto, puede usar getattrdentro de un generador de comprensión y combinar usando pd.concat:

# input data
list_of_dates = ['2012-12-31', '2012-12-29', '2012-12-30']
df = pd.DataFrame({'ArrivalDate': pd.to_datetime(list_of_dates)})

# define list of attributes required    
L = ['year', 'month', 'day', 'dayofweek', 'dayofyear', 'weekofyear', 'quarter']

# define generator expression of series, one for each attribute
date_gen = (getattr(df['ArrivalDate'].dt, i).rename(i) for i in L)

# concatenate results and join to original dataframe
df = df.join(pd.concat(date_gen, axis=1))

print(df)

  ArrivalDate  year  month  day  dayofweek  dayofyear  weekofyear  quarter
0  2012-12-31  2012     12   31          0        366           1        4
1  2012-12-29  2012     12   29          5        364          52        4
2  2012-12-30  2012     12   30          6        365          52        4
jpp
fuente
1
df['year_month']=df.datetime_column.apply(lambda x: str(x)[:7])

Esto funcionó bien para mí, no pensé que los pandas interpretarían la fecha de cadena resultante como fecha, pero cuando hice la trama, conocía muy bien mi agenda y la cadena year_month donde se ordenó correctamente ... ¡tengo que amar a los pandas!

TICH
fuente
1

Hay dos pasos para extraer el año para todo el marco de datos sin utilizar el método.

Paso 1

convertir la columna a fecha y hora:

df['ArrivalDate']=pd.to_datetime(df['ArrivalDate'], format='%Y-%m-%d')

Paso 2

extraer el año o el mes usando el DatetimeIndex()método

 pd.DatetimeIndex(df['ArrivalDate']).year
abdellah el atouani
fuente
1

LÍNEA ÚNICA: Agregar una columna con pares 'año-mes ' : ('pd.to_datetime' primero cambia el tipo de columna a fecha-hora antes de la operación)

df['yyyy-mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%Y-%m')

En consecuencia, para una columna adicional de 'año' o 'mes':

df['yyyy'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%Y')

df['mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%m')
Matthi9000
fuente