You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用apply()和DateOffset给Pandas日期列加月份的性能警告解决

问题描述

我有一个Pandas DataFrame,其中包含存储datetime类型值的"date"列,以及存储整数类型值的"months"列,该列代表需要为每行日期添加的月份数。我需要计算每行的新日期,即原"date"加上对应"months"列指定的月份数。

我使用了以下代码:

df['date'] + df["months"].apply(lambda y: pd.DateOffset(months=y))

这段代码能得到正确结果,但触发了一条PerformanceWarning,内容如下:

PerformanceWarning: Adding/subtracting object-dtype array to DatetimeArray not vectorized
  warnings.warn

我的数据集需要处理多组日期和月份列,当前代码执行耗时较长。

输入样例

datemonths
02020-01-013
12021-06-156
22022-02-281

输出样例

datemonthsnew_date
02020-01-0132020-04-01
12021-06-1562021-12-15
22022-02-2812022-03-28

请问该警告的含义是什么?如何优化代码以避免该警告并提升执行效率?


解答

一、警告含义解释

这个警告的核心是:你当前的操作是把object类型的数组(apply返回的是包含pd.DateOffset对象的Series,属于object dtype)和DatetimeArray类型的日期列做加法,Pandas无法对这种操作进行向量化优化,只能逐行处理,这就导致了性能低下,尤其是在处理大数据集时会明显变慢。

apply(lambda y: pd.DateOffset(months=y))会生成一个每个元素都是独立DateOffset对象的Series,本质是Python对象的集合,不是Pandas能高效处理的数值型数组,所以加法操作无法利用向量化的优势,只能循环执行。

二、优化方案

方案1:使用dateutil.relativedelta(边界处理精准)

直接对datetime对象添加可变月份,避免生成DateOffset对象数组:

from dateutil.relativedelta import relativedelta

df['new_date'] = df.apply(lambda row: row['date'] + relativedelta(months=row['months']), axis=1)

这种方法逻辑直观,能准确处理月末、跨年等边界日期,性能比原方法有所提升。

方案2:向量化日期拆分计算(性能最优)

通过拆分日期的年、月字段,用数值运算直接计算新日期,完全利用Pandas向量化能力,无Python级循环:

import numpy as np

# 拆分原日期的年、月、日
years = df['date'].dt.year
months = df['date'].dt.month
days = df['date'].dt.day

# 计算新的年和月
new_total_months = years * 12 + months + df['months']
new_years = new_total_months // 12
new_months = new_total_months % 12
# 处理月份为0的特殊情况(如1月减1个月变为去年12月)
new_years = np.where(new_months == 0, new_years - 1, new_years)
new_months = np.where(new_months == 0, 12, new_months)

# 重新组合日期,自动处理月末边界
df['new_date'] = pd.to_datetime({
    'year': new_years,
    'month': new_months,
    'day': days
}, errors='coerce')

这种方法是性能最高的方案,不会触发性能警告,适合处理超大数据集。

方案3:周期转换法(代码简洁)

先将日期转换为月度周期,完成月份加减后再转回日期:

# 转换为月度周期,添加月份后转回日期
df['new_date'] = df['date'].dt.to_period('M').add(df['months']).dt.to_timestamp()
# 修正月末日期(如2022-02-28加1个月默认变为2022-03-01,需调整回原日)
df['new_date'] = df.apply(
    lambda row: row['new_date'].replace(day=row['date'].day) 
    if row['date'].day == row['date'].dt.days_in_month.iloc[0]
    else row['new_date'],
    axis=1
)

代码简洁易读,适合普通场景,仅需额外处理月末日期的特殊情况。


内容的提问来源于stack exchange,提问作者Cristian Jara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:07:44