【问题标题】:Is it possible to apply seasonal_decompose() after a groupby() where date is not the index of the data frame?是否可以在日期不是数据框索引的 groupby() 之后应用seasonal_decompose()?
【发布时间】:2021-06-28 20:57:28
【问题描述】:

所以,对于一个预测项目,我有一个非常长的 Dataframe,其中包含以下类型的多个时间序列(它有一个数字索引):

date time_series_id value
2015-08-01 0 0
2015-08-02 0 1
2015-08-03 0 2
2015-08-04 0 3
2015-08-01 1 2
2015-08-02 1 3
2015-08-03 1 4
2015-08-04 1 5

我的目标是为这些数据集添加 3 个新列,分别对应于 trend、seasonal 和 resid 的每个单独的时间序列(每个 id)。 根据数据集的特点,他们倾向于在日期的开头和结尾都有Nans。

我试图做的是:

from statsmodels.tsa.seasonal import seasonal_decompose

df.assign(trend = lambda x: x.groupby("time_series_id")["value"].transform(lambda s: s.mask(~s.isna(), other= seasonal_decompose(s[~s.isna()], model='aditive', extrapolate_trend='freq').trend))

预期输出(趋势值不是实际值)应该是:

date time_series_id value trend
2015-08-01 0 0 1
2015-08-02 0 1 1
2015-08-03 0 2 1
2015-08-04 0 3 1
2015-08-01 1 2 1
2015-08-02 1 3 1
2015-08-03 1 4 1
2015-08-04 1 5 1

但我收到以下错误消息:

AttributeError: 'Int64Index' object has no attribute 'inferred_freq'

在我的代码的上一次迭代中,这适用于我的个人时间序列数据帧,因为我已将 date 列嵌入为数据帧的索引而不是附加列,因此“x”表示lambda 函数需要一个适合seasonal_decompose 函数的“日期时间”索引。

df.assign(
      trend = lambda x: x["value"].mask(~x["value"].isna(), other = 
      seasonal_decompose(x["value"][~x["value"].isna()], model='aditive', extrapolate_trend='freq').trend))

我的问题是,首先:是否可以使用 groupby 来实现这一点?或者第二个可能的其他方法:是否可以处理不占用太多内存的问题?我正在处理的原始数据集大约有 1MM ~ 行,因此非常欢迎任何帮助:)。

【问题讨论】:

    标签: python pandas time-series pandas-groupby statsmodels


    【解决方案1】:

    你有 lambda x: x.groupby(..., 你没有任何东西要分组;你告诉它分组一行(我相信)。你可以尝试这样的设置,也许

    在这里,您定义一个函数以作用于您通过 apply() 方法发送的组。然后你应该可以使用你的原始代码了。

    我没有测试过这个,但我经常使用这个设置来处理小组。

    def trend_function(x):
        # do your lambda function here as you are sending each grouping
          x.assign(
          trend = lambda x: x["value"].mask(~x["value"].isna(), other = 
          seasonal_decompose(x["value"][~x["value"].isna()], model='aditive', extrapolate_trend='freq').trend))
        return x
    
    dfnew = df.groupby('time_series_id').apply(trend_function)
    

    【讨论】:

      【解决方案2】:

      使用 extrapolate_trend='freq' 作为参数。您将趋势、季节性和残差添加到字典并绘制字典 从 statsmodels.graphics 导入 tsaplots 将 statsmodels.api 导入为 sm

      date=['2015-08-01','2015-08-02','2015-08-03','2015-08-04','2015-08-01','2015-08-02','2015-08-03','2015-08-04']
      time_series_id=[0,0,0,0,1,1,1,1]
      value=[0,1,2,3,2,3,4,5]
      
      df=pd.DataFrame({'date':date,'time_series_id':time_series_id,'value':value})
      
      df['date']=pd.to_datetime(df['date'])
      df=df.set_index('date')
      print(df)
      
      index_day = df.index.day
      value_by_day = df.groupby(index_day)['value'].mean()
      fig,ax = plt.subplots(figsize=(12,4))
      value_by_day.plot(ax=ax)
      plt.title('value by month')
      plt.show()
      
      df[['value']].boxplot()
      plt.show()
      
      fig,ax = plt.subplots(figsize=(12,4))
      df[['value']].hist(ax=ax, bins=5)
      plt.show()
      fig,ax = plt.subplots(figsize=(12,4))
      df[['value']].plot(kind='density', ax=ax)
      plt.show()
      
      plt.clf()
      fig,ax = plt.subplots(figsize=(12,4))
      plt.style.use('seaborn-pastel')
      fig = tsaplots.plot_acf(df['value'], lags=4,ax=ax)
      plt.show()
      
      decomposition=sm.tsa.seasonal_decompose(x=df['value'],model='additive',     extrapolate_trend='freq', period=1)
      decomposition.plot()
      plt.show()
      
      decomposition_trend=decomposition.trend
      ax= decomposition_trend.plot(figsize=(14,2))
      ax.set_xlabel('Date')
      ax.set_ylabel('Trend of time series')
      ax.set_title('Trend values of the time series')
      plt.show()
      

      【讨论】:

        【解决方案3】:

        已经提出的解决方案之一是否有效?如果是这样,或者您找到了不同的解决方案,请分享。我尝试了每一个都没有成功,但我是 Python 新手,所以可能遗漏了一些东西。

        这是我想出的,使用 for 循环。对于我的数据集,分解包含 6,000 个不同子集的 2000 万行需要 8 分钟。这行得通,但我希望它更快。

        Date Time Segment ID Travel Time(Minutes)
        2021-11-09 07:15:00 1 30
        2021-11-09 07:30:00 1 18
        2021-11-09 07:15:00 2 30
        2021-11-09 07:30:00 2 17
        segments = set(frame['Segment ID'])
        data = pd.DataFrame([])
        for s in segments:
            df = frame[frame['Segment ID'] == s].set_index('Date Time').resample('H').mean()
            comp = sm.tsa.seasonal_decompose(x=df['Travel Time(Minutes)'], period=24*7, two_sided=False) 
            df = df.join(comp.trend).join(comp.seasonal).join(comp.resid)
        
            #optional columns with some statistics to find outliers and trend changes
            df['resid zscore'] = (df['resid'] - df['resid'].mean()).div(df['resid'].std())
            df['trend pct_change'] = df.trend.pct_change()
            df['trend pct_change zscore'] = (df['trend pct_change'] - df['trend pct_change'].mean()).div(df['trend pct_change'].std())
        
            data = data.append(df.dropna())
        

        【讨论】:

          猜你喜欢
          • 2021-11-22
          • 1970-01-01
          • 2020-02-19
          • 2015-12-08
          • 2020-12-30
          • 2011-01-31
          • 1970-01-01
          • 2019-04-28
          • 2013-06-02
          相关资源
          最近更新 更多