【问题标题】:How to impute missing value in time series data with mean value of the same day and time in python如何在python中用同一天和同一时间的平均值估算时间序列数据中的缺失值
【发布时间】:2021-07-08 03:24:00
【问题描述】:

我有缺失值的多元时间序列数据。有什么方法可以用一周中同一天和同一时间的平均值来估算缺失值?例如,账户 1 在 2019-2-1(星期五)凌晨 2 点的值应填入账户 1 在每周五凌晨 2 点的平均值。

account              1     2     3
2019-2-1 01:00:00    15    12    10
2019-2-1 02:00:00    Nan   11    9
2019-2-1 03:00:00    10    11    3
...
2019-31-1 22:00:00   11    Nan   4
2019-31-1 23:00:00   Nan   12    4
2019-31-1 24:00:00   10    10    Nan

我已经尝试使用多项式interpolate,但这种方法与我的情况无关。也许groupby 可以提供帮助,但不确定在使用时间序列索引时如何使用它。

【问题讨论】:

  • 您是否担心可能会使用未来的值进行估算?
  • 不,我不限于此。未来的价值也可以包括在内

标签: python time-series interpolation imputation


【解决方案1】:

使用 group by 获取星期几和小时的平均值,然后使用 dow 和小时作为键映射值为 nan 的平均值

 data=[
 ('2019-02-07 01:00:00',  15,    12,    10),
 ('2019-02-07 02:00:00',    np.nan,   11,    9),
 ('2019-02-07 03:00:00',    10,    11,    3),
 ('2019-01-31 22:00:00',   11,    np.nan,   4),
 ('2019-01-31 02:00:00',   np.nan,    12,    4),
 ('2019-01-24 02:00:00',   10,    10,    float('nan'))]

 df=pd.DataFrame(data,columns=['date','acct1','acct2','acct3'])
 df['date']=df['date'].apply(lambda row: datetime2.strptime(str(row),"%Y-%m-%d %H:%M:%S"))
 df['dow']=df['date'].apply(lambda row: row.dayofweek)
 df['hour']=df['date'].apply(lambda row: row.hour)
 df.set_index('date')

 grouped_acct1=df.groupby(['dow','hour'])['acct1'].mean()
 grouped_acct2=df.groupby(['dow','hour'])['acct2'].mean()
 grouped_acct3=df.groupby(['dow','hour'])['acct3'].mean()

 for key,item in df.iterrows():
     df.loc[key,'acct1_mean']= grouped_acct1[(grouped_acct1.index.get_level_values(0)==item.dow) & (grouped_acct1.index.get_level_values(1)==item.hour)].values
     df.loc[key,'acct2_mean']= grouped_acct2[(grouped_acct2.index.get_level_values(0)==item.dow) & (grouped_acct2.index.get_level_values(1)==item.hour)].values
     df.loc[key,'acct3_mean']= grouped_acct3[(grouped_acct3.index.get_level_values(0)==item.dow) & (grouped_acct3.index.get_level_values(1)==item.hour)].values


 for key,item in df.iterrows():
      if math.isnan(item['acct1']):
          df.loc[key,'acct1']=item['acct1_mean'] 
      else: 
          df.loc[key,'acct1']=item['acct1']   

     if math.isnan(item['acct2']):
         df.loc[key,'acct2']=item['acct2_mean'] 
     else: 
         df.loc[key,'acct2']=item['acct2']   

     if math.isnan(item['acct3']):
         df.loc[key,'acct3']=item['acct3_mean'] 
     else: 
         df.loc[key,'acct3']=item['acct3']   

  print(df)

输出

  date                  acct1  acct2  acct3  dow  hour  acct1_mean  acct2_mean  \
0 2019-02-07 01:00:00   15.0   12.0   10.0    3     1        15.0        12.0   
1 2019-02-07 02:00:00   10.0   11.0    9.0    3     2        10.0        11.0   
2 2019-02-07 03:00:00   10.0   11.0    3.0    3     3        10.0        11.0   
3 2019-01-31 22:00:00   11.0    NaN    4.0    3    22        11.0         NaN   
4 2019-01-31 02:00:00   10.0   12.0    4.0    3     2        10.0        11.0   
5 2019-01-24 02:00:00   10.0   10.0    6.5    3     2        10.0        11.0    

   acct3_mean  
0        10.0  
1         6.5  
2         3.0  
3         4.0  
4         6.5  
5         6.5  

