Python “我怎么能?”;“邮政分拣”;并行numpy阵列?
如果我有两个平行列表,并希望按照第一个列表中元素的顺序对它们进行排序,这非常简单:Python “我怎么能?”;“邮政分拣”;并行numpy阵列?,python,sorting,numpy,Python,Sorting,Numpy,如果我有两个平行列表,并希望按照第一个列表中元素的顺序对它们进行排序,这非常简单: >>> a = [2, 3, 1] >>> b = [4, 6, 7] >>> a, b = zip(*sorted(zip(a,b))) >>> print a (1, 2, 3) >>> print b (7, 4, 6) 如何在不将numpy数组解包到常规Python列表的情况下使用numpy数组实现同样的功能?b[
>>> a = [2, 3, 1]
>>> b = [4, 6, 7]
>>> a, b = zip(*sorted(zip(a,b)))
>>> print a
(1, 2, 3)
>>> print b
(7, 4, 6)
如何在不将numpy数组解包到常规Python列表的情况下使用numpy数组实现同样的功能?b[a.argsort()]
应该可以做到这一点
下面是它的工作原理。首先,你需要找到一个排序为a的排列argsort
是一种计算以下内容的方法:
>>> a = numpy.array([2, 3, 1])
>>> p = a.argsort()
>>> p
[2, 0, 1]
您可以轻松检查这是否正确:
>>> a[p]
array([1, 2, 3])
现在对b应用相同的置换
>>> b = numpy.array([4, 6, 7])
>>> b[p]
array([7, 4, 6])
这是一种不创建中间Python列表的方法,尽管它确实需要一个NumPy“记录数组”来用于排序。如果您的两个输入数组实际上是相关的(如电子表格中的列),那么这可能会提供一种处理数据的有利方法,而不是始终保留两个不同的数组,在这种情况下,您已经有了一个记录数组,而您最初的问题将仅通过调用sort()得到解决在你的阵列上 这会在将两个数组打包到记录数组后执行以下操作:
>>> from numpy import array, rec
>>> a = array([2, 3, 1])
>>> b = array([4, 6, 7])
>>> c = rec.fromarrays([a, b])
>>> c.sort()
>>> c.f1 # fromarrays adds field names beginning with f0 automatically
array([7, 4, 6])
已编辑要使用rec.fromarrays()为简单起见,请跳过冗余数据类型,使用默认排序键,使用默认字段名而不是指定(基于)。这可能是最简单、最通用的方法。(我在这里使用了三个数组,但这适用于任何形状的数组,无论是两列还是两百列)
lexsort的一个怪癖是,您必须以相反的顺序指定键,即,将主键放在第二位,将副键放在第一位。在我的示例中,我想使用第二列作为主键进行排序,因此我将其列在第二列;第1列仅解析关系,但它列在第一位)。与@Peter Hansen的答案类似,这会在对数组排序之前复制数组。但它很简单,主排序到位,使用第二个数组进行辅助排序,应该非常快:
a = np.array([2, 3, 1])
b = np.array([4, 6, 2])
# combine, sort and break apart
a, b = np.sort(np.array([a, b]))
更新:正如一条评论中指出的那样,上面的代码实际上不起作用。下面是一些更好的代码。这应该是相当有效的,例如,它避免了显式地创建阵列的额外副本。很难说它的效率有多高,因为它没有给出关于numpy.lexsort
算法的任何细节。但是它应该工作得很好,因为这正是lexsort
编写的工作
a = np.array([5, 3, 1])
b = np.array([4, 6, 7])
new_order = np.lexsort([b, a])
a = a[new_order]
b = b[new_order]
print(a, b)
# (array([1, 3, 5]), array([7, 6, 4]))
我遇到了同样的问题,并想知道排序一个数组并相应地重新排序另一个数组的不同方法的性能 两种阵列情况下的性能比较 我认为这里提到的解决方案列表是全面的,但我也想知道性能。因此,我实现了所有算法并进行了性能比较 使用zip进行两次排序
def zip_sort(s, p):
ordered_s, ordered_p = zip(*sorted(list(zip(s, p))))
return np.array(ordered_s, dtype=s.dtype), np.array(ordered_p, dtype=p.dtype)
def zip_sort(*arrays):
ordered_lists = zip(*sorted(list(zip(*arrays))))
return tuple(
(np.array(l, dtype=arrays[i].dtype) for i, l in enumerate(ordered_lists))
)
使用argsort进行排序。这不会考虑另一个数组进行辅助排序
def argsort(s, p):
indexes = s.argsort()
return s[indexes], p[indexes]
def argsort(*arrays):
indexes = arrays[0].argsort()
return tuple((a[indexes] for a in arrays))
使用numpy重新排列进行排序
def recarray_sort(s, p):
rec = np.rec.fromarrays([s, p])
rec.sort()
return rec.f0, rec.f1
def recarray_sort(*arrays):
rec = np.rec.fromarrays(arrays)
rec.sort()
return tuple((getattr(rec, field) for field in rec.dtype.names))
使用numpy lexsort进行排序
def lexsort(s, p):
indexes = np.lexsort([p, s])
return s[indexes], p[indexes]
def lexsort(*arrays):
indexes = np.lexsort(arrays[::-1])
return tuple((a[indexes] for a in arrays))
对100000个随机整数的两个列表p和q进行排序将产生以下性能
zip_sort
258 ms ± 7.32 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
argsort
9.67 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
recarray_sort
86.4 ms ± 707 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
lexsort
12.4 ms ± 288 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
zip_sort
13.9 s ± 570 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
argsort
49.8 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
recarray_sort
491 ms ± 14.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
lexsort
881 ms ± 15.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
因此,argsort是最快的,但也会产生与其他算法稍有不同的结果。如果不需要辅助排序,则应使用argsort
多阵列情况下的性能比较
接下来,可能需要对多个数组进行排序。修改算法以处理多个数组
使用zip进行两次排序
def zip_sort(s, p):
ordered_s, ordered_p = zip(*sorted(list(zip(s, p))))
return np.array(ordered_s, dtype=s.dtype), np.array(ordered_p, dtype=p.dtype)
def zip_sort(*arrays):
ordered_lists = zip(*sorted(list(zip(*arrays))))
return tuple(
(np.array(l, dtype=arrays[i].dtype) for i, l in enumerate(ordered_lists))
)
使用argsort进行排序。这不会考虑其他数组进行辅助排序
def argsort(s, p):
indexes = s.argsort()
return s[indexes], p[indexes]
def argsort(*arrays):
indexes = arrays[0].argsort()
return tuple((a[indexes] for a in arrays))
使用numpy重新排列进行排序
def recarray_sort(s, p):
rec = np.rec.fromarrays([s, p])
rec.sort()
return rec.f0, rec.f1
def recarray_sort(*arrays):
rec = np.rec.fromarrays(arrays)
rec.sort()
return tuple((getattr(rec, field) for field in rec.dtype.names))
使用numpy lexsort进行排序
def lexsort(s, p):
indexes = np.lexsort([p, s])
return s[indexes], p[indexes]
def lexsort(*arrays):
indexes = np.lexsort(arrays[::-1])
return tuple((a[indexes] for a in arrays))
对100个数组的列表进行排序,每100000个随机整数(arrays=[np.random.randint(10,size=100000)表示范围内的(100)]
)现在产生以下性能
zip_sort
258 ms ± 7.32 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
argsort
9.67 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
recarray_sort
86.4 ms ± 707 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
lexsort
12.4 ms ± 288 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
zip_sort
13.9 s ± 570 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
argsort
49.8 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
recarray_sort
491 ms ± 14.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
lexsort
881 ms ± 15.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
argsort保持最快的速度,这似乎是因为忽略了辅助排序。对于其他具有辅助列排序的算法,基于重新排列的解决方案现在优于lexsort变体
免责声明:结果可能因其他数据类型而异,也取决于数组数据的随机性。我使用42作为种子。这不使用
b
进行“辅助排序”,例如当a
有重复的元素时。有关详细信息,请参阅我的答案。otoh,辅助排序并不总是需要的。@YGA,您的输入数组“a”是否会有非唯一值?如果是这样的话,您希望这类人在这种情况下表现如何?任意命令?稳定排序?使用数组“b”中的相应值进行二次排序?这不是最好的例子,因为解决方案(一起排序a
和b
与单独排序a
和b
是一样的。Steve-好的观点。我现在更新了下面的问题和所有答案,这样两个数组就不会有相同的独立排序顺序。谢谢!我真希望我能接受两个答案。这个不那么简单,但更一般。不过,我已经将其向上投票,至少我可以这样做:-)@YGA,您的编辑是否只是为了避免由于两个列表中都有一个“2”和/或显示f0是排序键而可能产生混淆,因此f1不一定会被排序?如果不是,我看不出原因。如果是,谢谢:很好的触摸。:-)这是因为上面有一条评论指出,两个数组具有相同的排序顺序可能会引起混淆;它独立地对两个数组进行排序。谢谢,看起来我最初假设np.sort
的工作方式类似于list.sort
,但在测试中没有捕捉到它,因为示例数组应该以相同的方式单独或按字典顺序进行排序。我现在给出了一个更好的答案(结果是更简单的版本)。