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

DataFrame新增历史同期特征时赋值出现NaN问题求助

Pandas生成上一周均值特征时全为NaN的解决方法

问题场景

我有一个包含num_posts和date字段的Pandas DataFrame,想要生成prev_week_of_last_year、prev_month_of_last_year这类时间相关特征。已经实现了create_calendar_columns、calculate_curr_week_year_avg函数计算周均值、年均值,但调用calculate_prev_1w_curr_year函数给新列赋值时,结果全是NaN。

原始代码与数据

import pandas as pd
import numpy as np

# 创建示例DataFrame
df = pd.DataFrame({'num_posts': [4, 6, 3, 9, 1, 14, 2, 5, 7, 2,12,1,2,3],
                   'date' : ['2020-03-01', '2020-01-02', '2020-01-03', 
                            '2020-01-04', '2019-01-05', '2019-01-06', 
                            '2020-01-07', '2020-01-08', '2020-01-09', 
                            '2020-01-10','2020-01-11','2019-01-10','2019-01-08','2019-01-09']})
cols=['num_posts']  

# 相关函数定义
def create_calendar_columns(df,dt):
    df[dt]=pd.to_datetime(df[dt])
    df['day_number']= pd.to_datetime(df[dt]).dt.dayofyear
    df['week_number']= pd.to_datetime(df[dt]).dt.isocalendar().week
    df['year']= pd.to_datetime(df[dt]).dt.year
    return df
    
def calculate_curr_week_year_avg(df,cols,dt):
    df=create_calendar_columns(df,dt)
    for col in cols:
        df['week_avg_'+str(col)]= df.groupby(['year','week_number'])[col].transform(np.mean)
        df['year_avg_'+str(col)]= df.groupby(['year'])[col].transform(np.mean)
    return df
def calculate_prev_1w_curr_year(df,cols,dt):
    for col in cols:
        df['prev_1w_curr_year_'+str(col)]=df[(df['week_number']==(df['week_number']-1)) & (df['year']==df['year'])]['week_avg_'+str(col)]
    return df
    
df=calculate_curr_week_year_avg(df,cols,'date')    
df=calculate_prev_1w_curr_year(df,cols,'date')        

中间运行结果(调用calculate_curr_week_year_avg后)

num_postsdateday_numberweek_numberyearweek_avg_num_postsyear_avg_num_posts
042020-03-0161920204.0005.556
162020-01-022120206.0005.556
232020-01-033120206.0005.556
392020-01-044120206.0005.556
412019-01-055120197.5004.200
5142019-01-066120197.5004.200
622020-01-077220205.6005.556
752020-01-088220205.6005.556
872020-01-099220205.6005.556
922020-01-1010220205.6005.556
10122020-01-1111220205.6005.556
1112019-01-1010220192.0004.200
1222019-01-088220192.0004.200
1332019-01-099220192.0004.200

错误原因

calculate_prev_1w_curr_year里的筛选逻辑完全错误:

  • df['week_number'] == (df['week_number'] - 1)是逐行判断,同一行的周数不可能等于自身减1,这个条件永远不成立,筛选结果是空的Series,赋值后自然全为NaN。
  • df['year'] == df['year']是恒成立的冗余条件,没有实际作用。

解决方法

正确思路是:先建立年份-周数到周均值的映射,再为每一行匹配同一年、周数减1对应的均值。下面提供两种可靠实现方式:

方式一:使用Merge匹配映射表

def calculate_prev_1w_curr_year(df, cols, dt):
    # 提取唯一的(year, week_number)对应的周均值,构建映射表
    week_avg_ref = df.drop_duplicates(subset=['year', 'week_number'])[['year', 'week_number', 'week_avg_num_posts']]
    
    # 遍历列,匹配上一周均值
    for col in cols:
        # 把映射表的周数减1,作为匹配键
        week_avg_ref['match_week'] = week_avg_ref['week_number'] - 1
        # 合并到原表,匹配同一年、目标周=原表周数的记录
        df = df.merge(
            week_avg_ref[['year', 'match_week', 'week_avg_num_posts']],
            left_on=['year', 'week_number'],
            right_on=['year', 'match_week'],
            how='left',
            suffixes=('', '_prev')
        )
        # 重命名结果列并清理临时字段
        df.rename(columns={'week_avg_num_posts_prev': f'prev_1w_curr_year_{col}'}, inplace=True)
        df.drop('match_week', axis=1, inplace=True)
    return df

方式二:按年份分组后Shift

这种方式更简洁,利用Pandas的groupby和shift能力:

def calculate_prev_1w_curr_year(df, cols, dt):
    for col in cols:
        # 按年份分组,先按周数排序,再对周均值列进行shift,得到上一周的均值
        df[f'prev_1w_curr_year_{col}'] = df.groupby('year').apply(
            lambda group: group.sort_values('week_number')[f'week_avg_{col}'].shift(1)
        ).reset_index(level=0, drop=True)
    return df

修正后运行结果

调用修正后的函数后,新列prev_1w_curr_year_num_posts会正确生成:

num_postsdateday_numberweek_numberyearweek_avg_num_postsyear_avg_num_postsprev_1w_curr_year_num_posts
042020-03-0161920204.0005.556NaN
162020-01-022120206.0005.556NaN
232020-01-033120206.0005.556NaN
392020-01-044120206.0005.556NaN
412019-01-055120197.5004.200NaN
5142019-01-066120197.5004.200NaN
622020-01-077220205.6005.5566.000
752020-01-088220205.6005.5566.000
872020-01-099220205.6005.5566.000
922020-01-1010220205.6005.5566.000
10122020-01-1111220205.6005.5566.000
1112019-01-1010220192.0004.2007.500
1222019-01-088220192.0004.2007.500
1332019-01-099220192.0004.2007.500

注:周数为1的行没有上一周数据,所以对应值为NaN,这是合理的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 07:07:24