如何加快元素列表的分配?对于标量数据,答案是预先分配一个数组:
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
。
答案 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