能否为Pandas DataFrame.shift()传入Series作为period参数?附NaN填充需求
问题描述
我有一张时间序列数据表,想用上一年最后一个月的计算后值填充当前年份的NaN值。数据表包含整数类型的月份列,理论上通过偏移对应月份数可以获取上一年最后一个月的值,但shift()方法不支持传入Series作为period参数。
简化后的示例代码如下:
import pandas as pd import numpy as np np.random.seed(0) # 构造DataFrame start_date = '2022-01-31' end_date = '2023-12-31' dates = pd.date_range(start=start_date, end=end_date, freq='M') data = { 'date': dates, 'year': dates.year, 'month': dates.month, 'metric': np.random.randint(1, 15, len(dates)) } df = pd.DataFrame(data) # 将2023年的metric设为NaN df.loc[df['year'] == 2023, 'metric'] = np.nan
需求说明:
- 创建新列
metric_new:2022年的所有值为原metric乘以1.5; - 2023年的NaN值用上一年
metric_new的最后值乘以2填充(比如2022-12的metric_new是7.5,那2023全年的metric_new都是15); - 新增年份(如2024)时逻辑需自动生效:2024年取2023年
metric_new的最后值乘以2。
我尝试过以下逻辑,但因为shift()不接受Series作为period参数而失败:
df['metric_new'] = np.where(df['year'] == 2022, 1.5*df['metric'], 2*df['metric_new'].shift(period = df['month']))
期望得到的结果表:
date year month metric metric_new 0 2022-01-31 2022 1 13.0 19.5 1 2022-02-28 2022 2 6.0 9.0 2 2022-03-31 2022 3 1.0 1.5 3 2022-04-30 2022 4 4.0 6.0 4 2022-05-31 2022 5 12.0 18.0 5 2022-06-30 2022 6 4.0 6.0 6 2022-07-31 2022 7 8.0 12.0 7 2022-08-31 2022 8 10.0 15.0 8 2022-09-30 2022 9 4.0 6.0 9 2022-10-31 2022 10 6.0 9.0 10 2022-11-30 2022 11 3.0 4.5 11 2022-12-31 2022 12 5.0 7.5 12 2023-01-31 2023 1 NaN 15.0 13 2023-02-28 2023 2 NaN 15.0 14 2023-03-31 2023 3 NaN 15.0 15 2023-04-30 2023 4 NaN 15.0 16 2023-05-31 2023 5 NaN 15.0 17 2023-06-30 2023 6 NaN 15.0 18 2023-07-31 2023 7 NaN 15.0 19 2023-08-31 2023 8 NaN 15.0 20 2023-09-30 2023 9 NaN 15.0 21 2023-10-31 2023 10 NaN 15.0 22 2023-11-30 2023 11 NaN 15.0 23 2023-12-31 2023 12 NaN 15.0
解决方案
核心思路是先计算基准年份的metric_new,再按年份分组提取每年最后一个metric_new的值,后续年份直接用上一年的最终值乘以2填充全年。这种方法无需使用shift(),且能自动适配新增年份。
具体实现代码:
import pandas as pd import numpy as np np.random.seed(0) # 构造原始DataFrame start_date = '2022-01-31' end_date = '2023-12-31' dates = pd.date_range(start=start_date, end=end_date, freq='M') data = { 'date': dates, 'year': dates.year, 'month': dates.month, 'metric': np.random.randint(1, 15, len(dates)) } df = pd.DataFrame(data) df.loc[df['year'] == 2023, 'metric'] = np.nan # 初始化metric_new:填充2022年的计算值 df['metric_new'] = np.where(df['year'] == 2022, df['metric'] * 1.5, np.nan) # 提取每个年份最后一个metric_new的值,构建年份-最终值映射 year_last_values = df.groupby('year')['metric_new'].last().dropna().to_dict() # 遍历后续年份,填充对应值 for year in sorted(year_last_values.keys())[1:]: prev_year = year - 1 if prev_year in year_last_values: df.loc[df['year'] == year, 'metric_new'] = year_last_values[prev_year] * 2 # ------------------------------ # 验证新增2024年的自动适配逻辑 dates_2024 = pd.date_range(start='2024-01-31', end='2024-12-31', freq='M') data_2024 = { 'date': dates_2024, 'year': dates_2024.year, 'month': dates_2024.month, 'metric': [np.nan]*12 } df_2024 = pd.DataFrame(data_2024) df = pd.concat([df, df_2024], ignore_index=True) # 更新年份映射并填充2024年的值 year_last_values = df.groupby('year')['metric_new'].last().dropna().to_dict() df.loc[df['year'] == 2024, 'metric_new'] = year_last_values[2023] * 2 # 查看2024年结果 print(df.tail(12))
结果说明
- 2022年的
metric_new正确计算为原metric的1.5倍; - 2023年所有值为2022年最后一个
metric_new(7.5)的2倍,即15; - 新增2024年时,自动取2023年最后一个
metric_new(15)的2倍,即30填充全年。
内容的提问来源于stack exchange,提问作者Aco
相关产品推荐
相关产品推荐

