超级干货 | 数据平滑9大妙招
作者:Peter 编辑:Peter
对数据进行平滑处理的方法有很多种,具体的选择取决于数据的性质和处理的目的。今天给大家分享9大常见数据平滑方法:
-
移动平均Moving Average -
指数平滑Exponential Smoothing -
低通滤波器 -
多项式拟合 -
贝塞尔曲线拟合 -
局部加权散点平滑Loess -
Kalman滤波 -
小波变换 -
Savitzky-Golay滤波
移动平均Moving Average
移动平均(Moving Average)是一种最简单的数据平滑方法,用于平滑时间序列数据。它通过计算一定窗口内数据点的平均值来减少噪音,同时保留数据的趋势。移动平均可以是简单移动平均(SMA)或指数加权移动平均(EMA)。
简单移动平均(SMA): 简单移动平均是一种通过计算数据点在一个固定窗口内的平均值来平滑数据的方法。窗口的大小决定了平滑程度。较大的窗口将导致更平滑的曲线,但会减缓对趋势的反应,而较小的窗口将更敏感地跟随数据的波动。
import numpy as np
import matplotlib.pyplot as plt
# 生成示例数据
data = np.array([10, 15, 12, 18, 20, 14, 16, 22, 19, 25])
# 定义移动平均窗口大小
window_size = 3
# 计算简单移动平均
sma = np.convolve(data, np.ones(window_size) / window_size, mode='valid')
# 绘制原始数据和移动平均曲线
plt.figure(figsize=(10, 6))
plt.plot(data, label="原始数据", marker='o', color='blue')
plt.plot(np.arange(window_size - 1, len(data)), sma, label="移动平均", color='red')
plt.legend()
plt.title("简单移动平均示例")
plt.xlabel("数据点")
plt.ylabel("数值")
plt.grid(True)
plt.show()
指数加权移动平均-Exponential Weighted Moving Average,EWMA: 指数加权移动平均是一种通过对数据点应用指数权重来平滑数据的方法。它对最近的数据点给予较高的权重,而对较早的数据点给予较低的权重。这使得EMA更适合用于追踪快速变化的数据。
import numpy as np
import matplotlib.pyplot as plt
# 生成示例数据
data = np.array([10, 15, 12, 18, 20, 14, 16, 22, 19, 25])
# 定义平滑参数(通常称为平滑因子)
alpha = 0.2
# 计算EMA
ema = [data[0]] # 初始EMA值等于第一个数据点
for i in range(1, len(data)):
ema.append(alpha * data[i] + (1 - alpha) * ema[-1])
# 绘制原始数据和EMA曲线
plt.figure(figsize=(10, 6))
plt.plot(data, label="原始数据", marker='o', color='blue')
plt.plot(ema, label="EMA", color='red')
plt.legend()
plt.title("指数加权移动平均(EMA)示例")
plt.xlabel("数据点")
plt.ylabel("数值")
plt.grid(True)
plt.show()
指数平滑Exponential Smoothing
指数平滑(Exponential Smoothing)是一种常用的时间序列数据平滑和预测方法,用于处理具有趋势和季节性的数据。它通过分配不同权重给历史数据点,将较高权重分配给较新的数据,以捕获数据的变化趋势。指数平滑通常用于生成预测,特别是在需要对未来时间点进行预测的情况下。
指数平滑的主要特点包括:
-
加权平滑:指数平滑使用指数权重来平滑数据。较新的数据点获得更高的权重,而较旧的数据点获得较低的权重。这意味着它对最近的数据更为敏感,从而更好地捕获了数据的最新趋势。
-
三种主要形式:指数平滑有三种主要形式:简单指数平滑、双指数平滑和三重指数平滑。每种形式用于不同类型的数据和模式。
-
简单指数平滑(Simple Exponential Smoothing)用于平滑具有趋势和季节性的数据。
-
双指数平滑(Double Exponential Smoothing)用于平滑具有趋势但无季节性的数据。
-
三重指数平滑(Triple Exponential Smoothing)用于平滑同时具有趋势和季节性的数据。
-
递归更新:指数平滑是一种递归方法,它将先前的平滑结果与新数据点相结合,以生成下一个时间点的平滑结果。
-
预测能力:指数平滑不仅用于平滑数据,还可以用于生成未来时间点的预测。这使得它在需求预测、股票价格预测和销售预测等领域非常有用。
-
适用性:指数平滑适用于平稳或非平稳的时间序列数据,它能够很好地处理趋势、季节性和噪声。
pip install statsmodels
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing
# 生成示例时间序列数据
np.random.seed(0)
n = 100
index = pd.date_range(start="2022-01-01", periods=n, freq="D")
data = np.sin(np.linspace(0, 4 * np.pi, n)) + np.random.normal(0, 0.2, n)
time_series = pd.Series(data, index=index)
# 执行指数平滑
model = ExponentialSmoothing(time_series, trend='add', seasonal='add', seasonal_periods=7)
results = model.fit()
# 生成平滑后的数据和预测
smoothed = results.fittedvalues
forecast = results.forecast(steps=30) # 预测未来30个时间点
# 绘制原始数据、平滑后的数据和预测
plt.figure(figsize=(10, 6))
plt.plot(time_series, label="原始数据", color='blue', alpha=0.6)
plt.plot(smoothed, label="指数平滑", color='red')
plt.plot(forecast, label="未来预测", color='green')
plt.legend()
plt.title("指数平滑示例")
plt.xlabel("时间")
plt.ylabel("数据值")
plt.grid(True)
plt.show()
低通滤波器
低通滤波器是一种信号处理工具,用于去除信号中高频成分,从而保留低频成分。在数据平滑的上下文中,低通滤波器用于减小或去除数据中的高频噪声或快速变化,以使数据趋于平滑,保留慢变化的趋势或周期性特征。
低通滤波器的工作原理是将信号通过一个滤波器,该滤波器允许低频成分通过,而抑制高频成分。这通常通过滤波器的频率响应函数来实现,频率响应函数定义了不同频率上的滤波器的行为。低通滤波器通常用于以下应用:
-
去除高频噪声:在测量、传感器或通信中,信号通常会受到高频噪声的干扰,低通滤波器可用于去除这些噪声。 -
数据平滑:在时间序列分析中,低通滤波器可以用来平滑数据,去除短期波动,从而更好地识别趋势和周期性特征。 -
图像处理:在图像处理中,低通滤波器可用于去除图像中的高频噪声,使图像更加平滑。 -
音频处理:在音频处理中,低通滤波器可用于去除音频信号中的高频噪声,改善音质。
提供一个Python案例:
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter
# 生成示例数据
fs = 1000 # 采样频率
t = np.linspace(0, 5, 5 * fs, endpoint=False)
data = 5 * np.sin(2 * np.pi * 3 * t) + 2 * np.sin(2 * np.pi * 50 * t)
# 设计巴特沃斯低通滤波器
cutoff_freq = 10 # 截止频率(以Hz为单位)
nyquist_freq = 0.5 * fs
normal_cutoff = cutoff_freq / nyquist_freq
b, a = butter(4, normal_cutoff, btype='low', analog=False)
# 使用滤波器平滑数据
smoothed_data = lfilter(b, a, data)
# 绘制原始数据和平滑后的数据
plt.figure(figsize=(10, 6))
plt.plot(t, data, label="原始数据", color='blue')
plt.plot(t, smoothed_data, label="低通滤波后的数据", color='red')
plt.legend()
plt.title("巴特沃斯低通滤波器示例")
plt.xlabel("时间 (秒)")
plt.ylabel("数值")
plt.grid(True)
plt.show()
多项式拟合
多项式拟合(Polynomial Fitting)是一种数据平滑和曲线拟合的方法,它通过使用多项式函数来逼近或拟合原始数据,以便更好地描述数据的趋势或模式。多项式拟合的目标是找到一个多项式函数,它在给定的数据点上经过并能够很好地拟合这些点。
多项式拟合的一般形式如下:
其中, 是自变量, 是依赖于 的因变量, 是多项式系数。通过调整这些系数,可以使多项式函数更好地拟合数据。
多项式拟合常用于以下情况:
-
数据平滑:多项式拟合可以用来消除数据中的噪声或波动,从而获得平滑的曲线。
-
趋势分析:多项式拟合可用于识别数据中的趋势,例如线性趋势(一阶多项式)、二次趋势(二阶多项式)或更高阶的趋势。
-
曲线拟合:多项式拟合可用于拟合实验数据,以获得与理论模型或理论曲线的最佳拟合。
-
数据插值:多项式插值是多项式拟合的特殊情况,它通过已知数据点之间的多项式来估计中间值。
多项式拟合的一般原则是选择合适的多项式阶数。阶数过低可能无法很好地拟合数据,而阶数过高可能会导致过度拟合,对新数据的波动非常敏感。因此,选择适当的多项式阶数是关键。
提供一个3阶多项式拟合的案例:
import numpy as np
import matplotlib.pyplot as plt
# 示例数据
x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
y = np.array([10, 8, 7, 6, 5, 4, 3, 2, 1])
# 三阶多项式拟合
degree = 3
coefficients = np.polyfit(x, y, degree)
# 构建多项式函数
poly = np.poly1d(coefficients)
# 生成用于绘图的新X值
x_new = np.linspace(min(x), max(x), 100)
# 计算拟合后的Y值
y_new = poly(x_new)
# 绘制原始数据和三阶多项式拟合曲线
plt.figure(figsize=(10, 6))
plt.scatter(x, y, label="原始数据", color='blue')
plt.plot(x_new, y_new, label="三阶多项式拟合", color='red')
plt.legend()
plt.title("三阶多项式拟合示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
贝塞尔曲线
贝塞尔曲线(Bézier Curve)是一种数学曲线,常用于计算机图形和数据可视化领域。它的特点是平滑、连续,由控制点(Control Points)定义,可以用于创建平滑曲线或路径。贝塞尔曲线最常见的形式是二次贝塞尔曲线和三次贝塞尔曲线,分别由2个和3个控制点定义。
以下是二次和三次贝塞尔曲线的简要介绍:
-
二次贝塞尔曲线:二次贝塞尔曲线由三个点定义,分别是起始点(P0)、控制点(P1),和结束点(P2)。曲线从起始点出发,经过控制点,最终到达结束点。曲线的形状由控制点的位置决定,控制点引导了曲线的弯曲和走势。 -
三次贝塞尔曲线:三次贝塞尔曲线由四个点定义,分别是起始点(P0)、两个控制点(P1和P2),和结束点(P3)。曲线从起始点出发,经过两个控制点,最终到达结束点。与二次贝塞尔曲线相比,三次贝塞尔曲线具有更高的灵活性,可以描述更复杂的曲线形状。
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import make_interp_spline
# 生成示例数据
x = np.linspace(0, 10, 20) # X坐标
y = np.random.rand(20) * 10 # 随机生成Y坐标
# 创建贝塞尔曲线插值器
tck = make_interp_spline(x, y, k=3)
# 生成平滑后的数据点
x_new = np.linspace(min(x), max(x), 200) # 新的X坐标范围
y_smooth = tck(x_new)
# 绘制原始数据和平滑后的贝塞尔曲线
plt.figure(figsize=(10, 6))
plt.scatter(x, y, label="原始数据", color='blue')
plt.plot(x_new, y_smooth, label="平滑后的贝塞尔曲线", color='red')
plt.legend()
plt.title("贝塞尔曲线数据平滑示例")
plt.xlabel("X坐标")
plt.ylabel("Y坐标")
plt.grid(True)
plt.show()
局部加权散点平滑(Loess平滑)
Loess(局部加权散点平滑,Locally Weighted Scatterplot Smoothing)是一种非参数的数据平滑方法,通常用于拟合或平滑散点数据,以捕获数据的局部趋势和模式。Loess平滑是一种局部回归方法,它对于不同区域的数据采用不同的权重,以确保在数据的每个局部区域上都能获得较好的拟合。
Loess平滑的核心思想是在每个数据点附近拟合一个局部多项式模型,然后使用这些局部模型的加权平均来获得平滑曲线。在每个局部区域,距离某个数据点越近的数据点将获得更大的权重,而距离较远的数据点将获得较小的权重。这样,Loess能够更好地拟合数据的局部特性,同时降低了全局模型的过拟合风险。
Loess平滑的主要特点包括:
-
非参数性:Loess不需要假设数据的特定分布或形式,因此适用于各种类型的数据。 -
局部性:Loess平滑主要关注数据的局部趋势,而不是全局趋势,这使它特别适用于具有复杂、非线性趋势的数据。 -
可调参数:Loess允许用户指定平滑窗口的大小或带宽,以控制局部模型的拟合程度。较小的带宽会导致更强的局部适应,而较大的带宽会导致更平滑的曲线。
Loess平滑通常用于探索性数据分析、数据可视化、时间序列分析和回归建模的预处理步骤。它可以帮助用户识别数据中的局部特性、趋势和周期性,从而更好地理解数据的结构。在统计学和数据科学中,Loess平滑是一种有用的工具,用于减少噪声并提取数据中的信号。
提供一个Python案例:
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
# 生成示例数据
x = np.linspace(0, 10, 100)
y = np.sin(x) + np.random.normal(0, 0.2, 100)
# 执行Loess平滑
lowess = sm.nonparametric.lowess(y, x, frac=0.3) # frac参数控制平滑带宽,可以调整以获得不同的平滑度
# 获取平滑后的数据
x_smooth, y_smooth = lowess.T
# 绘制原始数据和Loess平滑曲线
plt.figure(figsize=(10, 6))
plt.scatter(x, y, label="原始数据", color='blue', alpha=0.6)
plt.plot(x_smooth, y_smooth, label="Loess平滑", color='red')
plt.legend()
plt.title("Loess平滑示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
Kalman滤波
卡尔曼滤波(Kalman Filter)是一种用于状态估计和数据融合的强大数学和统计工具,最初由R.E. Kalman于1960年开发。卡尔曼滤波是一种递归的、最优的状态估计方法,通常用于处理含有噪声的时间序列数据,以估计系统的状态,同时提供估计的不确定性。
卡尔曼滤波的主要用途包括:
-
状态估计:卡尔曼滤波可以用于估计线性或非线性动态系统的状态变量,尤其是在系统中存在不完全或噪声观测的情况下。这对于跟踪运动物体、导航、定位以及环境感知等应用非常有用。 -
数据融合:卡尔曼滤波可以将多个传感器提供的信息融合在一起,以提供更准确的估计,降低估计误差。 -
控制系统:卡尔曼滤波还用于控制系统,以估计系统状态并制定控制策略,从而实现系统的自动控制。
卡尔曼滤波基于状态空间模型,其中系统状态通过线性或非线性动态模型演化,并且由观测模型通过传感器观测进行测量。卡尔曼滤波使用贝叶斯估计方法来不断更新系统状态的估计,考虑了过去的状态估计和新的观测数据,以提供最优的状态估计结果。卡尔曼滤波的主要目标是最小化估计误差的方差,从而提供最优的状态估计结果。
卡尔曼滤波有多个变种,包括扩展卡尔曼滤波(Extended Kalman Filter,EKF)和无迹卡尔曼滤波(Unscented Kalman Filter,UKF),用于处理非线性系统。这些滤波器的应用范围广泛,包括航空航天、自动驾驶汽车、机器人技术、金融建模和天气预测等领域。它们在数据平滑和状态估计方面提供了强大的工具。
先安装:
pip install pykalman
import numpy as np
import matplotlib.pyplot as plt
from pykalman import KalmanFilter
# 生成示例数据
np.random.seed(0)
n = 100
x = np.linspace(0, 10, n)
y = 0.1 * x + np.random.normal(0, 0.5, n)
# 创建卡尔曼滤波器
kf = KalmanFilter(initial_state_mean=0, n_dim_obs=1)
kf = kf.em(y, n_iter=10)
# 获取平滑后的数据
(filtered_state_means, _) = kf.filter(y)
# 绘制原始数据和卡尔曼滤波后的数据
plt.figure(figsize=(10, 6))
plt.plot(x, y, label="原始数据", color='blue', alpha=0.6)
plt.plot(x, filtered_state_means, label="卡尔曼滤波", color='red')
plt.legend()
plt.title("卡尔曼滤波数据平滑示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
小波变换
小波变换(Wavelet Transform)是一种用于信号处理和数据分析的数学方法。它是一种多尺度分析技术,可以将信号分解成不同尺度和频率的分量,以便更好地理解信号的局部特征和结构。小波变换在许多领域,包括信号处理、图像处理、数据压缩和模式识别等方面都有广泛的应用。
小波变换的主要特点包括:
-
多尺度分析:小波变换能够在不同尺度上分解信号,因此可以检测信号中的局部特征,从高频细节到低频整体。 -
时频局部性:与傅立叶变换不同,小波变换具有时频局部性,可以在时间和频率上同时分析信号。这使得它在分析非平稳信号和非线性信号时非常有用。 -
数据压缩:小波变换可以用于数据压缩,通过保留主要的小波系数,可以减小数据的存储空间和传输带宽。 -
特征提取:小波变换可以用于从信号中提取特征,用于模式识别、分类和检测任务。
小波变换有两种主要类型:连续小波变换(Continuous Wavelet Transform,CWT)和离散小波变换(Discrete Wavelet Transform,DWT)。
-
连续小波变换(CWT)是一种在不同尺度上滑动小波函数,对信号进行连续分解的方法。CWT的主要特点是它提供了连续的尺度信息,但计算成本较高。 -
离散小波变换(DWT)是一种通过滤波和下采样操作将信号分解成不同尺度的方法。DWT通过将信号分解成高频和低频部分,然后继续对低频部分进行分解,从而实现多尺度分析。DWT的计算效率高,适用于实际工程应用。
先安装:
pip install PyWavelets
Python案例:
import numpy as np
import matplotlib.pyplot as plt
import pywt
# 生成示例数据
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x) + np.random.normal(0, 0.1, len(x))
# 执行小波变换
wavelet = 'db4' # 选择小波基函数
level = 3 # 分解的级数
coeffs = pywt.wavedec(y, wavelet, level=level)
# 将高频部分系数置零,以实现平滑
coeffs_smoothed = [coeffs[0]] + [np.zeros_like(coeffs[i]) for i in range(1, len(coeffs))]
# 重构平滑后的信号
y_smoothed = pywt.waverec(coeffs_smoothed, wavelet)
# 绘制原始数据和平滑后的数据
plt.figure(figsize=(10, 6))
plt.plot(x, y, label="原始数据", color='blue', alpha=0.6)
plt.plot(x, y_smoothed, label="小波平滑", color='red')
plt.legend()
plt.title("小波变换数据平滑示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
Savitzky-Golay滤波器
Savitzky-Golay滤波是一种信号处理技术,用于对离散数据序列进行平滑和去噪。它是一种线性平滑滤波器,通过拟合多项式来估计数据点的平均值,以减小噪声和突发波动。Savitzky-Golay滤波器的主要思想是在局部窗口内对数据进行多项式拟合,从而获得平滑后的估计值。
Savitzky-Golay滤波的主要特点包括:
-
局部拟合:Savitzky-Golay滤波器使用一个固定大小的局部窗口来拟合多项式,通常窗口大小为奇数。对于每个数据点,它使用窗口内的数据点来执行多项式拟合,以获得该点的平滑估计值。
-
多项式拟合:滤波器使用多项式来拟合窗口内的数据点。常用的多项式阶数是2(二次多项式)或4(四次多项式),但可以根据需要选择不同的阶数。
-
数据平滑:Savitzky-Golay滤波器旨在平滑数据,减小数据中的高频噪声和突发波动。它保留了数据中的趋势和主要特征,同时去除了噪声。
-
适用性:Savitzky-Golay滤波器适用于各种领域,包括信号处理、光谱分析、化学分析、生物医学数据处理和图像处理等。它特别适用于光谱数据,因为它可以去除仪器噪声和光谱线的窄化。
-
平滑度可调:通过调整窗口大小和多项式阶数,可以控制滤波器的平滑度。较大的窗口和较高的多项式阶数可以提供更平滑的结果,而较小的窗口和较低的多项式阶数可以更好地保留数据的细节。
Savitzky-Golay滤波器是一种有效的数据平滑和去噪技术,它可以在许多领域用于处理具有噪声的数据,以提高数据的可解释性和分析能力。在Python中,你可以使用scipy.signal.savgol_filter
函数来执行Savitzky-Golay滤波。
# pip install scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
# 生成示例数据
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x) + np.random.normal(0, 0.2, len(x))
# 执行Savitzky-Golay滤波
window_length = 11 # 窗口长度(奇数)
polyorder = 2 # 多项式阶数
y_smoothed = savgol_filter(y, window_length, polyorder)
# 绘制原始数据和平滑后的数据
plt.figure(figsize=(10, 6))
plt.plot(x, y, label="原始数据", color='blue', alpha=0.6)
plt.plot(x, y_smoothed, label="Savitzky-Golay滤波", color='red')
plt.legend()
plt.title("Savitzky-Golay滤波器数据平滑示例")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
往期精彩回顾
交流群
欢迎加入机器学习爱好者微信群一起和同行交流,目前有机器学习交流群、博士群、博士申报交流、CV、NLP等微信群,请扫描下面的微信号加群,备注:”昵称-学校/公司-研究方向“,例如:”张小明-浙大-CV“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~(也可以加入机器学习交流qq群772479961)