矢量化前瞻性功能熊猫数据帧

use*_*348 5 python numpy r vectorization pandas

我想在大熊猫的DataFrame(可以被认为是一个系列)上做一个"奇怪的"计算.DataFrame必须被视为时间序列或类似的(元素的顺序很重要).

  • 给定索引[i]的值(值[i])
  • 给出一个步骤(例如1)[整数或实数]
  • 给定乘数rr(例如2)[整数或实数]

向前在元素[I:],并指定为值[I]的一个"类":

  • 如果随后的值达到值[i] - 步骤之前达到值[i] + step*rr的水平,则+1
  • -1如果随后的值达到值[i] +步骤之前达到值[i] - 步骤*rr的水平
  • 在每种其他情况下为0(即,当随后的值触摸值[i] - 步骤然后值[i] +步骤或反之亦然.

我知道这听起来很疯狂.想象一下随机步行+ 1/-1步.像这样的序列:

  • 0,1,2将分配给+1级(它也可以是0,1,0,0,1,1,0,1,1,2)
  • 0,-1,-2将分配给-1类(它也可以是0,-1,0,0,0,-1,-1,-1,-2)
  • 0,+ 1,0,-1或0,-1,0,0,-1,0,1等等将为0级.

我通过定义一个函数解决了它的"经典"(也许不是那么pythonic)方式:

import numpy as np
import pandas as pd

def FindClass(inarr, i=0, step=0.001, rr=2):
    j = 0
    foundClass = None
    while i+j < len(inarr) - 1:
        j += 1
        if inarr[i+j] >= inarr[i] + step:
            direction = 1
            break
        if inarr[i+j] <= inarr[i] - step:
            direction = -1
            break

    while i+j < len(inarr)-1:
        j += 1
        if direction == 1 and inarr[i+j] >= inarr[i] + (step * rr):
            foundClass = 1
            break
        elif direction == 1 and inarr[i+j] <= inarr[i] - step:
            foundClass = 0
            break
        elif direction == -1 and inarr[i+j] <= inarr[i] - (step * rr):
            foundClass = -1
            break
        elif direction == -1 and inarr[i+j] >= inarr[i] + step:
            foundClass = 0
            break
    if foundClass is None:
        foundClass = np.nan

    return foundClass
Run Code Online (Sandbox Code Playgroud)

然后迭代它:

if __name__ == "__main__":
    steps = np.random.randint(-1, 2, size= 10000)

    randomwalk = steps.cumsum(0)
    rc = pd.DataFrame({'rw':randomwalk, 'result': np.nan})

    for c in range(0, len(rc)-1):
        rc.result[c] = FindClass(rc.rw, i=c, step=1)

    print rc
Run Code Online (Sandbox Code Playgroud)

在我的笔记本电脑上(以及运行python 2.7),我得到的分析对于10000个元素的系列来说并不"太糟糕":

python -m cProfile -s cumulative fbmk.py
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10000 entries, 0 to 9999
Data columns (total 2 columns):
result    9996  non-null values
rw        10000  non-null values
dtypes: float64(1), int32(1)
         932265 function calls (929764 primitive calls) in 2.643 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.106    0.106    2.646    2.646 fbmk.py:1(<module>)
     9999    0.549    0.000    1.226    0.000 fbmk.py:4(FindClass)
   158062    0.222    0.000    0.665    0.000 series.py:616(__getitem__)
        2    0.029    0.014    0.561    0.281 __init__.py:3(<module>)
   158062    0.226    0.000    0.443    0.000 index.py:718(get_value)
    19998    0.070    0.000    0.442    0.000 frame.py:2082(__getattr__)
    19998    0.111    0.000    0.331    0.000 frame.py:1986(__getitem__)
Run Code Online (Sandbox Code Playgroud)

问题是:

有没有人看到有可能以提高性能的方式在pandas/numpy中矢量化这个函数?

如果在R中用较少的努力可以做到这一点,那也会很好!

非常感谢提前!

Ram*_*hah 0

根据问题的属性,您可以用来np.where查找级别交叉的位置并对时间序列进行分类。

这里最大的缺点是np.where将为您提供时间序列高于value[i] + step等的每个索引,这可能会将线性时间算法变成二次时间算法。根据您要处理的问题的大小,我预计您会在前置因素中获益匪浅;你甚至可能最终得到二次时间 numpy 解决方案。

经过摸索,“查找第一个索引”等效项np.where仍然是一个请求的功能。