Python 如何加速嵌套循环?

Python 如何加速嵌套循环?,python,numpy,scipy,finance,Python,Numpy,Scipy,Finance,我正在用python执行下面包含的嵌套循环。这是搜索现有金融时间序列并在时间序列中查找符合某些特征的时段的基本方法 在这种情况下,有两个独立的、大小相同的数组,表示“成交”(即资产价格)和“交易量”(即在该期间交换的资产金额)。对于每个时间段,我希望展望长度在1到INTERVAL_LENGTH之间的所有未来时间段,并查看这些时间段中是否有与我的搜索匹配的特征(在这种情况下,收盘值的比率大于1.0001,小于1.5,总体积大于100) 我的理解是,在使用NumPy时,加速的一个主要原因是解释器不需

我正在用python执行下面包含的嵌套循环。这是搜索现有金融时间序列并在时间序列中查找符合某些特征的时段的基本方法

在这种情况下,有两个独立的、大小相同的数组,表示“成交”(即资产价格)和“交易量”(即在该期间交换的资产金额)。对于每个时间段,我希望展望长度在1到
INTERVAL_LENGTH
之间的所有未来时间段,并查看这些时间段中是否有与我的搜索匹配的特征(在这种情况下,收盘值的比率大于1.0001,小于1.5,总体积大于100)

我的理解是,在使用NumPy时,加速的一个主要原因是解释器不需要在每次计算某个值时都对操作数进行类型检查,只要您对整个数组进行操作(例如,
NumPy\u array*2
),但下面的代码显然没有利用这一点

有没有一种方法可以用某种可能导致加速的窗口函数替换内部循环,或者使用
numpy
/
scipy
在本机python中大大加速内部循环

P>可选的,是否有更好的方法来完成这一点(例如,在C++中使用这个循环,使用编织)要快得多吗?< /P>
数组长度=500000
间隔长度=15
闭合=np.数组(xrange(数组长度))
体积=np.数组(xrange(数组长度))
close,volume=close.astype('float64'),volume.astype('float64'))
结果=[]
对于X范围内的i(长度(闭合)-间隔长度):
对于X范围内的j(i+1,i+间隔长度):
ret=关闭[j]/关闭[i]
体积=总和(体积[i+1:j+1])
如果ret>1.0001且ret<1.5且vol>100:
结果。追加([i,j,ret,vol])
打印结果

一种加速方法是删除
总和
部分,因为在这个实现中,它将长度2的列表总和到
间隔长度
。相反,只需将
volume[j+1]
添加到循环最后一次迭代的vol的上一个结果中。因此,每次只需添加两个整数,而不是每次对整个列表求和并对其进行切片。另外,不要从求和开始(卷[i+1:j+1]),只要做
vol=volume[i+1]+volume[j+1]
,正如您所知,这里的初始情况总是两个索引

另一个加速是使用
.extend
而不是
.append
,因为python实现的
extend
运行速度要快得多

您还可以分解最后的
if
语句,以便仅在需要时进行某些计算。例如,您知道在下面的“new_function2”中,如果vol更新:(几乎)完全矢量化的版本,

我将添加一些注释来解释一些事情

它提供了约50倍的加速比,如果您同意输出为numpy数组而不是列表,则可以获得更大的加速比。原样:

In [86]: %timeit new_function2(close, volume, INTERVAL_LENGTH)
1 loops, best of 3: 1.15 s per loop
您可以使用对np.cumsum()的调用替换内部循环。。。请参阅下面我的“新函数”函数。这给了相当大的加速

In [61]: %timeit new_function(close, volume, INTERVAL_LENGTH)
1 loops, best of 3: 15.7 s per loop
vs

它应该可以矢量化整个东西,并完全避免for循环,不过。。。给我一分钟,我看看我能做什么

import numpy as np

ARRAY_LENGTH = 500000
INTERVAL_LENGTH = 15
close = np.arange(ARRAY_LENGTH, dtype=np.float)
volume = np.arange(ARRAY_LENGTH, dtype=np.float)

