从熊猫日期时间列中分别提取月份和年份


220

我有一个数据框df,其中包含以下列:

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

该列的元素是pandas.tslib.Timestamp。

我只想包括年份和月份。我以为会有一种简单的方法,但是我无法弄清楚。

这是我尝试过的:

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

我收到以下错误:

Only valid with DatetimeIndex or PeriodIndex 

然后我尝试了:

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

我收到以下错误:

'Timestamp' object has no attribute '__getitem__' 

有什么建议?

编辑:我想通了。

df.index = df['ArrivalDate']

然后,我可以使用索引对另一列进行重新采样。

但是我仍然想要一种重新配置整个列的方法。有任何想法吗?


11
最好的答案显然是.. df ['mnth_yr'] = df.date_column.dt.to_period('M')如下,来自@ jaknap32
ihightower

1
您甚至不必做to_period:(df.date_column.dt.month.year,或.day)工作
elz


2
@elphz:.dt.month虽然输了一年。并将.dt.to_period('M')数据类型更改为不再是datetime64的数据。我最终使用了Juan的答案,建议.astype('datetime64[M]')截断这些值。
Nickolay

您可以更改最佳答案吗?
贡萨洛·加西亚(

Answers:


305

如果希望新列分别显示年和月,则可以执行以下操作:

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

要么...

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

然后,您可以将它们组合或按原样使用它们。


7
有没有办法在一行中做到这一点?我想避免多次遍历同一列。
fixxxer

2
进行一些快速基准测试,timeit表明该DatetimeIndex方法比.map/.apply或都快得多.dt
Snorfalorpagus

2
最好的答案显然是.. df ['mnth_yr'] = df.date_column.dt.to_period('M')如下,来自@ jaknap32
ihightower

pd.Datetimeindex实际上是做什么的?
约翰(JOHN)

我有时会这样做: df['date_column_trunc'] = df[date_column'].apply(lambda s: datetime.date(s.year, s.month, 1)
Stewbaca

229

找到最好的方法

df['date_column']必须是日期时间格式。

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

您也可以将DDay,2M2个月等用于不同的采样间隔,并且如果其中一个带有时间戳的时间序列数据,我们可以进行细化的采样间隔,例如45Min45分钟,15Min15分钟采样等。


8
请注意,结果列不再是datetime64dtype。df.my_date_column.astype('datetime64[M]')@Juan的答案中使用,将转换为代表每个月第一天的日期。
Nickolay

3
我很惊讶这一切都在这里
蒂姆

154

您可以直接访问yearmonth属性,或请求一个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

组合年和月的一种方法是对它们进行整数编码,例如:201408对于2014年8月。在整列中,您可以这样做:

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

或其许多变体。

不过,我并不是这样做的忠实拥护者,因为它会使日期对齐和算术在以后变得很痛苦,对于那些不遵循相同约定而使用您的代码或数据的其他人来说尤其痛苦。更好的方法是选择一个月的日期约定,例如最终的非美国假日工作日或第一天等,并使用所选的日期约定以日期/时间格式保留数据。

calendar模块对于获取某些日期(例如最后一个工作日)的数值很有用。然后,您可以执行以下操作:

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

如果您碰巧正在寻找一种解决简单问题的方法,那就只是将datetime列格式化为某种字符串表示形式,为此,您可以使用类中的strftime函数datetime.datetime,如下所示:

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
性能可能很差,因此最好最好地利用辅助函数,向量化操作和pandas拆分应用组合技术。我上面的建议并不意味着它们是您案例中性能最高的方法,只是它们在样式上是一系列案例中有效的Pythonic选择。
2014年

@KieranPC在下面的回答要快得多
Ben

2
最好的答案显然是.. df ['mnth_yr'] = df.date_column.dt.to_period('M')如下,来自@ jaknap32
ihightower

2
你应该乘以100 in df['YearMonth'] = df['ArrivalDate'].map(lambda x: 1000*x.year + x.month)
Git Gud

1
@ zthomas.nc我认为它们可以作为两个单独的答案发挥更好的作用,因为它们提供了两种非常不同的解决方法。
ely

34

如果要月对唯一,则使用apply非常时尚。

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

一列输出月-年。

我通常会忘记,不要忘记先将格式更改为日期时间。

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

您也可以避免使用lambda函数:df['month_year'] = df['date_column'].dt.strftime('%B-%Y')
利沙伯

13

从['2018-03-04']中提取年份的说法

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

df ['Year']创建一个新列。如果要提取月份,请使用.month


1
谢谢,这真的很有帮助date_1 = pd.DatetimeIndex(df ['date'])--year = date_1.year#对于年份---month = date_1.month#对于月份---dy = date_1。第一天
埃德温·托雷斯

7

您可以首先使用pandas.to_datetime转换日期字符串,这使您可以访问所有的numpy datetime和timedelta工具。例如:

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

这对我来说确实非常有效,因为我一直在寻找类似于pyspark的功能trunc。是否有关于该astype('datetime64[M]')公约的文档?
h1-the-swan

6

感谢jaknap32,我想根据Year和Month汇总结果,所以可以这样:

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

输出整洁:

0    201108
1    201108
2    201108

6

@KieranPC的解决方案是适用于Pandas的正确方法,但对于任意属性而言却不容易扩展。为此,您可以getattr在生成器理解中使用并结合使用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])

这对我来说很好用,没想到熊猫会把结果字符串日期解释为日期,但是当我做情节时,它非常了解我的议程和正确订购年份的字符串year_month……必须爱熊猫!


1

不使用方法套用两个步骤提取所有数据框的年份。

第1步

将列转换为datetime:

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

第2步

使用DatetimeIndex()方法提取年或月

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

1

单行:添加具有“年-月 ”对的列:(“ pd.to_datetime”首先将列dtype更改为操作之前的日期时间)

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

因此,对于额外的“年”或“月”列:

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

df['mm'] = pd.to_datetime(df['ArrivalDate']).dt.strftime('%m')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.