高效地将列表分配给变量或变量片

时间:2017-06-01 17:05:11

标签: python arrays numpy slice

如何加快元素列表的分配?对于标量数据,答案是预先分配一个数组:

import numpy as np
maxTime=1000; A=np.zeros(maxTime)
for t in range(maxTime): 
 data=get_fancy_data() 
 A[t]=data

但是,如果您的fancy_data是每个时间步长不同大小的列表,那么如何才能有效地完成此任务?

python -m timeit -s "import numpy as np; N=10**4; r=np.random.random(N); A=np.zeros(N);" \
"for i in range(N): A[:i+1] = r[:i+1]"
# 100 loops, best of 3: 14.9 msec per loop

python -m timeit -s "import numpy as np; N=10**4; r=np.random.random(N); A=np.zeros(N);" \
"for i in range(N): A = r[:i+1]"
# 1000 loops, best of 3: 1.68 msec per loop

在第二个示例中不预先分配A=np.zeros(N)不会显着改变所花费的时间。

我不确定为什么第二个例子更快。我怀疑A[:i+1]在分配给la Python list slicing efficiency之前创建了A部分的副本。

我有一些代码在这样的操作中存在瓶颈,但找不到更快的方法。

*

我注意到这与another question有关,因为它涉及A[0:2]的含义 - 这是修改原始数组A,而不是创建新数组并丢弃旧数组。但是,此问题涉及一种以更快的方式修改数组A的方法,而不是重复创建新数组A

1 个答案:

答案 0 :(得分:0)

我无法解开为什么我试图预分配的纯pythonic方法不快的谜团,所以我决定进入低级别。我决定使用Cython,所以我没有必要弄清楚如何将numpy数组传递给纯C代码而不复制它们。

我的解决方案是这样的:

<强> modify_array.pyx

import numpy as np
cimport numpy as np
cimport cython
ctypedef np.float64_t DTYPE_t
@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False)  # turn off negative index wrapping for entire function

cpdef foo(unsigned int T, unsigned int S, np.ndarray[DTYPE_t,ndim=2] A):
 cdef size_t t,s;
 for t in range(T): 
  for s in range(S): A[t,s]=data[t,S-s];
 return 0;

数组似乎是通过引用传递的,除非我弄错了(因为纯概念中不存在这个概念)。在任何情况下,对foo所做的任何更改都会反映在调用Python代码中。也可以使用numpy数组执行此操作:

<强> modify_array_np.pyx

def baz(unsigned int T, unsigned int S, np.ndarray[DTYPE_t,ndim=2] A):
 cdef size_t t,s;
 for t in range(T): 
  for s in range(S): A[t,s]=data[t,S-s]; 
   #n.b. Do not be tempted to try a more pythonic approach like A=data[:,::-1]:
   #it will require calls to the python interpreter and gut your speed boost.
return 0;

你应该避免使用任何numpy或python函数 - 也就是说,你应该显式循环索引而不是使用np.sum,以确保算法可以正确转换为C代码。 modify_array_np.pyx比我在问题中提出的等效解决方案快两倍。 modify_array.pyx再次快几十个百分点。它没有我希望的那么多,但它更好。如果有人有任何更快的建议,我很高兴听到他们。

使用Python中的函数

作为参考,使用

从命令行编译上面的Cython代码

cython modify_array.pyx

生成一个C文件,然后使用C编译器进行编译(请参阅here了解所需的包含/标志)。然后,在python文件中:

import modify_array
import numpy as np
T=100; A=np.zeros((T,T))

for t in range(T):
 data=get_fancy_data()
 returnValue=modify_array.foo(t,data,A) #The function modifies A directly
 #Do more stuff