Extrahieren Sie nur Monat und Jahr getrennt von der Spalte Pandas Datetime


221

Ich habe einen Datenrahmen, df, mit der folgenden Spalte:

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

Die Elemente der Spalte sind pandas.tslib.Timestamp.

Ich möchte nur das Jahr und den Monat angeben. Ich dachte, es gäbe einen einfachen Weg, aber ich kann es nicht herausfinden.

Folgendes habe ich versucht:

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

Ich habe folgenden Fehler erhalten:

Only valid with DatetimeIndex or PeriodIndex 

Dann habe ich versucht:

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

Ich habe folgenden Fehler erhalten:

'Timestamp' object has no attribute '__getitem__' 

Irgendwelche Vorschläge?

Edit: Ich habe es irgendwie herausgefunden.

df.index = df['ArrivalDate']

Dann kann ich mithilfe des Index eine weitere Spalte neu abtasten.

Aber ich möchte immer noch eine Methode zum Neukonfigurieren der gesamten Spalte. Irgendwelche Ideen?


11
Die beste Antwort ist eindeutig .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') wie unten von @ jaknap32
ihightower

1
Sie müssen nicht einmal tun to_period: df.date_column.dt.month(oder .year, oder .day) arbeitet
elz


2
@elphz: .dt.monthverliert das Jahr. Und .dt.to_period('M')ändert den Datentyp in etwas, das kein datetime64 mehr ist. Ich landete mit bis Juan Antwort darauf hindeutet , .astype('datetime64[M]')um die Werte zu kürzen.
Nickolay

Können Sie die beste Antwort ändern?
Gonzalo Garcia

Antworten:


306

Wenn Sie möchten, dass neue Spalten Jahr und Monat getrennt anzeigen, können Sie dies tun:

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

oder...

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

Dann können Sie sie kombinieren oder so arbeiten, wie sie sind.


7
Gibt es eine Möglichkeit, dies in einer einzigen Zeile zu tun? Ich möchte vermeiden, dieselbe Spalte mehrmals zu durchlaufen.
Fixxxer

2
Einige schnelle Benchmarking-Tests timeitdeuten darauf hin, dass der DatetimeIndexAnsatz erheblich schneller ist als entweder .map/.applyoder .dt.
Snorfalorpagus

2
Die beste Antwort ist eindeutig. df ['mnth_yr'] = df.date_column.dt.to_period ('M') wie unten von @ jaknap32
ihightower

Was macht pd.Datetimeindex eigentlich?
JOHN

Ich mache manchmal das: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca

229

Bester Weg gefunden !!

Das df['date_column']muss im Datums- / Uhrzeitformat sein.

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

Sie können auch Dfür Tag, 2M2 Monate usw. für verschiedene Abtastintervalle verwenden. Wenn Zeitreihendaten mit Zeitstempel vorliegen, können Sie granulare Abtastintervalle verwenden, z. B. 45Minfür 45 Minuten, 15Minfür 15 Minuten Abtastung usw.


8
Beachten Sie, dass die resultierende Spalte nicht mehr vom datetime64Typ dtype ist. Wenn Sie df.my_date_column.astype('datetime64[M]')wie in @ Juans Antwort verwenden , werden Daten in Daten konvertiert, die den ersten Tag eines jeden Monats darstellen.
Nickolay

3
Ich bin überrascht, dass dies hier unten ist.
Tim

154

Sie können direkt auf die Attribute yearund zugreifen monthoder Folgendes anfordern 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

Eine Möglichkeit, Jahr und Monat zu kombinieren, besteht darin, eine Ganzzahl zu erstellen, die sie codiert, z. B.: 201408Für August 2014. In einer ganzen Spalte können Sie Folgendes tun:

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

oder viele Varianten davon.

Ich bin jedoch kein großer Fan davon, da dies die Datumsausrichtung und Arithmetik später schmerzhaft macht und besonders für andere schmerzhaft ist, die auf Ihren Code oder Ihre Daten ohne dieselbe Konvention stoßen. Eine bessere Möglichkeit besteht darin, eine Tag-Tag-Konvention auszuwählen, z. B. den letzten Wochentag außerhalb der USA oder den ersten Tag usw., und die Daten in einem Datums- / Zeitformat mit der gewählten Datumskonvention zu belassen.

