Estrarre solo il mese e l'anno separatamente dalla colonna Pandas Datetime


221

Ho un Dataframe, df, con la seguente colonna:

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

Gli elementi della colonna sono pandas.tslib.Timestamp.

Voglio solo includere l'anno e il mese. Ho pensato che ci sarebbe stato un modo semplice per farlo, ma non riesco a capirlo.

Ecco cosa ho provato:

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

Ho ricevuto il seguente errore:

Only valid with DatetimeIndex or PeriodIndex 

Poi ho provato:

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

Ho ricevuto il seguente errore:

'Timestamp' object has no attribute '__getitem__' 

Eventuali suggerimenti?

Modifica: l'ho capito.

df.index = df['ArrivalDate']

Quindi, posso ricampionare un'altra colonna usando l'indice.

Ma vorrei ancora un metodo per riconfigurare l'intera colonna. Qualche idea?


11
la risposta migliore è chiaramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') come sotto da @ jaknap32
ihightower il

1
Non devi nemmeno fare to_period: df.date_column.dt.month(o .year, o .day) opere
elz


2
@elphz: .dt.monthperde l'anno però. E .dt.to_period('M')cambia il tipo di dati in qualcosa che non è più un datetime64. Ho finito per usare la risposta di Juan suggerendo .astype('datetime64[M]')di troncare i valori.
Nickolay,

Puoi cambiare la risposta migliore?
Gonzalo Garcia,

Risposte:


306

Se vuoi nuove colonne che mostrano separatamente anno e mese puoi farlo:

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

Quindi puoi combinarli o lavorare con loro così come sono.


7
C'è un modo per farlo in una sola riga? Voglio evitare di attraversare più volte la stessa colonna.
fixxxer

2
Alcuni benchmark rapidi con timeitsuggeriscono che l' DatetimeIndexapproccio è significativamente più veloce di uno .map/.applyo .dt.
Snorfalorpagus,

2
la risposta migliore è chiaramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') come sotto da @ jaknap32
ihightower il

cosa fa effettivamente pd.Datetimeindex?
JOHN

A volte lo faccio: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca,

229

Il modo migliore trovato !!

il df['date_column']deve essere in formato data e ora.

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

Puoi anche usare Dper Giorno, 2Mper 2 mesi ecc. Per diversi intervalli di campionamento e nel caso in cui uno abbia dati di serie temporali con timestamp, possiamo scegliere intervalli di campionamento granulari come 45Min45 minuti, 15Min15 minuti di campionamento ecc.


8
Si noti che la colonna risultante non è più del datetime64tipo. Usando df.my_date_column.astype('datetime64[M]'), come nella risposta di @ Juan, si converte in date che rappresentano il primo giorno di ogni mese.
Nickolay,

3
I "m sorpreso questo è tutta la strada fino qui.
Tim

154

Puoi accedere direttamente agli attributi yeare month, oppure richiedere 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

Un modo per combinare anno e mese è creare un numero intero che li codifichi, come ad esempio: 201408per agosto 2014. Lungo un'intera colonna, puoi farlo come:

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

o molte sue varianti.

Non sono un grande fan di farlo, però, dal momento che rende doloroso l'allineamento delle date e l'aritmetica in seguito e soprattutto doloroso per gli altri che si imbattono nel tuo codice o nei tuoi dati senza questa stessa convenzione. Un modo migliore è scegliere una convenzione del giorno del mese, come un giorno feriale non festivo, o il primo giorno, ecc. E lasciare i dati in un formato data / ora con la convenzione data prescelta.

Il calendarmodulo è utile per ottenere il valore numerico di determinati giorni come l'ultimo giorno della settimana. Quindi potresti fare qualcosa del tipo:

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 ti capita di cercare un modo per risolvere il problema più semplice della semplice formattazione della colonna datetime in una rappresentazione rigorosa, per questo puoi semplicemente utilizzare la strftimefunzione della datetime.datetimeclasse, in questo modo:

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

4
Le prestazioni possono essere pessime, quindi è sempre bene sfruttare al meglio le funzioni di supporto, le operazioni vettoriali e pandasle tecniche di divisione-applicazione-combinazione. I miei suggerimenti sopra non intendono essere presi a sostegno del fatto che sono gli approcci più efficaci per il tuo caso, ma solo che sono scelte Pythonic stilisticamente valide per una serie di casi.
ely,

La risposta sotto di @KieranPC è molto più veloce
Ben

2
la risposta migliore è chiaramente .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') come sotto da @ jaknap32
ihightower il

2
Dovresti moltiplicare per 100 pollici df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month).
Git Gud,

1
@ zthomas.nc Penso che funzionino meglio come due risposte separate, poiché offrono due modi molto diversi di risolverlo.
ely,

34

Se si desidera la coppia univoca dell'anno del mese, l'utilizzo di apply è piuttosto elegante.

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

Emette mese-anno in una colonna.

Non dimenticare di cambiare prima il formato in data-ora prima, in genere dimentico.

df['date_column'] = pd.to_datetime(df['date_column'])

Puoi anche evitare la funzione lambda:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
Rishabh

13

Estrarre l'anno dire da ['2018-03-04']

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

Il df ['Anno'] crea una nuova colonna. Mentre se vuoi estrarre il mese usa semplicemente .month


1
Grazie, è stato davvero utile date_1 = pd.DatetimeIndex (df ['date']) --year = date_1.year # Per anni-- --month = date_1.month # Per mesi-- --dy = date_1. giorno # Per giorni--
Edwin Torres,

7

Puoi prima convertire le stringhe di date con pandas.to_datetime , che ti dà accesso a tutte le funzionalità di datetime e timedelta intorpidite . Per esempio:

df['ArrivalDate'] = pandas.to_datetime(df['ArrivalDate'])
df['Month'] = df['ArrivalDate'].values.astype('datetime64[M]')

Questo ha funzionato davvero bene per me, poiché cercavo funzionalità analoghe a quelle di pyspark trunc. C'è qualche documentazione per la astype('datetime64[M]')convention?
h1-the-swan,

6

Grazie a jaknap32 , volevo aggregare i risultati in base a Anno e Mese, quindi ha funzionato:

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

L'output era pulito:

0    201108
1    201108
2    201108

6

La soluzione di @ KieranPC è l'approccio corretto per i panda, ma non è facilmente estendibile per attributi arbitrari. Per questo, è possibile utilizzare getattrall'interno di una comprensione del generatore e combinare utilizzando 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

1
df['year_month']=df.datetime_column.apply(lambda x: str(x)[:7])

Questo ha funzionato bene per me, non pensavo che i panda avrebbero interpretato la data della stringa risultante come data, ma quando ho fatto la trama, conosceva molto bene la mia agenda e la stringa year_month dove erano stati ordinati correttamente ... Devo amare i panda!


1

Esistono due passaggi per estrarre l'anno per tutti i frame di dati senza utilizzare il metodo applicare.

Passo 1

converti la colonna in datetime:

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

Passo 2

estrarre l'anno o il mese usando il DatetimeIndex()metodo

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

1

LINEA SINGOLA: aggiunta di una colonna con coppie 'year-month': ('pd.to_datetime' cambia prima il tipo di colonna in data-ora prima dell'operazione)

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

Di conseguenza per una colonna aggiuntiva "anno" o "mese":

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

df['mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%m')
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.