【问题标题】:In place insertion into list (or array)就地插入列表(或数组)
【发布时间】:2017-11-13 09:22:43
【问题描述】:

我正在 Python 中运行一个脚本,我需要将新数字插入到特定索引位置的数组(或列表)中。问题是,显然当我插入新数字时,索引位置无效。有没有一种聪明的方法可以一次在索引位置插入新值?还是在我添加时增加索引号(对的第一个值)的唯一解决方案?

示例测试代码sn-ps:

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]

【问题讨论】:

  • @Divakar Typo for insertion_indices 我猜。

标签: python arrays numpy indexing list-comprehension


【解决方案1】:

按倒序插入这些值。像这样:

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) 的值并继续“向后”,则无需更新任何索引。

【讨论】:

    【解决方案2】:

    被 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
    

    性能猛增,因为转换为列表时没有运行时开销。

    【讨论】:

    • 这个答案很好!
    • 感谢您提供的两个选项!性能是一个问题,所以定制的功能很棒!
    【解决方案3】:

    在你的 for 循环之前添加这个:

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

    【讨论】:

      【解决方案4】:

      每次插入后将所需索引增加 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]
      

      【讨论】:

        【解决方案5】:

        不太优雅,但也很有效:使用 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
        

        【讨论】:

          猜你喜欢
          • 2022-12-21
          • 2015-07-04
          • 1970-01-01
          • 1970-01-01
          • 2015-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多