我有一个2-D numpy数组,如x = array([[ 1., 5.],[ 3., 4.]])
,我必须将每一行与矩阵中的每一行进行比较,并从两行中创建一个新的最小值数组,并取最小行的总和并将其保存在新矩阵中。最后,我将得到一个对称矩阵。
例如:我将数组[1,5]与自身进行比较。新的2-D数组是array([[ 1., 5.],[ 1., 5.]])
,我沿轴= 0创建一个最小数组,即[1.,5]然后取数组的总和为6.同样我重复所有行的操作和我最终得到了一个2 * 2矩阵array([[ 6, 5.],[ 5, 7.]])
。
import numpy as np
x=np.array([[1,5],[3,4]])
y=np.zeros((len(x),len(x)))
for i in range(len(x)):
array_a=x[i]
for j in range(len(x)):
array_b=x[j]
array_c=np.array([array_a,array_b])
min_array=np.min(array_c,axis=0)
array_sum=np.sum(min_array)
y[i,j]=array_sum
我的2-D阵列非常大,执行上述操作需要花费大量时间。我是python的新手,所以任何提高性能的建议都会非常有用。
答案 0 :(得分:1)
节省大约一半时间的明显改进是仅在i> = j指数上运行。为了优雅和节省,您还可以使用更少的变量。
import numpy as np
import time
x=np.random.randint(0, 10, (500, 500))
y=np.zeros((len(x),len(x)))
# OP version
t0 = time.time()
for i in range(len(x)):
array_a=x[i]
for j in range(len(x)):
array_b=x[j]
array_c=np.array([array_a,array_b])
min_array=np.min(array_c,axis=0)
array_sum=np.sum(min_array)
y[i,j]=array_sum
print(time.time() - t0)
z=np.zeros((len(x),len(x)))
# modified version
t0 = time.time()
for i in range(len(x)):
for j in range(i, len(x)):
z[i, j]=np.sum(np.min([x[i], x[j]], axis=0))
z[j, i] = z[i, j]
print(time.time() - t0)
# verify that the result are the same
print(np.all(z == y))
我机器上的结果:
4.2974278926849365
2.746302604675293
True
答案 1 :(得分:0)
加速代码的显而易见的方法是在numpy中完成所有循环。我有一个第一个解决方案(下面代码中的f2
),它会生成一个矩阵,其中包含需要比较的所有组合,然后将该矩阵缩减为执行np.min
和{的最终结果{1}}命令。不幸的是,该方法非常耗费内存,因此当矩阵很大时变得很慢,因为对于NxN输入矩阵,中间矩阵是NxNx2xN。
但是,我找到了一个不同的解决方案,它使用一个for循环(下面np.sum
)并且看起来相当快。对于1000x1000矩阵,OP发布的原始速度约为4倍。这里的代码有一些测试:
f3
这里输出:
import numpy as np
import timeit
def f(x):
y = np.zeros_like(x)
for i in range(x.shape[0]):
a = x[i]
for j in range(x.shape[1]):
b = x[j]
y[i,j] = np.sum(np.min([a,b], axis=0))
return y
def f2(x):
y = np.empty((x.shape[0],1,2,x.shape[0]))
y[:,0,0,:] = x[:,:]
y = np.repeat(y, x.shape[0],axis=1)
y[:,:,1,:] = x[:,:]
return np.sum(np.min(y,axis=2),axis=2)
def f3(x):
y = np.empty_like(x)
for i in range(x.shape[1]):
y[:,i] = np.sum(np.minimum(x[i,:],x[:,:]),axis=1)
return y
##some testing that the functions work
x = np.array([[1,5],[3,4]])
a=f(x)
b=f2(x)
c=f3(x)
print(np.all(a==b))
print(np.all(a==c))
x = np.array([[1,7,5],[2,3,8],[5,2,4]])
a=f(x)
b=f2(x)
c=f3(x)
print(np.all(a==b))
print(np.all(a==c))
x = np.random.randint(0,10,(100,100))
a=f(x)
b=f2(x)
c=f3(x)
print(np.all(a==b))
print(np.all(a==c))
##some speed testing:
print('-'*50)
print("speed test small")
x = np.random.randint(0,100,(100,100))
print("original")
print(min(timeit.Timer(
'f(x)',
setup = 'from __main__ import f,x',
).repeat(3,10)))
print("using np.repeat")
print(min(timeit.Timer(
'f2(x)',
setup = 'from __main__ import f2,x',
).repeat(3,10)))
print("one for loop")
print(min(timeit.Timer(
'f3(x)',
setup = 'from __main__ import f3,x',
).repeat(3,10)))
print('-'*50)
print("speed test big")
x = np.random.randint(0,100,(1000,1000))
print("original")
print(min(timeit.Timer(
'f(x)',
setup = 'from __main__ import f,x',
).repeat(3,1)))
print("one for loop")
print(min(timeit.Timer(
'f3(x)',
setup = 'from __main__ import f3,x',
).repeat(3,1)))
换句话说,True
True
True
True
True
True
--------------------------------------------------
speed test small
original
1.3070102719939314
using np.repeat
0.15176948899170384
one for loop
0.029766165011096746
--------------------------------------------------
speed test big
original
17.505746565002482
one for loop
4.437685210024938
对于不会耗尽记忆力的矩阵来说非常快,但对于大型矩阵来说,f2
是我能找到的最快的。{/ p>
修改强>:
受@ Aguy的回答和this post的启发,这里仍然是一个修改,只计算矩阵的下三角形,然后将结果复制到上三角形:
f3
现在提供1000x1000矩阵的速度测试
def f4(x):
y = np.empty_like(x)
for i in range(x.shape[1]):
y[i:,i] = np.sum(np.minimum(x[i,:],x[i:,:]),axis=1)
i_upper = np.triu_indices(x.shape[1],1)
y[i_upper] = y.T[i_upper]
return y
编辑2 :
此处仍然使用speed test big
original
18.71281115297461
one for loop over lower triangle
2.0939957330119796
加速版本。根据{{3}},在这种情况下最好明确地编写循环:
numba
相关的速度测试给出:
import numba as nb
@nb.jit(nopython=True)
def f_nb(x):
res = np.empty_like(x)
for j in range(res.shape[1]):
for i in range(j,res.shape[0]):
res[j,i] = res[i,j] = np.sum(np.minimum(x[i,:], x[j,:]))
return res
表示100x100矩阵0.015975199989043176
表示1000x1000矩阵0.37946902704425156
表示10000x10000矩阵 467.06363476096885
的10000x10000速度测试似乎根本不想完成,所以我把它留了出来。如果你的矩阵比这大得多,你可能会遇到内存问题 - 你是否考虑过这个问题?