我试图用numpy改组多维数组时遇到问题。 可以使用以下代码重现该问题:
import numpy as np
s=(300000, 3000)
n=s[0]
print ("Allocate")
A=np.zeros(s)
B=np.zeros(s)
print ("Index")
idx = np.arange(n)
print ("Shuffle")
idx = np.random.shuffle(idx)
print ("Arrange")
B[:,:] = A[idx,:] # THIS REQUIRES A LARGE AMOUNT OF MEMORY
当运行此代码(python 2.7以及win7 64bit上的numpy 1.13.1的python 3.6)时,最后一行代码的执行需要大量内存(~10 Gb),这听起来很奇怪我
实际上,我希望将数据从一个数组复制到另一个数组,这两个数据都是预先分配的,所以我可以理解副本会消耗时间,但不明白它为什么需要内存。
我想我做错了但却找不到......也许有人可以帮助我?
答案 0 :(得分:3)
问题不在于复制问题是你的阵列是巨大的:
>>> 300000 * 3000 * 8 / 1024 / 1024 / 1024 # 8 byte floats, 300000 * 3000 elements converted to GB
6.705522537231445
因此这些阵列几乎是7GB。那么为什么它只在分配线B[:,:] = A[idx,:]
触发?
那是因为zeros
实际上没有分配数组,直到您想要使用它。并且在您将其编入索引之前不会使用它(如果是A
:A[idx, :]
)或分配给它(如果是B
:B[:,:] =
)。
所以没有什么奇怪的事情发生,它只是A
和B
实际需要的内存量。
答案 1 :(得分:2)
来自'索引数组':
下的numpy
文档
NumPy数组可以用其他数组(或任何其他序列)索引 - 喜欢可以转换为数组的对象,例如列表 元组除外;请参阅本文档的最后部分,了解其原因。 索引数组的使用范围从简单,直接的情况到 复杂,难以理解的案例。 对于索引数组的所有情况,什么 返回的是原始数据的副本,而不是获取的视图 切片。强>
换句话说,您假设您的行B[:,:] = A[idx,:]
(在更正@MSeifert指出的行之后)仅导致将元素从A
复制到B
是不正确的。相反,numpy
首先从索引A
创建一个新数组,然后将其元素复制到B
。
为什么内存使用量的变化超出我的范围。但是,查看原始数组形状s=(300000,3000)
,对于64位数字,如果我没有计算错误,则大约为6.7 GB。因此创建额外的数组,实际上额外的内存使用似乎是合理的。
修改强>:
对OP的评论作出反应,我做了一些关于将混合行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
- 循环执行得不是很糟糕,特别是如果你只想分配部分行而不是整个数组。令人惊讶的是numba
似乎没有提高性能。