首页 > 解决方案 > 是否可以在日期不是数据框索引的 groupby() 之后应用seasonal_decompose()?

问题描述

因此,对于一个预测项目,我有一个非常长的以下类型的多个时间序列的数据框(它有一个数字索引):

日期 time_series_id 价值
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 个新列,分别对应于 和 的每个单独的时间序列(每个trendid seasonalresid。根据数据集的特点,它们往往在日期的开头和结尾都有 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))

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

日期 time_series_id 价值 趋势
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列嵌入为数据帧的索引而不是附加列,因此 lambda 函数采用的“x”已经适合该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 ~ 行,因此非常欢迎任何帮助:)。

标签: pythonpandastime-seriespandas-groupbystatsmodels

解决方案


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

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

约会时间 细分 ID 行程时间(分钟)
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())

推荐阅读