【讨论】:

    【解决方案2】:

    如果您不担心使用未来值进行插补,您可以对每一列使用此值:

    示例 pandas 数据帧(具有 dtype='datetime64[ns]' 的 'date_time' 索引):

                        acct_1  acct_2  acct_3
    date_time           
    2019-02-07 01:00:00   15.0    12.0    10.0
    2019-02-07 02:00:00    NaN    11.0     9.0
    2019-02-07 03:00:00   10.0    11.0     3.0
    2019-01-31 22:00:00   11.0     NaN     4.0
    2019-01-31 02:00:00    NaN    12.0     4.0
    2019-01-24 02:00:00   10.0    10.0     NaN
    

    代码和结果:

    df['acct_1'] = (df
        .groupby((df.index.dayofweek * 24) + (df.index.hour))
        .transform(lambda x: x.fillna(x.mean())
    )
    df
                        acct_1  acct_2  acct_3
    date_time           
    2019-02-07 01:00:00   15.0    12.0    10.0
    2019-02-07 02:00:00   10.0    11.0     9.0
    2019-02-07 03:00:00   10.0    11.0     3.0
    2019-01-31 22:00:00   11.0     NaN     4.0
    2019-01-31 02:00:00   10.0    12.0     4.0
    2019-01-24 02:00:00   10.0    10.0     NaN
    

    【讨论】:

    • 非常感谢@AlexK 的想法。既然我有数千个帐户,有什么方法可以更有效地做到这一点?我试过 'df=(df.groupby('(df.index.dayofweek* 24) + (df.index.hour)).transform(lambda x: x.fillna(x.mean()), axis=1 ))' 但出现错误 Transform function invalid for data types
    • @m_ana 我认为如果您只是从代码中删除 axis=1 参数,那应该可以解决问题并将转换应用于每一列。这适用于这个玩具数据集。
    • 是的,这就像魅力。非常感谢
    【解决方案3】:

    您可以尝试使用groupbyinterpolaterolling,如下所示:

    import pandas as pd
    import numpy as np
    if __name__ == "__main__":
        df = pd.DataFrame({"account": pd.date_range(start="2019-02-01", periods=1000, freq="H"), "1": range(1000), "2": range(1000,2000), "3": range(1000,0,-1)})
        df.loc[2:3, "1"] = np.nan
        df.loc[6, "2"] = np.nan
        df.loc[7, "3"] = np.nan
        df["day"] = df["account"].dt.date
        print(df)
    #     df_result = df.groupby("day").apply(lambda day: day.interpolate(method="linear"))
        df_groupby = df.groupby("day")
        df["1"] = df_groupby["1"].transform(lambda col: col.rolling(2, min_periods=1).mean())
        print(df)
    

    结果:

                    account      1       2       3         day
    0   2019-02-01 00:00:00    0.0  1000.0  1000.0  2019-02-01
    1   2019-02-01 01:00:00    1.0  1001.0   999.0  2019-02-01
    2   2019-02-01 02:00:00    NaN  1002.0   998.0  2019-02-01
    3   2019-02-01 03:00:00    NaN  1003.0   997.0  2019-02-01
    4   2019-02-01 04:00:00    4.0  1004.0   996.0  2019-02-01
    ..                  ...    ...     ...     ...         ...
    995 2019-03-14 11:00:00  995.0  1995.0     5.0  2019-03-14
    996 2019-03-14 12:00:00  996.0  1996.0     4.0  2019-03-14
    997 2019-03-14 13:00:00  997.0  1997.0     3.0  2019-03-14
    998 2019-03-14 14:00:00  998.0  1998.0     2.0  2019-03-14
    999 2019-03-14 15:00:00  999.0  1999.0     1.0  2019-03-14
    
    [1000 rows x 5 columns]
                    account      1       2       3         day
    0   2019-02-01 00:00:00    0.0  1000.0  1000.0  2019-02-01
    1   2019-02-01 01:00:00    0.5  1001.0   999.0  2019-02-01
    2   2019-02-01 02:00:00    1.0  1002.0   998.0  2019-02-01
    3   2019-02-01 03:00:00    NaN  1003.0   997.0  2019-02-01
    4   2019-02-01 04:00:00    4.0  1004.0   996.0  2019-02-01
    ..                  ...    ...     ...     ...         ...
    995 2019-03-14 11:00:00  994.5  1995.0     5.0  2019-03-14
    996 2019-03-14 12:00:00  995.5  1996.0     4.0  2019-03-14
    997 2019-03-14 13:00:00  996.5  1997.0     3.0  2019-03-14
    998 2019-03-14 14:00:00  997.5  1998.0     2.0  2019-03-14
    999 2019-03-14 15:00:00  998.5  1999.0     1.0  2019-03-14
    
    [1000 rows x 5 columns]
    

    【讨论】:

    • 我认为 OP 希望考虑时间,而不仅仅是日期。而且您的答案仍然留下NaNs 并通过滚动更改列中的每个值进行转换,而不仅仅是NaNs。
    • 是的。我为 OP 留下了 NaN 值,以便根据他或她的需要调整参数。他或她可以应用interpolate linear 方法,该方法在我的代码中进行了注释,将删除所有NaN 值,或更改rolling 方法的参数,或多次应用上述方法以删除NaNs。跨度>
    猜你喜欢
    • 1970-01-01
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 2021-03-07
    • 2016-11-21
    • 2021-07-29
    相关资源
    最近更新 更多