用NumPy按列对数组进行排序

510

我该如何按照NumPy数组的第n列进行排序?

例如,给定以下数组:

a = array([[9, 2, 3],
           [4, 5, 6],
           [7, 0, 5]])

我想通过第二列来排序a的行,得到:

array([[7, 0, 5],
       [9, 2, 3],
       [4, 5, 6]])
16个回答

997

按照a的第二列进行排序:

a[a[:, 1].argsort()]

6
这不够清晰,这里的“1”是什么?需要排序的索引吗? - orezvani
44
[:,1] 表示矩阵 a 的第二列。 - Steve Tjoa
87
如果你想要反向排序,把这个改成a[a[:,1].argsort()[::-1]] - Steven C. Howell
26
我觉得这样更容易阅读:ind = np.argsort(a[:,1]); a = a[ind] - poppie
3
a[a[:,k].argsort()] 等同于 a[a[:,k].argsort(),:]。这个规律也适用于另一个维度(使用行来排序列):a[:,a[j,:].argsort()](希望我打对了)。 - bean
显示剩余6条评论

181

@steveanswer实际上是最优雅的方法。

要了解“正确”的方法,请参阅numpy.ndarray.sort的order关键字参数。

但是,您需要将数组视为具有字段的数组(结构化数组)。

如果您最初没有定义带字段的数组,则“正确”的方法相当丑陋...

作为一个快速示例,对其进行排序并返回副本:

In [1]: import numpy as np

In [2]: a = np.array([[1,2,3],[4,5,6],[0,0,1]])

In [3]: np.sort(a.view('i8,i8,i8'), order=['f1'], axis=0).view(np.int)
Out[3]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

要原地排序:

In [6]: a.view('i8,i8,i8').sort(order=['f1'], axis=0) #<-- returns None

In [7]: a
Out[7]: 
array([[0, 0, 1],
       [1, 2, 3],
       [4, 5, 6]])

@Steve的方法是我所知道的最优雅的方法...

这种方法的唯一优点是"order"参数是一个按字段排序搜索结果的列表。例如,您可以通过提供order=['f1','f2','f0']来按第二列、第三列和第一列的顺序排序。


4
在我的NumPy 1.6.1rc1版本中,出现了ValueError: new type not compatible with array.的错误提示。 - Clippit
13
有必要提出一个功能请求来改善“正确”的方式,使其更加美观吗? - endolith
6
如果数组中的值是浮点数(float),我需要改动什么吗? - Marco
14
这种方法相对于Steve的方法的一个主要优点是它允许非常大的数组进行原地排序。对于足够大的数组,np.argsort返回的索引本身可能会占用相当多的内存,而且使用数组进行索引也会生成正在排序的数组的副本。 - ali_m
6
请问一下,'i8,i8,i8' 的含义是什么?它是针对每列还是每行的?如果排序不同的数据类型会发生什么变化?如何找出正在使用的位数?谢谢。 - evn
显示剩余10条评论

59

按照Steve Tjoa的方法,您可以使用稳定排序(如归并排序)在多列上进行排序,并将最不重要的列到最重要的列的索引进行排序:

a = a[a[:,2].argsort()] # First sort doesn't need to be stable.
a = a[a[:,1].argsort(kind='mergesort')]
a = a[a[:,0].argsort(kind='mergesort')]

这将按列0、1、2进行排序。


6
为什么第一次排序不需要稳定性? - Little Bobby Tables
15
稳定指当出现相等的情况时,你需要保持原始顺序,而未排序的文件的原始顺序是无关紧要的。 - J.J
这似乎是一个非常重要的观点。拥有一个不会自动排序的列表将是不好的。 - Clumsy cat

25

如果有人希望在程序的关键部分使用排序,这里是不同提议的性能比较:

import numpy as np
table = np.random.rand(5000, 10)

%timeit table.view('f8,f8,f8,f8,f8,f8,f8,f8,f8,f8').sort(order=['f9'], axis=0)
1000 loops, best of 3: 1.88 ms per loop

%timeit table[table[:,9].argsort()]
10000 loops, best of 3: 180 µs per loop

import pandas as pd
df = pd.DataFrame(table)
%timeit df.sort_values(9, ascending=True)
1000 loops, best of 3: 400 µs per loop

看起来使用argsort进行索引是目前最快的方法...


24

