我正在构建一个时间序列,试图找到一种更有效的方法来实现这一点——理想的矢量化。 熊猫应用列表理解步骤非常慢(在大数据集上)
import datetime
import pandas as pd
# Dummy data:
todays_date = datetime.datetime.now().date()
xdates = pd.date_range(todays_date-datetime.timedelta(10), periods=4, freq='D')
categories = list(2*'A') + list(2*'B')
d = {'xdate': xdates, 'periods': [8]*2 + [2]*2, 'interval': [3]*2 + [12]*2}
df = pd.DataFrame(d,index=categories)
# This step is slow:
df['sdates'] = df.apply(lambda x: [x.xdate + pd.DateOffset(months=k*x.interval) for k in range(x.periods)], axis=1)
# This step is quite quick, but shown here for completeness
df = df.explode('sdates')
也许是这样的:
df['sdates'] = [df.xdate + df.periods * [df.interval.astype('timedelta64[M]')]]
但是语法不太正确。 此代码
df = pd.DataFrame(d,index=categories)
df['m_offsets'] = df.interval.apply(lambda x: list(range(0, 72, x)))
df = df.explode('m_offsets')
df['sdate'] = df.xdate + df.m_offsets * pd.DateOffset(months=1)
我认为与其中一个答案类似,但最后一步pd.DateOffset给出了一个警告:
PerformanceWarning: Adding/subtracting array of DateOffsets to DatetimeArray not vectorized
我试着按照一个答案来构建一些东西,但正如前面提到的,模块化算术需要做很多调整来处理边缘情况,而且还没有弄清楚(calendar monthrange的表现并不好)。 此函数不运行:
from calendar import monthrange
def add_months(df, date_col, n_col):
""" Adds ncol months do date_col """
z = df.copy()
# calculate new year/month/day and convert to datetime
z['year'] = (z[date_col].dt.year * 12 + (z[date_col].dt.month-1) + z[n_col]) // 12
z['month'] = ((z[date_col].dt.month + z[n_col] - 1) % 12) + 1
x,x = monthrange(z.year, z.month)
z['days_in_month'] = monthrange(z.year, z.month)
z['target_day'] = z[date_col].dt.day
# z['day'] = min(z.target_day, z.days_in_month)
z['day'] = z.days_in_month
z['sdates'] = pd.to_datetime(z[['year', 'month', 'day']])
return z['sdates']
目前这是可行的,但日期偏移量是一个非常沉重的步骤
df = pd.DataFrame(d,index=categories)
df['m_offsets'] = df.interval.apply(lambda x: list(range(0, 72, x)))
df = df.explode('m_offsets')
df['sdates'] = df.apply(lambda x: x.xdate + pd.DateOffset(months=x.m_offsets), axis=1)
半矢量化方式
正如我在下面所说的,我不认为有一种纯粹的矢量化方法可以将变量和一般的
DateOffset
添加到Timestamp
的Series
中@perl解决方案适用于DateOffset
是1个月的精确倍数的情况现在,添加单个常量
DateOffset
是矢量化的,因此我们可以使用以下内容。它利用了一个事实,即日期偏移量有一组有限的不同值。它也相对较快,对于任何DateOffset
和日期都是正确的:时间:
(注意:对于使用上述生成的10K行,我看到的时间约为240ms,但这当然取决于数据中有多少不同的月份偏移)
示例结果(对于上述170K行的一次绘制):
对初始答案的更正
我的观点是正确的:我的原始答案也没有矢量化。第一部分,分解数据帧并构建要添加的月数,是矢量化的,速度非常快。但第二部分,增加了一个月数可变的
DateOffset
,则不是我希望我错了,但我不认为目前有一种方法可以以矢量化的方式完成第二部分
直接日期部分操作(例如
month = (month - 1 + n_months) % 12 + 1
等)对于转角情况(例如'2021-02-31'
)注定会失败。除了复制DateOffset
中使用的逻辑之外,这在某些情况下是行不通的初始答案
以下是一种矢量化方式:
这里有一个选择。您正在添加月份,因此我们实际上可以通过以矢量化方式处理整数来计算新年/月/日,然后根据这些y/m/d组合创建日期时间:
为了将性能与原始数据进行比较,我生成了一个包含10000行的测试数据集
以下是我的计时(10公里加速约23倍):
另外,对于170K,在我的机器上使用
f_proposed
大约需要1.39秒,使用f_original
大约需要33.6秒相关问题 更多 >
编程相关推荐