【问题标题】:Numpy sort ndarray on multiple columns多列上的 Numpy 排序 ndarray
【发布时间】:2021-09-26 13:10:45
【问题描述】:

我得到一个 ndarray 从文件中读取它,像这样

my_data = np.genfromtxt(input_file, delimiter='\t', skip_header=0)

示例输入(已解析)

[[   2.    1.    2.    0.]
 [   2.    2.  100.    0.]
 [   2.    3.  100.    0.]
 [   3.    1.    2.    0.]
 [   3.    2.    4.    0.]
 [   3.    3.    6.    0.]
 [   4.    1.    2.    0.]
 [   4.    2.    4.    0.]
 [   4.    3.    6.    0.]]

更长的example input(未解析)。

前两列应该是int,而最后两列应该是float,但这就是我得到的。欢迎提出建议。

主要问题是,我正在尝试使用 Numpy 对其进行排序,以便对行进行排序,优先考虑第二列上的数字,然后是第一列上的数字。

所需输出示例

[[   2.    1.    2.    0.]
 [   3.    1.    2.    0.]
 [   4.    1.    2.    0.]
 [   2.    2.  100.    0.]
 [   3.    2.    4.    0.]
 [   4.    2.    4.    0.]
 [   2.    3.  100.    0.]
 [   3.    3.    6.    0.]
 [   4.    3.    6.    0.]]

我知道this answer,它适用于对单列上的行进行排序。

我尝试对第二列进行排序,因为第一列已经排序,但这还不够。有时,第一列也会被重新排序,非常糟糕。

new_data = my_data[my_data[:, 1].argsort()]
print(new_data)

#output
[[   2.    1.    2.    0.]
 [   4.    1.    2.    0.] #ouch
 [   3.    1.    2.    0.] #ouch
 [   2.    2.  100.    0.]
 [   3.    2.    4.    0.]
 [   4.    2.    4.    0.]
 [   2.    3.  100.    0.]
 [   3.    3.    6.    0.]
 [   4.    3.    6.    0.]]

我也查了this question

答案提到

这里的问题是 np.lexsort 或 np.sort 不适用于 dtype 对象的数组。为了解决这个问题,您可以在创建 order_list 之前对 rows_list 进行排序:

import operator
rows_list.sort(key=operator.itemgetter(0,1,2))

但我在sort 类型的ndarray 函数中没有key 参数。在我的情况下,合并字段不是一种选择。

另外,我没有标题,所以,如果我尝试使用 order 参数进行排序,我会收到错误。

ValueError: Cannot specify order when the array has no fields.

我宁愿就地排序或至少获得相同类型的结果ndarray。然后我想把它保存到一个文件中。

如何在不弄乱数据类型的情况下做到这一点?

