就地插入列表(或数组)

时间:2017-11-13 09:22:43

标签: python arrays numpy indexing list-comprehension

我在Python中运行脚本,我需要在某些索引位置将数字插入到数组(或列表)中。问题是,当我插入新数字时,索引位置无效。是否有一种巧妙的方法可以同时在索引位置插入新值?或者是我添加时增加索引号(该对的第一个值)的唯一解决方案?

示例测试代码段:

original_list = [0, 1, 2, 3, 4, 5, 6, 7]
insertion_indices = [1, 4, 5]
new_numbers = [8, 9, 10]
pairs = [(insertion_indices[i], new_numbers[i]) for i in range(len(insertion_indices))]

for pair in pairs:
    original_list.insert(pair[0], pair[1])

结果:

[0, 8, 1, 2, 9, 10, 3, 4, 5, 6, 7]

而我想:

[0, 8, 1, 2, 3, 9, 4, 10, 5, 6, 7]

5 个答案:

答案 0 :(得分:8)

按向后顺序插入这些值。像这样:

original_list = [0, 1, 2, 3, 4, 5, 6, 7]
insertion_indices = [1, 4, 5]
new_numbers = [8, 9, 10]

new = zip(insertion_indices, new_numbers)
new.sort(reverse=True)

for i, x in new:
    original_list.insert(i, x)

其工作原因基于以下观察:

list的开头插入一个值会将所有其他值的索引偏移1.尽管在最后插入一个值,索引保持不变。因此,如果您首先插入具有最大索引(10)的值并继续"向后"你不必更新任何索引。

答案 1 :(得分:7)

由于NumPy被标记,因为输入被称为列表/数组,您可以简单地使用内置numpy.insert -

np.insert(original_list, insertion_indices, new_numbers)

要将理论推广为定制理论(主要是为了表现),我们可以使用掩模,就像这样 -

def insert_numbers(original_list,insertion_indices, new_numbers):
    # Length of output array                               
    n = len(original_list)+len(insertion_indices)

    # Setup mask array to selecrt between new and old numbers
    mask = np.ones(n,dtype=bool)
    mask[insertion_indices+np.arange(len(insertion_indices))] = 0

    # Setup output array for assigning values from old and new lists/arrays
    # by using mask and inverted mask version
    out = np.empty(n,dtype=int)
    out[mask] = original_list
    out[~mask] = new_numbers
    return out

对于列表输出,请附加.tolist()

示例运行 -

In [83]: original_list = [0, 1, 2, 3, 4, 5, 6, 7]
    ...: insertion_indices = [1, 4, 5]
    ...: new_numbers = [8, 9, 10]
    ...: 

In [85]: np.insert(original_list, insertion_indices, new_numbers)
Out[85]: array([ 0,  8,  1,  2,  3,  9,  4, 10,  5,  6,  7])

In [86]: np.insert(original_list, insertion_indices, new_numbers).tolist()
Out[86]: [0, 8, 1, 2, 3, 9, 4, 10, 5, 6, 7]

10000x缩放数据集的运行时测试 -

In [184]: original_list = range(70000)
     ...: insertion_indices = np.sort(np.random.choice(len(original_list), 30000, replace=0)).tolist()
     ...: new_numbers = np.random.randint(0,10, len(insertion_indices)).tolist()
     ...: out1 = np.insert(original_list, insertion_indices, new_numbers)
     ...: out2 = insert_numbers(original_list, insertion_indices, new_numbers)
     ...: print np.allclose(out1, out2)
True

In [185]: %timeit np.insert(original_list, insertion_indices, new_numbers)
100 loops, best of 3: 5.37 ms per loop

In [186]: %timeit insert_numbers(original_list, insertion_indices, new_numbers)
100 loops, best of 3: 4.8 ms per loop

让我们测试数组作为输入 -

In [190]: original_list = np.arange(70000)
     ...: insertion_indices = np.sort(np.random.choice(len(original_list), 30000, replace=0))
     ...: new_numbers = np.random.randint(0,10, len(insertion_indices))
     ...: out1 = np.insert(original_list, insertion_indices, new_numbers)
     ...: out2 = insert_numbers(original_list, insertion_indices, new_numbers)
     ...: print np.allclose(out1, out2)
True

In [191]: %timeit np.insert(original_list, insertion_indices, new_numbers)
1000 loops, best of 3: 1.48 ms per loop

In [192]: %timeit insert_numbers(original_list, insertion_indices, new_numbers)
1000 loops, best of 3: 1.07 ms per loop

性能刚好开始,因为转换到列表时没有运行时开销。

答案 2 :(得分:1)

每次插入后将所需索引增加1

original_list = [0, 1, 2, 3, 4, 5, 6, 7]
insertion_indices = [1, 4, 5]
new_numbers = [8, 9, 10]

for i in range(len(insertion_indices)):
    original_list.insert(insertion_indices[i]+i,new_numbers[i])

print(original_list)

输出

[0, 8, 1, 2, 3, 9, 4, 10, 5, 6, 7]

#Required list
[0, 8, 1, 2, 3, 9, 4, 10, 5, 6, 7]

答案 3 :(得分:1)

在for循环之前添加:

for i in range(len(insertion_indices)):
    insertion_indices[i]+=i

答案 4 :(得分:0)

不太优雅,但也工作:使用numpy ndarray,每次增加指数:

import numpy as np

original_list = [0, 1, 2, 3, 4, 5, 6, 7]
insertion_indices = [1, 4, 5]
new_numbers = [8, 9, 10]
pairs = np.array([[insertion_indices[i], new_numbers[i]] for i in range(len(insertion_indices))])

for pair in pairs:
    original_list.insert(pair[0], pair[1])
    pairs[:, 0] += 1