向量化前瞻性函数 pandas 数据框

Posted

技术标签:

【中文标题】向量化前瞻性函数 pandas 数据框【英文标题】:vectorizing forward-looking function pandas dataframe 【发布时间】:2014-06-11 18:46:25 【问题描述】:

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

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

向前查看元素 [i:] 并为 value[i] 分配一个“类”:

+1 如果随后的值达到 value[i] + step * rr before达到 value[i] - step -1 如果随后的值达到 value[i] 的水平 - step * rr before达到 value[i] + step 0 在其他情况下(即当随后的值触摸 value[i] - step 然后 value[i] + step 或反之亦然。

我知道这听起来很疯狂。想象一下 +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 or 0, -1, 0, 0, -1, 0, 1 等将是 0 类。

我通过定义一个函数以“经典”(也许不是 Python 式)的方式解决了它:

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 中可以用更少的努力来实现,那也很好!

提前非常感谢!

【问题讨论】:

不是矢量化的,但也许你可以在cython中编写函数findClass 是的,当然这是可能的。这里的问题主要是因为这是一个逐行重复的任务,人们通常说对于 pandas 和类似的你必须“思考矢量”,避免循环......我试过了,但没有成功! 除了我在回答中的想法之外,我认为您可以通过重写函数以利用条件句获得显着的速度。您在 while 循环中放置了长条件,但您的逻辑允许您在大部分时间排除许多选项。这将导致执行的代码少得多,并且可能使您的执行时间缩短 2-4 倍。 也许shift函数对你有用,看看:pandas.pydata.org/pandas-docs/dev/generated/… 【参考方案1】:

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

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

从四处寻找,np.where 等价的“查找第一个索引”仍然是一个请求的功能。

【讨论】:

以上是关于向量化前瞻性函数 pandas 数据框的主要内容,如果未能解决你的问题,请参考以下文章

为 Scikit-Learn 向量化 Pandas 数据框

R语言金融市场量化交易:布林带价差策略RSI交易策略,回测COMP 226|附代码数据

主流Java语言数据库连接池比较及前瞻

向复杂的正则表达式添加例外(使用前瞻和后瞻)

[正则]前瞻

Pandas:向量化局部范围操作([i:i+2] 行的最大值和总和)