本文目录导读:

我来为你详细介绍使用Pandas进行数据平滑的几种常用方法,并给出完整案例。
数据平滑的常用方法
移动平均法
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 创建示例数据(带噪声的正弦波)
np.random.seed(42)
t = np.linspace(0, 10, 100)
data = np.sin(t) + np.random.normal(0, 0.3, 100)
df = pd.DataFrame({'time': t, 'value': data})
# 移动平均平滑
df['sma_3'] = df['value'].rolling(window=3, center=True).mean() # 3点移动平均
df['sma_5'] = df['value'].rolling(window=5, center=True).mean() # 5点移动平均
df['sma_10'] = df['value'].rolling(window=10, center=True).mean() # 10点移动平均
# 可视化
plt.figure(figsize=(12, 6))
plt.plot(df['time'], df['value'], 'o-', label='原始数据', alpha=0.5)
plt.plot(df['time'], df['sma_3'], label='3点移动平均', linewidth=2)
plt.plot(df['time'], df['sma_5'], label='5点移动平均', linewidth=2)
plt.plot(df['time'], df['sma_10'], label='10点移动平均', linewidth=2)
plt.legend()'移动平均平滑效果对比')
plt.grid(True, alpha=0.3)
plt.show()
指数加权移动平均
# 指数加权移动平均(对近期数据给予更高权重) df['ewm_01'] = df['value'].ewm(alpha=0.1, adjust=False).mean() # 平滑系数0.1 df['ewm_03'] = df['value'].ewm(alpha=0.3, adjust=False).mean() # 平滑系数0.3 df['ewm_05'] = df['value'].ewm(alpha=0.5, adjust=False).mean() # 平滑系数0.5 # 可视化 plt.figure(figsize=(12, 6)) plt.plot(df['time'], df['value'], 'o-', label='原始数据', alpha=0.5) plt.plot(df['time'], df['ewm_01'], label='alpha=0.1', linewidth=2) plt.plot(df['time'], df['ewm_03'], label='alpha=0.3', linewidth=2) plt.plot(df['time'], df['ewm_05'], label='alpha=0.5', linewidth=2) plt.legend()'指数加权移动平均平滑效果对比') plt.grid(True, alpha=0.3) plt.show()
中值滤波
# 中值滤波(对异常值具有很好的鲁棒性) df['median_3'] = df['value'].rolling(window=3, center=True).median() df['median_5'] = df['value'].rolling(window=5, center=True).median() # 可视化 plt.figure(figsize=(12, 6)) plt.plot(df['time'], df['value'], 'o-', label='原始数据', alpha=0.5) plt.plot(df['time'], df['median_3'], label='3点中值滤波', linewidth=2) plt.plot(df['time'], df['median_5'], label='5点中值滤波', linewidth=2) plt.legend()'中值滤波平滑效果对比') plt.grid(True, alpha=0.3) plt.show()
实战案例:股票价格平滑
# 模拟股票价格数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
price = 100 + np.cumsum(np.random.normal(0, 2, 100)) + np.random.normal(0, 1, 100)
stock_df = pd.DataFrame({'date': dates, 'price': price})
stock_df.set_index('date', inplace=True)
# 多种平滑方法
stock_df['SMA_5'] = stock_df['price'].rolling(window=5).mean()
stock_df['SMA_10'] = stock_df['price'].rolling(window=10).mean()
stock_df['EWMA'] = stock_df['price'].ewm(span=10, adjust=False).mean()
stock_df['HMA'] = stock_df['price'].rolling(window=10).mean().rolling(window=5).mean()
# 可视化
plt.figure(figsize=(14, 7))
plt.plot(stock_df['price'], label='原始价格', alpha=0.6, linewidth=1)
plt.plot(stock_df['SMA_5'], label='5日移动平均', linewidth=2)
plt.plot(stock_df['SMA_10'], label='10日移动平均', linewidth=2)
plt.plot(stock_df['EWMA'], label='指数加权平均(span=10)', linewidth=2, linestyle='--')
plt.plot(stock_df['HMA'], label='双移动平均', linewidth=2, linestyle=':')
plt.legend(loc='best')'股票价格平滑分析')
plt.xlabel('日期')
plt.ylabel('价格')
plt.xticks(rotation=45)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
批量处理多个数据列
# 创建多列数据
multi_df = pd.DataFrame({
'A': np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.2, 100),
'B': np.cos(np.linspace(0, 10, 100)) + np.random.normal(0, 0.2, 100),
'C': np.sin(np.linspace(0, 10, 100)) * 2 + np.random.normal(0, 0.2, 100)
})
def smooth_data(df, window=5, method='mean'):
"""批量平滑数据的函数"""
if method == 'mean':
return df.rolling(window=window, center=True).mean()
elif method == 'median':
return df.rolling(window=window, center=True).median()
elif method == 'ewm':
return df.ewm(span=window, adjust=False).mean()
# 应用平滑
window = 5
smooth_mean = smooth_data(multi_df, window, 'mean')
smooth_median = smooth_data(multi_df, window, 'median')
smooth_ewm = smooth_data(multi_df, window, 'ewm')
# 可视化
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
for i, col in enumerate(multi_df.columns):
axes[i].plot(multi_df[col], 'o-', label='原始数据', alpha=0.5, markersize=3)
axes[i].plot(smooth_mean[col], label=f'{window}点移动平均', linewidth=2)
axes[i].plot(smooth_median[col], label=f'{window}点中值滤波', linewidth=2)
axes[i].plot(smooth_ewm[col], label=f'EWM(span={window})', linewidth=2, linestyle='--')
axes[i].set_title(f'列 {col}')
axes[i].legend()
axes[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
平滑效果评估
# 评估平滑效果
def evaluate_smoothing(original, smoothed, name):
"""评估平滑效果"""
# 计算平滑前后的变化
original_variance = original.var()
smoothed_variance = smoothed.var()
reduction = (1 - smoothed_variance / original_variance) * 100
# 计算均方误差(MSE)
mse = ((original - smoothed) ** 2).mean()
return {
'方法': name,
'原始方差': f'{original_variance:.4f}',
'平滑后方差': f'{smoothed_variance:.4f}',
'方差减少(%)': f'{reduction:.2f}%',
'MSE': f'{mse:.4f}'
}
# 评估不同平滑方法
results = []
for col in ['value']:
results.append(evaluate_smoothing(
df[col].dropna(),
df['sma_5'].dropna(),
'5点移动平均'
))
results.append(evaluate_smoothing(
df[col].dropna(),
df['ewm_03'].dropna(),
'指数平滑(alpha=0.3)'
))
results.append(evaluate_smoothing(
df[col].dropna(),
df['median_5'].dropna(),
'5点中值滤波'
))
# 创建评估结果DataFrame
evaluation_df = pd.DataFrame(results)
print("平滑效果评估:")
print(evaluation_df.to_string(index=False))
选择平滑方法的建议
- 移动平均:适用平稳序列,计算简单
- 指数加权移动平均:需要更多关注近期数据时使用
- 中值滤波:数据有较多异常值时效果最好
- 组合方法:可以先用中值滤波去异常,再用移动平均平滑
根据你的具体数据和需求选择合适的方法,通常需要尝试不同的窗口大小和参数来得到最佳效果。