繁体   English   中英

如何从pickle文件一次加载一行?

[英]How to load one line at a time from a pickle file?

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

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

如何从pickle文件中只读取一些随机选择的(无替换)行?

您可以逐步将pickle写入文件,这样您也可以逐步加载它们。

以下面的例子为例。 在这里,我们迭代列表中的项目,并依次挑选每个项目。

>>> 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.'

由于您不知道泡菜的内部工作原理,您需要使用另一种存储方法。 下面的脚本使用tobytes()函数将数据按行保存在原始文件中。

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

然而,最大的免责声明是阵列的大小没有保存(这也可以添加,但需要更多的复杂性),并且这种方法可能不像腌制阵列那样便携。

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

评论性能:阅读评论后,我做了一个简短的基准测试。 在我的机器上(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()

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM