来自“索引数组”下的numpy 文档:
NumPy 数组可以用其他数组(或任何其他序列-
像可以转换为数组的对象,例如列表,使用
元组除外;为什么会这样,请参阅本文档的末尾)。
索引数组的使用范围从简单、直接的案例到
复杂的,难以理解的案例。 对于索引数组的所有情况,什么
返回的是原始数据的副本,而不是获得的视图
切片。
换句话说,您认为您的行B[:,:] = A[idx,:](在更正@MSeifert 指出的行之后)仅导致将元素从A 复制到B 的假设是不正确的。相反,numpy 首先从索引的A 创建一个新数组,然后将其元素复制到B。
为什么内存使用变化如此之大,我无法理解。但是,查看您的原始数组形状s=(300000,3000),如果我没有计算错的话,对于 64 位数字,这将达到大约 6.7 GB。因此创建那个额外的数组,额外的内存使用实际上似乎是合理的。
编辑:
针对 OP 的 cmets,我针对将 A 的随机行分配给 B 的不同方法的性能进行了一些测试。首先,这是一个小测试,B=A[idx,:] 确实创建了一个新的ndarray,而不仅仅是A 的视图:
>>> import numpy as np
>>> a = np.arange(9).reshape(3,3)
>>> a
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> b = a[[2,0,1],:]
>>> b
array([[6, 7, 8],
[0, 1, 2],
[3, 4, 5]])
>>> b[0]=-5
>>> b
array([[-5, -5, -5],
[ 0, 1, 2],
[ 3, 4, 5]])
>>> a
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
确实,为b 分配新值会使a 保持不变。然后我做了一些时间测试,以最快的方式打乱A的行并将它们放入B:
import numpy as np
import timeit
import numba as nb
s=(300000, 3000)
A = np.arange(s[0]*s[1]).reshape(s)
idx = np.arange(s[0])
#directly keep the indexed array
def test1(x,idx):
return x[idx,:]
#the method of the OP
def test2(x, y, idx):
y[:,:]=x[idx,:]
return y
#using a simple for loop, e.g. if only part of the rows should be assigned
def test3(x,y,idx):
for i in range(len(idx)):
y[i,:] = x[idx[i],:]
return y
#like test3, but numba-compiled
@nb.jit(nopython=True)
def test4(x,y,idx):
for i in range(len(idx)):
y[i,:] = x[idx[i],:]
return y
B = np.zeros(s)
res = timeit.Timer(
'test1(A,idx)',
setup = 'from __main__ import test1, A, idx'
).repeat(7,1)
print('test 1:', np.min(res), np.max(res), np.mean(res))
B = np.zeros(s)
res = timeit.Timer(
'test2(A,B,idx)',
setup = 'from __main__ import test2, A, B, idx'
).repeat(7,1)
print('test 2:', np.min(res), np.max(res), np.mean(res))
B = np.zeros(s)
res = timeit.Timer(
'test3(A,B,idx)',
setup = 'from __main__ import test3, A, B, idx'
).repeat(7,1)
print('test 3:', np.min(res), np.max(res), np.mean(res))
B = np.zeros(s)
res = timeit.Timer(
'test4(A,B,idx)',
setup = 'from __main__ import test4, A, B, idx'
).repeat(7,1)
print('test 4:', np.min(res), np.max(res), np.mean(res))
7 次运行的结果(最小值、最大值、平均值)为:
test 1: 19.880664938 21.354912988 20.2604536371
test 2: 73.419507756 139.534279557 122.949712777
test 3: 40.030043285 78.001182537 64.7852914216
test 4: 40.001512514 73.397133578 62.0058947516
最后,一个简单的for-loop 的性能不会太差,特别是如果您只想分配部分行,而不是整个数组。令人惊讶的是numba 似乎并没有提高性能。