Pandas时间序列重采样与插值的结合

7

我有时间戳传感器数据。由于技术细节,我以大约一分钟的间隔从传感器获取数据。数据可能如下所示:

   tstamp               val
0  2016-09-01 00:00:00  57
1  2016-09-01 00:01:00  57
2  2016-09-01 00:02:23  57
3  2016-09-01 00:03:04  57
4  2016-09-01 00:03:58  58
5  2016-09-01 00:05:00  60

现在,我希望能够在每分钟得到所有数据,但实际上并非如此。为了保持分布并在每分钟内获得数据,唯一的方法就是进行插值。例如,在行索引1和2之间有83秒钟的时间,获取准确分钟数的自然方法就是在两行数据之间进行插值(在这种情况下,结果是57,但不是所有情况都是如此)。
目前,我的做法是:
date = pd.to_datetime(df['measurement_tstamp'].iloc[0].date())
ts_d = df['measurement_tstamp'].dt.hour * 60 * 60 +\
       df['measurement_tstamp'].dt.minute * 60 +\
       df['measurement_tstamp'].dt.second
ts_r = np.arange(0, 24*60*60, 60)
data = scipy.interpolate.interp1d(x=ts_d, y=df['speed'].values)(ts_r)
req = pd.Series(data, index=pd.to_timedelta(ts_r, unit='s'))
req.index = date + req.index

但我觉得这个过程有些冗长。Pandas有很好的方法来进行重采样、舍入等操作。我已经一整天在研究它们,但是发现没有一种方法可以按照我想要的方式进行插值处理。resamplegroupby一样工作,对齐时间点并计算平均值。fillna进行插值处理,但不能用于resample已经通过计算平均值修改了数据的情况。

我有所遗漏吗?还是我的方法是最好的?


为了简单起见,假设我按天和传感器对数据进行分组,因此每次只对一个传感器的24小时进行插值处理。

2个回答

10
d = df.set_index('tstamp')
t = d.index
r = pd.date_range(t.min().date(), periods=24*60, freq='T')

d.reindex(t.union(r)).interpolate('index').ix[r]

请注意,periods=24*60 适用于每日数据,而不是问题中提供的样本。对于该样本,periods=6 可以使用。

进入图片介绍


但是resample在统计相同组中出现两个或更多观察值时会进行平均处理,不是吗? - Kartik
不错!但是,你能否将其与我在问题中的解决方案进行比较?我有一个数据集(仅针对9月份),大约有1.14亿条记录(2,655个传感器,每天报告近1440个数据点)。我认为在每秒进行插值会使它变慢。 - Kartik
是的,我一直在尝试修复那个问题...还在思考中。 - piRSquared
我想我必须对我的解决方案感到满意。它已经足够好了。在一个包含6百万行数据集上,仅花费了20.5秒(部分归功于groupby().apply释放了GIL)... - Kartik
@Kartik 加油! - piRSquared
显示剩余2条评论

4

五年后,pandas 已经有了一些变化(主要是 ix 函数已被弃用)。无论如何,我已经重新编写了 piRSquared 的答案,使其适用于当前的 pandas 版本,并且还改进了答案中存在的日期范围问题:

import pandas as pd
from datetime import datetime

df = pd.DataFrame({"tstamp": [
    datetime(2016, 9, 1, 0, 0, 0),
    datetime(2016, 9, 1, 0, 1, 0),
    datetime(2016, 9, 1, 0, 2, 23),
    datetime(2016, 9, 1, 0, 3, 4),
    datetime(2016, 9, 1, 0, 3, 58),
    datetime(2016, 9, 1, 0, 5, 0)], 
    "val": [57, 57, 57, 57, 58, 60]})


d = df.set_index('tstamp')
t = d.index

r = pd.date_range(t.min(), t.max(), freq='T')

d = d.reindex(t.union(r)).interpolate('index').loc[r]

d:

                           val
2016-09-01 00:00:00  57.000000
2016-09-01 00:01:00  57.000000
2016-09-01 00:02:00  57.000000
2016-09-01 00:03:00  57.000000
2016-09-01 00:04:00  58.064516
2016-09-01 00:05:00  60.000000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接