numpy 1.6.1 argsort() 奇怪行为?

2 投票
1 回答
1047 浏览
提问于 2025-04-20 00:48

我有一个数组 data,它的形状是 (N,6)。我想根据最后一列对这个数组进行排序,具体方法如下:

sortx = numpy.argsort( data[:,-1] )[::-1]
sortedData = data[ sortx, : ]

这里的 [::-1] 是用来把这一列从高到低排序的,而不是从低到高,并且数据类型是 float64。然后,我把这个排序后的数组保存到一个 .npy 文件中,方法如下:

numpy.save( 'file.npy', sortedData )

但是,当我把数组重新加载回来并检查数据的排序时,发现它似乎并没有按顺序排列!只有部分行是正确的,这让人觉得很奇怪。

data_again = numpy.load( 'file.npy' )
order = numpy.argsort( data_again[:,-1] )[::-1]
r = numpy.arange( len(data_again) )

如果你比较 rorder,用 numpy.sum( order == r) 来检查,你会发现这个结果并不等于 N。大约有 2% 的数据没有按正确的顺序排列!

首先,我是不是正确理解了上面的代码?其次,有人能复现这个问题吗?我在 Linux 上使用的是 Python 2.7.2 和 numpy 1.6.1。

更新:这种情况甚至在第一次排序后、保存之前就发生了。所以问题出在排序本身。排序列中有重复的值。

1 个回答

4

如果最后一列有重复的值,我可以复现这个问题的症状:

import numpy as np
np.random.seed(0)
data = np.random.random((8,2))
data[::2,-1] = data[1::2,-1]
print(data)
# [[ 0.5488135   0.54488318]
#  [ 0.60276338  0.54488318]
#  [ 0.4236548   0.891773  ]
#  [ 0.43758721  0.891773  ]
#  [ 0.96366276  0.52889492]
#  [ 0.79172504  0.52889492]
#  [ 0.56804456  0.0871293 ]
#  [ 0.07103606  0.0871293 ]]
sortx = np.argsort( data[:,-1] )[::-1]
sorted_data = data[ sortx, : ]

order = np.argsort( sorted_data[:,-1] )[::-1]
r = np.arange( len(sorted_data) )
print(order)
# [1 0 3 2 5 4 7 6]
print(r)
# [0 1 2 3 4 5 6 7]
print(np.allclose(order, r))
# False

np.argsort 默认使用快速排序。快速排序是不稳定的,这意味着当有相同的行时,它们的顺序不一定和原始数据的顺序一样。

不过,即使你使用像 mergesort 这样的稳定排序,当你对 np.argsort 的结果进行 反转 时,对于那些相同的行,较高 的索引会排在前面。

因此,当你第二次调用 np.argsort 时,你得到的 order 不会等于 r

你可以通过对最后一列进行排序,并使用 np.arange(len(data),0,-1) 作为解决相同值的依据,来得到那个顺序:

sortx = np.lexsort((np.arange(len(data),0,-1), data[:,-1]))[::-1]
sorted_data = data[ sortx, : ]

order = np.lexsort((np.arange(len(data),0,-1), sorted_data[:,-1]))[::-1]
r = np.arange( len(sorted_data) )
print(order)
# [0 1 2 3 4 5 6 7]
print(r)
# [0 1 2 3 4 5 6 7]
print(np.allclose(order, r))
# True

使用 np.arange(len(data),0,-1) 会把较高的索引放在前面(对于相同的行),这样当你反转索引时,较低的索引就会排在前面。

撰写回答