股票成交量突破计算异常:Python rolling均值返回NaN,结果全为No
问题分析
你的代码问题核心出在两个点:
- Rolling窗口逻辑与Excel不匹配:Excel公式里用的是当日之前21天的成交量平均值(
I3:I24是I2的前21行,不含当日),但Pandas默认的rolling(window=21).mean()是包含当前行在内的21行数据的平均值;如果你的日期数据是从新到旧排序的,rolling的计算方向会完全颠倒。 - NaN值的成因:average_volume全为NaN,要么是
VOLUME列本身有大量缺失值,要么是数据排序错误导致rolling窗口无法获取有效历史数据。
解决方案
步骤1:确保数据按日期从旧到新排序
先检查并修正日期列的排序(假设日期列名为DATE):
# 转换日期列为datetime类型(若未转换) df['DATE'] = pd.to_datetime(df['DATE']) # 按日期从旧到新排序,重置索引 df = df.sort_values('DATE').reset_index(drop=True)
步骤2:修正Rolling平均值计算逻辑
要实现Excel中“过去21日(不含当日)平均值”的逻辑,需用shift(1)将rolling结果偏移一行,对应取当前行之前的21天数据:
import pandas as pd import numpy as np def add_vol_breakout_column(self, df): # 确保日期排序正确 df['DATE'] = pd.to_datetime(df['DATE']) df = df.sort_values('DATE').reset_index(drop=True) # 计算过去21日(不含当日)的成交量平均值 # rolling(21)取当前行及前20行,shift(1)后变为当前行之前的完整21行数据 average_volume = df['VOLUME'].rolling(window=21).mean().shift(1) # 处理前21行的NaN(无足够历史数据,默认标记为No) volume_breakout = df['VOLUME'] > (average_volume * 3) df['VolBrkOut'] = np.where(volume_breakout.fillna(False), 'Yes', 'No') print(f"Updated DataFrame:\n{df}") return df
步骤3:排查VOLUME列缺失值
若仍出现大量NaN,先检查成交量列的缺失情况:
print("VOLUME列缺失值数量:", df['VOLUME'].isna().sum())
如果有缺失值,可选择填充或删除缺失行:
# 用0填充缺失值 df['VOLUME'] = df['VOLUME'].fillna(0) # 或删除包含缺失值的行 df = df.dropna(subset=['VOLUME'])
逻辑验证
修改后,第22行(索引21)的VolBrkOut计算逻辑,完全对应Excel公式=IF(I22>(AVERAGE(I1:I21)*3),"Yes","No")的逻辑,和你最初的需求一致。
内容的提问来源于stack exchange,提问作者drk
相关产品推荐
相关产品推荐

