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

时间:2014-04-25 21:40:02

标签: python r numpy pandas vectorization

我想在pandas中对DataFrame(可以被认为是一个系列)进行“奇怪”的计算。 DataFrame必须被视为时间序列或类似的(元素的顺序很重要)。

  • 给出index [i](value [i])
  • 的值
  • 给出步骤(例如1)[整数或实数]
  • 给定乘数rr(例如2)[整数或实数]

在元素[i:]上查找前进,并将值[i]分配给“类”:

  • +1,如果随后的值在达到值[i]之前达到值[i] + step * rr - 步骤
  • -1如果随后的值达到值[i] - 步骤* rr 达到值[i] +步骤
  • 之前
  • 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

然后迭代它:

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

在我的笔记本电脑上(并运行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__)

问题是:

是否有人认为有可能以提高性能的方式在pandas / numpy中对此功能进行矢量化?

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

提前多多感谢!

1 个答案:

答案 0 :(得分:0)

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

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

从四处寻找,相当于np.where的“找到第一个索引”仍然是一个请求的功能。