Wydobywanie tylko miesiąca i roku osobno z kolumny Pandas Datetime

221

Mam Dataframe, df, z następującą kolumną:

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

Elementy kolumny to pandas.tslib.Timestamp.

Chcę tylko podać rok i miesiąc. Myślałem, że będzie prosty sposób, ale nie mogę tego rozgryźć.

Oto, co próbowałem:

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

Wystąpił następujący błąd:

Only valid with DatetimeIndex or PeriodIndex 

Potem spróbowałem:

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

Wystąpił następujący błąd:

'Timestamp' object has no attribute '__getitem__' 

Jakieś sugestie?

Edycja: W pewnym sensie to rozgryzłem.

df.index = df['ArrivalDate']

Następnie mogę ponownie próbkować kolejną kolumnę za pomocą indeksu.

Ale nadal chciałbym metodę rekonfiguracji całej kolumny. Jakieś pomysły?

monkeybiz7
źródło
11
najlepsza odpowiedź jest jednoznaczna .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') jak poniżej z @ jaknap32
ihightower
1
Nie musisz nawet wykonywać to_period: df.date_column.dt.month( .yearlub .day) prac
elz
2
@elphz: .dt.monthtraci jednak rok. I .dt.to_period('M')zmienia typ danych na coś, co nie jest już datetime64. Skończyło się na tym, że Juan użył odpowiedzi sugerującej .astype('datetime64[M]')skrócenie wartości.
Nickolay
Czy potrafisz zmienić najlepszą odpowiedź?
Gonzalo Garcia

Odpowiedzi:

306

Jeśli chcesz, aby nowe kolumny z osobnym rokiem i miesiącem były dostępne, możesz to zrobić:

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

lub...

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

Następnie możesz je połączyć lub pracować z nimi takimi, jakimi są.

KieranPC
źródło
7
Czy można to zrobić w jednym wierszu? Chcę uniknąć wielokrotnego przechodzenia przez tę samą kolumnę.
fixxxer,
2
Niektóre szybkie testy porównawcze z timeitsugerują, że DatetimeIndexpodejście jest znacznie szybsze niż jeden .map/.applylub .dt.
Snorfalorpagus
2
najlepsza odpowiedź jest jednoznaczna .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') jak poniżej z @ jaknap32
ihightower
co właściwie robi pd.Datetimeindex?
JAN
Czasami robię to: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca
229

Najlepszy sposób znaleziony !!

df['date_column']musi być w formacie czasowym dat.

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

Możesz także użyć opcji DDzień, 2M2 miesiące itp. Do różnych przedziałów próbkowania, a jeśli dane szeregów czasowych są opatrzone znacznikiem czasu, możemy przejść do szczegółowych przedziałów próbkowania, takich jak 45Min45 minut, 15Min15 minut próbkowania itp.

kabrapankaj32
źródło
8
Zauważ, że wynikowa kolumna nie jest datetime64już typu dtype. Używając df.my_date_column.astype('datetime64[M]'), jak w odpowiedzi @ Juana, konwertuje się na daty reprezentujące pierwszy dzień każdego miesiąca.
Nickolay
3
Jestem zaskoczony, że to wszystko tutaj.
Tim
154

Można bezpośrednio uzyskać dostęp do yeari monthatrybutów, lub poprosić o 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

Jednym ze sposobów łączenia roku i miesiąca jest utworzenie liczby całkowitej kodującej je, na przykład: 201408dla sierpnia 2014 r. Wzdłuż całej kolumny można to zrobić w następujący sposób:

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

lub wiele ich wariantów.

Nie jestem wielkim fanem robienia tego, ponieważ powoduje to, że wyrównanie daty i arytmetyka są bolesne później, a szczególnie bolesne dla innych, którzy napotkają twój kod lub dane bez tej samej konwencji. Lepszym sposobem jest wybranie konwencji dotyczącej dnia, na przykład końcowego dnia tygodnia poza USA, pierwszego dnia itp., I pozostawienie danych w formacie daty / godziny z wybraną konwencją dat.

calendarModuł jest użyteczny do uzyskania wartości liczbowej określonych dni, takie jak końcowego tygodnia. Następnie możesz zrobić coś takiego:

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

