Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python NumPy—按频率快速稳定的大型阵列arg排序_Python_Arrays_Numpy_Sorting_Frequency - Fatal编程技术网

Python NumPy—按频率快速稳定的大型阵列arg排序

Python NumPy—按频率快速稳定的大型阵列arg排序,python,arrays,numpy,sorting,frequency,Python,Arrays,Numpy,Sorting,Frequency,我有一个大的1D数组a,它包含任何可比较的dtype,它的一些元素可能会重复 如何找到排序索引ix,它将按降序/升序的值频率对a进行稳定排序(稳定) 我想找到最快最简单的方法。也许现有的标准numpy函数可以做到这一点 还有一个相关的,但它特别要求删除重复的数组,即只输出唯一的排序值,我需要原始数组的所有值,包括重复的值 我已经编写了我的第一次尝试来完成这项任务,但它不是最快的(使用Python的循环),也可能不是最短/最简单的形式。如果相等元素的重复次数不高且数组很大,那么python循环可能

我有一个大的1D数组
a
,它包含任何可比较的
dtype
,它的一些元素可能会重复

如何找到排序索引
ix
,它将按降序/升序的值频率对
a
进行稳定排序(稳定)

我想找到最快最简单的方法。也许现有的标准numpy函数可以做到这一点

还有一个相关的,但它特别要求删除重复的数组,即只输出唯一的排序值,我需要原始数组的所有值,包括重复的值

