操纵numpy二维阵列

时间:2018-02-15 06:42:03

标签: python arrays numpy

我有一个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的新手,所以任何提高性能的建议都会非常有用。

2 个答案:

答案 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速度测试似乎根本不想完成,所以我把它留了出来。如果你的矩阵比这大得多,你可能会遇到内存问题 - 你是否考虑过这个问题?