您好我想随机化153mil行文本文件的行,但我使用的当前方式使我在执行时耗尽内存:
with open(inputfile,'r') as source:
data = [ (random.random(), line) for line in source ]
data.sort()
with open(outputfile,'w') as target:
for _, line in data:
target.write( line )
如果不在内存中读取数据并且没有太多复杂性,我将如何做到这一点:
首先计算文件中的最大行长度(使用二进制模式)
with open(inputfile,'rb') as source:
max_line_len = max(len(line) for line in source)
然后使用正确的填充将另一个文件写入磁盘,因此每行具有完全相同的大小(您需要两倍以上的大小,但因为您没有内存......)。同时计算行数。
with open(inputfile,'rb') as source, open(outputfile,'wb') as dest:
for count,line in enumerate(source):
dest.write(line + b"*"*(max_line_len-len(line))) # write padded
您刚刚创建了一个更大的文件,但现在这些行的长度完全相同。好吧,我们在换行后填充,这将在以后有用。示例输出将是(例如,如果max len = 20):
the first line
****the second line
***another line
******
(不确定添加的星的确切数量,但你明白了,请注意填充字符无关紧要,只要它不是\n
)
这意味着您可以通过max_line_len
(如记录文件或数据库)的简单乘法来查找任何行的开头
现在您可以生成行索引列表:
indexes = list(range(count+1))
random.shuffle(indexes)
现在迭代这个索引列表,寻找正确的位置,读取一个块并使用我们在换行后填充的事实进行拆分,所以现在我们可以拆分它以丢弃填充内容。
with open(outputfile,'rb') as source:
for idx in indexes:
source.seek(idx * max_line_len)
random_line = source.read(max_line_len).decode().split("\n")[0]
print(random_line) # or store to another file
我没有测试过这个但是如果你有足够的磁盘它应该可以工作。当然,如果你有一条非常长的线,这是非常浪费的,其余的很短。
使用h5py,您可以将数据文件移植到HDF5格式,然后随机化:
https://stackoverflow.com/a/44866734/3841261
您可以使用random.shuffle(数据集)。对于配备Core i5处理器,8 GB RAM和256 GB SSD的笔记本电脑上的30 GB数据集,这需要11分钟多一点
做一些粗略的卫生巾计算,估计每行120个字符乘以153 M行...大约18.5 GB的数据。 (我假设每个字符有1个字节,但它更多的是由于Unicode ...但是你得到了重点)。所以你需要至少那么多的RAM才能完全阅读文本。这就是你在读入时获得内存不足的原因。
您可以采取的一种方法是将作业分成块。读入文件的一部分,随机化这些文件,然后附加到一个新文件,写入文件并清除内存。当然,问题是你只能在特定的块中随机化。
你可以在这里采取许多方法,但如果你没有内存,就无法解决所有文本都无法读取的问题。
编辑
我非常喜欢Chad使用h5py和HDF5的想法。它基本上是在硬盘驱动器上的文件中进行所有的洗牌......有点像强制硬盘驱动器交换,但有更多的控制。我喜欢!它确实需要h5py。
我已经发布了an answer,但它确实是次优的,因为它意味着创建另一个文件。
这是一个更简单的解决方案:
我们必须使用二进制模式,否则我们可能会遇到自动行尾转换问题。
import random
current_offset = 0
offsets = []
with open("input.txt","rb") as f:
for line in f:
offsets.append(current_offset)
current_offset += len(line)
offsets.pop() # remove last offset (end of file)
random.shuffle(offsets)
with open("input.txt","rb") as f:
for offset in offsets:
f.seek(offset)
print(f.readline().decode().rstrip # or write to another file
对于1.53亿行,你仍然需要大约1到1.5千兆字节的RAM来存储索引(python 3使用长整数,你可以将它存储在numpy
数组中而不是减少内存)。如果这是可以接受的,这是一个非常简单的解决方案。