不使用Swifter、Parallel并行化工具且绕过索引实现数据集即时计算的可行性问询
不用并行工具、不依赖索引的即时计算方案
当然可以实现!而且我们能找到比你现在嵌套循环高效得多的方式——不管是用apply,还是Pandas原生的向量化操作,都能做到不依赖并行工具、不直接操作索引来完成这个滚动均值的计算。
先说说你当前代码的问题:嵌套循环+反复用loc筛选索引,这种写法的性能开销极大,每次loc调用都要重新遍历DataFrame,200*200的操作量已经够慢了,换成更大的数据集根本没法用。
下面给你两种可行的方案:
方案一:用apply/列表推导式实现(满足你的需求)
我们可以逐行处理数据,基于行的位置而非索引值来计算窗口均值,完全避开显式的索引筛选操作:
%%time import random import pandas as pd df = pd.DataFrame({'A': random.sample(range(200), 200)}) def calculate_row_stats(row_pos): # 取当前行之前的所有A值(基于位置,不是索引) prev_values = df['A'].iloc[:row_pos] row_results = [] for j in range(200): # 截取最后j个元素作为窗口(j=0时没有有效元素) window = prev_values[-j:] if j != 0 else [] # 计算均值,空窗口返回NaN mean_val = window.mean() if len(window) > 0 else float('nan') row_results.append(mean_val) return row_results # 用列表推导式逐行计算(比apply更高效一点,逻辑一致) results_df = pd.DataFrame( [calculate_row_stats(i) for i in range(len(df))], columns=[f'A_last_{j}' for j in range(200)] ) # 合并到原数据集 df = pd.concat([df, results_df], axis=1)
这里我们用iloc[:row_pos]基于行的位置获取数据,完全没用到索引值筛选,也没依赖任何并行工具,完美符合你的要求。如果偏好apply写法,也可以替换成:
results_df = df.apply(lambda row: calculate_row_stats(row.name), axis=1, result_type='expand') results_df.columns = [f'A_last_{j}' for j in range(200)]
方案二:用Pandas向量化操作(性能最优)
其实Pandas的rolling函数天生就是用来处理这类滚动窗口统计的,而且是向量化实现,效率比apply高几个量级,同样不需要操作索引:
%%time import random import pandas as pd df = pd.DataFrame({'A': random.sample(range(200), 200)}) for j in range(200): if j == 0: # j=0时没有符合条件的前置数据,直接设为NaN df[f'A_last_{j}'] = float('nan') else: # 滚动窗口大小为j,计算均值后下移一行,正好对应当前行之前j个元素的均值 # min_periods=1保证窗口元素不足j个时也能计算均值(比如前j行的情况) df[f'A_last_{j}'] = df['A'].rolling(window=j, min_periods=1).mean().shift(1)
这个写法完全利用了Pandas的内置优化,代码简洁,运行速度快得多——你可以对比两种方案的%%time输出,差距非常明显。
总结一下:
- 不用并行工具、不依赖索引的即时计算完全可行
- 逻辑复杂时用
apply/列表推导式,灵活但性能一般 - 像滚动统计这类标准化需求,优先用Pandas向量化函数,性能最优
内容的提问来源于stack exchange,提问作者Alexandr Kosolapov
相关产品推荐
相关产品推荐

