Python 如何将二进制值数组的行排序为长二进制数?

Python 如何将二进制值数组的行排序为长二进制数?,python,arrays,sorting,numpy,Python,Arrays,Sorting,Numpy,有一个大约500000行的2D numpy数组,每行512个值: [ [1,0,1,...,0,0,1], # 512 1's or 0's [0,1,0,...,0,1,1], ... [0,0,1,...,1,0,1], # row number 500000 ] 如何对行进行升序排序,就像每行是一个长512位整数一样 [ [0,0,1,...,1,0,1], [0,1,0,...,0,1,1], [1,0,1,...,0,0,1], ... ] 您可以按

有一个大约500000行的2D numpy数组,每行512个值:

[
  [1,0,1,...,0,0,1], # 512 1's or 0's
  [0,1,0,...,0,1,1],
  ...
  [0,0,1,...,1,0,1], # row number 500000
]
如何对行进行升序排序,就像每行是一个长512位整数一样

[
  [0,0,1,...,1,0,1],
  [0,1,0,...,0,1,1],
  [1,0,1,...,0,0,1],
  ...
]
您可以按512次的方式对它们进行排序,首先从最右边的位开始

  • 按最后一位排序
  • 按倒数第二位排序,稳定(不会弄乱上一次排序的结果)
  • 。。。
  • 按第一位排序,稳定

  • 一个较小的示例:假设您希望按位对这三个2位数字进行排序:

    11
    01
    00
    
    在第一步中,按正确的位进行排序,结果是:

    00
    11
    01
    
    现在按第一位排序,在本例中,该列中有两个
    0
    。如果您的排序算法不稳定,则允许在结果中以任何顺序放置这些相等的项,这可能会导致
    01
    出现在
    00
    之前,这是我们不希望看到的,因此我们对第一列使用稳定排序,保持相等项的相对顺序,从而产生所需的:

    00
    01
    11
    
    创建每个
    行的
    字符串
    ,然后应用
    np.sort()
    因此,如果我们有一个
    数组要测试:

    a = np.array([[1,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,1,1]])
    
    我们可以使用以下方法创建每个
    行的
    字符串

    这将使
    现在成为一个

    array(['1010', '0010', '0011', '0011'], dtype='<U4')
    
    使
    a

    array(['0010', '0011', '0011', '1010'], dtype='<U4')
    
    array([[0, 0, 1, 0],
           [0, 0, 1, 1],
           [0, 0, 1, 1],
           [1, 0, 1, 0]])
    
    这使得
    成为一个

    array(['0010', '0011', '0011', '1010'], dtype='<U4')
    
    array([[0, 0, 1, 0],
           [0, 0, 1, 1],
           [0, 0, 1, 1],
           [1, 0, 1, 0]])
    
    如果你想把这些都塞进一行:

    a = np.array([[int(i) for i in r] for r in np.sort(np.apply_along_axis(lambda r: ''.join([str(c) for c in r]), 0, a))])
    

    除了转换为字符串,您还可以使用数据的
    void
    视图(从@Jaime开始)和
    argsort

    def sort_bin(b):
        b_view = np.ascontiguousarray(b).view(np.dtype((np.void, b.dtype.itemsize * b.shape[1])))
        return b[np.argsort(b_view.ravel())] #as per Divakar's suggestion
    
    测试

    np.random.seed(0)
    
    b = np.random.randint(0, 2, (10,5))
    print(b)
    print(sort_bin(b))
    
    [[0 1 1 0 1]
     [1 1 1 1 1]
     [1 0 0 1 0]
     ..., 
     [1 0 1 1 0]
     [0 1 0 1 1]
     [1 1 1 0 1]]
    [[0 0 0 0 1]
     [0 1 0 1 1]
     [0 1 1 0 0]
     ..., 
     [1 1 1 0 1]
     [1 1 1 1 0]
     [1 1 1 1 1]]
    
    由于
    b_视图
    只是
    b

    t = np.random.randint(0,2,(2000,512))
    
    %timeit sort_bin(t)
    100 loops, best of 3: 3.09 ms per loop
    
    %timeit np.array([[int(i) for i in r] for r in np.sort(np.apply_along_axis(lambda r: ''.join([str(c) for c in r]), 0, t))])
    1 loop, best of 3: 3.29 s per loop
    

    实际上大约快1000倍

    这很慢,但可以完成任务

    def sort_col(arr, col_num=0):
    # if we have sorted over all columns return array
    if col_num >= arr.shape[1]:
        return arr
    
    # sort array over given column
    arr_sorted = arr[arr[:, col_num].argsort()]
    
    # if the number of 1s in the given column is not equal to the total number
    # of rows neither equal to 0, split on 1 and 0, sort and then merge
    if len(arr) > np.sum(arr_sorted[:, col_num]) > 0:
        arr_sorted0s = sort_col(arr_sorted[arr_sorted[:, col_num]==0], col_num+1)
        arr_sorted1s = sort_col(arr_sorted[arr_sorted[:, col_num]==1], col_num+1)
        # change order of stacking if you want ascenting order
        return np.vstack((arr_sorted0s, arr_sorted1s))
    
    # if the number of 1s in the given column is equal to the total number
    # of rows or equal to 0, just go to the next iteration
    return sort_col(arr_sorted, col_num + 1)
    
    
    
    np.random.seed(0)
    a = np.random.randint(0, 2, (5, 4))
    print(a)
    print(sort_col(a))
    
    # prints
    [[0 1 1 0]
     [1 1 1 1]
     [1 1 1 0]
     [0 1 0 0]
     [0 0 0 1]]
    [[0 0 0 1]
     [0 1 0 0]
     [0 1 1 0]
     [1 1 1 0]
     [1 1 1 1]]
    

    编辑。或者最好使用Daniels解决方案。在我发布代码之前,我没有检查新答案。

    我不确定,但函数“sorted”可以对字符数组中的字符串进行排序,也许它可以处理整数数组?@Orions坏主意。不要在涉及numpy数组的地方使用python函数。谢谢你的建议。将单个位转换为字符字节似乎是“错误的”。也许每一行都是64个uint8,然后使用你的方法?你可以使用
    np.lexsort
    来实现这一点,但我不确定它会比字符串/空格转换更快。聪明的一个!
    b[np.argsort(b_view.ravel())]
    有一些明显的改进。谢谢Divakar,我忘记了
    。flatte()
    制作了一个副本。
    def sort_col(arr, col_num=0):
    # if we have sorted over all columns return array
    if col_num >= arr.shape[1]:
        return arr
    
    # sort array over given column
    arr_sorted = arr[arr[:, col_num].argsort()]
    
    # if the number of 1s in the given column is not equal to the total number
    # of rows neither equal to 0, split on 1 and 0, sort and then merge
    if len(arr) > np.sum(arr_sorted[:, col_num]) > 0:
        arr_sorted0s = sort_col(arr_sorted[arr_sorted[:, col_num]==0], col_num+1)
        arr_sorted1s = sort_col(arr_sorted[arr_sorted[:, col_num]==1], col_num+1)
        # change order of stacking if you want ascenting order
        return np.vstack((arr_sorted0s, arr_sorted1s))
    
    # if the number of 1s in the given column is equal to the total number
    # of rows or equal to 0, just go to the next iteration
    return sort_col(arr_sorted, col_num + 1)
    
    
    
    np.random.seed(0)
    a = np.random.randint(0, 2, (5, 4))
    print(a)
    print(sort_col(a))
    
    # prints
    [[0 1 1 0]
     [1 1 1 1]
     [1 1 1 0]
     [0 1 0 0]
     [0 0 0 1]]
    [[0 0 0 1]
     [0 1 0 0]
     [0 1 1 0]
     [1 1 1 0]
     [1 1 1 1]]