如何高效遍历Pandas DataFrame并计算同ID历史行均值
解决Pandas中按ID计算上方行均值的高效方法
首先,咱们先分析下你原代码里的问题:
- 条件判断错误:你用了
if df['id'][row] > 1,这是在判断ID的数值大于1,而不是判断当前ID是否存在其他行。正确的条件应该是df['others_count'][row] > 1(也就是当前行不是该ID的第一行)。 - 循环效率低:逐行遍历DataFrame在数据量大的时候会非常慢,Pandas的核心优势是向量化操作,应该尽量避免循环。
下面给你两种高效的向量化解决方案,完美解决你的需求:
方法一:用expanding().mean()结合shift()
这种方法直接利用分组后的滚动均值,再偏移一行(排除当前行),逻辑清晰简洁:
import numpy as np import pandas as pd # 构造你的示例数据 size = 10 d = { 'id': np.array([3,4,1,6,4,8,8,7,9,1]), 'value': np.array([76,12,96,33,49,72,68,78,99,66]) } df = pd.DataFrame(data=d) df['others_count'] = df.groupby(['id']).cumcount() + 1 # 计算每个ID组内的滚动均值(不包含当前行) df['value_estimated'] = df.groupby('id')['value'].apply( lambda x: x.expanding().mean().shift() ) # 对于ID的第一行(others_count=1),设置为NaN(因为没有上方行) df.loc[df['others_count'] == 1, 'value_estimated'] = np.nan
运行后得到的结果完全符合预期:
| id | value | others_count | value_estimated | |
|---|---|---|---|---|
| 0 | 3 | 76 | 1 | NaN |
| 1 | 4 | 12 | 1 | NaN |
| 2 | 1 | 96 | 1 | NaN |
| 3 | 6 | 33 | 1 | NaN |
| 4 | 4 | 49 | 2 | 12.0 |
| 5 | 8 | 72 | 1 | NaN |
| 6 | 8 | 68 | 2 | 72.0 |
| 7 | 7 | 78 | 1 | NaN |
| 8 | 9 | 99 | 1 | NaN |
| 9 | 1 | 66 | 2 | 96.0 |
方法二:用cumsum()和cumcount()手动计算均值
如果你想更直观地控制均值的计算过程,可以用累计和减去当前值得到前n-1行的和,再除以累计行数减1:
# 计算每个ID组内当前行之前的value总和 df['sum_prev'] = df.groupby('id')['value'].cumsum() - df['value'] # 计算每个ID组内当前行之前的行数 df['count_prev'] = df.groupby('id')['value'].cumcount() # 计算均值,同时处理除数为0的情况(others_count=1时count_prev=0) df['value_estimated'] = np.where( df['others_count'] > 1, df['sum_prev'] / df['count_prev'], np.nan ) # 可以删除中间辅助列 df = df.drop(['sum_prev', 'count_prev'], axis=1)
这个方法同样是向量化操作,效率和方法一相当,结果完全一致。
为什么这两种方法更好?
- 效率高:向量化操作是Pandas的最优实践,比循环快几个数量级,尤其是当数据量达到万级甚至百万级时,差距会非常明显。
- 逻辑清晰:直接利用分组和窗口函数,代码可读性强,不容易出现像原循环里的条件判断错误。
- 稳定性好:避免了逐行操作可能带来的索引错误、性能瓶颈等问题。
内容的提问来源于stack exchange,提问作者espogian
相关产品推荐
相关产品推荐

