如何从单个迭代的元组中填充两个(或更多)numpy数组?

Lev*_*sky 9 python arrays iteration numpy

我遇到的实际问题是我想(float, str)在RAM中存储一个长排序的元组列表.普通列表不适合我的4Gb RAM,所以我想我可以使用两个numpy.ndarray.

数据源是可重复的2元组.numpyfromiter功能,但我怎么用呢?迭代中的项目数是未知的.由于内存限制,我无法首先将其消耗到列表中.我想到了itertools.tee,但这似乎增加了很多内存开销.

我想我能做的就是以块的形式使用迭代器并将它们添加到数组中.然后我的问题是,如何有效地做到这一点?我应该制作2个2D阵列并为它们添加行吗?(然后我需要将它们转换为1D).

或者可能有更好的方法?我真正需要的是通过对数时间内相应数字的值搜索字符串数组(这就是我想按浮点值排序的原因)并尽可能保持紧凑.

PS迭代没有排序.

unu*_*tbu 8

也许使用以下方法构建单个结构化数组np.fromiter:

import numpy as np


def gendata():
    # You, of course, have a different gendata...
    for i in xrange(N):
        yield (np.random.random(), str(i))

N = 100

arr = np.fromiter(gendata(), dtype='<f8,|S20')
Run Code Online (Sandbox Code Playgroud)

按第一列排序,使用第二列进行排序,需要O(N log N)时间:

arr.sort(order=['f0','f1'])
Run Code Online (Sandbox Code Playgroud)

按第一列中的值查找行可以searchsorted在O(log N)时间内完成:

# Some pseudo-random value in arr['f0']
val = arr['f0'][10]
print(arr[10])
# (0.049875262239617246, '46')

idx = arr['f0'].searchsorted(val)
print(arr[idx])
# (0.049875262239617246, '46')
Run Code Online (Sandbox Code Playgroud)

你在评论中提出了许多重要问题; 让我试着在这里回答他们:

  • numpy中解释了基本的dtypes.可能有一个或两个额外的dtypes(float16自编写该书以来已经添加了这些dtypes ,但基本知识都在那里解释.)

    也许在线文档中有更彻底的讨论.这是你在这里提到的例子的一个很好的补充.

  • Dtypes可用于定义具有列名称或默认列名称的结构化数组.'f0','f1'等等是默认列名.由于我将dtype定义为'<f8,|S20'无法提供列名,因此NumPy命名第一列'f0',第二 列命名'f1'.如果我们使用过

    dtype='[('fval','<f8'), ('text','|S20')]
    
    Run Code Online (Sandbox Code Playgroud)

    然后结构化数组arr将具有列名称'fval''text'.

  • 不幸的是,dtype必须在np.fromiter被调用的时候被修复.你可以想象,迭代gendata一次,发现字符串的最大长度,构建你的dtype,然后调用np.fromiter(并再次 迭代gendata),但这是相当繁琐的.如果您事先知道字符串的最大大小,那当然会更好.(|S20将字符串字段定义为具有20个字节的固定长度.)
  • NumPy数组将预定义大小的数据放在固定大小的数组中.将数组(甚至是多维数组)视为一维内存的连续块.(这是一个过于简单化 - 有非连续数组 - 但会帮助你想象以下.)NumPy通过利用固定大小(由设置dtype)快速计算访问所需的偏移来获得大部分速度.数组中的元素.如果字符串具有可变大小,那么NumPy很难找到正确的偏移量.很难,我的意思是NumPy需要索引或以某种方式重新设计.NumPy根本就不是这种方式构建的.
  • NumPy确实有一个objectdtype,它允许你将一个4字节的指针放到你想要的任何Python对象上.这样,您可以使NumPy数组具有任意Python数据.不幸的是,该np.fromiter 函数不允许您创建dtype数组object.我不确定为什么会有这个限制......
  • 请注意,指定np.fromiter时具有更好的性能count.通过知道count(行数)和 dtype(以及每行的大小),NumPy可以为结果数组预先分配足够的内存.如果你没有指定count,那么NumPy会猜测数组的初始大小,如果太小,它会尝试调整数组的大小.如果原始的内存块可以扩展,那么你很幸运.但是如果NumPy必须分配一个全新的内存块,那么所有旧数据都必须被复制到新位置,这将显着降低性能.