【问题标题】:How to load one line at a time from a pickle file?如何从泡菜文件一次加载一行?
【发布时间】:2016-06-21 20:49:10
【问题描述】:

我有一个大型数据集:20,000 x 40,000 作为 numpy 数组。我已将其保存为泡菜文件。

我不想将这个庞大的数据集读入内存,而是一次只读取几行(比如 100 行),以用作小批量。

如何从 pickle 文件中仅读取几行随机选择(不替换)的行?

【问题讨论】:

  • 以允许随机或增量访问的其他格式存储它。
  • 你有什么推荐的?我可以将它从泡菜转换为另一种格式而无需打开它吗?
  • 你必须加载它并以另一种格式再次转储
  • 如果它是一个数字数组,您可以将其存储为二进制文件并使用file.seek() 访问文件中的任何一行。 struct 模块可用于写入和读取文件。

标签: python numpy pickle


【解决方案1】:

您可以将泡菜增量写入文件,这样您就可以加载它们 也是递增的。

举个例子。在这里,我们遍历列表的项目,并且 依次腌制。

>>> import cPickle
>>> myData = [1, 2, 3]
>>> f = open('mydata.pkl', 'wb')
>>> pickler = cPickle.Pickler(f)
>>> for e in myData:
...     pickler.dump(e)
<cPickle.Pickler object at 0x7f3849818f68>
<cPickle.Pickler object at 0x7f3849818f68>
<cPickle.Pickler object at 0x7f3849818f68>
>>> f.close()

现在我们可以反向执行相同的过程并根据需要加载每个对象。为了 示例的目的,假设我们只想要第一项而不想要 想要遍历整个文件。

>>> f = open('mydata.pkl', 'rb')
>>> unpickler = cPickle.Unpickler(f)
>>> unpickler.load()
1

此时,文件流只前进到第一个 目的。未加载剩余的对象,这正是您的行为 想。为了证明,您可以尝试阅读文件的其余部分并查看其余部分 还坐在那里。

>>> f.read()
'I2\n.I3\n.'

【讨论】:

  • 这是一个优雅的解决方案。感谢您指出这一点!
【解决方案2】:

由于你不知道pickle的内部运作,你需要使用另一种存储方式。下面的脚本使用tobytes() 函数将数据逐行保存在原始文件中。

由于每行的长度是已知的,它在文件中的偏移量可以通过seek() 和read() 计算和访问。之后,使用frombuffer() 函数将其转换回数组。

然而,最大的免责声明是未保存数组的大小(这也可以添加,但需要更多复杂性),并且此方法可能不像腌制数组那样可移植。

正如@PadraicCunningham 在他的comment 中指出的那样,memmap 可能是另一种优雅的解决方案。

性能评论:阅读完 cmets 后,我做了一个简短的基准测试。在我的机器(16GB RAM,加密 SSD)上,我能够在 24 秒内进行 40000 次随机行读取(当然,使用 20000x40000 矩阵,而不是示例中的 10x10)。

from __future__ import print_function
import numpy
import random

def dumparray(a, path):
    lines, _ = a.shape
    with open(path, 'wb') as fd:
        for i in range(lines):
            fd.write(a[i,...].tobytes())

class RandomLineAccess(object):
    def __init__(self, path, cols, dtype):
        self.dtype = dtype
        self.fd = open(path, 'rb')
        self.line_length = cols*dtype.itemsize

    def read_line(self, line):
        offset = line*self.line_length
        self.fd.seek(offset)
        data = self.fd.read(self.line_length)

        return numpy.frombuffer(data, self.dtype)

    def close(self):
        self.fd.close()


def main():
    lines = 10
    cols = 10
    path = '/tmp/array'

    a = numpy.zeros((lines, cols))
    dtype = a.dtype

    for i in range(lines):
        # add some data to distinguish lines
        numpy.ndarray.fill(a[i,...], i)

    dumparray(a, path)
    rla = RandomLineAccess(path, cols, dtype)

    line_indices = list(range(lines))
    for _ in range(20):
        line_index = random.choice(line_indices)
        print(line_index, rla.read_line(line_index))

if __name__ == '__main__':
    main()

【讨论】:

    【解决方案3】:

    谢谢大家。我最终找到了一种解决方法(一台具有更多 RAM 的机器,因此我实际上可以将数据集加载到内存中)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 2021-02-12
      • 1970-01-01
      相关资源
      最近更新 更多