从生成器创建两个串联数组
Creating two concatenated arrays from a generator
考虑以下 Python 2.7 中的示例。我们有一个任意函数 f()
returns 两个一维 numpy 数组。请注意,通常 f()
可能 returns 不同大小的数组,并且大小可能取决于输入。
现在我们想在 f()
上调用 map
并将结果连接到两个单独的新数组中。
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 循环的解决方案,因为重复 concatenate/append 数组效率很低。
提前致谢。
Is there a way to use the generator only once and still create these two concatenated arrays?
是的,可以使用 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。更进一步意味着删除中间列表并预分配 x
和 y
。请注意,您不需要知道数组的 确切 大小,只需要知道上限:
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()
事实上,您甚至不需要上限。只需确保 x
和 y
足够大以容纳新项目:
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)
考虑以下 Python 2.7 中的示例。我们有一个任意函数 f()
returns 两个一维 numpy 数组。请注意,通常 f()
可能 returns 不同大小的数组,并且大小可能取决于输入。
现在我们想在 f()
上调用 map
并将结果连接到两个单独的新数组中。
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 循环的解决方案,因为重复 concatenate/append 数组效率很低。
提前致谢。
Is there a way to use the generator only once and still create these two concatenated arrays?
是的,可以使用 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。更进一步意味着删除中间列表并预分配 x
和 y
。请注意,您不需要知道数组的 确切 大小,只需要知道上限:
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()
事实上,您甚至不需要上限。只需确保 x
和 y
足够大以容纳新项目:
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)