在多个列上对Numpy的ndarray进行排序

25

我从文件中读取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.]]

更长的示例输入(未解析)。

前两列应该是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.]]

我知道这个答案,它适用于单列排序。

我尝试按第二列排序,因为第一列已经排序了,但还不够。有时,第一列也会被重新排序,而且很糟糕。

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.]]

我还查看了这个问题
回答中提到:
问题在于np.lexsort或np.sort无法处理dtype为object的数组。为了解决这个问题,您可以在创建order_list之前对rows_list进行排序:
import operator
rows_list.sort(key=operator.itemgetter(0,1,2))

但是,在类型为ndarraysort函数中没有key参数。而在我的情况下,合并字段也不是一个选择。
另外,我没有一个标题,所以如果我尝试使用order参数进行排序,就会出现错误。
ValueError: Cannot specify order when the array has no fields.

我更倾向于原地排序,或者至少获得同类型的结果ndarray。然后我希望将其保存到文件中。

我应该如何做到这一点,而不会破坏数据类型?

4个回答

30

按第一列、第二列或第三列对numpy的ndarray进行排序:

>>> 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])

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

根据文档,最后一列是主要排序关键字。


>>> 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]
array([[  2  30 200]
       [  1  30 200]
       [  1  10 200]])

2
可以像这样做吗 a[a[:,2].argsort()[::-1]] 而不是这样 a[a[:,2].argsort()][::-1]?这样做会更有效率吗? - Agostino

22

使用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]

这样可以确保相同的项目顺序被保留。通常使用的快速排序算法并不这样做,尽管它更快。


1
你使用的my_data和这里其他示例中使用的是同一个吗?如果是,请将其粘贴为输入以完成您的答案。谢谢。 - Agostino
是的,我加了输入。 - Jim

11

导入时让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

7

这种方法适用于任何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.]]

你的输入和输出看起来一样,这里排序了什么? - ScientificPythonNovice
1
糟糕,我忘记在我的代码片段中交换[my_data[:,0],my_data[:,1]]以匹配所要求的1,0顺序。谢谢,已更新。 - lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接