基于动态范围计算A列平均值并新增列的Pandas技术问题
解决动态范围计算A列平均值的问题
先直接说你的问题出在哪:你遇到的TypeError: Cannot index by location index with a non-integer key,是因为你在切片时犯了两个关键错误——一是用整个df['D'](一个Series)作为iloc的起始索引,而iloc只接受单个整数;二是df.loc['C'][-1]的写法完全不对,这是取C列的最后一个值(也就是'YES'),根本不是你要的当前行索引。
要实现你需要的逻辑,我们得逐行处理每一行的动态区间:当C列为YES时,用当前行的D值做起始索引,当前行的索引减1做结束索引,计算A列在这个区间的平均值;否则设为NaN。下面给你两种可行的实现方式:
方法1:用apply逐行计算(直观易读,适合小数据集)
这是最直接的写法,逻辑和你的需求完全对应,新手也能一眼看懂:
import numpy as np import pandas as pd test = {'A' : [100, 120, 70, 300, 190, 70, 300, 190, 70], 'B' : [80, 50, 64, 288, 172, 64, 288, 172, 64], 'C' : ['NO', 'NO', 'YES', 'NO', 'YES', 'YES', 'NO', 'YES', 'YES'], 'D' : [0, 1, 0, 3, 2, 2, 3, 1, 4] } df = pd.DataFrame(data=test) def get_dynamic_average(row): if row['C'] == 'YES': start = row['D'] end = row.name - 1 # row.name就是当前行的索引 # 处理边界情况:如果起始索引大于结束索引,直接返回NaN if start > end: return np.nan # iloc是左闭右开区间,所以要给end加1才能包含该行 return df['A'].iloc[start:end+1].mean() else: return np.nan df['Dyn_Ave'] = df.apply(get_dynamic_average, axis=1)
运行后你就能得到期望的结果,比如:
- 索引2的行(C=YES):取A列0到1行的平均值,(100+120)/2=110
- 索引8的行(C=YES):取A列4到7行的平均值,(190+70+300+190)/4=187.5
方法2:前缀和优化(适合大数据集,速度更快)
如果你的数据集很大,apply的效率会有点低,我们可以用前缀和减少重复计算:
import numpy as np import pandas as pd test = {'A' : [100, 120, 70, 300, 190, 70, 300, 190, 70], 'B' : [80, 50, 64, 288, 172, 64, 288, 172, 64], 'C' : ['NO', 'NO', 'YES', 'NO', 'YES', 'YES', 'NO', 'YES', 'YES'], 'D' : [0, 1, 0, 3, 2, 2, 3, 1, 4] } df = pd.DataFrame(data=test) # 计算A列的前缀和,开头加个0方便计算区间和 prefix_sum = pd.Series([0]).append(df['A'].cumsum(), ignore_index=True) def get_dynamic_average_fast(row): if row['C'] == 'YES': start = row['D'] end = row.name - 1 if start > end: return np.nan # 区间和 = 前缀和[end+1] - 前缀和[start] total = prefix_sum[end+1] - prefix_sum[start] count = end - start + 1 return total / count else: return np.nan df['Dyn_Ave'] = df.apply(get_dynamic_average_fast, axis=1)
这种方法通过预计算前缀和,把每次求区间和的时间从O(n)降到O(1),数据量越大优势越明显。
再复盘下你的原代码问题
你的代码里:
df['Dyn_Ave'] = np.where(df['C'] == 'YES', df['A'].iloc[df['D']:df.loc['C'][-1]].mean(), np.nan)
df['D']是一整列数据,不是单个整数,iloc根本没法用它来切片df.loc['C'][-1]取的是C列最后一个值('YES'),不是当前行的索引,完全偏离了需求
所以必须逐行获取当前行的D值和行索引,才能正确计算动态区间的平均值。
内容的提问来源于stack exchange,提问作者windwalker
相关产品推荐
相关产品推荐

