【问题标题】:Numpy: Fastest way to insert value into array such that array's in orderNumpy:将值插入数组的最快方法,使数组按顺序排列
【发布时间】:2018-07-24 10:03:28
【问题描述】:

假设我有一个数组my_array 和一个奇异值my_val。 (注意my_array 总是排序的)。

my_array = np.array([1, 2, 3, 4, 5])
my_val = 1.5

因为my_val 是1.5,我想把它放在1 和2 之间,给我数组[1, 1.5, 2, 3, 4, 5]

我的问题是:当my_array 任意增长时,生成有序输出数组的最快方法是什么(即以微秒为单位)?

我最初的想法是将值连接到原始数组然后排序:

arr_out = np.sort(np.concatenate((my_array, np.array([my_val]))))
[ 1.   1.5  2.   3.   4.   5. ]

我知道np.concatenate 很快,但我不确定np.sort 将如何随着my_array 的增长而扩展,即使my_array 始终会被排序。

编辑:

我已经汇总了接受答案时列出的各种方法的时间:

输入:

import timeit

timeit_setup = 'import numpy as np\n' \
               'my_array = np.array([i for i in range(1000)], dtype=np.float64)\n' \
               'my_val = 1.5'
num_trials = 1000

my_time = timeit.timeit(
    'np.sort(np.concatenate((my_array, np.array([my_val]))))',
    setup=timeit_setup, number=num_trials
)

pauls_time = timeit.timeit(
    'idx = my_array.searchsorted(my_val)\n'
    'np.concatenate((my_array[:idx], [my_val], my_array[idx:]))',
    setup=timeit_setup, number=num_trials
)

sanchit_time = timeit.timeit(
    'np.insert(my_array, my_array.searchsorted(my_val), my_val)',
    setup=timeit_setup, number=num_trials
)

print('Times for 1000 repetitions for array of length 1000:')
print("My method took {}s".format(my_time))
print("Paul Panzer's method took {}s".format(pauls_time))
print("Sanchit Anand's method took {}s".format(sanchit_time))

输出:

Times for 1000 repetitions for array of length 1000:
My method took 0.017865657746239747s
Paul Panzer's method took 0.005813951002013821s
Sanchit Anand's method took 0.014003945532323987s

对于长度为 1,000,000 的数组重复 100 次也是如此:

Times for 100 repetitions for array of length 1000000:
My method took 3.1770704101754195s
Paul Panzer's method took 0.3931240139911161s
Sanchit Anand's method took 0.40981490723551417s

【问题讨论】:

  • 您可以简单地运行一个实验来查看每种方法如何随着列表的增大而扩展。你期待别人为你做这件事吗?
  • @YilunZhang 我更想知道还有哪些我没有想到的方法。
  • @YilunZhang 那么您将如何识别正确的索引并执行插入?这显然是 OP 正在寻找的方法,他们已经在这个问题上表现出了努力。
  • 其实查找索引的速度并不重要,因为插入一个numpy数组需要线性时间,任何搜索索引的提升都可以忽略不计,我们最多只能赢50% ,但对于大型数组来说仍然会(非常)慢。
  • insert 创建一个新数组。要么使用连接,要么创建一个空白并复制值。你不能就地增长 ndarray 。它的大小是固定的。

标签: python sorting numpy concatenation


【解决方案1】:

使用np.searchsorted以对数时间查找插入点:

>>> idx = my_array.searchsorted(my_val)
>>> np.concatenate((my_array[:idx], [my_val], my_array[idx:]))
array([1. , 1.5, 2. , 3. , 4. , 5. ])

注意 1:我建议查看 @Willem Van Onselm 和 @hpaulj 的富有洞察力的 cmets。

注意 2:如果所有数据类型从一开始就匹配,使用 @Sanchit Anand 建议的 np.insert 可能会更方便一些。然而,值得一提的是,这种便利是以大量开销为代价的:

>>> def f_pp(my_array, my_val):
...      idx = my_array.searchsorted(my_val)
...      return np.concatenate((my_array[:idx], [my_val], my_array[idx:]))
... 
>>> def f_sa(my_array, my_val):
...      return np.insert(my_array, my_array.searchsorted(my_val), my_val)
...
>>> my_farray = my_array.astype(float)
>>> from timeit import repeat
>>> kwds = dict(globals=globals(), number=100000)
>>> repeat('f_sa(my_farray, my_val)', **kwds)
[1.2453778409981169, 1.2268288589984877, 1.2298014000116382]
>>> repeat('f_pp(my_array, my_val)', **kwds)
[0.2728819379990455, 0.2697303680033656, 0.2688361559994519]

【讨论】:

    【解决方案2】:

    试试

    my_array = np.insert(my_array,my_array.searchsorted(my_val),my_val)
    

    [编辑] 确保数组是 float32 或 float64 类型,或在初始化时将小数点添加到任何列表元素。

    【讨论】:

    • 您是否真的费心查看您的建议的结果?
    • 请注意,这会使所有内容默认为numpy.int32
    • 这有什么问题?它在我的电脑上运行良好。您必须将数组声明为 float64 或 float32 类型(或者在初始化时在某处添加一个小数点)。
    • @SanchitAnand 您必须将您的数组声明为 float64 类型。您不认为在您的回答中值得一提吗?
    猜你喜欢
    • 1970-01-01
    • 2021-11-15
    • 2019-03-19
    • 1970-01-01
    • 2021-11-11
    • 2013-12-18
    相关资源
    最近更新 更多