我有一个几百万行文本的大文件。我想随机均匀地从该文件中提取较小的(250000 行)。我做了以下代码,但它出奇地非常慢,实际上慢得无法使用。我能做些什么来加快速度?
def get_shorter_subset(fname, new_len):
"""Extract a random shorter subset of length new_len from a given file"""
out_lines = []
with open(fname + "short.out", 'w') as out_file:
with open(fname, 'r') as in_file:
all_lines = in_file.readlines()
total = len(all_lines)
print "Total lines:", total
for i in range(new_len):
line = np.random.choice(all_lines)
out_lines.append(line.rstrip('trn'))
#out_file.write(line.rstrip('trn'))
print "Done with", i, "lines"
all_lines.remove(line)
out_file.write("n".join(out_lines))
所以,问题:
all_lines = in_file.readlines()
将所有行读取到内存中可能不是执行此操作的最佳方法......但是如果你要这样做,那么绝对不要这样做:all_lines.remove(line)
因为这是一个O(N)操作,你在一个循环中执行,给你二次复杂度。
我怀疑通过简单地做一些事情来获得巨大的性能改进:
idx = np.arange(total, dtype=np.int32)
idx = np.random.choice(idx, size=new_len, replace=False)
for i in idx:
outfile.write(all_lines[i])
您也可以尝试使用 mmap:
https://docs.python.org/3.6/library/mmap.html
读取所有行,将它们保存在内存中,然后对生成的文本执行 250K 大字符串操作。 每次从文件中删除一行时,Python 都必须为剩余的行创建一个新副本。
相反,只需随机抽取样本。 例如,如果您有 500 万行,则需要 5% 的文件。 读取文件,一次一行。 掷出随机浮点数。 如果为 <= 0.05,请将该行写入输出。
对于如此大的样本,您最终将获得所需大小的输出。
利用 Python numpy 库。numpy.choice()
功能提供您需要的功能。它将在一次调用中获取最大所需大小的线路样本。所以你的函数看起来像:
import numpy as np
def get_shorter_subset(fname, new_len):
"""Extract a random shorter subset of length new_len from a given file"""
with open(fname + " short.out", 'w') as out_file, open(fname, 'r') as in_file:
out_file.write(''.join(np.random.choice(list(in_file), new_len, False)))
get_shorter_subset('input.txt', 250000)
感谢您的回答,我做了一个解决方案,在每个索引处生成一个随机数(概率对应于 new_size/full_size),并据此选择或丢弃每个元素。所以代码是:
def get_shorter_subset(fname, new_len):
"""Extract a random shorter subset of length new_len from a given
file"""
out_lines = []
with open(fname + "short.out", 'w') as out_file:
with open(fname, 'r') as in_file:
all_lines = in_file.readlines()
total = len(all_lines)
freq = total/new_len + 1
print "Total lines:", total, "new freq:", freq
for i, line in enumerate(all_lines):
t = np.random.randint(1,freq+1)
if t == 1:
out_lines.append(line.rstrip('trn'))
#out_file.write(line.rstrip('trn'))
if i % 10000 == 0:
print "Done with", i, "lines"
out_file.write("n".join(out_lines))