【问题标题】:Lazy Evaluation for iterating through NumPy arrays用于遍历 NumPy 数组的惰性求值
【发布时间】:2011-03-24 12:01:21
【问题描述】:

我有一个 Python 程序,它处理相当大的 NumPy 数组(数百兆字节),这些数组以 pickle 文件的形式存储在磁盘上(每个文件一个 ~100MB 数组)。当我想对数据运行查询时,我通过 pickle 加载整个数组,然后执行查询(这样从 Python 程序的角度来看,整个数组都在内存中,即使操作系统正在将其换出) .我这样做主要是因为我相信能够对 NumPy 数组使用矢量化操作会比在每个项目中使用 for 循环快得多。

我在一个网络服务器上运行它,它有内存限制,我很快就会遇到这种限制。我对数据运行了许多不同类型的查询,因此编写“分块”代码从单独的 pickle 文件加载部分数据,处理它们,然后继续到下一个块可能会增加很多复杂性。让这种“分块”对处理这些大型数组的任何函数透明肯定会更好。

似乎理想的解决方案类似于生成器,它定期从磁盘加载数据块,然后将数组值一个接一个地传递出去。这将大大减少程序所需的内存量,而无需对单个查询函数进行任何额外的工作。有可能做这样的事情吗?

【问题讨论】:

  • 也许是一个有用的参考:刚刚发现这被称为“核外”任务。

标签: python memory-management numpy lazy-evaluation


【解决方案1】:

PyTables 是一个用于管理分层数据集的包。它旨在为您解决这个问题。

【讨论】:

    【解决方案2】:

    NumPy 的内存映射数据结构memmap)在这里可能是一个不错的选择。

    您可以从磁盘上的二进制文件访问 NumPy 数组,而无需一次将整个文件加载到内存中。

    (请注意,我相信,但我不确定,Numpys 的 memmap 对象与 Pythons 不同——特别是,NumPys 类似于数组,Python 类似于文件。)

    方法签名是:

    A = NP.memmap(filename, dtype, mode, shape, order='C')
    

    所有参数都很简单(即,它们与 NumPy 中其他地方使用的含义相同)除了 'order',它指的是 ndarray 内存布局的顺序。我相信默认值是“C”,而(唯一的)其他选项是“F”,对于 Fortran——与其他地方一样,这两个选项分别代表行优先和列优先。

    这两种方法是:

    flush(将您对阵列所做的任何更改写入磁盘);和

    close(将数据写入 memmap 数组,或者更准确地说,写入到存储在磁盘上的数据的类似数组的内存映射)

    示例使用:

    import numpy as NP
    from tempfile import mkdtemp
    import os.path as PH
    
    my_data = NP.random.randint(10, 100, 10000).reshape(1000, 10)
    my_data = NP.array(my_data, dtype="float")
    
    fname = PH.join(mkdtemp(), 'tempfile.dat')
    
    mm_obj = NP.memmap(fname, dtype="float32", mode="w+", shape=1000, 10)
    
    # now write the data to the memmap array:
    mm_obj[:] = data[:]
    
    # reload the memmap:
    mm_obj = NP.memmap(fname, dtype="float32", mode="r", shape=(1000, 10))
    
    # verify that it's there!:
    print(mm_obj[:20,:])
    

    【讨论】:

    • 如果您不想麻烦安装 PyTables,这真的很方便。
    【解决方案3】:

    似乎理想的解决方案是 就像一个发电机 定期加载一个块 数据从磁盘,然后通过 数组值一一输出。这 将大大减少金额 程序所需的内存 不需要任何额外的工作 个别查询的部分 职能。有没有可能做 像这样?

    是的,但不是通过将磁盘上的数组保存在单个 pickle 中——pickle 协议并不是为“增量反序列化”而设计的。

    可以将多个pickle写入同一个打开的文件,一个接一个(使用dump不是dumps),然后是“懒惰的评估器” for 迭代”只需要每次使用pickle.load即可。

    示例代码(Python 3.1 -- 在 2.any 中你会想要 cPickle 而不是 pickle-1 用于协议等,当然;-):

    >>> import pickle
    >>> lol = [range(i) for i in range(5)]
    >>> fp = open('/tmp/bah.dat', 'wb')
    >>> for subl in lol: pickle.dump(subl, fp)
    ... 
    >>> fp.close()
    >>> fp = open('/tmp/bah.dat', 'rb')
    >>> def lazy(fp):
    ...   while True:
    ...     try: yield pickle.load(fp)
    ...     except EOFError: break
    ... 
    >>> list(lazy(fp))
    [range(0, 0), range(0, 1), range(0, 2), range(0, 3), range(0, 4)]
    >>> fp.close()
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 2012-06-07
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      • 1970-01-01
      • 2012-04-26
      • 2019-09-04
      • 2020-01-29
      相关资源
      最近更新 更多