Extraindo apenas mês e ano separadamente da coluna Pandas Datetime

221

Eu tenho um Dataframe, df, com a seguinte coluna:

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
...

Os elementos da coluna são pandas.tslib.Timestamp.

Quero incluir apenas o ano e o mês. Eu pensei que haveria uma maneira simples de fazer isso, mas não consigo descobrir.

Aqui está o que eu tentei:

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

Eu recebi o seguinte erro:

Only valid with DatetimeIndex or PeriodIndex 

Então eu tentei:

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

Eu recebi o seguinte erro:

'Timestamp' object has no attribute '__getitem__' 

Alguma sugestão?

Edit: Eu meio que descobri isso.

df.index = df['ArrivalDate']

Em seguida, posso reamostrar outra coluna usando o índice.

Mas eu ainda gostaria de um método para reconfigurar a coluna inteira. Alguma ideia?

monkeybiz7
fonte
11
a melhor resposta é claramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') como abaixo em @ jaknap32
ihightower
1
Você nem sequer tem que fazer to_period: df.date_column.dt.month(ou .year, ou .day) funciona
Elz
2
@elphz: .dt.monthperde o ano embora. E .dt.to_period('M')altera o tipo de dados para algo que não é mais um datetime64. Acabei usando a resposta de Juan sugerindo .astype('datetime64[M]')truncar os valores.
Nickolay
Você pode mudar a melhor resposta?
Gonzalo Garcia

Respostas:

306

Se você deseja que novas colunas sejam exibidas ano e mês separadamente, faça o seguinte:

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

ou...

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

Então você pode combiná-los ou trabalhar com eles exatamente como são.

KieranPC
fonte
7
Existe uma maneira de fazer isso em uma única linha? Eu quero evitar atravessar a mesma coluna várias vezes.
Fixxxer #
2
Alguns testes comparativos rápidos timeitsugerem que a DatetimeIndexabordagem é significativamente mais rápida que um .map/.applyou outro .dt.
Snorfalorpagus 25/10
2
a melhor resposta é claramente .. df [ 'mnth_yr'] = df.date_column.dt.to_period ( 'M') como abaixo de @ jaknap32
ihightower
o que realmente faz o pd.Datetimeindex?
JOHN
Às vezes, faço o seguinte: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca
229

Melhor maneira encontrada !!

o df['date_column']deve estar no formato de data e hora.

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

Você também pode usar o Ddia, 2Mpor 2 meses etc. para diferentes intervalos de amostragem e, caso haja dados de séries temporais com registro de data e hora, podemos optar por intervalos de amostragem granulares, como 45Min45 minutos, 15Min15 minutos etc.

kabrapankaj32
fonte
8
Observe que a coluna resultante não é mais do datetime64dtype. Usando df.my_date_column.astype('datetime64[M]'), como na resposta de @ Juan, converte-se em datas que representam o primeiro dia de cada mês.
Nickolay
3
Estou surpreso que este seja todo o caminho até aqui.
Tim
154

Você pode acessar diretamente o yeare monthatributos, ou solicitar um 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

Uma maneira de combinar ano e mês é criar um número inteiro codificando-os, como: 201408para agosto de 2014. Ao longo de uma coluna inteira, você pode fazer o seguinte:

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

ou muitas variantes dos mesmos.

Porém, não sou muito fã disso, pois torna o alinhamento e a aritmética de datas dolorosos mais tarde e especialmente dolorosos para outras pessoas que acessam seu código ou dados sem a mesma convenção. Uma maneira melhor é escolher uma convenção do dia do mês, como o dia da semana final sem feriado nos EUA ou o primeiro dia etc. e deixar os dados em um formato de data / hora com a convenção de data escolhida.

O calendarmódulo é útil para obter o valor numérico de determinados dias, como o dia da semana final. Então você pode fazer 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])
    )
)

Se você estiver procurando uma maneira de resolver o problema mais simples de formatar apenas a coluna datetime em alguma representação estritificada, basta usar a strftimefunção da datetime.datetimeclasse da seguinte maneira:

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
fonte
4
O desempenho pode ser ruim, por isso é sempre bom fazer o melhor uso possível das funções auxiliares, operações vetorizadas e pandastécnicas de combinação de aplicação e divisão. Minhas sugestões acima não devem ser consideradas como um endosso de que elas são as abordagens de melhor desempenho para o seu caso - apenas que são escolhas Pythonic estilisticamente válidas para uma variedade de casos.
Ely
A resposta abaixo por @KieranPC é muito, muito mais rápida #
Ben Ben
2
a melhor resposta é claramente .. df [ 'mnth_yr'] = df.date_column.dt.to_period ( 'M') como abaixo de @ jaknap32
ihightower
2
Você deve multiplicar por 100 pol df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month).
Git Gud
1
@ zthomas.nc Acho que funcionam melhor como duas respostas separadas, pois oferecem duas maneiras muito diferentes de resolvê-lo.
ely
34

Se você deseja o par único do ano e do mês, o uso de apply é bastante elegante.

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

Produz mês-ano em uma coluna.

Não se esqueça de mudar primeiro o formato para data e hora antes, geralmente esqueço.

df['date_column'] = pd.to_datetime(df['date_column'])
kabrapankaj32
fonte
Você também pode evitar a função lambda:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
Rishabh
13

Extraindo o ano, digamos de ['2018-03-04']

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

O df ['Ano'] cria uma nova coluna. Enquanto se você quiser extrair o mês, use .month

Douglas
fonte
1
Obrigado, tem sido realmente útil date_1 = pd.DatetimeIndex (df ['date']) --ano = data_1.ano # Por anos-- --month = date_1.month # Por meses-- --dy = date_1. day # Para dias--
Edwin Torres
7

Você pode primeiro converter suas seqüências de datas com pandas.to_datetime , que fornece acesso a todos os recursos numpy datetime e timedelta . Por exemplo:

df['ArrivalDate'] = pandas.to_datetime(df['ArrivalDate'])
df['Month'] = df['ArrivalDate'].values.astype('datetime64[M]')
Juan A. Navarro
fonte
Isso funcionou muito bem para mim, pois eu estava procurando uma funcionalidade análoga à do pyspark trunc. Existe alguma documentação para a astype('datetime64[M]')convenção?
H1-the-swan
6

Graças a jaknap32 , eu queria agregar os resultados de acordo com o ano e o mês, então isso funcionou:

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

A saída foi organizada:

0    201108
1    201108
2    201108
Subspaciano
fonte
6

A solução do @ KieranPC é a abordagem correta para o Pandas, mas não é facilmente extensível a atributos arbitrários. Para isso, você pode usar getattrdentro de uma compreensão de gerador e 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
fonte
1
df['year_month']=df.datetime_column.apply(lambda x: str(x)[:7])

Isso funcionou bem para mim, não achava que os pandas interpretariam a data resultante da string como data, mas quando eu fiz o enredo, ele conhecia muito bem minha agenda e a string year_month onde foi solicitada corretamente ... tenho que amar os pandas!

TICH
fonte
1

Há duas etapas para extrair o ano para todo o quadro de dados sem usar o método apply.

Passo 1

converta a coluna em datetime:

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

Passo 2

extrair o ano ou o mês usando o DatetimeIndex()método

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

ÚNICA LINHA: Adicionando uma coluna com pares 'ano-mês': ('pd.to_datetime' primeiro altera o dtype da coluna para data e hora antes da operação)

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

Assim, para uma coluna extra de 'ano' ou 'mês':

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

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