【问题标题】:Max value per diagonal in 2d array二维数组中每个对角线的最大值
【发布时间】:2020-03-29 01:26:13
【问题描述】:

我有数组,需要动态窗口的最大滚动差异。

a = np.array([8, 18, 5,15,12])
print (a)
[ 8 18  5 15 12]

所以首先我自己创造差异:

b = a - a[:, None]
print (b)
[[  0  10  -3   7   4]
 [-10   0 -13  -3  -6]
 [  3  13   0  10   7]
 [ -7   3 -10   0  -3]
 [ -4   6  -7   3   0]]

然后将上三角矩阵替换为0:

c = np.tril(b)
print (c)
[[  0   0   0   0   0]
 [-10   0   0   0   0]
 [  3  13   0   0   0]
 [ -7   3 -10   0   0]
 [ -4   6  -7   3   0]]

最后需要每个对角线的最大值,所以这意味着:

max([0,0,0,0,0]) = 0  
max([-10,13,-10,3]) = 13
max([3,3,-7]) = 3
max([-7,6]) = 6
max([-4]) = -4

所以预期的输出是:

[0, 13, 3, 6, -4]

什么是不错的矢量化解决方案?或者是否可以通过其他方式获得预期的输出?

【问题讨论】:

    标签: python numpy max vectorization diagonal


    【解决方案1】:

    你可以使用numpy.diagonal:

    a = np.array([8, 18, 5,15,12])
    b = a - a[:, None]
    c = np.tril(b)
    for i in range(b.shape[0]):
        print(max(c.diagonal(-i)))
    

    输出:

    0
    13
    3
    6
    -4
    

    【讨论】:

    • 我认为矢量化,没有循环
    【解决方案2】:

    使用ndarray.diagonal

    v = [max(c.diagonal(-i)) for i in range(b.shape[0])]
    print(v) # [0, 13, 3, 6, -4]
    

    【讨论】:

      【解决方案3】:

      这是strides 的矢量化解决方案 -

      from skimage.util import view_as_windows
      
      n = len(a)
      z = np.zeros(n-1,dtype=a.dtype)
      p = np.concatenate((a,z))
      
      s = view_as_windows(p,n)
      mask = np.tri(n,k=-1,dtype=bool)[:,::-1]
      v = s[0]-s
      out = np.where(mask,v.min()-1,v).max(1)
      

      单循环提高内存效率 -

      n = len(a)
      out = [max(a[:-i+n]-a[i:]) for i in range(n)]
      

      使用np.max 代替max 以更好地使用数组内存。

      【讨论】:

      • @jezrael 取决于我认为的数据大小。对于大尺寸,我认为具有切片 + 最大值的 loopy 可能会因为内存效率而获胜。
      【解决方案4】:

      您可以滥用这样一个事实:将形状为 (N+1, N) 的非方形数组重塑为 (N, N+1) 会使对角线显示为列

      from scipy.linalg import toeplitz
      a = toeplitz([1,2,3,4], [1,4,3])
      # array([[1, 4, 3],
      #        [2, 1, 4],
      #        [3, 2, 1],
      #        [4, 3, 2]])
      a.reshape(3, 4)
      # array([[1, 4, 3, 2],
      #        [1, 4, 3, 2],
      #        [1, 4, 3, 2]])
      

      然后你可以像这样使用(注意我已经交换了符号并将下三角形设置为零)

      smallv = -10000  # replace this with np.nan if you have floats
      
      a = np.array([8, 18, 5,15,12])
      b = a[:, None] - a
      
      b[np.tril_indices(len(b), -1)] = smallv
      d = np.vstack((b, np.full(len(b), smallv)))
      
      d.reshape(len(d) - 1, -1).max(0)[:-1]
      # array([ 0, 13,  3,  6, -4])
      

      【讨论】:

        【解决方案5】:

        考虑到所涉及的高级索引,不确定这到底有多有效,但这是一种方法:

        import numpy as np
        
        a = np.array([8, 18, 5, 15, 12])
        b = a[:, None] - a
        # Fill lower triangle with largest negative
        b[np.tril_indices(len(a))] = np.iinfo(b.dtype).min  # np.finfo for float
        # Put diagonals as rows
        s = b.strides[1]
        diags = np.ndarray((len(a) - 1, len(a) - 1), b.dtype, b, offset=s, strides=(s, (len(a) + 1) * s))
        # Get maximum from each row and add initial zero
        c = np.r_[0, diags.max(1)]
        print(c)
        # [ 0 13  3  6 -4]
        

        编辑:

        另一种可能不是您想要的替代方法,只是使用 Numba,例如:

        import numpy as np
        import numba as nb
        
        def max_window_diffs_jdehesa(a):
            a = np.asarray(a)
            dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
            out = np.full_like(a, dtinf.min)
            _pwise_diffs(a, out)
            return out
        
        @nb.njit(parallel=True)
        def _pwise_diffs(a, out):
            out[0] = 0
            for w in nb.prange(1, len(a)):
                for i in range(len(a) - w):
                    out[w] = max(a[i] - a[i + w], out[w])
        
        a = np.array([8, 18, 5, 15, 12])
        print(max_window_diffs(a))
        # [ 0 13  3  6 -4]
        

        将这些方法与原始方法进行比较:

        import numpy as np
        import numba as nb
        
        def max_window_diffs_orig(a):
            a = np.asarray(a)
            b = a - a[:, None]
            out = np.zeros(len(a), b.dtype)
            out[-1] = b[-1, 0]
            for i in range(1, len(a) - 1):
                out[i] = np.diag(b, -i).max()
            return out
        
        def max_window_diffs_jdehesa_np(a):
            a = np.asarray(a)
            b = a[:, None] - a
            dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
            b[np.tril_indices(len(a))] = dtinf.min
            s = b.strides[1]
            diags = np.ndarray((len(a) - 1, len(a) - 1), b.dtype, b, offset=s, strides=(s, (len(a) + 1) * s))
            return np.concatenate([[0], diags.max(1)])
        
        def max_window_diffs_jdehesa_nb(a):
            a = np.asarray(a)
            dtinf = np.iinfo(b.dtype) if np.issubdtype(b.dtype, np.integer) else np.finfo(b.dtype)
            out = np.full_like(a, dtinf.min)
            _pwise_diffs(a, out)
            return out
        
        @nb.njit(parallel=True)
        def _pwise_diffs(a, out):
            out[0] = 0
            for w in nb.prange(1, len(a)):
                for i in range(len(a) - w):
                    out[w] = max(a[i] - a[i + w], out[w])
        
        np.random.seed(0)
        a = np.random.randint(0, 100, size=100)
        r = max_window_diffs_orig(a)
        print((max_window_diffs_jdehesa_np(a) == r).all())
        # True
        print((max_window_diffs_jdehesa_nb(a) == r).all())
        # True
        
        %timeit max_window_diffs_orig(a)
        # 348 µs ± 986 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
        %timeit max_window_diffs_jdehesa_np(a)
        # 91.7 µs ± 1.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
        %timeit max_window_diffs_jdehesa_nb(a)
        # 19.7 µs ± 88.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
        
        np.random.seed(0)
        a = np.random.randint(0, 100, size=10000)
        %timeit max_window_diffs_orig(a)
        # 651 ms ± 26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
        %timeit max_window_diffs_jdehesa_np(a)
        # 1.61 s ± 6.19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
        %timeit max_window_diffs_jdehesa_nb(a)
        # 22 ms ± 967 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
        

        第一个可能更适合较小的数组,但不适用于较大的数组。另一方面,Numba 在所有情况下都非常好。

        【讨论】:

        • 你能添加一些时间来回答吗,例如a 中的 10、100、1000 个值?
        • @jezrael 添加了一个可能的 Numba 解决方案和一些时间措施。我的 NumPy 解决方案不能很好地扩展,Numba 很好,虽然我不确定它是否对你有用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-07-24
        • 2023-03-23
        • 2015-09-19
        • 1970-01-01
        • 2011-05-25
        • 1970-01-01
        • 2016-03-16
        相关资源
        最近更新 更多