首页 > 解决方案 > 将生成器中的数据加载到已分配的 numpy 数组中

问题描述

我有一个大数组

data = np.empty((n, k))

两者nk很大。我也有很多生成器g,每个都有k元素,我想将每个生成器加载到data. 我可以:

data[i] = list(g)

或类似的东西,但这会复制g. 我可以使用 for 循环加载:

for j, x in enumerate(g):
    data[i, j] = x

但我想知道 numpy 是否已经有办法做到这一点,而无需在 Python 中复制或循环。

我知道这有提前g的长度,并且很乐意在必要时进行一些子类修补。在创建新数组时会接受类似的东西,但由于我的上下文的限制,如果可能的话,我宁愿加载到这个已经存在的数组中。k__len__np.fromiter

标签: pythonnumpy

解决方案


正如评论中所述,您无能为力。

尽管您可以考虑以下两种解决方案:

使用numpy.fromiter

不是data = np.empty((n, k))自己创建,而是使用numpy.fromitercount参数,它是专门从这种情况下制作的,您事先知道项目的数量。这样 numpy 就不必“猜测”大小并重新分配,直到猜测足够大。使用fromiter允许for在 C 而不是 python 中运行循环。这可能会快一点,但真正的瓶颈可能在你的生成器中。

请注意,fromiter仅处理平面数组,因此您需要阅读所有展平的内容(例如使用chain.from_iterable),然后才调用reshape

from itertools import chain

n = 20
k = 4
generators = (
   (i*j for j in range(k))
   for i in range(n)
)

flat_gen = chain.from_iterable(generators)
data = numpy.fromiter(flat_gen, 'int64', count=n*k)
data = data.reshape((n, k))
"""
array([[ 0,  0,  0,  0],
       [ 0,  1,  2,  3],
       [ 0,  2,  4,  6],
       [ 0,  3,  6,  9],
       [ 0,  4,  8, 12],
       [ 0,  5, 10, 15],
       [ 0,  6, 12, 18],
       [ 0,  7, 14, 21],
       [ 0,  8, 16, 24],
       [ 0,  9, 18, 27],
       [ 0, 10, 20, 30],
       [ 0, 11, 22, 33],
       [ 0, 12, 24, 36],
       [ 0, 13, 26, 39],
       [ 0, 14, 28, 42],
       [ 0, 15, 30, 45],
       [ 0, 16, 32, 48],
       [ 0, 17, 34, 51],
       [ 0, 18, 36, 54],
       [ 0, 19, 38, 57]])
"""

使用 cython

如果您可以重复使用data并希望避免重新分配内存,则不能再使用 numpyfromiter了。恕我直言,避免 pythonfor循环的唯一方法是在 cython 中实现它。同样,这很可能是矫枉过正,因为您仍然必须阅读 python 中的生成器。

作为参考,C 实现fromiter看起来像这样:https ://github.com/numpy/numpy/blob/v1.18.3/numpy/core/src/multiarray/ctors.c#L4001-L4118


推荐阅读