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

如何为Pandas的df2新增列,值为df1指定年份区间的weight均值?

问题描述

我有两个形状不同的Pandas DataFrame:df1和df2。

df1的模拟数据:

year,weight
2023,0.392
2022,0.382
2021,0.858
2020,0.135

df2的模拟数据:

start_year,end_year
2022,2023
2021,2023
2020,2023
2020,2020
2021,2022
2022,2022

需求是给df2新增一列weight,值为df1中year落在对应行start_year和end_year区间内的weight的平均值。比如第一行结果是(0.382 + 0.392)/2=0.387,最终目标df2如下:

start_year,end_year,weight
2022,2023,0.387
2021,2023,0.544
2020,2023,0.44175
2020,2020,0.135
2021,2022,0.625
2022,2022,0.382

我尝试了以下代码:

df2["weight"] = df1[df1["year"].between(df2["start_year"], df2["end_year"], inclusive="both")]["weight"].mean()

但报错:

ValueError: Can only compare identically-labeled Series objects

我推测这是因为Pandas默认按行比较,而非笛卡尔积匹配。已知循环可以实现,但df1约110行,df2最多数万行,循环效率太低,希望用矢量化操作解决。

解决方案

方法1:利用Numpy广播创建布尔矩阵计算均值

这种方法通过广播将df1的year与df2的区间进行比较,生成布尔矩阵后计算每行的均值,完全矢量化,效率很高:

import pandas as pd
import numpy as np

# 构造示例数据
df1 = pd.DataFrame({'year': [2023,2022,2021,2020], 'weight': [0.392,0.382,0.858,0.135]})
df2 = pd.DataFrame({'start_year': [2022,2021,2020,2020,2021,2022], 'end_year': [2023,2023,2023,2020,2022,2022]})

# 广播生成布尔矩阵:df2的每一行对应df1所有year是否在区间内
mask = (df1['year'].values[:, None] >= df2['start_year'].values) & (df1['year'].values[:, None] <= df2['end_year'].values)

# 计算每行的平均权重
df2['weight'] = (df1['weight'].values[:, None] * mask).sum(axis=0) / mask.sum(axis=0)

方法2:使用Pandas的merge+groupby(适合数据量适中的场景)

先做笛卡尔积合并,再筛选符合区间的行,最后分组求均值:

# 笛卡尔积合并
merged = df1.assign(key=1).merge(df2.assign(key=1), on='key').drop('key', axis=1)

# 筛选year在区间内的行
filtered = merged[(merged['year'] >= merged['start_year']) & (merged['year'] <= merged['end_year'])]

# 分组求均值并合并回df2
df2 = df2.merge(filtered.groupby(['start_year', 'end_year'])['weight'].mean().reset_index(), 
                on=['start_year', 'end_year'], how='left')

方法3:使用apply结合矢量化判断(比纯循环高效,但不如前两种)

如果觉得广播的矩阵占用内存,可以用apply逐行处理,但内部用矢量化判断:

def get_avg_weight(row):
    mask = df1['year'].between(row['start_year'], row['end_year'], inclusive='both')
    return df1.loc[mask, 'weight'].mean()

df2['weight'] = df2.apply(get_avg_weight, axis=1)

方法对比

  • 方法1:纯矢量化,速度最快,适合df2行数多的场景(数万行完全没问题),内存占用仅为110*N的矩阵,压力极小。
  • 方法2:笛卡尔积会生成大量中间数据,当df2是数万行时,中间表会有110*N行,内存占用较高,速度不如方法1。
  • 方法3:比纯循环快,但本质还是逐行处理,速度不如方法1,适合内存紧张的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 23:59:52