時系列平滑化手法の Python の詳細な実装: 移動平均、線形指数平滑法、二次指数平滑法、および三次指数平滑法
1. スムージング方法
一般的に使用される平滑化方法には次のものがあります。
移動平均
第一指数平滑法、第二指数平滑法、三次指数平滑法
具体的な理論はより理解しやすいので、以下を参照してください。
https://zhuanlan.zhihu.com/p/441373033
https://zhuanlan.zhihu.com/p/78848809
2.Pythonの実装
以下の書き込みメソッドはすべてストリーミング方式で記述されます。つまり、1 つのデータが処理され、1 種類のストリーミング データが処理されます。
2.1 移動平均
class MovAvgSmoothing(object):
def __init__(self, window_size=7):
self.window_size = window_size
self.data_queue = []
def update(self, data):
if len(self.data_queue) == self.window_size:
del self.data_queue[0]
self.data_queue.append(data)
return sum(self.data_queue) / len(self.data_queue)
2.2 指数平滑化
2.2.1 一次指数平滑法
class ExpSmoothing(object):
def __init__(self, alpha=0.9):
self.alpha = alpha
self.prev_smooth = None
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
else:
smooth = self.alpha * data + (1 - self.alpha) * self.prev_smooth
self.prev_smooth = smooth
return smooth
2.2.2 二次指数平滑法
"""
Holt指数平滑,方法中包含一个预测方程和两个平滑方程(水平平滑方程+趋势预测方程)
趋势部分又可分为加性趋势和乘性趋势
对于较大时间步长的预测,趋势可能不会无限延长,就需要抑制这种趋势,加性趋势和乘性趋势的抑制分别对应加性抑制(抑制线性趋势)、乘性抑制(抑制指数趋势
"""
class SmoothMode(Enum):
Addition = "add mode"
Multiplication = "multiply mode"
class DoubleExpSmoothing(object):
def __init__(self, alpha=0.5, beta=0.5, mode=SmoothMode.Addition):
self.alpha = alpha
self.beta = beta
self.prev_smooth = None
self.prev_trend = None
self.mode = mode
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
elif self.prev_trend is None:
smooth = None
if self.mode == SmoothMode.Addition:
self.prev_trend = data - self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
elif self.mode == SmoothMode.Multiplication:
self.prev_trend = data / self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth * self.prev_trend)
self.prev_smooth = smooth
return smooth
else:
trend = None
smooth = None
if self.mode == SmoothMode.Addition:
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
elif self.mode == SmoothMode.Multiplication:
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth * self.prev_trend)
trend = self.beta * (smooth / self.prev_smooth) + (1 - self.beta) * self.prev_trend
self.prev_smooth = smooth
self.prev_trend = trend
return smooth
2.2.3 3次指数平滑法
"""
Holt-Winters加法模型(Holt-Winters指数平滑)
方法中包含一个预测方程和三个平滑方程 (一个用于水平,一个用于趋势,一个用于季节性分量)
当季节变化在时间序列中大致保持不变时,通常选择加法模型
"""
class SmoothMode(Enum):
Addition = "add mode"
Multiplication = "multiply mode"
class TripleExpSmoothing(object):
def __init__(self, alpha=0.5, beta=0.5, gamma=0.5, m=12, mode=SmoothMode.Addition):
self.alpha = alpha
self.beta = beta
self.gamma = gamma
self.m = m
self.prev_smooth = None
self.prev_trend = None
self.seasonal = [0] * m
self.mode = mode
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
elif self.prev_trend is None:
trend, smooth = None, None
if self.mode == SmoothMode.Addition:
trend = data - self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + trend)
elif self.mode == SmoothMode.Multiplication:
trend = data / self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + trend)
self.prev_smooth = smooth
self.prev_trend = trend
return smooth
else:
season, trend, smooth = None, None, None
pre_m_season = self.seasonal[len(self.seasonal) - self.m]
if self.mode == SmoothMode.Addition:
smooth = self.alpha * (data - pre_m_season) + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
season = self.gamma * (data - self.prev_smooth - self.prev_trend) + (1 - self.gamma) * pre_m_season
elif self.mode == SmoothMode.Multiplication:
smooth = self.alpha * (data / pre_m_season) + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
season = self.gamma * (data / (self.prev_smooth + self.prev_trend)) + (1 - self.gamma) * pre_m_season
self.seasonal.append(season)
self.season_garbage_collection()
self.prev_smooth, self.prev_trend = smooth, trend
return smooth
# 60s 一个点,一天1440,周期如果是一周则是1440*7
def season_garbage_collection(self):
if len(self.seasonal) >= 1440 * 7:
self.seasonal = self.seasonal[-1440 * 7:]
3. 一元管理
クラスを使用してこれらの平滑化メソッドを動的かつ柔軟に管理し、使用するスケジュールに基づいてどれを使用するかを選択できます。
class MovSmoothManager(object):
def __init__(self, m):
self.avg_smooth = MovAvgSmoothing(window_size=3)
self.exp_smooth = ExpSmoothing(alpha=0.75)
self.double_exp_smooth_add = DoubleExpSmoothing(alpha=0.75, beta=0.75)
self.double_exp_smooth_multiply = DoubleExpSmoothing(alpha=0.75, beta=0.75, mode=SmoothMode.Multiplication)
self.triple_exp_smooth_add = TripleExpSmoothing(alpha=0.75, beta=0.75, gamma=0.75, m=m)
self.triple_exp_smooth_multiply = TripleExpSmoothing(alpha=0.75, beta=0.75, gamma=0.75, m=m, mode=SmoothMode.Multiplication)
def update_data(self, fea_pd: pd.DataFrame, fea_cols):
smooth_pd = fea_pd.copy(deep=True)
smooth_fea = ['avgSmooth', 'expSmooth', 'doubleExpSmoothAdd', 'doubleExpSmoothMultiply',
'tripleExpSmoothAdd', 'tripleExpSmoothMultiply']
smooth_data = {}
for fea_col in fea_cols:
smooth_pd['avgSmooth'] = smooth_pd[fea_col].apply(lambda x: self.avg_smooth.update(x))
smooth_pd['expSmooth'] = smooth_pd[fea_col].apply(lambda x: self.exp_smooth.update(x))
smooth_pd['doubleExpSmoothAdd'] = smooth_pd[fea_col].apply(lambda x: self.double_exp_smooth_add.update(x))
smooth_pd['doubleExpSmoothMultiply'] = smooth_pd[fea_col].apply(lambda x: self.double_exp_smooth_multiply.update(x))
smooth_pd['tripleExpSmoothAdd'] = smooth_pd[fea_col].apply(lambda x: self.triple_exp_smooth_add.update(x))
smooth_pd['tripleExpSmoothMultiply'] = smooth_pd[fea_col].apply(lambda x: self.triple_exp_smooth_multiply.update(x))
smooth_data[fea_col] = smooth_pd[smooth_fea]
return smooth_data
4. 合計コード
from enum import Enum
import pandas as pd
class SmoothMode(Enum):
Addition = "add mode"
Multiplication = "multiply mode"
class MovAvgSmoothing(object):
def __init__(self, window_size=7):
self.window_size = window_size
self.data_queue = []
def update(self, data):
if len(self.data_queue) == self.window_size:
del self.data_queue[0]
self.data_queue.append(data)
return sum(self.data_queue) / len(self.data_queue)
class ExpSmoothing(object):
def __init__(self, alpha=0.9):
self.alpha = alpha
self.prev_smooth = None
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
else:
smooth = self.alpha * data + (1 - self.alpha) * self.prev_smooth
self.prev_smooth = smooth
return smooth
"""
Holt指数平滑,方法中包含一个预测方程和两个平滑方程(水平平滑方程+趋势预测方程)
趋势部分又可分为加性趋势和乘性趋势
对于较大时间步长的预测,趋势可能不会无限延长,就需要抑制这种趋势,加性趋势和乘性趋势的抑制分别对应加性抑制(抑制线性趋势)、乘性抑制(抑制指数趋势
"""
class DoubleExpSmoothing(object):
def __init__(self, alpha=0.5, beta=0.5, mode=SmoothMode.Addition):
self.alpha = alpha
self.beta = beta
self.prev_smooth = None
self.prev_trend = None
self.mode = mode
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
elif self.prev_trend is None:
smooth = None
if self.mode == SmoothMode.Addition:
self.prev_trend = data - self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
elif self.mode == SmoothMode.Multiplication:
self.prev_trend = data / self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth * self.prev_trend)
self.prev_smooth = smooth
return smooth
else:
trend = None
smooth = None
if self.mode == SmoothMode.Addition:
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
elif self.mode == SmoothMode.Multiplication:
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth * self.prev_trend)
trend = self.beta * (smooth / self.prev_smooth) + (1 - self.beta) * self.prev_trend
self.prev_smooth = smooth
self.prev_trend = trend
return smooth
"""
Holt-Winters加法模型(Holt-Winters指数平滑)
方法中包含一个预测方程和三个平滑方程 (一个用于水平,一个用于趋势,一个用于季节性分量)
当季节变化在时间序列中大致保持不变时,通常选择加法模型
"""
class TripleExpSmoothing(object):
def __init__(self, alpha=0.5, beta=0.5, gamma=0.5, m=12, mode=SmoothMode.Addition):
self.alpha = alpha
self.beta = beta
self.gamma = gamma
self.m = m
self.prev_smooth = None
self.prev_trend = None
self.seasonal = [0] * m
self.mode = mode
def update(self, data):
if self.prev_smooth is None:
self.prev_smooth = data
return data
elif self.prev_trend is None:
trend, smooth = None, None
if self.mode == SmoothMode.Addition:
trend = data - self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + trend)
elif self.mode == SmoothMode.Multiplication:
trend = data / self.prev_smooth
smooth = self.alpha * data + (1 - self.alpha) * (self.prev_smooth + trend)
self.prev_smooth = smooth
self.prev_trend = trend
return smooth
else:
season, trend, smooth = None, None, None
pre_m_season = self.seasonal[len(self.seasonal) - self.m]
if self.mode == SmoothMode.Addition:
smooth = self.alpha * (data - pre_m_season) + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
season = self.gamma * (data - self.prev_smooth - self.prev_trend) + (1 - self.gamma) * pre_m_season
elif self.mode == SmoothMode.Multiplication:
smooth = self.alpha * (data / pre_m_season) + (1 - self.alpha) * (self.prev_smooth + self.prev_trend)
trend = self.beta * (smooth - self.prev_smooth) + (1 - self.beta) * self.prev_trend
season = self.gamma * (data / (self.prev_smooth + self.prev_trend)) + (1 - self.gamma) * pre_m_season
self.seasonal.append(season)
self.season_garbage_collection()
self.prev_smooth, self.prev_trend = smooth, trend
return smooth
# 60s 一个点,一天1440,周期如果是一周则是1440*7
def season_garbage_collection(self):
if len(self.seasonal) >= 1440 * 7:
self.seasonal = self.seasonal[-1440 * 7:]
class MovSmoothManager(object):
def __init__(self, m):
self.avg_smooth = MovAvgSmoothing(window_size=3)
self.exp_smooth = ExpSmoothing(alpha=0.75)
self.double_exp_smooth_add = DoubleExpSmoothing(alpha=0.75, beta=0.75)
self.double_exp_smooth_multiply = DoubleExpSmoothing(alpha=0.75, beta=0.75, mode=SmoothMode.Multiplication)
self.triple_exp_smooth_add = TripleExpSmoothing(alpha=0.75, beta=0.75, gamma=0.75, m=m)
self.triple_exp_smooth_multiply = TripleExpSmoothing(alpha=0.75, beta=0.75, gamma=0.75, m=m, mode=SmoothMode.Multiplication)
def update_data(self, fea_pd: pd.DataFrame, fea_cols):
smooth_pd = fea_pd.copy(deep=True)
smooth_fea = ['avgSmooth', 'expSmooth', 'doubleExpSmoothAdd', 'doubleExpSmoothMultiply',
'tripleExpSmoothAdd', 'tripleExpSmoothMultiply']
smooth_data = {}
for fea_col in fea_cols:
smooth_pd['avgSmooth'] = smooth_pd[fea_col].apply(lambda x: self.avg_smooth.update(x))
smooth_pd['expSmooth'] = smooth_pd[fea_col].apply(lambda x: self.exp_smooth.update(x))
smooth_pd['doubleExpSmoothAdd'] = smooth_pd[fea_col].apply(lambda x: self.double_exp_smooth_add.update(x))
smooth_pd['doubleExpSmoothMultiply'] = smooth_pd[fea_col].apply(lambda x: self.double_exp_smooth_multiply.update(x))
smooth_pd['tripleExpSmoothAdd'] = smooth_pd[fea_col].apply(lambda x: self.triple_exp_smooth_add.update(x))
smooth_pd['tripleExpSmoothMultiply'] = smooth_pd[fea_col].apply(lambda x: self.triple_exp_smooth_multiply.update(x))
smooth_data[fea_col] = smooth_pd[smooth_fea]
return smooth_data
推奨読書:
公開アカウント:AI カタツムリ車
謙虚さを保ち、規律を保ち、改善し続ける
[カタツムリ]を送って「手をつないでAIプロジェクト」(AIカタツムリカート)を入手
[1222] を送信して、適切な leetcode テストノートを入手してください
[AI に関する 4 冊の古典的な書籍] を送信すると、4 冊の古典的な AI 電子書籍が入手できます