Jeśli szukasz sposobu na rozwiązanie prostszego problemu polegającego na sformatowaniu kolumny daty i godziny na jakąś strunową reprezentację, możesz po prostu skorzystać z strftimefunkcji datetime.datetimeklasy:

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
źródło
4
Wydajność może być zła, dlatego zawsze najlepiej jest jak najlepiej wykorzystać funkcje pomocnicze, operacje wektoryzowane i pandastechniki dzielenia i łączenia. Moje powyższe sugestie nie powinny być traktowane jako poparcie dla tego, że są najbardziej wydajnym podejściem do twojej sprawy - po prostu, że są one stylistycznie poprawnymi wyborami w Pythonie dla szeregu przypadków.
ely
Odpowiedź poniżej autorstwa @KieranPC jest znacznie szybsza
Ben
2
najlepsza odpowiedź jest jednoznaczna .. df ['mnth_yr'] = df.date_column.dt.to_period ('M') jak poniżej z @ jaknap32
ihightower
2
Powinieneś pomnożyć przez 100 cali df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month).
Git Gud,
1
@ zthomas.nc Myślę, że działają lepiej jako dwie osobne odpowiedzi, ponieważ oferują dwa bardzo różne sposoby rozwiązania tego problemu.
ely
34

Jeśli chcesz mieć unikalną parę miesiąc-rok, użycie Apply jest dość eleganckie.

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

Dane wyjściowe miesiąc-rok w jednej kolumnie.

Nie zapomnij najpierw zmienić formatu na datę, zwykle zapominam.

df['date_column'] = pd.to_datetime(df['date_column'])
kabrapankaj32
źródło
Możesz także uniknąć funkcji lambda:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
Rishabh
13

Wydobywając rok powiedzmy z ['2018-03-04']

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

Plik df [„Year”] tworzy nową kolumnę. Natomiast jeśli chcesz wyodrębnić miesiąc, użyj .month

Douglas
źródło
1
Dzięki, to była naprawdę pomocna data_1 = pd.DatetimeIndex (df ['data']) - rok = data_1. Rok # Od lat-- - miesiąc = data_1.miesiąc # Przez miesiące-- --dy = data_1. dzień # Na dni--
Edwin Torres
7

Możesz najpierw przekonwertować ciągi dat za pomocą pandas.to_datetime , co daje dostęp do wszystkich funkcji numpy datetime i timedelta . Na przykład:

df['ArrivalDate'] = pandas.to_datetime(df['ArrivalDate'])
df['Month'] = df['ArrivalDate'].values.astype('datetime64[M]')
Juan A. Navarro
źródło
Działa to dla mnie bardzo dobrze, ponieważ szukałem funkcjonalności analogicznej do pyspark trunc. Czy jest jakaś dokumentacja do astype('datetime64[M]')konwencji?
h1-the-swan
6

Dzięki jaknap32 chciałem agregować wyniki według roku i miesiąca, więc zadziałało:

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

Wynik był czysty:

0    201108
1    201108
2    201108
Podspacian
źródło
6

@ Rozwiązanie KieranPC jest poprawnym podejściem do Pand, ale nie jest łatwe do rozszerzenia o dowolne atrybuty. W tym celu możesz użyć getattrrozumienia generatora i połączyć, używając 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
źródło
1
df['year_month']=df.datetime_column.apply(lambda x: str(x)[:7])

To działało dobrze dla mnie, nie sądziłem, że pandy zinterpretują wynikową datę sznurka jako datę, ale kiedy zrobiłem fabułę, znał bardzo dobrze mój program i ciąg roku_miesiąc, w którym właściwie zamówiłem ... muszę pokochać pandy!

TICH
źródło
1

Istnieją dwa kroki, aby wyodrębnić rok dla wszystkich ramek danych bez zastosowania metody.

Krok 1

przekonwertuj kolumnę na datetime:

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

Krok 2

wyodrębnij rok lub miesiąc za pomocą DatetimeIndex()metody

 pd.DatetimeIndex(df['ArrivalDate']).year
abdellah el atouani
źródło
1

JEDNA LINIA: Dodanie kolumny z parami „rok-miesiąc”: („pd.to_datetime” najpierw zmienia typ kolumny na datę-czas przed operacją)

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

Odpowiednio dla dodatkowej kolumny „rok” lub „miesiąc”:

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

df['mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%m')
Matthi9000
źródło