Python 如何将二进制值数组的行排序为长二进制数?
有一个大约500000行的2D numpy数组,每行512个值:Python 如何将二进制值数组的行排序为长二进制数?,python,arrays,sorting,numpy,Python,Arrays,Sorting,Numpy,有一个大约500000行的2D numpy数组,每行512个值: [ [1,0,1,...,0,0,1], # 512 1's or 0's [0,1,0,...,0,1,1], ... [0,0,1,...,1,0,1], # row number 500000 ] 如何对行进行升序排序,就像每行是一个长512位整数一样 [ [0,0,1,...,1,0,1], [0,1,0,...,0,1,1], [1,0,1,...,0,0,1], ... ] 您可以按
[
[1,0,1,...,0,0,1], # 512 1's or 0's
[0,1,0,...,0,1,1],
...
[0,0,1,...,1,0,1], # row number 500000
]
如何对行进行升序排序,就像每行是一个长512位整数一样
[
[0,0,1,...,1,0,1],
[0,1,0,...,0,1,1],
[1,0,1,...,0,0,1],
...
]
您可以按512次的方式对它们进行排序,首先从最右边的位开始
一个较小的示例:假设您希望按位对这三个2位数字进行排序:
11
01
00
在第一步中,按正确的位进行排序,结果是:
00
11
01
现在按第一位排序,在本例中,该列中有两个0
。如果您的排序算法不稳定,则允许在结果中以任何顺序放置这些相等的项,这可能会导致01
出现在00
之前,这是我们不希望看到的,因此我们对第一列使用稳定排序,保持相等项的相对顺序,从而产生所需的:
00
01
11
创建每个行的字符串,然后应用np.sort()
因此,如果我们有一个数组要测试:
a = np.array([[1,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,1,1]])
我们可以使用以下方法创建每个行的字符串
:
这将使现在成为一个:
array(['1010', '0010', '0011', '0011'], dtype='<U4')
使a
:
array(['0010', '0011', '0011', '1010'], dtype='<U4')
array([[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 0, 1, 1],
[1, 0, 1, 0]])
这使得成为一个
:
array(['0010', '0011', '0011', '1010'], dtype='<U4')
array([[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 0, 1, 1],
[1, 0, 1, 0]])
如果你想把这些都塞进一行:
a = np.array([[int(i) for i in r] for r in np.sort(np.apply_along_axis(lambda r: ''.join([str(c) for c in r]), 0, a))])
除了转换为字符串,您还可以使用数据的void
视图(从@Jaime开始)和argsort
def sort_bin(b):
b_view = np.ascontiguousarray(b).view(np.dtype((np.void, b.dtype.itemsize * b.shape[1])))
return b[np.argsort(b_view.ravel())] #as per Divakar's suggestion
测试
np.random.seed(0)
b = np.random.randint(0, 2, (10,5))
print(b)
print(sort_bin(b))
[[0 1 1 0 1]
[1 1 1 1 1]
[1 0 0 1 0]
...,
[1 0 1 1 0]
[0 1 0 1 1]
[1 1 1 0 1]]
[[0 0 0 0 1]
[0 1 0 1 1]
[0 1 1 0 0]
...,
[1 1 1 0 1]
[1 1 1 1 0]
[1 1 1 1 1]]
由于b_视图
只是b
t = np.random.randint(0,2,(2000,512))
%timeit sort_bin(t)
100 loops, best of 3: 3.09 ms per loop
%timeit np.array([[int(i) for i in r] for r in np.sort(np.apply_along_axis(lambda r: ''.join([str(c) for c in r]), 0, t))])
1 loop, best of 3: 3.29 s per loop
实际上大约快1000倍这很慢,但可以完成任务
def sort_col(arr, col_num=0):
# if we have sorted over all columns return array
if col_num >= arr.shape[1]:
return arr
# sort array over given column
arr_sorted = arr[arr[:, col_num].argsort()]
# if the number of 1s in the given column is not equal to the total number
# of rows neither equal to 0, split on 1 and 0, sort and then merge
if len(arr) > np.sum(arr_sorted[:, col_num]) > 0:
arr_sorted0s = sort_col(arr_sorted[arr_sorted[:, col_num]==0], col_num+1)
arr_sorted1s = sort_col(arr_sorted[arr_sorted[:, col_num]==1], col_num+1)
# change order of stacking if you want ascenting order
return np.vstack((arr_sorted0s, arr_sorted1s))
# if the number of 1s in the given column is equal to the total number
# of rows or equal to 0, just go to the next iteration
return sort_col(arr_sorted, col_num + 1)
np.random.seed(0)
a = np.random.randint(0, 2, (5, 4))
print(a)
print(sort_col(a))
# prints
[[0 1 1 0]
[1 1 1 1]
[1 1 1 0]
[0 1 0 0]
[0 0 0 1]]
[[0 0 0 1]
[0 1 0 0]
[0 1 1 0]
[1 1 1 0]
[1 1 1 1]]
编辑。或者最好使用Daniels解决方案。在我发布代码之前,我没有检查新答案。我不确定,但函数“sorted”可以对字符数组中的字符串进行排序,也许它可以处理整数数组?@Orions坏主意。不要在涉及numpy数组的地方使用python函数。谢谢你的建议。将单个位转换为字符字节似乎是“错误的”。也许每一行都是64个uint8,然后使用你的方法?你可以使用np.lexsort
来实现这一点,但我不确定它会比字符串/空格转换更快。聪明的一个!b[np.argsort(b_view.ravel())]
有一些明显的改进。谢谢Divakar,我忘记了。flatte()
制作了一个副本。
def sort_col(arr, col_num=0):
# if we have sorted over all columns return array
if col_num >= arr.shape[1]:
return arr
# sort array over given column
arr_sorted = arr[arr[:, col_num].argsort()]
# if the number of 1s in the given column is not equal to the total number
# of rows neither equal to 0, split on 1 and 0, sort and then merge
if len(arr) > np.sum(arr_sorted[:, col_num]) > 0:
arr_sorted0s = sort_col(arr_sorted[arr_sorted[:, col_num]==0], col_num+1)
arr_sorted1s = sort_col(arr_sorted[arr_sorted[:, col_num]==1], col_num+1)
# change order of stacking if you want ascenting order
return np.vstack((arr_sorted0s, arr_sorted1s))
# if the number of 1s in the given column is equal to the total number
# of rows or equal to 0, just go to the next iteration
return sort_col(arr_sorted, col_num + 1)
np.random.seed(0)
a = np.random.randint(0, 2, (5, 4))
print(a)
print(sort_col(a))
# prints
[[0 1 1 0]
[1 1 1 1]
[1 1 1 0]
[0 1 0 0]
[0 0 0 1]]
[[0 0 0 1]
[0 1 0 0]
[0 1 1 0]
[1 1 1 0]
[1 1 1 1]]