有效地连接变量但长度已知的一维数组

时间:2018-07-07 20:54:30

标签: python arrays performance numpy concatenation

问题

我有一个generator,它产生了一些可变的但已知大小的小的一维NumPy数组。出于以下问题的目的,可以通过以下代码模仿我的设置:

import numpy as np

n = 1000
sizes = np.random.randint(1,10,n)

def generator():
    for size in sizes:
        yield np.random.random(size)

我反复需要将生成器产生的数组连接成一个大的一维数组,大致如下:

runs = 1000
output = np.empty(sum(sizes))
further_preparations(sizes)
for _ in range(runs):
    my_concatenate(generator(),sizes,output)

我希望尽可能有效地做到这一点(无需使用C API,Cython或类似软件)。

我到目前为止的想法

以下方法是按照timeit可以立即使用的方式编写的,例如:

import timeit
print(timeit.timeit(with_hstack,number=runs))

供参考:仅迭代生成器(for _ in generator(): pass)需2.6 s。另外,将输出转换为列表(list(generator()))需要2.9 s。在下面,我不减去这些时间:

  • np.hstack(5.6秒):

    def with_hstack():
        np.hstack(generator())
    
    • Pro:接受生成器作为输入。
    • 缺点:由于从一开始就不知道最终大小,因此需要经常调整目标数组的大小。
  • np.concatenate(3.1秒):

    def with_concatenate():
        np.concatenate(list(generator()),out=output)
    
    • Pro:不需要分配目标数组。
    • 缺点:生成器的输出需要首先转换为列表,这是在Python中发生的,需要重新分配。
  • 带索引的循环(4.5秒):

    def with_loop():
        i = 0
        for array in generator():
            j = i + array.size
            output[i:j] = array
            i = j
    
    • 专业人士:无需分配扩展的数据结构。
    • 骗局:Python循环。
  • 阵列视图列表(3.7秒):

    steps = np.hstack((0,np.cumsum(sizes)))
    views = [ output[i:j] for i,j in zip(steps,steps[1:]) ]
    def with_views():
        for view,array in zip(views,generator()):
            np.copyto(view,array)
    
    • 专业人士:无需分配扩展的数据结构。
    • 骗局:Python循环。

问题

上述方法的缺点之间没有完全重叠的部分,这表明可能有更好的解决方案。例如,以上数据表明,避免使用concatenate进行列表转换可以节省大约0.3 s。有没有更好的解决方案?

0 个答案:

没有答案