【问题讨论】:

    标签: python arrays sorting numpy


    【解决方案1】:

    使用np.lexsort,您可以同时基于多个列进行排序。您要排序的列需要反向传递。这意味着np.lexsort((col_b,col_a)) 首先按 col_a 排序,然后按 col_b:

    my_data = np.array([[   2.,    1.,    2.,    0.],
                        [   2.,    2.,  100.,    0.],
                        [   2.,    3.,  100.,    0.],
                        [   3.,    1.,    2.,    0.],
                        [   3.,    2.,    4.,    0.],
                        [   3.,    3.,    6.,    0.],
                        [   4.,    1.,    2.,    0.],
                        [   4.,    2.,    4.,    0.],
                        [   4.,    3.,    6.,    0.]])
    
    ind = np.lexsort((my_data[:,0],my_data[:,1]))
    my_data[ind]
    

    结果:

    array([[  2.,   1.,   2.,   0.],
           [  3.,   1.,   2.,   0.],
           [  4.,   1.,   2.,   0.],
           [  2.,   2., 100.,   0.],
           [  3.,   2.,   4.,   0.],
           [  4.,   2.,   4.,   0.],
           [  2.,   3., 100.,   0.],
           [  3.,   3.,   6.,   0.],
           [  4.,   3.,   6.,   0.]])
    

    如果您知道您的第一列已经排序,您可以使用:

    ind = my_data[:,1].argsort(kind='stable')
    my_data[ind]
    

    这可确保为相同的项目保留订单。通常使用的快速排序算法不会这样做,尽管它更快。

    【讨论】:

    • 您使用的 my_data 是否与此处其他示例中使用的相同?如果是这样,请将其粘贴为输入以完成您的答案。谢谢。
    • 是的。我添加了输入。
    【解决方案2】:

    此方法适用于任何 numpy 数组:

    import numpy as np
    
    my_data = [[   2.,    1.,    2.,    0.],
               [   2.,    2.,  100.,    0.],
               [   2.,    3.,  100.,    0.],
               [   3.,    1.,    2.,    0.],
               [   3.,    2.,    4.,    0.],
               [   3.,    3.,    6.,    0.],
               [   4.,    1.,    2.,    0.],
               [   4.,    2.,    4.,    0.],
               [   4.,    3.,    6.,    0.]]
    my_data = np.array(my_data)
    r = np.core.records.fromarrays([my_data[:,1],my_data[:,0]],names='a,b')
    my_data = my_data[r.argsort()]
    print(my_data)
    

    结果:

    [[  2.   1.   2.   0.]
     [  3.   1.   2.   0.]
     [  4.   1.   2.   0.]
     [  2.   2. 100.   0.]
     [  3.   2.   4.   0.]
     [  4.   2.   4.   0.]
     [  2.   3. 100.   0.]
     [  3.   3.   6.   0.]
     [  4.   3.   6.   0.]]
    

    【讨论】:

    • 你的输入和输出看起来一样,这里排序的是什么?
    • 哎呀忘了从我的代码 sn-p 交换 [my_data[:,0],my_data[:,1]] 以匹配要求的 1,0 订单。谢谢,已更新。
    【解决方案3】:

    numpy ndarray 按第 1、2 或 3 列排序:

    >>> a = np.array([[1,30,200], [2,20,300], [3,10,100]])
    
    >>> a
    array([[  1,  30, 200],         
           [  2,  20, 300],          
           [  3,  10, 100]])
    
    >>> a[a[:,2].argsort()]           #sort by the 3rd column ascending
    array([[  3,  10, 100],
           [  1,  30, 200],
           [  2,  20, 300]])
    
    >>> a[a[:,2].argsort()][::-1]     #sort by the 3rd column descending
    array([[  2,  20, 300],
           [  1,  30, 200],
           [  3,  10, 100]])
    
    >>> a[a[:,1].argsort()]        #sort by the 2nd column ascending
    array([[  3,  10, 100],
           [  2,  20, 300],
           [  1,  30, 200]])
    

    解释这里发生了什么:argsort() 正在传回一个包含其父级整数序列的数组: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html

    >>> x = np.array([15, 30, 4, 80, 6])
    >>> np.argsort(x)
    array([2, 4, 0, 1, 3])
    

    按第 3 列排序,然后按第 2 列,再按 1:

    >>> a = np.array([[2,30,200], [1,30,200], [1,10,200]])
    
    >>> a
    array([[  2,  30, 200],
           [  1,  30, 200],
           [  1,  10, 200]])
    
    >>> a[np.lexsort((a[:,2], a[:,1],a[:,0]))]
    array([[  1,  10, 200],
           [  1,  30, 200],
           [  2,  30, 200]])
    
    >>> a[np.lexsort((a[:,2], a[:,1],a[:,0]))][::-1]        #reverse
    array([[  2  30 200]
           [  1  30 200]
           [  1  10 200]])
    

    【讨论】:

    • 是否可以用a[a[:,2].argsort()[::-1]] 来代替a[a[:,2].argsort()][::-1]?会不会更有效率?
    【解决方案4】:

    Import 让 Numpy 猜测类型并就地排序:

    import numpy as np
    
    # let numpy guess the type with dtype=None
    my_data = np.genfromtxt(infile, dtype=None, names=["a", "b", "c", "d"])
    
    # access columns by name
    print(my_data["b"]) # column 1
    
    # sort column 1 and column 0 
    my_data.sort(order=["b", "a"])
    
    # save specifying required format (tab separated values)
    np.savetxt("sorted.tsv", my_data, fmt="%d\t%d\t%.6f\t%.6f"
    

    或者,指定输入格式并排序到新数组:

    import numpy as np
    
    # tell numpy the first 2 columns are int and the last 2 are floats
    my_data = np.genfromtxt(infile, dtype=[('a', '<i8'), ('b', '<i8'), ('x', '<f8'), ('d', '<f8')])
    
    # access columns by name
    print(my_data["b"]) # column 1
    
    # get the indices to sort the array using lexsort
    # the last element of the tuple (column 1) is used as the primary key
    ind = np.lexsort((my_data["a"], my_data["b"]))
    
    # create a new, sorted array
    sorted_data = my_data[ind]
    
    # save specifying required format (tab separated values)
    np.savetxt("sorted.tsv", sorted_data, fmt="%d\t%d\t%.6f\t%.6f")
    

    输出:

    2   1   2.000000    0.000000
    3   1   2.000000    0.000000
    4   1   2.000000    0.000000
    2   2   100.000000  0.000000
    3   2   4.000000    0.000000
    4   2   4.000000    0.000000
    2   3   100.000000  0.000000
    3   3   6.000000    0.000000
    4   3   6.000000    0.000000
    

    【讨论】:

      猜你喜欢
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 2018-10-10
      • 2022-01-14
      • 2018-12-24
      • 2018-07-28
      • 1970-01-01
      相关资源
      最近更新 更多