【问题标题】:Rearrange 2D NumPy Array Efficiently有效地重新排列 2D NumPy 数组
【发布时间】:2020-05-21 14:08:59
【问题描述】:

假设我有一个 2D NumPy 数组:

x = np.random.rand(100, 100000)

然后我检索按列排序的索引(即,每列独立于其他列排序并返回索引):

idx = np.argsort(x, axis=0) 

然后,对于每一列,我需要 index = [10, 20, 30, 40, 50] 中的值首先是(该列的)前 5 行,然后是其余的排序值(不是索引!)。

一种天真的方法可能是:

indices = np.array([10, 20, 30, 40, 50])
out = np.empty(x.shape, dtype=int64)

for col in range(x.shape[1]):
    # For each column, fill the first few rows with `indices`
    out[:indices.shape[0], col] = x[indices, col]  # Note that we want the values, not the indices

    # Then fill the rest of the rows in this column with the remaining sorted values excluding `indices`
    n = indices.shape[0]
    for row in range(indices.shape[0], x.shape[0]):
        if idx[row, col] not in indices:
            out[n, col] = x[row, col]  # Again, note that we want the value, not the index
            n += 1

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    方法#1

    这是一个基于 previous post 的,不需要 idx -

    xc = x.copy()
    xc[indices] = (xc.min()-np.arange(len(indices),0,-1))[:,None]
    out = np.take_along_axis(x,xc.argsort(0),axis=0)
    

    方法 #2

    另一个使用np.isin 掩码的idx -

    mask = np.isin(idx, indices)
    p2 = np.take_along_axis(x,idx.T[~mask.T].reshape(x.shape[1],-1).T,axis=0)
    out = np.vstack((x[indices],p2))
    

    方法 #2 - 替代方案 如果您不断编辑 out 以更改除 indices 之外的所有内容,则数组赋值可能适合您 -

    n = len(indices)
    out[:n] = x[indices]
    
    mask = np.isin(idx, indices)
    lower = np.take_along_axis(x,idx.T[~mask.T].reshape(x.shape[1],-1).T,axis=0)
    out[n:] = lower
    

    【讨论】:

    • 假设我必须多次执行此操作(而不是一次),但 out 的大小在我所有的迭代中都是相同的。一次创建具有适当大小的out 数组然后将x[indices]p2 复制到其中会“更好”或更有效吗?这样,我可以避免昂贵的内存创建?
    • @slaw 我明白你的意思。看看Approach #2- Alternative 是否适合你。将相应地编辑Approach #1
    • 是的,我认为这样可以避免np.vstack!我想,从技术上讲,我们可以只做out[n:] = np.take_along_axis(...) 并可能在迭代中重用mask。我将阅读take_long_axis,以便了解那里发生了什么。
    【解决方案2】:

    这应该可以帮助您消除最内层循环和if 条件。首先,您可以传入x[:, col] 作为输入参数x

    def custom_ordering(x, idx, indices):
        # First get only the desired indices at the top
        out = x[indices, :]
    
        # delete `indices` from `idx` so `idx` doesn't have the values in `indices`
        idx2 = np.delete(idx, indices)
    
        # select `idx2` rows and concatenate
        out = np.concatenate((out, x[idx2, :]), axis=0)
    
        return out
    

    【讨论】:

      【解决方案3】:

      这是我对问题的解决方案:

      rem_indices = [_ for _ in range(x.shape[0]) if _ not in indices]    # get all remaining indices
      xs = np.take_along_axis(x, idx, axis = 0)                                        # the sorted array
      out = np.empty(x.shape)
      
      out[:indices.size, :] = xs[indices, :]                                                  # insert specific values at the beginning
      out[indices.size:, :] = xs[rem_indices, :]                                         # insert the remaining values after the previous
      

      如果我理解你的问题,请告诉我。

      【讨论】:

        【解决方案4】:

        我使用较小的数组和较少的索引来执行此操作,以便我可以轻松地检查结果,但它应该转化为您的用例。我认为这个解决方案非常有效,因为一切都已到位。

        import numpy as np
        x = np.random.randint(10, size=(12,3)) 
        indices = np.array([5,7,9])
        
        # Swap top 3 rows with the rows 5,7,9 and vice versa
        x[:len(indices)], x[indices] = x[indices], x[:len(indices)].copy()
        # Sort the wanted portion of array
        x[len(indices):].sort(axis=0) 
        

        这是输出:

        >>> import numpy as np
        >>> x = np.random.randint(10, size=(10,3))
        >>> indices = np.array([5,7,9])
        >>> x
        array([[7, 1, 8],
               [7, 4, 6],
               [6, 5, 2],
               [6, 8, 4],
               [2, 0, 2],
               [3, 0, 4],  # 5th row
               [4, 7, 4],
               [3, 1, 1],  # 7th row
               [3, 5, 3],
               [0, 5, 9]]) # 9th row
        
        >>> # We want top of array to be
        >>> x[indices]
        array([[3, 0, 4],
               [3, 1, 1],
               [0, 5, 9]])
        
        >>> # Swap top 3 rows with the rows 5,7,9 and vice versa
        >>> x[:len(indices)], x[indices] = x[indices], x[:len(indices)].copy()
        >>> # Assert that rows have been swapped correctly
        >>> x
        array([[3, 0, 4],  #
               [3, 1, 1],  # Top of array looks like above
               [0, 5, 9],  #
               [6, 8, 4],
               [2, 0, 2],
               [7, 1, 8],  # Previous top row
               [4, 7, 4],
               [7, 4, 6],  # Previous second row
               [3, 5, 3],
               [6, 5, 2]]) # Previous third row
        
        >>> # Sort the wanted portion of array
        >>> x[len(indices):].sort(axis=0)
        >>> x
        array([[3, 0, 4], #
               [3, 1, 1], # Top is the same, below is sorted
               [0, 5, 9], #
               [2, 0, 2],
               [3, 1, 2],
               [4, 4, 3],
               [6, 5, 4],
               [6, 5, 4],
               [7, 7, 6],
               [7, 8, 8]])
        

        编辑: 如果indices 中的任何元素小于len(indices),则此版本应处理

        import numpy as np
        x = np.random.randint(10, size=(12,3)) 
        indices = np.array([1,2,4])
        
        tmp = x[indices]
        
        # Here I just assume that there aren't any values less or equal to -1. If you use 
        # float, you can use -np.inf, but there is no such equivalent for ints (which I 
        # use in my example).
        x[indices] = -1
        
        # The -1 will create dummy rows that will get sorted to be on top of the array,
        # which can switch with tmp later
        x.sort(axis=0) 
        x[indices] = tmp
        

        【讨论】:

        • 哦,有趣!我真的很喜欢这可以就地完成,并且不需要额外的步骤,不需要心理体操。排序前的简单交换非常优雅
        • 一点点吹毛求疵:如果您需要保证排序稳定,这是行不通的。
        • @PaulPanzer 当您说“稳定排序”时,您指的是平局的情况吗?我认为只有在 argsort 的情况下才重要?
        • @slaw 好点。如果您最终只对值感兴趣,并且比较相等的值确实难以区分,那么“稳定”并没有多大意义。
        • 嗯,不幸的是,我在交换行中遇到了争用情况或 FIFO 情况。我认为通过中间数组进行交换会更安全
        猜你喜欢
        • 1970-01-01
        • 2013-12-14
        • 2019-10-25
        • 2020-12-27
        • 1970-01-01
        • 2016-08-24
        • 1970-01-01
        • 2021-11-21
        相关资源
        最近更新 更多