pythonnumpy:为什么nparray[i][j]比nparray[i,j]慢?
我正在初始化一个numpy数组,然后在任意位置插入循环值;出于某种原因,使用双括号编制索引的速度似乎是使用逗号表示法编制索引的速度的两倍pythonnumpy:为什么nparray[i][j]比nparray[i,j]慢?,python,arrays,numpy,Python,Arrays,Numpy,我正在初始化一个numpy数组,然后在任意位置插入循环值;出于某种原因,使用双括号编制索引的速度似乎是使用逗号表示法编制索引的速度的两倍 import numpy as np x = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) def access(arr): for i in range(1000): arr[1][2] = i def access2(arr): for i in range(1000):
import numpy as np
x = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
def access(arr):
for i in range(1000):
arr[1][2] = i
def access2(arr):
for i in range(1000):
arr[1,2] = i
t1 = Timer('access(x)', 'from __main__ import access, x')
print(t1.timeit(number = 1000))
0.425940990448
t2 = Timer('access2(x)', 'from __main__ import access2, x')
print(t2.timeit(number = 1000))
0.217132806778
编辑:根据Mike的回答,我想了解如何将多维索引作为单个操作来实现,以及使用第一个符号是否有意义
import numpy as np
x = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
def access(arr):
for i in range(1000):
arr[1][2] = i
def access2(arr):
for i in range(1000):
arr[1,2] = i
t1 = Timer('access(x)', 'from __main__ import access, x')
print(t1.timeit(number = 1000))
0.425940990448
t2 = Timer('access2(x)', 'from __main__ import access2, x')
print(t2.timeit(number = 1000))
0.217132806778
答复
这:
表示两个索引操作
基本上就是这样:
tmp = nparray[i]
tmp[j]
因此,您可以创建一个中间数组tmp
,以后不再需要它了。
这是额外的工作
而这:
nparray[i, j]
只有一个索引操作。NumPy使用此方法效率更高,因为它不需要在此处创建任何中间数组
工作原理
这就是执行nparray[i,j]
时发生的情况。
类ndarray
重写特殊方法\uuuu getitem\uuuuu
和\uuuu setitem\uuuuu
。
例如:
>>> class A:
... def __getitem__(self, indices):
... print(indices)
... def __setitem__(self, indices, value):
... print(indices, value)
...
>>> a = A()
>>> a[1, 2]
(1, 2)
>>> a[1, 2] = 23
(1, 2) 23
您可以看到[1,2]
中的1,2
作为元组到达那里。
现在,NumPy可以利用这些信息做任何适当的事情。
在我们的例子中,执行一些2D索引
较慢方法的可能用例
使用此方法的场合不多:
nparray[i][j]
其中之一可能是当您将列表列表用于二维结构,并且(出于某种原因)希望使用NumPy数组作为替换项时。虽然速度较慢,但代码可以处理列表列表以及2D NumPy数组。让我们比较一下反汇编:
>>> dis.dis(access)
2 0 SETUP_LOOP 34 (to 37)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 20 (to 36)
16 STORE_FAST 1 (i)
#------------- differences start here -------------#
3 19 LOAD_FAST 1 (i)
22 LOAD_FAST 0 (arr)
25 LOAD_CONST 2 (1)
28 BINARY_SUBSCR
29 LOAD_CONST 3 (2)
32 STORE_SUBSCR
#-------------- differences end here --------------#
33 JUMP_ABSOLUTE 13
>> 36 POP_BLOCK
>> 37 LOAD_CONST 0 (None)
40 RETURN_VALUE
>>> dis.dis(access2)
2 0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (1000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 1 (i)
#------------- differences start here -------------#
3 19 LOAD_FAST 1 (i)
22 LOAD_FAST 0 (arr)
25 LOAD_CONST 4 ((1, 2))
28 STORE_SUBSCR
#-------------- differences end here --------------#
29 JUMP_ABSOLUTE 13
>> 32 POP_BLOCK
>> 33 LOAD_CONST 0 (None)
36 RETURN_VALUE
由此我们可以看出,第一个版本涉及两个额外的操作(
LOAD\u CONST
,然后是BINARY\u SUBSCR
)。您的实验表明,与第二个函数一样,通过元组进行单个下标查找的成本更高。但是,使用第一个符号有什么意义呢?有过吗?多维索引是如何作为一个操作来实现的?@jeremyradcliff:nparray.\uuu getitem\uuuuu((i,j))
vsnparray.\uu getitem\uuuuu(i)。\uuu getitem\uuuuuj
nparray.\uu setitem\uuuuuuuuu((i,j),val)vsnparray.\uu getitem\uuj(i)。\uuuu item\uj
要了解numpy
如何处理多维索引,您需要了解数组的存储方式。您需要阅读有关数据缓冲区
、形状
和步幅
的文档。这超出了你现在的问题。谢谢你,看到拆卸非常有说服力。使用双索引比使用更快的单索引更有意义吗?如果没有,你知道为什么他们(numpy)把它放在那里吗?你可能想看看2D数组的一维切片。[i]
索引正好为您提供了这一点。由于OP似乎在使用Python 3,您可以利用dis
在py3中接受字符串作为输入,并执行dis.dis('arr[1,2]'))
如果您愿意:)这将删除函数调用不必要的操作码并返回。@JimFasarakis Hilliard:这很有用,谢谢!