移動平均、線形指数平滑法、二次指数平滑法、三次指数平滑法といった時系列平滑化手法の Python の詳細...

時系列平滑化手法の 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

推奨読書:

私の2022年のインターネットスクール募集の共有

私の2021年のまとめ

アルゴリズムの立場と開発の立場の違いについての簡単な説明

インターネットスクール募集 研究開発給与概要

公開アカウント:AI カタツムリ車

謙虚さを保ち、規律を保ち、改善し続ける

9cc7ec25479b33cff8852a7f9059e6b3.jpeg

[カタツムリ]を送って「手をつないでAIプロジェクト」(AIカタツムリカート)を入手

[1222] を送信して、適切な leetcode テストノートを入手してください

[AI に関する 4 冊の古典的な書籍] を送信すると、4 冊の古典的な AI 電子書籍が入手できます

おすすめ

転載: blog.csdn.net/qq_33431368/article/details/134901683