按多个轴对二维 numpy 数组进行排序
- 2025-03-17 09:10:00
- admin 原创
- 56
问题描述:
我有一个形状为 (N,2) 的 2D numpy 数组,其中包含 N 个点(x 和 y 坐标)。例如:
array([[3, 2],
[6, 2],
[3, 6],
[3, 4],
[5, 3]])
我想对它进行排序,使我的点按 x 坐标排序,然后当 x 坐标相同时按 y 坐标排序。因此上面的数组应如下所示:
array([[3, 2],
[3, 4],
[3, 6],
[5, 3],
[6, 2]])
如果这是一个普通的 Python 列表,我会简单地定义一个比较器来执行我想要的操作,但据我所知,numpy 的排序函数不接受用户定义的比较器。有什么想法吗?
编辑:感谢您的想法!我设置了一个包含 1000000 个随机整数点的快速测试用例,并对我可以运行的整数点进行了基准测试(抱歉,目前无法升级 numpy)。
Mine: 4.078 secs
mtrw: 7.046 secs
unutbu: 0.453 secs
解决方案 1:
使用lexsort:
import numpy as np
a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)])
ind = np.lexsort((a[:,1],a[:,0]))
a[ind]
# array([[3, 2],
# [3, 4],
# [3, 6],
# [5, 3],
# [6, 2]])
a.ravel()
`a如果是,则返回一个视图
C_CONTIGUOUS`。如果这是真的,
@ars 的方法(通过使用ravel
而不是稍作修改)flatten
会产生一种很好的a
就地排序方法:
a = np.array([(3, 2), (6, 2), (3, 6), (3, 4), (5, 3)])
dt = [('col1', a.dtype),('col2', a.dtype)]
assert a.flags['C_CONTIGUOUS']
b = a.ravel().view(dt)
b.sort(order=['col1','col2'])
由于b
是的视图a
,因此排序也同样b
排序:a
print(a)
# [[3 2]
# [3 4]
# [3 6]
# [5 3]
# [6 2]]
解决方案 2:
标题写着“对二维数组进行排序”。虽然提问者使用的是(N,2)
形数组,但可以将 unutbu 的解决方案推广到任何(N,M)
数组,因为这可能是人们真正想要的。
可以transpose
使用带有负数的切片符号step
将所有列按lexsort
相反的顺序传递:
>>> import numpy as np
>>> a = np.random.randint(1, 6, (10, 3))
>>> a
array([[4, 2, 3],
[4, 2, 5],
[3, 5, 5],
[1, 5, 5],
[3, 2, 1],
[5, 2, 2],
[3, 2, 3],
[4, 3, 4],
[3, 4, 1],
[5, 3, 4]])
>>> a[np.lexsort(np.transpose(a)[::-1])]
array([[1, 5, 5],
[3, 2, 1],
[3, 2, 3],
[3, 4, 1],
[3, 5, 5],
[4, 2, 3],
[4, 2, 5],
[4, 3, 4],
[5, 2, 2],
[5, 3, 4]])
解决方案 3:
numpy_indexed包(免责声明:我是它的作者)可用于以高效的完全矢量化方式解决这些类型的对 nd 数组的处理问题:
import numpy_indexed as npi
npi.sort(a) # by default along axis=0, but configurable
解决方案 4:
您可以使用np.complex_sort
。这会产生将数据更改为浮点数的副作用,我希望这不是问题:
>>> a = np.array([[3, 2], [6, 2], [3, 6], [3, 4], [5, 3]])
>>> atmp = np.sort_complex(a[:,0] + a[:,1]*1j)
>>> b = np.array([[np.real(x), np.imag(x)] for x in atmp])
>>> b
array([[ 3., 2.],
[ 3., 4.],
[ 3., 6.],
[ 5., 3.],
[ 6., 2.]])
解决方案 5:
我也遇到过同样的问题,最后得到了帮助并解决了问题。如果您的数组有列名(结构化数组),它就可以顺利运行,我认为这是一种使用与 excel 相同的逻辑进行排序的非常简单的方法:
array_name[array_name[['colname1','colname2']].argsort()]
请注意排序条件用双括号括起来。当然,您可以使用 2 列以上的列作为排序条件。
解决方案 6:
编辑:删除了错误的答案。
以下是使用中间结构化数组实现此目的的一种方法:
from numpy import array
a = array([[3, 2], [6, 2], [3, 6], [3, 4], [5, 3]])
b = a.flatten()
b.dtype = [('x', '<i4'), ('y', '<i4')]
b.sort()
b.dtype = '<i4'
b.shape = a.shape
print b
给出所需的输出:
[[3 2]
[3 4]
[3 6]
[5 3]
[6 2]]
但不确定这是否是最好的解决方法。
解决方案 7:
我找到了一种方法来做到这一点:
from numpy import array
a = array([(3,2),(6,2),(3,6),(3,4),(5,3)])
array(sorted(sorted(a,key=lambda e:e[1]),key=lambda e:e[0]))
必须进行两次排序(并使用普通的 pythonsorted
函数而不是更快的 numpy 排序)这是非常糟糕的,但它确实可以很好地放在一行上。
扫码咨询,免费领取项目管理大礼包!