【发布时间】:2021-02-14 05:10:38
【问题描述】:
我有 2 个文本文件 (*.txt),其中包含以下格式的唯一字符串:
udtvbacfbbxfdffzpwsqzxyznecbqxgebuudzgzn:refmfxaawuuilznjrxuogrjqhlmhslkmprdxbascpoxda
ltswbjfsnejkaxyzwyjyfggjynndwkivegqdarjg:qyktyzugbgclpovyvmgtkihxqisuawesmcvsjzukcbrzi
第一个文件包含 5000 万 行这样的行 (4.3 GB),第二个文件包含 100 万 行 (112 MB)。一行包含 40 个字符、分隔符 : 和另外 45 个字符。
任务:获取两个文件的唯一值。也就是说,您需要一个 csv 或 txt 文件,其中的行位于第二个文件中,而哪些行不在首先。
我正在尝试使用 vaex (Vaex):
import vaex
base_files = ['file1.txt']
for i, txt_file in enumerate(base_files, 1):
for j, dv in enumerate(vaex.from_csv(txt_file, chunk_size=5_000_000, names=['data']), 1):
dv.export_hdf5(f'hdf5_base/base_{i:02}_{j:02}.hdf5')
check_files = ['file2.txt']
for i, txt_file in enumerate(check_files, 1):
for j, dv in enumerate(vaex.from_csv(txt_file, chunk_size=5_000_000, names=['data']), 1):
dv.export_hdf5(f'hdf5_check/check_{i:02}_{j:02}.hdf5')
dv_base = vaex.open('hdf5_base/*.hdf5')
dv_check = vaex.open('hdf5_check/*.hdf5')
dv_result = dv_check.join(dv_base, on='data', how='inner', inplace=True)
dv_result.export(path='result.csv')
因此,我得到了具有唯一行值的 result.csv 文件。但是验证过程需要很长时间。此外,它使用所有可用的 RAM 和所有处理器资源。 如何加快这个过程?我究竟做错了什么?有什么可以做得更好?是否值得使用其他库(pandas、dask)进行此检查,它们会更快吗?
UPD 10.11.2020 到目前为止,我还没有发现比以下选项更快的方法:
from io import StringIO
def read_lines(filename):
handle = StringIO(filename)
for line in handle:
yield line.rstrip('\n')
def read_in_chunks(file_obj, chunk_size=10485760):
while True:
data = file_obj.read(chunk_size)
if not data:
break
yield data
file_check = open('check.txt', 'r', errors='ignore').read()
check_set = {elem for elem in read_lines(file_check)}
with open(file='base.txt', mode='r', errors='ignore') as file_base:
for idx, chunk in enumerate(read_in_chunks(file_base), 1):
print(f'Checked [{idx}0 Mb]')
for elem in read_lines(chunk):
if elem in check_set:
check_set.remove(elem)
print(f'Unique rows: [{len(check_set)}]')
2020 年 11 月 11 日更新: 感谢 @m9_psy 提供提高性能的提示。它真的更快!目前,最快的方法是:
from io import BytesIO
check_set = {elem for elem in BytesIO(open('check.txt', 'rb').read())}
with open('base.txt', 'rb') as file_base:
for line in file_base:
if line in check_set:
check_set.remove(line)
print(f'Unique rows: [{len(check_set)}]')
有没有办法进一步加快这个过程?
【问题讨论】:
-
您能否详细说明一下哪个过程需要很长时间?是从csv到hdf5的转换吗?是连接速度慢而且占用内存吗?
-
@Maarten Breddels 转换为 hdf5 很快。检查字符串很慢。
-
您是否考虑过在命令行中简单地使用
awk的替代方案?如果我正确理解了您的要求(即仅返回 file2.txt 中存在的行,但 file1.txt 中还没有),this answer 应该可以很好地完成这项工作。请注意,您需要将结果通过管道传输到文件,即awk 'FNR==NR {a[$0]++; next} !($0 in a)' file1.txt file2.txt > result.txt -
应该起作用的东西:预先对数据进行排序和/或索引数据以便能够在不进行全表扫描的情况下加入它。压缩数据也是一个好主意(减少 io/内存操作)列式存储是一个不错的选择。
-
有一种方法可以加速最快的 StringIO 版本 - 1) 使用 BytesIO 和 'rb' 读取避免字符串解码和操作原始字节。有趣的是,bytesio 比“filename.split() 更快——这提出了一些问题。2) 在“read_lines”中去掉生成器——只返回“handle”——你会去掉额外的生成器调用。3) 有问题测量,但你也可以摆脱“strip”方法 - 计数就可以了,因为所有字符串(不仅仅是一些)最后都会有“/n”