Как рассчитать SMAPE для групп в данных временных рядов в Python? - PullRequest
3 голосов
/ 28 апреля 2019

Мои данные выглядят следующим образом, и я использую Facebook FbProphet для прогноза. Далее я хотел бы рассчитать SMAPE для каждой группы в моем фрейме данных. Я нашел функцию, описанную пользователем Kaggle здесь Но я не уверен, как реализовать в моем текущем коде. Так что SMAPE можно рассчитать для каждой группы. Кроме того, я знаю, что fbProphet имеет функцию проверки, но я хотел бы рассчитать SMAPE для каждой группы.

Примечание: я новичок в Python, предоставьте объяснение с кодом.

Dataset

import pandas as pd
data = {'Date':['2017-01-01', '2017-01-01','2017-01-01','2017-01-01','2017-01-01','2017-01-01','2017-01-01','2017-01-01',
               '2017-02-01', '2017-02-01','2017-02-01','2017-02-01','2017-02-01','2017-02-01','2017-02-01','2017-02-01'],'Group':['A','A','B','B','C','C','D','D','A','A','B','B','C','C','D','D'],
       'Amount':['12.1','13.2','15.1','10.7','12.9','9.0','5.6','6.7','4.3','2.3','4.0','5.6','7.8','2.3','5.6','8.9']}
df = pd.DataFrame(data)
print (df)

Код пока ...

def get_prediction(df):
    prediction = {}
    df = df.rename(columns={'Date': 'ds','Amount': 'y', 'Group': 'group'})
    df=df.groupby(['ds','group'])['y'].sum()
    df=pd.DataFrame(df).reset_index()
    list_articles = df.group.unique()

    for group in list_articles:
        article_df = df.loc[df['group'] == group]
        # set the uncertainty interval to 95% (the Prophet default is 80%)
        my_model = Prophet(weekly_seasonality= True, daily_seasonality=True,seasonality_prior_scale=1.0)
        my_model.fit(article_df)
        future_dates = my_model.make_future_dataframe(periods=6, freq='MS')
        forecast = my_model.predict(future_dates)
        prediction[group] = forecast
        my_model.plot(forecast)
    return prediction

1 Ответ

3 голосов
/ 07 мая 2019

Вы по-прежнему можете использовать собственную функцию cross_validation в fbprophet, но можете использовать собственную оценку Вот хороший блог от Uber о том, как они проводят тестирование на истории (скользящее окно и расширяющееся окно): https://eng.uber.com/forecasting-introduction/

Функция cv fbprophet работает со скользящим окном. Если это нормально, вы можете использовать это в сочетании с пользовательской функцией оценки. Я думаю, что хороший способ - расширить Prophet и реализовать метод .score().

Вот пример реализации:

from fbprophet import Prophet
from fbprophet.diagnostics import cross_validation
import numpy as np

class ProphetEstimator(Prophet):
    """
        Wrapper with custom scoring
    """

    def __init__(self, *args, **kwargs):
        super(ProphetEstimator, self).__init__(*args, **kwargs)

    def score(self):
        # cross val score reusing prophets own cv implementation
        df_cv = cross_validation(self, horizon='6 days')
        # Here decide how you want to calculate SMAPE.
        # Here each sliding window is summed up, 
        # and the SMAPE is calculated over the sum of periods, for all windows.
        df_cv = df_cv.groupby('cutoff').agg({
            "yhat": "sum",
            'y': "sum"
        })
        smape = self.calc_smape(df_cv['yhat'], df_cv['y'])
        return smape

    def calc_smape(self, y_hat, y):
        return 100/len(y) * np.sum(2 * np.abs(y_hat - y) / (np.abs(y) + np.abs(y_hat)))


def get_prediction(df):
    prediction = {}
    df = df.rename(columns={'Date': 'ds','Amount': 'y', 'Group': 'group'})
    df=df.groupby(['ds','group'])['y'].sum()
    df=pd.DataFrame(df).reset_index()
    list_articles = df.group.unique()

    for group in list_articles:
        article_df = df.loc[df['group'] == group]
        # set the uncertainty interval to 95% (the Prophet default is 80%)
        my_model = ProphetEstimator(weekly_seasonality= True, daily_seasonality=True,seasonality_prior_scale=1.0)
        my_model.fit(article_df)
        smape = my_model.score() # store this somewhere
        future_dates = my_model.make_future_dataframe(periods=6, freq='MS')
        forecast = my_model.predict(future_dates)
        prediction[group] = (forecast, smape)
        my_model.plot(forecast)
    return prediction
...