来自NumPy邮件列表的另一个解决方案:

>>> a
array([[1, 2],
       [0, 0],
       [1, 0],
       [0, 2],
       [2, 1],
       [1, 0],
       [1, 0],
       [0, 0],
       [1, 0],
      [2, 2]])
>>> a[np.lexsort(np.fliplr(a).T)]
array([[0, 0],
       [0, 0],
       [0, 2],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 0],
       [1, 2],
       [2, 1],
       [2, 2]])

5
正确的概括是 a[np.lexsort(a.T[cols])],其中 cols=[1] 是原问题中给出的。 - Radio Controlled

23

正如Python文档维基所建议的:

a = ([[1, 2, 3], [4, 5, 6], [0, 0, 1]]); 
a = sorted(a, key=lambda a_entry: a_entry[1]) 
print a

输出:

[[[0, 0, 1], [1, 2, 3], [4, 5, 6]]]

23
使用这个解决方案,会得到一个列表而不是NumPy数组,所以这可能并不总是方便(占用更多内存,可能速度较慢等)。 - Eric O. Lebigot
这个“解决方案”比得到最多赞的答案慢了一个接近无限的因素。 - Jivan
1
@Jivan 实际上,这个解决方案比得到最多赞的回答快5倍 https://imgur.com/a/IbqtPBL - Antony Hatchkins
@AntonyHatchkins 但这并不能完成整个工作。它生成的是一个列表而不是一个数组。我得到了与你类似的时间(3.02毫秒对549微秒),但如果我将np.array应用于结果,时间会增加到4.3毫秒。 - Kelly Bundy

8

我有一个类似的问题。

我的问题:

我想计算SVD,并需要按降序排序我的特征值,但我想保留特征值和特征向量之间的映射关系。我的特征值在第一行,相应的特征向量在同一列下面。

因此,我想按第一行降序排列二维数组的列。

我的解决方案

a = a[::, a[0,].argsort()[::-1]]

那么这是如何工作的呢?

a[0,] 就是我想按照其排序的第一行。

现在我使用 argsort 来获取索引顺序。

我使用 [::-1] 是因为需要降序排列。

最后,我使用a[::, ...]来获取正确顺序的列视图。


4
import numpy as np
a=np.array([[21,20,19,18,17],[16,15,14,13,12],[11,10,9,8,7],[6,5,4,3,2]])
y=np.argsort(a[:,2],kind='mergesort')# a[:,2]=[19,14,9,4]
a=a[y]
print(a)

期望的输出是[[6,5,4,3,2],[11,10,9,8,7],[16,15,14,13,12],[21,20,19,18,17]]

请注意,argsort(numArray)返回一个numArray排序后的索引数组。

示例:

x=np.array([8,1,5]) 
z=np.argsort(x) #[1,3,0] are the **indices of the predicted sorted array**
print(x[z]) #boolean indexing which sorts the array on basis of indices saved in z

答案将是 [1,5,8]


1
你确定它不是 [1,2,0] 吗? - adir abargil

3
一个稍微复杂一点的lexsort示例——在第1列上按降序排列,第2列按升序排列。 lexsort的技巧是它按行排序(因此需要使用.T),并优先考虑最后一个。
In [120]: b=np.array([[1,2,1],[3,1,2],[1,1,3],[2,3,4],[3,2,5],[2,1,6]])
In [121]: b
Out[121]: 
array([[1, 2, 1],
       [3, 1, 2],
       [1, 1, 3],
       [2, 3, 4],
       [3, 2, 5],
       [2, 1, 6]])
In [122]: b[np.lexsort(([1,-1]*b[:,[1,0]]).T)]
Out[122]: 
array([[3, 1, 2],
       [3, 2, 5],
       [2, 1, 6],
       [2, 3, 4],
       [1, 1, 3],
       [1, 2, 1]])

1

这是另一种解决方案,考虑到所有列(比J.J的答案更紧凑);

ar=np.array([[0, 0, 0, 1],
             [1, 0, 1, 0],
             [0, 1, 0, 0],
             [1, 0, 0, 1],
             [0, 0, 1, 0],
             [1, 1, 0, 0]])

使用lexsort进行排序。

ar[np.lexsort(([ar[:, i] for i in range(ar.shape[1]-1, -1, -1)]))]

输出:

array([[0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 1, 0, 0]])

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