如何将包含二进制值字符(0
和1
)的大文本文件(> 16G)转换为numpy数组文件而不会在python中炸毁内存?假设我们在机器上有足够的存储空间但没有足够的RAM用于转换。
示例数据:
0,0,0,0,0,1,0,0,0
1,0,0,1,0,0,0,0,0
...
示例代码:
converted_data = [ map(int,line.split(',')) for line in f ]
答案 0 :(得分:2)
您创建了许多带有pickle的bin文件,并且您有一些代码可以加载和卸载数据的不同部分。
假设你有一个16GB的文件,你可以创建16个1GB的pickle文件。
如果你说你有足够的内存,那么在完成pickle文件之后,你应该能够将它全部加载到内存中。
答案 1 :(得分:2)
据我所知,您阅读文件的方法已经非常有效。
我假设获取open
的文件对象不将整个文件从文件系统读入RAM,而是根据需要访问文件系统上的文件。 / p>
然后迭代文件对象yield
s the file's lines(在您的情况下为字符串,因为您已经在文本模式下打开文件),即文件对象充当{{ 3}}。因此可以假设这里没有构建所有行的列表,并且逐行读取这些行以便连续消耗。
您可以在列表收缩中执行此操作。列表收缩是否收集了右侧产生的所有值(in
关键字后面的部分),然后将其传递到左侧(for
关键字之前的部分)进行处理?一个小实验可以告诉我们:
print('defining generator function')
def firstn(n):
num = 0
while num < n:
print('yielding ' + str(num))
yield num
num += 1
print('--')
[print('consuming ' + str(i)) for i in firstn(5)]
以上的输出是
defining generator function
--
yielding 0
consuming 0
yielding 1
consuming 1
yielding 2
consuming 2
yielding 3
consuming 3
yielding 4
consuming 4
所以答案是否定的,在从右手边产生任何其他值之前,左手边会立即消耗每个屈服值。文件中只有一行必须一次保存在内存中。
因此,如果您文件中的各行不长,您的阅读方式似乎就像内存效率一样。
当然,你的列表收缩仍然需要收集左手边处理的结果。毕竟,结果列表是您想要摆脱这一切的原因。因此,如果内存不足,结果列表可能会变得太大。
我不知道NumPy是否使用了这样一个事实,即布尔的集合可以比数字更有效地存储。但 if 确实如此,你必须让它意识到你的整数实际上是布尔值,以便从更高内存效率的数据类型中受益:
import numpy as np
f = open ( "data.txt" , 'r')
converted_data = [ np.fromstring(line, dtype=bool, sep=',') for line in f ]
如果您不是一次需要所有converted_data
,而是必须能够迭代它,请考虑将其设为生成器,而不是列表。您不需要使用yield
关键字来实现这一目标。只需用圆括号替换列表理解的方括号,就可以得到生成器表达式:
converted_data_generator = ( np.fromstring(line, dtype=bool, sep=',') for line in f )