我已经编写了我的第一次尝试来完成这项任务,但它不是最快的(使用Python的循环),也可能不是最短/最简单的形式。如果相等元素的重复次数不高且数组很大,那么python循环可能非常昂贵。如果在NumPy中有短函数来完成这一切也很好(例如,virtual
np.argsort\u by_freq()

产出:

rows: i_col(0) / original_a(1) / freqs(2) / sorted_a(3)
    / sorted_freqs(4) / sorting_ix(5)
[[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
 [ 1  1  1  1  3  0  5  0  3  1  1  0  0  4  6  1  3  5  5  0  0  0  5  0]
 [ 7  7  7  7  3  8  4  8  3  7  7  8  8  1  1  7  3  4  4  8  8  8  4  8]
 [ 0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  5  5  5  5  3  3  3  4  6]
 [ 8  8  8  8  8  8  8  8  7  7  7  7  7  7  7  4  4  4  4  3  3  3  1  1]
 [ 5  7 11 12 19 20 21 23  0  1  2  3  9 10 15  6 17 18 22  4  8 16 13 14]]
equal_order_by_val: False
       i_col  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
  original_a  g  g  c  f  d  d  g  a  a  a  f  f  f  g  f  c  f  a  e  b  g  d  c  b  f
       freqs  5  5  3  7  3  3  5  4  4  4  7  7  7  5  7  3  7  4  1  2  5  3  3  2  7
    sorted_a  f  f  f  f  f  f  f  g  g  g  g  g  a  a  a  a  c  d  d  c  d  c  b  b  e
sorted_freqs  7  7  7  7  7  7  7  5  5  5  5  5  4  4  4  4  3  3  3  3  3  3  2  2  1
  sorting_ix  3 10 11 12 14 16 24  0  1  6 13 20  7  8  9 17  2  4  5 15 21 22 19 23 18

equal_order_by_val: True
       i_col  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
  original_a  g  g  c  f  d  d  g  a  a  a  f  f  f  g  f  c  f  a  e  b  g  d  c  b  f
       freqs  5  5  3  7  3  3  5  4  4  4  7  7  7  5  7  3  7  4  1  2  5  3  3  2  7
    sorted_a  f  f  f  f  f  f  f  g  g  g  g  g  a  a  a  a  c  c  c  d  d  d  b  b  e
sorted_freqs  7  7  7  7  7  7  7  5  5  5  5  5  4  4  4  4  3  3  3  3  3  3  2  2  1
  sorting_ix  3 10 11 12 14 16 24  0  1  6 13 20  7  8  9 17  2 15 22  4  5 21 19 23 18

我可能遗漏了什么,但似乎使用
计数器
可以根据元素值的计数对每个元素的索引进行排序,然后使用元素值和索引来打破关系。例如:

from collections import Counter

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

t = [(counts[v], v, i) for i, v in enumerate(a)]
t.sort()
print([v[2] for v in t])
t.sort(reverse=True)
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
如果您希望使用计数相等的组来保持索引的升序,则可以使用lambda函数进行降序排序:

t.sort(key = lambda x:(-x[0],-x[1],x[2]))
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
如果要在元素计数相同的情况下保持元素在数组中最初出现的顺序,则不按值排序,而是按元素在数组中第一次出现的索引排序:

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

idxs = {}
t = []
for i, v in enumerate(a):
    if not v in idxs:
        idxs[v] = i
    t.append((counts[v], idxs[v], i))

t.sort()
print([v[2] for v in t])
t.sort(key = lambda x:(-x[0],x[1],x[2]))
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
要根据计数排序,然后在数组中定位,根本不需要值或第一个索引:

from collections import Counter

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

t = [(counts[v], i) for i, v in enumerate(a)]
t.sort()
print([v[1] for v in t])
t.sort(key = lambda x:(-x[0],x[1]))
print([v[1] for v in t])
这将为您的字符串数组生成与前面的示例数据代码相同的输出:

a = ['g',  'g',  'c',  'f',  'd',  'd',  'g',  'a',  'a',  'a',  'f',  'f',  'f',
     'g',  'f',  'c',  'f',  'a',  'e',  'b',  'g',  'd',  'c',  'b',  'f' ]
这将产生以下输出:

[18, 19, 23, 2, 4, 5, 15, 21, 22, 7, 8, 9, 17, 0, 1, 6, 13, 20, 3, 10, 11, 12, 14, 16, 24]
[3, 10, 11, 12, 14, 16, 24, 0, 1, 6, 13, 20, 7, 8, 9, 17, 2, 4, 5, 15, 21, 22, 19, 23, 18]

我可能遗漏了什么,但似乎使用
计数器
可以根据元素值的计数对每个元素的索引进行排序,然后使用元素值和索引来打破关系。例如:

from collections import Counter

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

t = [(counts[v], v, i) for i, v in enumerate(a)]
t.sort()
print([v[2] for v in t])
t.sort(reverse=True)
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
如果您希望使用计数相等的组来保持索引的升序,则可以使用lambda函数进行降序排序:

t.sort(key = lambda x:(-x[0],-x[1],x[2]))
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
如果要在元素计数相同的情况下保持元素在数组中最初出现的顺序,则不按值排序,而是按元素在数组中第一次出现的索引排序:

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

idxs = {}
t = []
for i, v in enumerate(a):
    if not v in idxs:
        idxs[v] = i
    t.append((counts[v], idxs[v], i))

t.sort()
print([v[2] for v in t])
t.sort(key = lambda x:(-x[0],x[1],x[2]))
print([v[2] for v in t])
输出:

[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[23, 21, 20, 19, 12, 11, 7, 5, 15, 10, 9, 3, 2, 1, 0, 22, 18, 17, 6, 16, 8, 4, 14, 13]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 14, 13]
[13, 14, 4, 8, 16, 6, 17, 18, 22, 0, 1, 2, 3, 9, 10, 15, 5, 7, 11, 12, 19, 20, 21, 23]
[5, 7, 11, 12, 19, 20, 21, 23, 0, 1, 2, 3, 9, 10, 15, 6, 17, 18, 22, 4, 8, 16, 13, 14]
要根据计数排序,然后在数组中定位,根本不需要值或第一个索引:

from collections import Counter

a = [ 1,  1,  1,  1,  3,  0,  5,  0,  3,  1,  1,  0,  0,  4,  6,  1,  3,  5,  5,  0,  0,  0,  5,  0]
counts = Counter(a)

t = [(counts[v], i) for i, v in enumerate(a)]
t.sort()
print([v[1] for v in t])
t.sort(key = lambda x:(-x[0],x[1]))
print([v[1] for v in t])
这将为您的字符串数组生成与前面的示例数据代码相同的输出:

a = ['g',  'g',  'c',  'f',  'd',  'd',  'g',  'a',  'a',  'a',  'f',  'f',  'f',
     'g',  'f',  'c',  'f',  'a',  'e',  'b',  'g',  'd',  'c',  'b',  'f' ]
这将产生以下输出:

[18, 19, 23, 2, 4, 5, 15, 21, 22, 7, 8, 9, 17, 0, 1, 6, 13, 20, 3, 10, 11, 12, 14, 16, 24]
[3, 10, 11, 12, 14, 16, 24, 0, 1, 6, 13, 20, 7, 8, 9, 17, 2, 4, 5, 15, 21, 22, 19, 23, 18]

我只是想,对于任何数据类型,我自己可能都可以使用非常快速的解决方案,只使用numpy函数,而不使用python循环,它可以在
O(N log N)
时间内工作。使用的numpy函数:
np.unique
np.argsort
和数组索引

虽然在最初的问题中没有被问到,但我实现了额外的标志
equal_order_by_val
,如果它为False,则具有相同频率的数组元素被排序为相等的稳定范围,这意味着可能会有
cdc
输出,就像下面的输出转储一样,因为这是元素在原始数组中以相同频率移动的顺序。当flag为True时,这些元素将按原始数组的值进行额外排序,从而产生
cdd
。换句话说,如果为False,我们只按键
freq
进行稳定排序,如果为True,我们按
(freq,value)
进行升序排序,按
(-freq,value)
进行降序排序


我只是想,对于任何数据类型,我自己可能都可以使用非常快速的解决方案,只使用numpy函数,而不使用python循环,它可以在
O(N log N)
时间内工作。使用的numpy函数:
np.unique
np.argsort
和数组索引

虽然在最初的问题中没有被问到,但我实现了额外的标志
equal_order_by_val
,如果它为False,则具有相同频率的数组元素被排序为相等的稳定范围,这意味着可能会有
cdc
输出,就像下面的输出转储一样,因为这是元素在原始数组中以相同频率移动的顺序。当flag为True时,这些元素将按原始数组的值进行额外排序,从而产生
cdd
。换句话说,如果为False,我们只按键
freq
进行稳定排序,如果为True,我们按
(freq,value)
进行升序排序,按
(-freq,value)
进行降序排序


你当前的解决方案有什么问题?@Nick我在上面写下了原因:1)它不是最快的(使用纯python循环)2)可能不是最短的3)重要的是降序不稳定(),但是稳定性问题我刚刚在一分钟前解决了。那么你对这个问题的预期输出是什么?Nick在最后一个输出示例中,我需要在第3行的末尾
46
而不是
64
,因为它们的频率相同,并且在原始数组中的顺序是
46
。但稳定性不是问题,我已经解决了它,就像上面的评论一样,只是对否定值进行argsort排序。我会修正我的问题,不让人们担心稳定性。重要的是我想要最快的解决方案(没有Python循环)和尽可能最短的解决方案。@Nick如果数组很大,并且其中重复的元素很少,那么这个Python循环将花费很长时间。您当前的解决方案有什么问题吗?@Nick我在上面写了原因:1)它不是最快的(使用纯Python循环)2)对于降序,可能不是最短的3)和重要的不稳定(),但稳定性我一分钟前刚刚解决了。那么你的答案是什么