Das calendarModul ist nützlich, um den Zahlenwert bestimmter Tage zu ermitteln, z. B. den letzten Wochentag. Dann könnten Sie so etwas tun wie:

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

Wenn Sie nach einer Möglichkeit suchen, das einfachere Problem zu lösen, bei dem nur die datetime-Spalte in eine String-Darstellung formatiert wird, können Sie einfach die strftimeFunktion aus der datetime.datetimeKlasse wie folgt verwenden :

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
Die Leistung kann schlecht sein, daher ist es immer gut, Hilfsfunktionen, vektorisierte Operationen und pandasSplit-Apply-Combine-Techniken bestmöglich zu nutzen . Meine obigen Vorschläge sind nicht als Bestätigung dafür zu verstehen, dass sie die performantesten Ansätze für Ihren Fall sind - nur, dass sie stilistisch gültige pythonische Entscheidungen für eine Reihe von Fällen sind.
Ely

Die Antwort unten von @KieranPC ist viel viel schneller
Ben

2
Die beste Antwort ist eindeutig. df ['mnth_yr'] = df.date_column.dt.to_period ('M') wie unten von @ jaknap32
ihightower

2
Du sollst mit 100 in multiplizieren df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month).
Git Gud

1
@ zthomas.nc Ich denke, sie funktionieren besser als zwei separate Antworten, da sie zwei sehr unterschiedliche Lösungsmöglichkeiten bieten.
Ely

34

Wenn Sie das einzigartige Paar für das Monat und Jahr möchten, ist die Verwendung von apply ziemlich elegant.

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

Gibt Monat-Jahr in einer Spalte aus.

Vergessen Sie nicht, zuerst das Format auf Datum und Uhrzeit zu ändern, ich vergesse es im Allgemeinen.

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

Sie können auch die Lambda-Funktion vermeiden:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
Rishabh

13

Auszug aus dem Jahr sagen aus ['2018-03-04']

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

Das df ['Jahr'] erstellt eine neue Spalte. Wenn Sie den Monat extrahieren möchten, verwenden Sie einfach .month


1
Danke, es war wirklich hilfreich date_1 = pd.DatetimeIndex (df ['date']) --year = date_1.year # Seit Jahren-- --month = date_1.month # Seit Monaten-- --dy = date_1. Tag # Für Tage--
Edwin Torres

7

Sie können Ihre Datumszeichenfolgen zuerst mit pandas.to_datetime konvertieren , wodurch Sie auf alle numpy datetime und timedelta zugreifen können können. Beispielsweise:

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

Dies funktionierte sehr gut für mich, da ich nach Funktionen suchte, die denen von pyspark entsprechen trunc. Gibt es Unterlagen für die astype('datetime64[M]')Konvention?
h1-the-Swan

6

Dank jaknap32 wollte ich die Ergebnisse nach Jahr und Monat aggregieren, also funktionierte dies:

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

Die Ausgabe war ordentlich:

0    201108
1    201108
2    201108

6

Die Lösung von @ KieranPC ist der richtige Ansatz für Pandas, kann jedoch für beliebige Attribute nicht einfach erweitert werden. Hierzu können Sie getattrinnerhalb eines Generatorverständnisses verwenden und kombinieren mit 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])

Das funktionierte gut für mich, ich dachte nicht, dass Pandas das resultierende String-Datum als Datum interpretieren würden, aber als ich die Handlung machte, wusste es sehr gut, dass meine Agenda und der String year_month richtig bestellt wurden ... ich muss Pandas lieben!


1

Es gibt zwei Schritte, um das Jahr für den gesamten Datenrahmen zu extrahieren, ohne die Methode apply anzuwenden.

Schritt 1

Konvertieren Sie die Spalte in datetime:

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

Schritt 2

Extrahieren Sie das Jahr oder den Monat mit der DatetimeIndex()Methode

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

1

SINGLE LINE: Hinzufügen einer Spalte mit 'Jahr-Monat'-Paaren: (' pd.to_datetime 'ändert zuerst den Spaltentyp in Datum-Uhrzeit vor der Operation)

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

Dementsprechend für eine zusätzliche Spalte "Jahr" oder "Monat":

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

df['mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%m')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.