Pandas:将日期时间转换为月末

36

我已经编写了一个函数,用于将Pandas日期时间转换为月末:

import pandas
import numpy
import datetime
from pandas.tseries.offsets import Day, MonthEnd

def get_month_end(d):
    month_end = d - Day() + MonthEnd() 
    if month_end.month == d.month:
        return month_end # 31/March + MonthEnd() returns 30/April
    else:
        print "Something went wrong while converting dates to EOM: " + d + " was converted to " + month_end
        raise

这个函数看起来相当慢,我想知道是否有更快的替代方案?我注意到它很慢的原因是我在一个包含 50,000 个日期的数据帧列上运行它,而且我发现自从引入了这个函数后,代码运行速度要慢得多(之前我是将日期转换为月末)。

df = pandas.read_csv(inpath, na_values = nas, converters = {open_date: read_as_date})
df[open_date] = df[open_date].apply(get_month_end)

我不确定这是否相关,但我是按照以下方式读取日期的:

def read_as_date(x):
    return datetime.datetime.strptime(x, fmt)
6个回答

58

修改后,将其转换为时间戳再转回时间格式即可解决问题。

In [104]: df = DataFrame(dict(date = [Timestamp('20130101'),Timestamp('20130131'),Timestamp('20130331'),Timestamp('20130330')],value=randn(4))).set_index('date')

In [105]: df
Out[105]: 
               value
date                
2013-01-01 -0.346980
2013-01-31  1.954909
2013-03-31 -0.505037
2013-03-30  2.545073

In [106]: df.index = df.index.to_period('M').to_timestamp('M')

In [107]: df
Out[107]: 
               value
2013-01-31 -0.346980
2013-01-31  1.954909
2013-03-31 -0.505037
2013-03-31  2.545073
请注意,这种类型的转换也可以像这样完成,尽管以上方法略微更快。
In [85]: df.index + pd.offsets.MonthEnd(0) 
Out[85]: DatetimeIndex(['2013-01-31', '2013-01-31', '2013-03-31', '2013-03-31'], dtype='datetime64[ns]', name=u'date', freq=None, tz=None)

3
只有每月一个条目才有效吗?我不认为安妮的所有50000个条目都是唯一的月份。 :) - Rutger Kassies
这就是为什么“重新采样”可能是正确的解决方案,除非她想要重复。 - Jeff
谢谢!为什么您需要在重新索引的末尾加上.values - Anne
在版本17中,@Jeff在第85行的输出仍然正确吗?当我运行它时,我得到了DatetimeIndex(['2012-12-31', '2012-12-31', '2013-02-28', '2013-02-28'], dtype='datetime64[ns]', name=u'date', freq=None) - itzy
句点是对象数据类型,因此它的性能并不好。 - Jeff
显示剩余2条评论

7
如果日期列采用日期时间格式并设置为月初,则将向其添加一个月的时间:
df['date1']=df['date'] + pd.offsets.MonthEnd(0) 

3
import pandas as pd
import numpy as np
import datetime as dt    

df0['Calendar day'] = pd.to_datetime(df0['Calendar day'], format='%m/%d/%Y')
df0['Calendar day'] = df0['Calendar day'].apply(pd.datetools.normalize_date)    
df0['Month Start Date'] = df0['Calendar day'].dt.to_period('M').apply(lambda r: r.start_time)

这段代码应该可行。Calendar Day是一个包含日期以%m/%d/%Y格式给出的列,例如:12/28/2014代表2014年12月28日。输出结果将是类型的2014-12-01。


你使用的Python版本是什么? - Web Ster

1
你也可以使用NumPy来更快地完成它:
import numpy as np
date_array = np.array(['2013-01-01', '2013-01-15', '2013-01-30']).astype('datetime64[ns]')
month_start_date = date_array.astype('datetime64[M]')

1

如果日期不在索引中,而是在另一列中(适用于Pandas 0.25.0):

import pandas as pd
import numpy as np

df = pd.DataFrame(dict(date = [pd.Timestamp('20130101'), 
                               pd.Timestamp('20130201'), 
                               pd.Timestamp('20130301'), 
                               pd.Timestamp('20130401')], 
                       value = np.random.rand(4)))
print(df.to_string())

df.date = df.date.dt.to_period('M').dt.to_timestamp('M')
print(df.to_string())

输出:

    date     value
0 2013-01-01  0.295791
1 2013-02-01  0.278883
2 2013-03-01  0.708943
3 2013-04-01  0.483467

        date     value
0 2013-01-31  0.295791
1 2013-02-28  0.278883
2 2013-03-31  0.708943
3 2013-04-30  0.483467

0

您可能正在寻找的是:

df.resample('M').last()

另一种方法如@Jeff所说:

df.index = df.index.to_period('M').to_timestamp('M')


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接