numpy 1.6.1 argsort() 奇怪行为?
我有一个数组 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) )
如果你比较 r
和 order
,用 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)
会把较高的索引放在前面(对于相同的行),这样当你反转索引时,较低的索引就会排在前面。