def old_function(close, volume, INTERVAL_LENGTH):
    results = []
    for i in xrange(len(close) - INTERVAL_LENGTH):
        for j in xrange(i+1, i+INTERVAL_LENGTH):
            ret = close[j] / close[i]
            vol = sum( volume[i+1:j+1] )
            if (ret > 1.0001) and (ret < 1.5) and (vol > 100):
                results.append( (i, j, ret, vol) )
    return results


def new_function(close, volume, INTERVAL_LENGTH):
    results = []
    for i in xrange(close.size - INTERVAL_LENGTH):
        vol = volume[i+1:i+INTERVAL_LENGTH].cumsum()
        ret = close[i+1:i+INTERVAL_LENGTH] / close[i]

        filter = (ret > 1.0001) & (ret < 1.5) & (vol > 100)
        j = np.arange(i+1, i+INTERVAL_LENGTH)[filter]

        tmp_results = zip(j.size * [i], j, ret[filter], vol[filter])
        results.extend(tmp_results)
    return results

def new_function2(close, volume, INTERVAL_LENGTH):
    vol, ret = [], []
    I, J = [], []
    for k in xrange(1, INTERVAL_LENGTH):
        start = k
        end = volume.size - INTERVAL_LENGTH + k
        vol.append(volume[start:end])
        ret.append(close[start:end])
        J.append(np.arange(start, end))
        I.append(np.arange(volume.size - INTERVAL_LENGTH))

    vol = np.vstack(vol)
    ret = np.vstack(ret)
    J = np.vstack(J)
    I = np.vstack(I)

    vol = vol.cumsum(axis=0)
    ret = ret / close[:-INTERVAL_LENGTH]

    filter = (ret > 1.0001) & (ret < 1.5) & (vol > 100)

    vol = vol[filter]
    ret = ret[filter]
    I = I[filter]
    J = J[filter]

    output = zip(I.flat,J.flat,ret.flat,vol.flat)
    return output

results = old_function(close, volume, INTERVAL_LENGTH)
results2 = new_function(close, volume, INTERVAL_LENGTH)
results3 = new_function(close, volume, INTERVAL_LENGTH)

# Using sets to compare, as the output 
# is in a different order than the original function
print set(results) == set(results2)
print set(results) == set(results3)
将numpy导入为np
数组长度=500000
间隔长度=15
close=np.arange(数组长度,dtype=np.float)
volume=np.arange(数组长度,dtype=np.float)
def old_功能(关闭、音量、间隔长度):
结果=[]
对于X范围内的i(长度(闭合)-间隔长度):
对于X范围内的j(i+1,i+间隔长度):
ret=关闭[j]/关闭[i]
体积=总和(体积[i+1:j+1])
如果(ret>1.0001)和(ret<1.5)以及(vol>100):
结果。追加((i,j,ret,vol))
返回结果
def新功能(关闭、音量、间隔长度):
结果=[]
对于X范围内的i(close.size-间隔长度):
vol=体积[i+1:i+间隔长度].cumsum()
ret=关闭[i+1:i+间隔长度]/close[i]
过滤器=(ret>1.0001)&(ret<1.5)&(vol>100)
j=np.arange(i+1,i+区间长度)[过滤器]
tmp_results=zip(j.size*[i],j,ret[filter],vol[filter])
结果。扩展(tmp_结果)
返回结果
def新功能2(关闭、音量、间隔长度):
音量,音量=[],[]
一、 J=[],[]
对于X范围内的k(1,间隔长度):
开始=k
end=volume.size-间隔长度+k
卷附加(卷[开始:结束])
ret.append(关闭[开始:结束])
附加(名词短语arange(开始,结束))
I.append(np.arange(volume.size-INTERVAL\u LENGTH))
体积=np.vstack(体积)
ret=np.vstack(ret)
J=np.vstack(J)
I=np.vstack(I)
体积=体积总和(轴=0)
ret=ret/close[:-间隔长度]
过滤器=(ret>1.0001)&(ret<1.5)&(vol>100)
vol=vol[过滤器]
ret=ret[过滤器]
I=I[过滤器]
J=J[过滤器]
输出=压缩(I.flat、J.flat、ret.flat、vol.flat)
返回输出
结果=旧函数(关闭、音量、间隔长度)
结果2=新函数(关闭、音量、间隔长度)
结果3=新的\u函数(关闭、音量、间隔\u长度)
#使用集合进行比较,作为输出
#与原始函数的顺序不同
打印集(结果)=集(结果2)
打印集(结果)=集(结果3)

