Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python NumPy阵列中的矢量化迭代加法_Python_Loops_Numpy_Optimization_Vectorization - Fatal编程技术网

Python NumPy阵列中的矢量化迭代加法

Python NumPy阵列中的矢量化迭代加法,python,loops,numpy,optimization,vectorization,Python,Loops,Numpy,Optimization,Vectorization,对于二维索引随机数组中的每个元素(具有潜在的重复项),我希望将“+=1”添加到二维零数组中相应的网格中。然而,我不知道如何优化计算。使用循环的标准,如下所示 def interadd(): U = 100 input = np.random.random(size=(5000,2)) * U idx = np.floor(input).astype(np.int) grids = np.zeros((U,U)) for i in rang

对于二维索引随机数组中的每个元素(具有潜在的重复项),我希望将“+=1”添加到二维零数组中相应的网格中。然而,我不知道如何优化计算。使用循环的标准,如下所示

def interadd():
    U = 100 
    input = np.random.random(size=(5000,2)) * U
    idx = np.floor(input).astype(np.int) 

    grids = np.zeros((U,U))      
    for i in range(len(input)):
        grids[idx[i,0],idx[i,1]] += 1
    return grids
运行时可能非常重要:

>> timeit(interadd, number=5000)
43.69953393936157

有没有一种方法可以将这个迭代过程矢量化?

您可以通过使用来稍微加快它,它可以正确地处理重复索引的情况:

def interadd(U, idx):
    grids = np.zeros((U,U))      
    for i in range(len(idx)):
        grids[idx[i,0],idx[i,1]] += 1
    return grids

def interadd2(U, idx):
    grids = np.zeros((U,U))
    np.add.at(grids, idx.T.tolist(), 1)
    return grids

def interadd3(U, idx):
    # YXD suggestion
    grids = np.zeros((U,U))
    np.add.at(grids, (idx[:,0], idx[:,1]), 1)
    return grids

>>> U = 100
>>> idx = np.floor(np.random.random(size=(5000,2))*U).astype(np.int)
>>> (interadd(U, idx) == interadd2(U, idx)).all()
True
>>> %timeit interadd(U, idx)
100 loops, best of 3: 8.48 ms per loop
>>> %timeit interadd2(U, idx)
100 loops, best of 3: 2.62 ms per loop

以及YXD的建议:

>>> (interadd(U, idx) == interadd3(U, idx)).all()
True
>>> %timeit interadd3(U, idx)
1000 loops, best of 3: 1.09 ms per loop

您可以将
R,C
索引从
idx
转换为线性索引,然后找出唯一的索引及其计数,最后将它们存储在输出
网格中作为最终输出。下面是实现相同目标的实现-

# Calculate linear indices corressponding to idx
lin_idx = idx[:,0]*U + idx[:,1]

# Get unique linear indices and their counts
unq_lin_idx,idx_counts = np.unique(lin_idx,return_counts=True)

# Setup output array and store index counts in raveled/flattened version
grids = np.zeros((U,U))  
grids.ravel()[unq_lin_idx] = idx_counts
运行时测试-

以下是涵盖所有方法(包括)并使用该解决方案中列出的相同定义的运行时测试-

In [63]: U = 100
    ...: idx = np.floor(np.random.random(size=(5000,2))*U).astype(np.int)
    ...: 

In [64]: %timeit interadd(U, idx)
100 loops, best of 3: 7.57 ms per loop

In [65]: %timeit interadd2(U, idx)
100 loops, best of 3: 2.59 ms per loop

In [66]: %timeit interadd3(U, idx)
1000 loops, best of 3: 1.24 ms per loop

In [67]: def unique_counts(U, idx):
    ...:     lin_idx = idx[:,0]*U + idx[:,1]
    ...:     unq_lin_idx,idx_counts = np.unique(lin_idx,return_counts=True)
    ...:     grids = np.zeros((U,U))  
    ...:     grids.ravel()[unq_lin_idx] = idx_counts
    ...:     return grids
    ...: 

In [68]: %timeit unique_counts(U, idx)
1000 loops, best of 3: 595 µs per loop

运行时表明,建议的基于
np.unique
的方法比第二快的方法快50%以上。

Divakar的回答让我尝试以下方法,这似乎是迄今为止最快的方法:

lin_idx = idx[:,0]*U + idx[:,1]
grids = np.bincount(lin_idx, minlength=U**2).reshape(U, U)
时间:

In [184]: U = 100 
     ...: input = np.random.random(size=(5000,2)) * U
     ...: idx = np.floor(input).astype(np.int)

In [185]: %timeit interadd3(U, idx)  # By DSM / XYD
1000 loops, best of 3: 1.68 ms per loop

In [186]: %timeit unique_counts(U, idx)  # By Divakar
1000 loops, best of 3: 676 µs per loop

In [187]: %%timeit
     ...: lin_idx = idx[:,0]*U + idx[:,1]
     ...: grids = np.bincount(lin_idx, minlength=U*U).reshape(U, U)
     ...: 
10000 loops, best of 3: 97.5 µs per loop

键入的内容几乎相同。您可以将
idx.T.tolist()
更改为
(idx[:,0],idx[:,1])
,应该更快。
np.unique
使用后台排序,因此时间复杂度比
np.add.at
更高,但另一方面,你的方法对
网格
数组具有更快的内存访问模式。@moarningsun是的,我认为它使用
排序
区分
。我想这在更快的运行时上是有意义的。想知道
add.at
到底发生了什么会很有趣。这让我想到了一个有趣的方法:
grids=np.bincount(lin_idx,minlength=U**2)。重塑(U,U)
@moarningsun
bincount
肯定是另一种看待
add.at
的方法,而且可能更快。发布一个解决方案,看看它的时间安排是否更好?似乎有了巨大的改进!