繁体   English   中英

在python中从文件中grep多个值的最快方法

[英]Fastest way to grep multiple values from file in python

  • 我有一个300米行(inputFile)的文件,所有文件都有2个由制表符分隔的列。
  • 我还有一个包含1000个独特项目(vals)的列表。

我想创建与作为密钥作为在INPUTFILE所有行,其中第一列发生在瓦尔斯值1列和第2列的字典。 val中的一些项目不会出现在文件中,这些值必须保存在新列表中。 我最多可以使用20个线程来加速这个过程。

实现这一目标的最快方法是什么?

我最好的尝试到现在为止:

newDict = {}
foundVals = []
cmd = "grep \"" + vals[0]
for val in vals:
     cmd = cmd + "\|^"+val+"[[:space:]]"
cmd = cmd + "\" " + self.inputFile
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, ''):
    info = line.split()
    foundVals.append(info[0])
    newDict.update({info[0]:info[1]})
p.wait()
notFound = [x for x in vals if x not in set(foundVals)]

示例 inputFile:

2       9913
3       9913
4       9646
...
594592886       32630
594592888       32630
594592890       32630

瓦尔斯:

[1,2,594592888]

通缉词典:

{2:9913,594592888:32630}

而在未发现:

[1]

您在评论中澄清了每个密钥在数据中最多出现一次。 由此可见,事实上只有1000个键,在Python中完成的工作量是微不足道的; 几乎所有的时间都花在等待grep输出上。 哪个好; 将线提取委托给专用实用程序的策略仍然合理。 但这意味着必须在线路提取方面找到性能提升。

您可以通过优化正则表达式来加快速度。 例如,而不是

^266[[:space:]]\|^801[[:space:]]\|^810[[:space:]]

你可以使用:

^\(266\|801\|810\)[[:space:]]

这样就不必为每个替代方案单独匹配锚。 我发现测试数据(1000万行,25个键)的改进率提高了15%。

进一步的优化是在交替中统一公共前缀: 266\\|801\\|810可以用等效的266\\|8\\(01\\|10\\)替换。 以这种方式重写25键正则表达式使测试数据的速度接近50%。

此时grep开始显示其局限性。 似乎它受CPU限制: iostat表明,当grep运行时,正则表达式中的每次连续改进都会增加每秒IO请求的数量。 并且使用加热的页面缓存重新运行grep并且--mmap选项不会加快速度(因为如果文件IO是瓶颈的话可能会这样)。 因此,更高的速度可能需要具有更快的正则表达式引擎的实用程序。

其中一个是ag (源代码在这里 ),其正则表达式实现也执行自动优化,因此您无需进行太多的手动调整。 虽然我无法在我的机器上在不到12秒的时间内获得grep来处理测试数据,但对于上述所有正则表达式变量, ag在~0.5秒内完成。

这不是非常有效的内存(对于3亿行的文件,这可能是一个问题)。 除了保存所有值(或读取文件两次)之外,我无法想到一种在理解中保存未找到值的方法。 我不认为线程会有多大帮助,因为文件I / O可能会成为性能瓶颈。 我假设选项卡是文件中的分隔符。 (您没有说,但示例数据看起来有一个标签。)

vals = [1,2,594592888]

with open(self.inputfile,'r') as i_file:
    all_vals = {
        int(t[0]):int(t[1])
        for t in (
                line.strip().split('\t')
                for line in i_file
        )
    }

newDict = {
    t[0]:t[1] for t in filter(lambda t: t[0] in vals, all_vals.items())
}

notFound = list(set(all_vals.keys()).difference(newDict.keys()))

如果我理解正确的话,你不希望任何文件的行不匹配您vals既然你在谈论巨大的文件,并希望值非常小数目,我会去是这样的:

vals_set = set(vals)
found_vals = {}

with open(inputfile,"r") as in_file:
    for line in in_file:
        line = line.split() # Assuming tabs or whitespaces
        if line[0] in vals_set:
            found_vals[line[0]] = line[1]


not_found_vals = vals_set.difference(found_vals)

这将是记忆保守,你将在found_vals和你的列表中使用not_found_vals 实际上,内存使用情况,AFAIK将仅取决于您要搜索的val数量,而不取决于文件的大小。

编辑:

我认为并行化此任务的最简单方法是将文件拆分并在每个部分中使用不同的进程单独搜索。 这样你就不需要处理线程之间的通信(我认为更简单,更快捷)。

一个很好的方法,因为我推断你使用BASH(你使用grep:P)是这个答案中提到的:

split -l 1000000 filename

将生成每个1000000行的文件。

您可以轻松修改脚本以将匹配保存到每个进程的新文件中,然后合并不同的输出文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM