【问题标题】:Resample pandas dataframe and interpolate missing values for timeseries data重新采样 pandas 数据帧并为时间序列数据插入缺失值
【发布时间】:2019-08-11 21:11:51
【问题描述】:

我需要在一小时内以 15 分钟的间隔重新采样时间序列数据并插入缺失值。每个 ID 每小时应该有四行数据。

在:

ID            Time  Value
1   1/1/2019 12:17      3
1   1/1/2019 12:44      2
2   1/1/2019 12:02      5
2   1/1/2019 12:28      7

输出:

ID                Time  Value
1  2019-01-01 12:00:00    3.0
1  2019-01-01 12:15:00    3.0
1  2019-01-01 12:30:00    2.0
1  2019-01-01 12:45:00    2.0
2  2019-01-01 12:00:00    5.0
2  2019-01-01 12:15:00    7.0
2  2019-01-01 12:30:00    7.0
2  2019-01-01 12:45:00    7.0

我为此编写了一个函数,但是在尝试处理更大的数据集时效率会急剧下降。

有没有更有效的方法来做到这一点?

import datetime
import pandas as pd


data = pd.DataFrame({'ID': [1,1,2,2], 
                    'Time': ['1/1/2019 12:17','1/1/2019 12:44','1/1/2019 12:02','1/1/2019 12:28'], 
                    'Value': [3,2,5,7]})


def clean_dataset(data):
    ids = data.drop_duplicates(subset='ID')
    data['Time'] = pd.to_datetime(data['Time'])
    data['Time'] = data['Time'].apply(
    lambda dt: datetime.datetime(dt.year, dt.month, dt.day, dt.hour,15*(dt.minute // 15)))
    data = data.drop_duplicates(subset=['Time','ID']).reset_index(drop=True)
    df = pd.DataFrame(columns=['Time','ID','Value'])
    for i in range(ids.shape[0]):
        times = pd.DataFrame(pd.date_range('1/1/2019 12:00','1/1/2019 13:00',freq='15min'),columns=['Time'])
        id_data = data[data['ID']==ids.iloc[i]['ID']]
        clean_data = times.join(id_data.set_index('Time'), on='Time')
        clean_data = clean_data.interpolate(method='linear', limit_direction='both')
        clean_data.drop(clean_data.tail(1).index,inplace=True)
        df = df.append(clean_data)
    return df


clean_dataset(data)

【问题讨论】:

    标签: python pandas numpy scipy data-science


    【解决方案1】:

    你可以使用:

    #round datetimes by 15 minutes
    data['Time'] = pd.to_datetime(data['Time'])
    minutes = pd.to_timedelta(15*(data['Time'].dt.minute // 15), unit='min')
    data['Time'] = data['Time'].dt.floor('H') + minutes
    
    #change date range for 4 values (to `12:45`)
    rng = pd.date_range('1/1/2019 12:00','1/1/2019 12:45',freq='15min')
    #create MultiIndex and reindex
    mux = pd.MultiIndex.from_product([data['ID'].unique(), rng], names=['ID','Time'])
    data = data.set_index(['ID','Time']).reindex(mux).reset_index()
    #interpolate per groups
    data['Value'] = (data.groupby('ID')['Value']
                         .apply(lambda x: x.interpolate(method='linear', limit_direction='both')))
    print (data)
       ID                Time  Value
    0   1 2019-01-01 12:00:00    3.0
    1   1 2019-01-01 12:15:00    3.0
    2   1 2019-01-01 12:30:00    2.0
    3   1 2019-01-01 12:45:00    2.0
    4   2 2019-01-01 12:00:00    5.0
    5   2 2019-01-01 12:15:00    7.0
    6   2 2019-01-01 12:30:00    7.0
    7   2 2019-01-01 12:45:00    7.0
    

    如果范围不能改变:

    data['Time'] = pd.to_datetime(data['Time'])
    minutes = pd.to_timedelta(15*(data['Time'].dt.minute // 15), unit='min')
    data['Time'] = data['Time'].dt.floor('H') + minutes
    
    #end in 13:00
    rng = pd.date_range('1/1/2019 12:00','1/1/2019 13:00',freq='15min')
    mux = pd.MultiIndex.from_product([data['ID'].unique(), rng], names=['ID','Time'])
    data = data.set_index(['ID','Time']).reindex(mux).reset_index()
    data['Value'] = (data.groupby('ID')['Value']
                         .apply(lambda x: x.interpolate(method='linear', limit_direction='both')))
    
    #remove last row per groups
    data = data[data['ID'].duplicated(keep='last')]
    print (data)
       ID                Time  Value
    0   1 2019-01-01 12:00:00    3.0
    1   1 2019-01-01 12:15:00    3.0
    2   1 2019-01-01 12:30:00    2.0
    3   1 2019-01-01 12:45:00    2.0
    5   2 2019-01-01 12:00:00    5.0
    6   2 2019-01-01 12:15:00    7.0
    7   2 2019-01-01 12:30:00    7.0
    8   2 2019-01-01 12:45:00    7.0
    

    编辑:

    merge 的另一种解决方案并用左连接代替reindex

    from  itertools import product
    
    #round datetimes by 15 minutes
    data['Time'] = pd.to_datetime(data['Time'])
    minutes = pd.to_timedelta(15*(data['Time'].dt.minute // 15), unit='min')
    data['Time'] = data['Time'].dt.floor('H') + minutes
    
    #change date range for 4 values (to `12:45`)
    rng = pd.date_range('1/1/2019 12:00','1/1/2019 12:45',freq='15min')
    #create helper DataFrame and merge with left join
    df = pd.DataFrame(list(product(data['ID'].unique(), rng)), columns=['ID','Time'])
    print (df)
       ID                Time
    0   1 2019-01-01 12:00:00
    1   1 2019-01-01 12:15:00
    2   1 2019-01-01 12:30:00
    3   1 2019-01-01 12:45:00
    4   2 2019-01-01 12:00:00
    5   2 2019-01-01 12:15:00
    6   2 2019-01-01 12:30:00
    7   2 2019-01-01 12:45:00
    

    data = df.merge(data, how='left')
    ##interpolate per groups
    data['Value'] = (data.groupby('ID')['Value']
                         .apply(lambda x: x.interpolate(method='linear', limit_direction='both')))
    print (data)
       ID                Time  Value
    0   1 2019-01-01 12:00:00    3.0
    1   1 2019-01-01 12:15:00    3.0
    2   1 2019-01-01 12:30:00    2.0
    3   1 2019-01-01 12:45:00    2.0
    4   2 2019-01-01 12:00:00    5.0
    5   2 2019-01-01 12:15:00    7.0
    6   2 2019-01-01 12:30:00    7.0
    7   2 2019-01-01 12:45:00    7.0
    

    【讨论】:

    • 这非常适合这项任务,谢谢!在数据集的放大版本(约 6000 万行)上进行测试时,我确实遇到了一个问题。翻译被杀。假设这可能是内存问题?您对处理这种大小的数据集有什么建议吗?
    • @primo7 - 6000 万行是非常大的 DataFrame,因此需要增加 RAM。或使用类似的库,如dask
    • @primo7 - 添加了另一个解决方案,请检查。
    • 谢谢!合并和左连接方法适用于大型数据帧
    【解决方案2】:

    对于大型数据集,线性插值确实会变慢。在你的代码中有一个循环也是造成减速的主要原因。可以从循环中删除并预先计算的任何内容都将有助于提高效率。例如,如果您预先定义了用于初始化times 的数据框,则代码的效率将提高 14%:

    times_template = pd.DataFrame(pd.date_range('1/1/2019 12:00','1/1/2019 13:00',freq='15min'),columns=['Time'])
    for i in range(ids.shape[0]):
        times = times_template.copy()
    

    分析您的代码确认插值花费的时间最长 (22.7%),其次是连接 (13.1%)、追加 (7.71%) 和删除 (7.67%) 命令。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      • 2021-01-09
      • 1970-01-01
      • 1970-01-01
      • 2019-01-19
      • 2017-04-05
      相关资源
      最近更新 更多