Python 滑动窗口上的滚动计算(间隔不均匀)

Python 滑动窗口上的滚动计算(间隔不均匀),python,pandas,Python,Pandas,假设您有一些不均匀的时间序列数据: import pandas as pd import random as randy ts = pd.Series(range(1000),index=randy.sample(pd.date_range('2013-02-01 09:00:00.000000',periods=1e6,freq='U'),1000)).sort_index() print ts.head() 2013-02-01 09:00:00.002895 995 2013-0

假设您有一些不均匀的时间序列数据:

import pandas as pd
import random as randy
ts = pd.Series(range(1000),index=randy.sample(pd.date_range('2013-02-01 09:00:00.000000',periods=1e6,freq='U'),1000)).sort_index()
print ts.head()


2013-02-01 09:00:00.002895    995
2013-02-01 09:00:00.003765    499
2013-02-01 09:00:00.003838    797
2013-02-01 09:00:00.004727    295
2013-02-01 09:00:00.006287    253
假设我想在1ms窗口上进行滚动求和,得到以下结果:

2013-02-01 09:00:00.002895    995
2013-02-01 09:00:00.003765    499 + 995
2013-02-01 09:00:00.003838    797 + 499 + 995
2013-02-01 09:00:00.004727    295 + 797 + 499
2013-02-01 09:00:00.006287    253
目前,我把所有的东西都放回longs,用cython做这件事,但这在纯熊猫身上可能吗?我知道你可以做一些类似.asfreq('U')的事情,然后填充和使用传统函数,但一旦你有了更多的行,这就无法扩展

作为参考,这里有一个不太快的黑客版Cython:

%%cython
import numpy as np
cimport cython
cimport numpy as np

ctypedef np.double_t DTYPE_t

def rolling_sum_cython(np.ndarray[long,ndim=1] times, np.ndarray[double,ndim=1] to_add, long window_size):
    cdef long t_len = times.shape[0], s_len = to_add.shape[0], i =0, win_size = window_size, t_diff, j, window_start
    cdef np.ndarray[DTYPE_t, ndim=1] res = np.zeros(t_len, dtype=np.double)
    assert(t_len==s_len)
    for i in range(0,t_len):
        window_start = times[i] - win_size
        j = i
        while times[j]>= window_start and j>=0:
            res[i] += to_add[j]
            j-=1
    return res   
在稍大一点的系列中演示此功能:

ts = pd.Series(range(100000),index=randy.sample(pd.date_range('2013-02-01 09:00:00.000000',periods=1e8,freq='U'),100000)).sort_index()

%%timeit
res2 = rolling_sum_cython(ts.index.astype(int64),ts.values.astype(double),long(1e6))
1000 loops, best of 3: 1.56 ms per loop

也许使用以下选项更有意义:


像这样的怎么样:

创建1毫秒的偏移量:

In [1]: ms = tseries.offsets.Milli()
创建一系列与时间序列长度相同的索引位置:

In [2]: s = Series(range(len(ts)))
应用lambda函数,对ts系列中的当前时间进行索引。函数返回
x-ms和x
之间的所有ts条目的总和

In [3]: s.apply(lambda x: ts.between_time(start_time=ts.index[x]-ms, end_time=ts.index[x]).sum())

In [4]: ts.head()
Out[4]:
2013-02-01 09:00:00.000558    348
2013-02-01 09:00:00.000647    361
2013-02-01 09:00:00.000726    312
2013-02-01 09:00:00.001012    550
2013-02-01 09:00:00.002208    758
上述功能的结果:

0     348
1     709
2    1021
3    1571
4     758

您可以使用总和和二进制搜索来解决大多数此类问题

from datetime import timedelta

def msum(s, lag_in_ms):
    lag = s.index - timedelta(milliseconds=lag_in_ms)
    inds = np.searchsorted(s.index.astype(np.int64), lag.astype(np.int64))
    cs = s.cumsum()
    return pd.Series(cs.values - cs[inds].values + s[inds].values, index=s.index)

res = msum(ts, 100)
print pd.DataFrame({'a': ts, 'a_msum_100': res})


                            a  a_msum_100
2013-02-01 09:00:00.073479  5           5
2013-02-01 09:00:00.083717  8          13
2013-02-01 09:00:00.162707  1          14
2013-02-01 09:00:00.171809  6          20
2013-02-01 09:00:00.240111  7          14
2013-02-01 09:00:00.258455  0          14
2013-02-01 09:00:00.336564  2           9
2013-02-01 09:00:00.536416  3           3
2013-02-01 09:00:00.632439  4           7
2013-02-01 09:00:00.789746  9           9

[10 rows x 2 columns]
您需要一种处理NAN的方法,并且根据您的应用程序,您可能需要滞后时间的主要值(即使用kdb+bin与使用np.searchsorted之间的差异)


希望这能有所帮助。

这是一个老问题,但对于那些从谷歌偶然发现这一点的人来说:在熊猫0.19中,这是内置的函数

因此,要获得1ms的窗口,通过执行

dft.rolling('1ms')
总数是

dft.rolling('1ms').sum()

这实际上不起作用——它以1ms的频率对数据进行采样,然后对桶进行求和,结果是:2013-02-01 09:00.002000995 2013-02-01 09:00:00.003000648 2013-02-01 09:00.004000295 2013-02-01 09:00:00.005000 NaN 2013-02-01 09:00:00.006000617这很光滑,但不幸的是无法缩放的。如果你放大到更大一点的值,比如periods=1e8,并抽取10000个样本,在我的笔记本电脑上大约需要5秒。添加时间四舍五入到毫秒的列,并按它分组,在每个组中应用cumsum
ts_df['millis_rounded']=ts.index.astype(int64)/1000000*1000000
res3=ts_df.groupby(“millis_rounded”)[0].agg(np.sum)
这就是你说的吗?这只是对特定毫秒桶中的条目求和,而不是滑动窗口。它也不是很快。(我想大概是上述赛昂的10倍)啊,第二个asof(s.asof(lag))是错误的。您真正需要的是来自第一个asof的索引。我将发布一个更正版本。这是一个伟大的解决方案;到目前为止,这是我测试过的最快的。这似乎比我发布的天真的cython版本提高了10倍。我猜这是因为在cython内部循环中,通过内存“向后走”是多么昂贵。是的,cython内部循环可能很慢。通过对其进行Cythonization并直接在numpy阵列上进行操作,您可能可以使其更快。看起来您正在分析滴答声数据。我对此进行了大量练习:)
dft.rolling('1ms').sum()