从生成器创建两个连接数组

时间:2016-03-22 13:26:12

标签: python arrays numpy concatenation map-function

考虑Python 2.7中的以下示例。我们有一个任意函数f(),它返回两个1维numpy数组。请注意,通常f()可能会返回不同大小的数组,并且大小可能取决于输入。

现在我们想在map上调用f()并将结果连接到两个单独的新数组中。

import numpy as np

def f(x):
    return np.arange(x),np.ones(x,dtype=int)   

inputs = np.arange(1,10)
result = map(f,inputs)
x = np.concatenate([i[0] for i in result]) 
y = np.concatenate([i[1] for i in result]) 

这给出了预期的结果。但是,由于结果可能占用大量内存,因此最好通过调用imap而不是map来使用生成器。

from itertools import imap
result = imap(f,inputs)
x = np.concatenate([i[0] for i in result]) 
y = np.concatenate([i[1] for i in result]) 

但是,这会产生错误,因为生成器在我们计算y的位置是空的。

有没有办法只使用一次生成器并仍然创建这两个连接数组?我正在寻找没有for循环的解决方案,因为重复连接/追加数组效率相当低。

提前致谢。

1 个答案:

答案 0 :(得分:3)

  

有没有办法只使用一次生成器并仍然创建这两个连接数组?

是的,可以使用tee克隆生成器:

import itertools
a, b = itertools.tee(result)

x = np.concatenate([i[0] for i in a]) 
y = np.concatenate([i[1] for i in b]) 

但是,使用tee对您的内存使用情况没有帮助。上述解决方案需要5 N内存才能运行:

  • N用于在tee
  • 中缓存生成器
  • 2 N代表np.concatenate次调用中的列表推导,
  • 2 N用于连接数组。

显然,我们可以通过放弃tee

来做得更好
x_acc = []
y_acc = []
for x_i, y_i in result:
    x_acc.append(x_i)
    y_acc.append(y_i)

x = np.concatenate(x_acc)
y = np.concatenate(y_acc)

这减少了一个N,留下4 N.进一步意味着删除中间列表并预先分配xy。请注意,您不需要知道数组的精确大小,只需要知道上限:

x = np.empty(capacity)
y = np.empty(capacity)
right = 0
for x_i, y_i in result:
    left = right
    right += len(x_i)  # == len(y_i)  
    x[left:right] = x_i
    y[left:right] = y_i

x = x[:right].copy()
y = y[:right].copy()

事实上,你甚至不需要上限。只需确保xy足够大,以容纳新项目:

for x_i, y_i in result:
    # ...
    if right >= len(x):
        # It would be slightly trickier for >1D, but the idea
        # remains the same: alter the 0-the dimension to fit 
        # the new item.
        new_capacity = max(right, len(x)) * 1.5
        x = x.resize(new_capacity)
        y = y.resize(new_capacity)