【時系列-05】Facebook/Prophet


Abstract
  • Prophet follows the sklearn model API. We create an instance of the Prophet class and then call its fit and predict methods.
  • Input of Prophet:ds(時間フォーマット、YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)and y(numeric、予測の測定値を表す);
  • Prophet is a procedure for forecasting time series data based on an additive model where non-linear trends are fit with yearly, weekly, and daily seasonality, plus holiday effects. It works best with time series that have strong seasonal effects and several seasons of historical data. Prophet is robust to missing data and shifts in the trend, and typically handles outliers well.
  • By default, Prophet uses a linear model for its forecast.

  • 時系列予測は大部分の会社にとって必要な需要があり、例えば電子商取引予測GMV、外食O 2 O予測成約量の変動は運力分配、ホテル予測間夜量で定価と販売を調整するなどである.しかし、通常、時間系列予測は多くの企業にとって難題です.主な原因は、時間系列予測自体が玄学(大霧)であるほか、アナリストに深い専門分野知識(domain knowledge)と時間系列モデリングの統計学知識を同時に備えることを要求している.さらに,時間系列モデルのパラメータ調整も比較的複雑で煩雑な作業である.
    prophetはこのような背景の下で製品であり、いくつかの時間系列をモデリングして一般的なプロセスとパラメータdefault化し、統計をよく知らないビジネスアナリストも需要に対して迅速に相対的に利用可能なモデルを構築することができる.
    Reference
    https://facebook.github.io/prophet/##公式サイト英語ドキュメント
    https://github.com/facebook/prophet##githubコード倉庫
    https://vectorf.github.io/##ある人は公式サイトの教程のする翻訳によって
    https://www.zhihu.com/question/56585493##問題を知る
    Detail
    future = m.make_future_dataframe(periods=6, freq='M'); ##予測される日付を格納し、frepは時間粒度を指定する.
    make_future_dataframe(self, periods, freq='D', include_history=True)
    periods:Int number of periods to forecast forward;periods個の時間単位を予測する必要があることを示す.
    freq:時間粒度を表し、Mは時間粒度が月を表す.指定されていない場合、デフォルトは「日」です.
    include_history:Boolean to include the historical dates in the data frame for predictions;
    future:extends forward from the end of self.history for the requested number of periods;モデルは、あなたが提供したデータに基づいて予測される日付を返します.
    fcst = m.predict(future)
    fcst:予測結果を返します.タイプはpandasです.core.frame.フィールドのセットを含むDataFrame:
    yhat(予測値):The predict method will assign each row in future a predicted value which it names yhat.
    モデル#モデル#
  • Prophetデフォルトでは線形モデル
  • が使用されます.
  • growthパラメータでモデルを指定:m=Prophet(growth='logistic')
  • 負荷能力:cap
  • 成長状況を予測すると、通常、総市場規模、総人口など、到達可能な最大限界値が存在する.これを荷重能力と呼ぶと、予測時にこの値に近づくと飽和傾向にあるべきである.
  • 各行のデータは、対応するcap値を指定する必要があります.

  • Saturating Minimum(飽和最小値):floor
  • の使用方法は、負荷能力capと同様の
  • を用いる.
  • df['floor'] = 1.5

  • トレンド変異点:Trend Changepoints
    By default, Prophet will automatically detect these changepoints and will allow the trend to adapt appropriately. 突然変異点を自動的に検出して適応処理を行うが、1)missed a rate change;2)overfitting rate changes in the history;
  • 自動検出変異点:m=Prophet(changepoint_range=0.9)、デフォルトでは、Prophetは最初の80%の時間系列のみを検出します:フィットを回避します.
  • トレンドの調整の柔軟性:changepoint_prior_scaleのデフォルト値は0.05->m=Prophet(changepoint_prior_scale=0.5)
  • です.
    changepoint_prior_scale値が高いほど、トレンドはより柔軟にフィットします.changepoint_prior_scale値が低いほど,トレンドフィットの柔軟性が低下する.
    オーバーフィット:柔軟性が強すぎる;不適合:柔軟性が不足しています.
  • 突然変異点の位置を指定:changepoints->m=Prophet(changepoints=['2014-01-01'])
  • 祝日
  • 休日に関するデータベースを作成する:(holiday||ds||lower_window||upper_window)
  • holiday:祝日名
  • ds:日付、複数の日付を同時に含むことができる
  • [lower_window,upper_window]休日を1つの区間に拡張します.例えば、[−1,0]はdsを含む前日の加入区間を表す.
  • 休日データ構築が完了した後、参照を行う:m=Prophet(holidays=holidays)
  • 休日設定先験規模:holidays_prior_scale、デフォルト値は10
  • です.
  • 休日効果が過度にフィッティングされていることが発見された場合、パラメータを設定することによって、それらの先行規模を調整して平らにすることができる.
  • m = Prophet(holidays=holidays, holidays_prior_scale=1).fit(df)
  • 季節性設定先行規模:seasonality_prior_scale

  • Script - update log
    20180714:初版コード、m=Prophet(seasonality_mode='multiplicative',weekly_seasonality=True,daily_seasonality=True).fit(class_df)
    20180717:モデルパラメータ構成(by yinque)m.add_を追加seasonality('quarterly', period=91.25, fourier_order=8, mode='additive')
    # Python
    import pandas as pd
    import numpy as np
    from fbprophet import Prophet
    #import matplotlib.pyplot as plt
    import xlwt
    
    
    def num2date(num_list):
        
        ## num   :20180709
        from datetime import datetime    
        date_list = []
        for num in num_list:
            num = str(num)
            date_list.append(datetime(int(num[0:4]), int(num[4:6]), int(num[6:8])).strftime('%Y-%m-%d'))
        return date_list
    
    
    year_list = ["2013", "2014", "2015", "2016", "2017", "2018"]
    month_list = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]
    
    all_leaf_class_name_dict = {cate_id: cate_name}
    
    
    playoffs = pd.DataFrame({
      'holiday': 'playoff',
      'ds': pd.to_datetime(['2008-01-13', '2009-01-03', '2010-01-16',
                            '2010-01-24', '2010-02-07', '2011-01-08',
                            '2013-01-12', '2014-01-12', '2014-01-19',
                            '2014-02-02', '2015-01-11', '2016-01-17',
                            '2016-01-24', '2016-02-07']),
      'lower_window': 0,
      'upper_window': 1,
    })
        
    superbowls = pd.DataFrame({
      'holiday': 'superbowl',
      'ds': pd.to_datetime(['2013-06-18', '2014-06-18', '2015-06-18', '2016-06-18', '2017-06-18', '2018-06-18',
                            '2013-11-11', '2014-11-11', '2015-11-11', '2016-11-11', '2017-11-11', '2018-11-11',
                            '2013-12-12', '2014-12-12', '2015-12-12', '2016-12-12', '2017-12-12', '2018-12-12'
                            ]),
      'lower_window': 0,
      'upper_window': 1,
    })
    
    holidays = pd.concat((playoffs, superbowls))
    
    
    def by_days(year_month_day):
    
        periods = 120
        df = pd.read_csv('source_data_2013_201805.csv', header=0, encoding='gbk')
        df.columns = ['ds', 'cate_id', 'cate_name', 'y']
        
        cate_name = "   "
        class_df = df[df.cate_name.str.startswith(cate_name)].reset_index(drop=True)
        class_df['ds'] = num2date(class_df['ds'].tolist())
        class_df = class_df[['ds','y']]
        
        stop_month_index = class_df[class_df.ds == year_month_day].index.tolist()[0]
        class_df = class_df[0: stop_month_index+1]
        class_df_now = class_df[0:len(class_df)-periods]
        
        #class_df["y"] = np.log10(np.log10(df['y']))
    #    print(class_df.head())
        
        m = Prophet()
        m.fit(class_df_now)
        future = m.make_future_dataframe(periods = periods)  ## periods: predict days
    #    future.tail()
        forecast = m.predict(future)
        print(forecast.tail(periods))
        #
        m.plot(forecast)
        m.plot_components(forecast)
    
    
    
    pred_list = []
    real_list = []
    error_list = []
    def by_month(year_month, periods):
        
        print("End time: {}".format(year_month))
        
        save_path = "result_cate_by_month_until_{}_num{}.xls".format(year_month[0:4], periods)
        target_data = xlwt.Workbook(encoding="utf-8")
        
        df = pd.read_csv('./dataset/cate_by_month_histroy.csv', header=0, encoding='gbk')
        df.columns = ['ds', 'cate_id', 'cate_name', 'y']
        
        for cate_id in all_leaf_class_name_dict.keys():
            
            cate_name = all_leaf_class_name_dict[cate_id]
            target_sheet = target_data.add_sheet(u'{}'.format(cate_name))
            
            class_df_all = df[df.cate_name.str.startswith(cate_name)].reset_index(drop=True)
            stop_month_index = class_df_all[class_df_all.ds == year_month].index.tolist()[0]
            class_df_all = class_df_all[0: stop_month_index+1]
            class_df = class_df_all[0:len(class_df_all)-periods]
            
    #        print(class_df_all)
    #        print(class_df)
            
            class_df = class_df[['ds','y']]
    #        class_df['y'] = np.log10(class_df['y'])
    #        m = Prophet(seasonality_mode='multiplicative', weekly_seasonality=True, daily_seasonality=True).fit(class_df)
            m = Prophet(seasonality_mode='multiplicative')
            m.add_seasonality('quarterly', period=91.25, fourier_order=8, mode='additive')
            #m.add_regressor('regressor', mode='additive')
            m.fit(class_df)
            future = m.make_future_dataframe(periods=periods, freq='M')
            fcst = m.predict(future)
            pred_pandas = fcst.tail(periods)
            pred_list.clear()
            
            for index, row in pred_pandas.iterrows():
    #            pred_list.append(10**row["yhat"])
                pred_list.append(row["yhat"])
                
            # =============================================================================
            real_list.clear()
            for index, row in (class_df_all.tail(periods)).iterrows():
                real_list.append(row["y"])
            
            # =============================================================================
            error_list.clear()
            excel_index_row = 0
    
            target_sheet.write(0, 1, "real-data")
            target_sheet.write(0, 2, "pred-data")
            target_sheet.write(0, 3, "error")
            
            for i in range(periods):
                target_sheet.write(i+1, 0, "{}-{}".format(year_month[0:4], int(year_month[5:7])-periods+1))
    
            
            for i in range(len(real_list)):
                temp_error = (pred_list[i] - real_list[i])/real_list[i]
                error_list.append(temp_error)
                
                excel_index_row += 1
                target_sheet.write(excel_index_row, 1, real_list[i])
                target_sheet.write(excel_index_row, 2, pred_list[i])
                target_sheet.write(excel_index_row, 3, temp_error)
            
            print("Done: {}".format(cate_name))
            
        target_data.save(save_path)
        
        
    # =============================================================================
    # main    
    # =============================================================================
    #by_days("2017-12-31")
    periods = 12  ##          
    by_month("2015-12", periods)