你好,我想随机化一个1.53亿行文本文件的行,但是我目前使用的方式使我在执行此操作时内存不足:
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 )
答案 0 :(得分:3)
使用h5py,您可以将数据文件移植为HDF5格式,然后随机化:
https://stackoverflow.com/a/44866734/3841261
您可以使用random.shuffle(dataset)。这需要超过11点 使用Core i5处理器的笔记本电脑上30 GB数据集所需的分钟数,为8 GB的RAM和256 GB的SSD
答案 1 :(得分:2)
进行一些粗略的餐巾纸计算,估计每行120个字符乘以1.53亿行...得出大约18.5 GB的数据。 (我假设每个字符1个字节,但是由于Unicode会更多,但是……您明白了这一点)。因此,您至少需要一定数量的RAM才能完全阅读文本。这就是为什么读入时出现内存不足异常的原因。
您可以采用的一种方法是将作业分成多个部分。读入文件的各个部分,将它们随机化,然后追加到新文件中,写入文件并随心清除内存。当然,问题在于您只会在特定的块内进行随机化。
您可以在此处采用多种方法,但是如果您没有足够的存储空间,就无法一次读完所有文本。
编辑
我真的很喜欢乍得使用h5py和HDF5的想法。它实际上是在对硬盘驱动器上的文件进行所有改组……有点像强制进行硬盘驱动器交换,但具有更多控制权。我喜欢!它确实需要有h5py。
答案 2 :(得分:1)
我已经发布了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 GB的RAM来存储索引(python 3使用长整数,可以将其存储在numpy
数组中以减少内存)。如果可以接受,这是一个非常简单的解决方案。
答案 3 :(得分:0)
在不读取内存中所有数据且没有太多复杂性的情况下如何做:
首先计算文件中的最大行长(使用二进制模式)
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
您刚刚创建了一个更大的文件,但是现在这些行的长度完全相同。好吧,我们在换行符后 进行了填充,这将在以后有用。示例输出为(例如,如果最大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
我尚未对此进行测试,但是如果您有足够的磁盘,它应该可以工作。当然,如果您的行很长而其余的很短,这将非常浪费。