【问题标题】:Is there a way to remove the loop using numpy?有没有办法使用 numpy 删除循环?
【发布时间】:2017-10-11 18:55:14
【问题描述】:

我有一个 3D numpy 数组 input_data (q x m x n),我用它来构建直方图数据以最终绘制,它存储在 plot_data (m x n x 2) 中。这一步是我流程中的一个不错的瓶颈,我想知道是否有更快、更“numpy”的方式来做到这一点。

num_bins = 3
for i in range(m):

    for j in range(n):

        data = input_data[:, i, j]

        hist, bins = np.histogram(data, bins=num_bins)

        # Create the (x, y) pairs to plot
        plot_data[i][j] = np.stack((bins[:-1], hist), axis=1)

【问题讨论】:

  • 更快是指更简洁吗?
  • 更快意味着运行时,因此理想地利用 numpy 的矢量化能力,类似于使用 np.sum() 来计算总和,而不是循环并手动计算它
  • 所以,我查了一些资料。也许,scipy 网站将是您正在寻找的东西?我发现了以下内容:docs.scipy.org/doc/numpy-1.13.0/reference/arrays.nditer.html
  • 您的数据是 (q,m,n)。进一步研究的 q,m,n 值是多少?
  • q, m, n 可以是任何东西,但为了近似我的数据,可能是 100000 x 10 x 360。@Maderas 我不认为它对多维数组有效。跨度>

标签: python performance numpy vectorization


【解决方案1】:

这是一种用于一般 bin 数量的矢量化方法 -

def vectorized_app(input_data, num_bins):
    s0 = input_data.min(0)
    s1 = input_data.max(0)

    m,n,r = input_data.shape
    ids = (num_bins*((input_data - s0)/(s1-s0))).astype(int).clip(max=num_bins-1)
    offset = num_bins*(r*np.arange(n)[:,None] + np.arange(r))
    ids3D = ids + offset
    count3D = np.bincount(ids3D.ravel(), minlength=n*r*num_bins).reshape(n,r,-1)
    bins3D = create_ranges_nd(s0, s1, num_bins+1)[...,:-1]

    out = np.empty((n,r,num_bins,2))
    out[...,0] = bins3D
    out[...,1] = count3D
    return out

辅助函数 -

# https://stackoverflow.com/a/46694364/ @Divakar
def create_ranges_nd(start, stop, N, endpoint=True):
    if endpoint==1:
        divisor = N-1
    else:
        divisor = N
    steps = (1.0/divisor) * (stop - start)
    return start[...,None] + steps[...,None]*np.arange(N)

运行时测试

原始方法-

def org_app(input_data, num_bins):
    q,m,n = input_data.shape
    plot_data = np.zeros((m,n,num_bins,2))
    for i in range(m):
        for j in range(n):
            data = input_data[:, i, j]
            hist, bins = np.histogram(data, bins=num_bins)
            plot_data[i][j] = np.stack((bins[:-1], hist), axis=1)
    return plot_data

时间和验证 -

让我们测试一个形状为(100, 100, 100) 的大型数据数组,bin 的数量为10:

In [967]: # Setup input
     ...: num_bins = 10
     ...: m = 100
     ...: n = 100
     ...: q = 100
     ...: input_data = np.random.rand(q,m,n)
     ...: 
     ...: out1 = org_app(input_data, num_bins)
     ...: out2 = vectorized_app(input_data, num_bins)
     ...: print np.allclose(out1, out2)
     ...: 
True

In [968]: %timeit org_app(input_data, num_bins)
1 loop, best of 3: 748 ms per loop

In [969]: %timeit vectorized_app(input_data, num_bins)
100 loops, best of 3: 12.7 ms per loop

In [970]: 748/12.7 # speedup with vectorized one over original
Out[970]: 58.89763779527559

【讨论】:

  • this 与 for 循环在大型 3d 数组上的性能如何?
  • @holtc 添加假设形状随机数 (100,100,100) 和 bin 数 = 10。
  • 所以当我在我的真实数据上运行它时,np.allclose() 是假的,而且由于我正在 jitting 我所有的方法,它实际上更慢,所以很遗憾我将无法使用这个。在我的情况下,input_data 将是 (100000, 8, 360) 的数量级,最多 100 个 bin
  • @holtc 是的,所以你的循环数只有 8 * 360。这不算多。保持你的循环/jittery-jit。
  • @holtc 如果您能在未来的问题中提前提及这些尺寸/形状细节,将会有所帮助。
【解决方案2】:

我认为您的样本相似,因此直方图相似。在这种情况下,您可以简化比较并以更加矢量化的方式进行:

a=np.random.rand(100000,10,10)


def f():  # roughly your approach.
    plotdata=np.zeros((10,10,3),np.int32)
    for i in range(10):
        for j in range(10):
            bins,hist=np.histogram(a[:,i,j],3)
            plotdata[i,j]=bins 
    return plotdata

def g(): #vectored comparisons 
    u=(a < 1/3).sum(axis=0)
    w=(a > 2/3).sum(axis=0)
    v=len(a)-u-w
    return np.dstack((u,v,w))

对于 8 倍的改进:

In [213]: %timeit f()
548 ms ± 15.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [214]: %timeit g()
77.7 ms ± 5.46 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

  • 很遗憾我们不能这样简化,我们需要真实的数据
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2019-12-31
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多