为什么不尝试将结果生成为单个列表(比追加或扩展要快得多),例如:

results = [ t for t in ( (i, j, close[j]/close[i], sum(volume[i+1:j+1]))
                         for i in xrange(len(close)-INT_LEN)
                             for j in xrange(i+1, i+INT_LEN)
                       )
            if t[3] > 100 and 1.0001 < t[2] < 1.5
          ]
results=[t代表t in((i,j,close[j]/close[i],sum(卷[i+1:j+1]))
对于X范围内的i(透镜(闭合)-内透镜)
对于X范围内的j(i+1,i+INT_LEN)
)
如果t[3]>100且1.0001
您的计算厕所
In [62]: %timeit old_function(close, volume, INTERVAL_LENGTH)
1 loops, best of 3: 53.1 s per loop
import numpy as np

ARRAY_LENGTH = 500000
INTERVAL_LENGTH = 15
close = np.arange(ARRAY_LENGTH, dtype=np.float)
volume = np.arange(ARRAY_LENGTH, dtype=np.float)

def old_function(close, volume, INTERVAL_LENGTH):
    results = []
    for i in xrange(len(close) - INTERVAL_LENGTH):
        for j in xrange(i+1, i+INTERVAL_LENGTH):
            ret = close[j] / close[i]
            vol = sum( volume[i+1:j+1] )
            if (ret > 1.0001) and (ret < 1.5) and (vol > 100):
                results.append( (i, j, ret, vol) )
    return results


def new_function(close, volume, INTERVAL_LENGTH):
    results = []
    for i in xrange(close.size - INTERVAL_LENGTH):
        vol = volume[i+1:i+INTERVAL_LENGTH].cumsum()
        ret = close[i+1:i+INTERVAL_LENGTH] / close[i]

        filter = (ret > 1.0001) & (ret < 1.5) & (vol > 100)
        j = np.arange(i+1, i+INTERVAL_LENGTH)[filter]

        tmp_results = zip(j.size * [i], j, ret[filter], vol[filter])
        results.extend(tmp_results)
    return results

def new_function2(close, volume, INTERVAL_LENGTH):
    vol, ret = [], []
    I, J = [], []
    for k in xrange(1, INTERVAL_LENGTH):
        start = k
        end = volume.size - INTERVAL_LENGTH + k
        vol.append(volume[start:end])
        ret.append(close[start:end])
        J.append(np.arange(start, end))
        I.append(np.arange(volume.size - INTERVAL_LENGTH))

    vol = np.vstack(vol)
    ret = np.vstack(ret)
    J = np.vstack(J)
    I = np.vstack(I)

    vol = vol.cumsum(axis=0)
    ret = ret / close[:-INTERVAL_LENGTH]

    filter = (ret > 1.0001) & (ret < 1.5) & (vol > 100)

    vol = vol[filter]
    ret = ret[filter]
    I = I[filter]
    J = J[filter]

    output = zip(I.flat,J.flat,ret.flat,vol.flat)
    return output

results = old_function(close, volume, INTERVAL_LENGTH)
results2 = new_function(close, volume, INTERVAL_LENGTH)
results3 = new_function(close, volume, INTERVAL_LENGTH)

# Using sets to compare, as the output 
# is in a different order than the original function
print set(results) == set(results2)
print set(results) == set(results3)
results = [ t for t in ( (i, j, close[j]/close[i], sum(volume[i+1:j+1]))
                         for i in xrange(len(close)-INT_LEN)
                             for j in xrange(i+1, i+INT_LEN)
                       )
            if t[3] > 100 and 1.0001 < t[2] < 1.5
          ]