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

能否为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:57:02