通过追加数据构建Numpy数组(事先不知道完整大小) [英] Building a Numpy array by appending data (without knowing the full size in advance)

查看:26
本文介绍了通过追加数据构建Numpy数组(事先不知道完整大小)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有许多文件,每个文件都以(n, 1000)形状的矩阵形式读取,其中n可能因文件而异。

我想将它们全部连接到一个大的Numpy数组中。我目前这样做:

dataset = np.zeros((100, 1000))
for f in glob.glob('*.png'):
    x = read_as_numpyarray(f)    # custom function; x is a matrix of shape (n, 1000)
    dataset = np.vstack((dataset, x))

但它的效率很低,因为我通过将现有数组与读取的下一个文件堆叠来多次重新定义dataset

如何使用Numpy更好地执行此操作,避免在内存中多次重写整个数据集?

nb:最终的大块块数组可能需要10 GB。

推荐答案

使用NumPy数组的本机list,然后np.concatenate

本机list将在需要时增加(by ~1.125)大小,因此不会发生太多重新分配,而且它只保存指向分散的(内存中不连续的)np.arrays保存实际数据的指针。

只调用concatenate一次即可解决您的问题。

伪码

dataset = []
for f in glob.glob('*.png'):
    x = read_as_numpyarray(f)    # custom function; x is a matrix of shape (n, 1000)
    dataset.append(x)

dataset_np = np.concatenate(dataset)

通知vstack内部使用concatenate


编辑以解决已编辑的问题:

假设数据总大小为20 GB。串联时, 系统仍必须保留20 GB(对于每个单独的阵列) 还要为新的串联数组分配20 GB,因此需要40 GB GB的RAM(数据集的两倍)。如何在不要求的情况下完成此操作 RAM的翻倍?(例如:如果我们只有32 GB,有没有解决方案 内存?)

我将按照当前答案中提出的一样,分阶段地解决这个问题。dataset_np1 = np.concatenate(half1_of_data)dataset_np2 = np.concatenate(half2_of_data)只需要150%的内存(而不是200%)。这可以递归地扩展,但以速度为代价,直到这成为问题中的命题的极限。我只能假设像dask这样的人能更好地处理这一点,但我自己还没有测试过。

只是为了澄清一下,在拥有DataSet_np1之后,您不再需要所有分片的小数组的列表,并且可以释放该列表。只有到那时,你才开始加载另一半。因此,您只需在内存中额外保存50%的数据。

伪码:


def load_half(buffer: np.array, shard_path: str, shard_ind: int):
    half_dataset = []
    for f in glob.glob(f'{shard_path}/*.png'):
        x = read_as_numpyarray(f)    # custom function; x is a matrix of shape (n, 1000)
        half_dataset.append(x)

    half_dataset_np = np.concatenate(half_dataset) # see comment *
    buffer[:buffer.shape[0] // 2 * (shard_ind + 1), ...] = half_dataset_np

half1_path = r"half1"  # preprocess the shards to be found by glob or otherwise
half2_path = r"half2"
assert os.path.isdir(half1_path)
assert os.path.isdir(half2_path)

buffer = np.zeros(size_shape)
half1_np = load_half(half1_path, buffer, 0) # only 50% of data temporarily loaded, then freed [can be done manually if needed]
half2_np = load_half(half2_path, buffer, 1) # only 50% of data temporarily loaded, then freed

人们可以(很容易或不那么容易)将其推广到四分之一、八分之一或递归任何所需的分数,以牺牲速度来减少内存成本,而无穷大的限制是问题中的原始命题。

  • 重要注释(参见代码中的注释*):
    您可能会注意到half_dataset_np = np.concatenate(half_dataset) 实际分配50%的数据集,另外50%分配 在碎片中,显然没有为我们节省任何东西。这是正确的,我可以 没有找到连接到缓冲区的方法。然而,实现这一点 按照建议的递归方式(并且未在伪代码中显示)将节省 内存,作为四分之一,每次只会使用2%*25%。这只是一个 实现细节,但我希望大意是明确的。

换一种说法,如果数据集是1000 GB,那么另一种方法将声明&qot;怎么办?那么,任何麻木数组都不会起作用。这就是数据库存在的原因,使用tools可以非常高效地查询它们。但同样,这在某种程度上是一个研究问题,在很大程度上取决于您的特定需求。作为非常不知情的预感,我会查看dask

这类库显然会将这类问题作为它们所做工作的子集来处理,我建议您不要自己实现这些事情,因为您将花费的总时间将远远超过选择和学习库的时间。


另一方面,我想知道这是否真的必须是一个如此巨大的数组,maybe a slightly different design or formulation of the problem可以让我们完全摆脱这个技术问题。

这篇关于通过追加数据构建Numpy数组(事先不知道完整大小)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