按列对NumPy中的数组排序


336

如何按第n列对NumPy中的数组排序?

例如,

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

我想按第二列对行进行排序,以便返回:

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

8
这是一个非常糟糕的例子,因为np.sort(a, axis=0)对于给定的矩阵,这将是令人满意的解决方案。我建议使用一个更好的示例进行编辑,但被拒绝了,尽管实际上这个问题要清楚得多。该示例应该类似于a = numpy.array([[1, 2, 3], [6, 5, 2], [3, 1, 1]])所需的输出array([[3, 1, 1], [1, 2, 3], [6, 5, 2]])
David

29
大卫,你不明白这个问题的意思。他想保持每一行中的顺序相同。
marcorossi

@marcorossi我确实理解了这一点,但是该示例的格式很糟糕,因为正如我所说,存在多个可能的答案(但是,这并不能满足OP的要求)。后来根据我的评论进行的编辑确实已被批准(不过,我的搞笑被拒绝了)。所以现在一切都很好。
David

Answers:


141

@steve答案实际上是最优雅的方法。

对于“正确”的方式,请参见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']来对第二列,第三列,第一列进行排序。


3
在我的numpy 1.6.1rc1中,它引发ValueError: new type not compatible with array.
Clippit

9
提出功能要求以减少“正确”方式的丑闻是否有意义?
endlith 2013年

4
如果数组中的值是float什么呢?我应该改变什么吗?
Marco Marco

1
对于混合类型,a = np.array([['a',1,2,3],['b',4,5,6],['c',0,0,1]])我应该遵循哪种方法?
ePascoal,2015年

10
与Steve相比,此方法的主要优点是它允许将非常大的数组排序到位。对于足够大的数组,由np.argsort它们返回的索引可能会占用相当多的内存,最重要的是,使用数组进行索引也会生成要排序的数组的副本。
ali_m 2015年

736

我想这可行: a[a[:,1].argsort()]

这表示的第二列,a并据此对其进行排序。


2
这还不清楚,1这里是什么?索引排序依据?
orezvani 2014年

29
[:,1]表示的第二列a
Steve Tjoa 2014年

60
如果您想要反向排序,请将其修改为a[a[:,1].argsort()[::-1]]
Steven C. Howell 2015年

1
看起来很简单而且可行!是不是快过了np.sort
瓦茨拉夫·帕夫里克

14
我觉得这更容易阅读:ind = np.argsort( a[:,1] ); a = a[ind]
poppie '17

32

您可以按照Steve Tjoa的方法对多个列进行排序,方法是使用诸如mergesort之类的稳定排序并对索引从最低有效列到最高有效列进行排序:

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。


4
为什么First Sort不需要稳定?
小鲍比桌子

10
好问题-稳定意味着在有平局时,您保持原始顺序,而未排序文件的原始顺序无关紧要。
JJ

这似乎是非常重要的一点。拥有一个没有排序的列表将是不好的。
笨拙的猫

20

我认为您可以从Python文档Wiki中进行以下操作:

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

20
使用此解决方案,可以获取列表而不是NumPy数组,因此这可能并不总是很方便(占用更多内存,可能更慢,等等)。
Eric O Lebigot 2011年

18

如果有人想在他们程序的关键部分使用排序,下面是对不同提案的性能比较:

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进行索引是迄今为止最快的方法...


16

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

3
正确的概括是a[np.lexsort(a.T[cols])]cols=[1]原始问题在哪里。
无线电控制的

5

我有一个类似的问题。

我的问题:

我想计算SVD,需要按降序对我的特征值进行排序。但是我想保留特征值和特征向量之间的映射。我的特征值在第一行中,而对应的特征向量在同一列中。

因此,我想按降序按第一行在列中对二维数组进行排序。

我的解决方案

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

那么这是如何工作的呢?

a[0,] 只是我要排序的第一行。

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

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

最后,我使用a[::, ...]正确的顺序查看各列。


1

稍微复杂一点的lexsort例子-在第一列下降,在第二列上升。的窍门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]])

0

这是考虑所有列的另一种解决方案(JJ的答案的更紧凑方式);

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

0

只需使用排序,即可使用要排序的列号。

a = np.array([1,1], [1,-1], [-1,1], [-1,-1]])
print (a)
a=a.tolist() 
a = np.array(sorted(a, key=lambda a_entry: a_entry[0]))
print (a)

0

这是一个古老的问题,但是如果您需要将其推广到2维以上的数组,则可以采用以下解决方案:

np.einsum('ij->ij', a[a[:,1].argsort(),:])

这对于两个维度来说是一个过大的杀伤力,并且a[a[:,1].argsort()]每个@steve的答案就足够了,但是不能将该答案推广到更高的维度。您可以在此问题中找到3D阵列的示例。

输出:

[[7 0 5]
 [9 2 3]
 [4 5 6]]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.