Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中高效匹配字典的正则表达式_Python_Regex_List_Dictionary_List Comprehension - Fatal编程技术网

在Python中高效匹配字典的正则表达式

在Python中高效匹配字典的正则表达式,python,regex,list,dictionary,list-comprehension,Python,Regex,List,Dictionary,List Comprehension,我的Python脚本中有一个单词词典:count pairs。从这个字典中,我想提取与字符串列表中的任何项匹配的条目 我已经找到了一个使用正则表达式的有效解决方案(见下文),但它需要永远(~10个小时的运行时间)。我觉得必须有一种更快的方法来做这件事——你们对如何改进我的代码有什么意见/想法吗 import re dicti={'the':20, 'a':10, 'over':2} regex_list=['the', 'an?'] extractddicti= {k:v for k,v

我的Python脚本中有一个单词词典:count pairs。从这个字典中,我想提取与字符串列表中的任何项匹配的条目

我已经找到了一个使用正则表达式的有效解决方案(见下文),但它需要永远(~10个小时的运行时间)。我觉得必须有一种更快的方法来做这件事——你们对如何改进我的代码有什么意见/想法吗

import re

dicti={'the':20, 'a':10, 'over':2}
regex_list=['the', 'an?'] 

extractddicti= {k:v for k,v in dicti.items() if any (re.match("^"+regex+"$",k) for regex in regex_list)} 
事实上,这本字典大约有60000个词条,regex_列表大约有1000个。正则表达式列表中的项是正则表达式字符串,即包含特殊字符,如?、括号(a | b | c)等。它们可能与词典中的多个条目相匹配

更新/编辑 (参见公认的答案,以更好地实现相同的想法)

按照Keozon和其他人的建议,我首先像这样编译了我的正则表达式:

regex_list=['the', 'an?']
regex_list_compiled=[re.compile("^"+i+"$") for i in regex_list]
然后稍微调整了我的搜索功能:

extractddicti= {k:v for k,v in dicti.items() if any (re.match(regex,k) for regex in regex_list_compiled)} 

性能上的差异非常惊人:一个包含14800项的字典和1100个正则表达式的列表的测试运行在没有编译的情况下花了34分钟,而在编译的情况下只花了不到一(!)分钟。没想到会这么戏剧性。谢谢你的帮助

将正则表达式编译一次,而不是每次使用它们,可能会获得相当大的性能改进。所以你会有这样的想法:

import re

dicti={'the':20, 'a':10, 'over':2}
patterns=['the', 'an?'] 
regex_matches = [re.compile("^"+pattern+"$").match for pattern in patterns]

extractddicti= {k:v for k,v in dicti.items()
                if any (regex_match(k) for regex_match in regex_matches)} 

将正则表达式编译一次,而不是每次使用它们,可能会获得相当大的性能改进。所以你会有这样的想法:

import re

dicti={'the':20, 'a':10, 'over':2}
patterns=['the', 'an?'] 
regex_matches = [re.compile("^"+pattern+"$").match for pattern in patterns]

extractddicti= {k:v for k,v in dicti.items()
                if any (regex_match(k) for regex_match in regex_matches)} 

如何将dict.keys合并到一个字符串中以减少循环数?这里似乎更快:

import re, random, string

#Generate random dictionary
dicti={''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5)):i for i in range(1000000)}

#Using ; as separator, you can find any match with [^;]
regex_list=['TH[^;]*', 'A[^;]*'] 
joined_keys = ';' + ';'.join(dicti.keys()) + ';'

extractddicti = {i[1:-1]:dicti[i[1:-1]] for sublist in 
                [re.findall(';'+k+';', joined_keys) for k in regex_list] 
                for i in sublist}
10个循环的Timeit结果:

╔════════════╦════════════════════╗
║ Algorithm  ║     Time (sec)     ║
╠════════════╬════════════════════╣
║ Mine       ║ 2.116118321594911  ║
║ E. Gordon  ║ 20.956314717412653 ║
╚════════════╩════════════════════╝
更新 正如@E.Gordon所建议的,您应该将分隔符更改为换行符
\n
,这样您就可以在
re.MULTILINE
中使用^and$特殊运算符

regex_list=['.*TH', 'AN.*'] 

joined_keys = '\n'.join(dicti.keys())   
all_regex = "^" + "$|^".join(regex_list) + "$" 
matched_keys = re.findall(all_regex, joined_keys, re.MULTILINE)

dicti_match = {k:dicti[k] for k in matched_keys}

如何将dict.keys合并到一个字符串中以减少循环数?这里似乎更快:

import re, random, string

#Generate random dictionary
dicti={''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5)):i for i in range(1000000)}

#Using ; as separator, you can find any match with [^;]
regex_list=['TH[^;]*', 'A[^;]*'] 
joined_keys = ';' + ';'.join(dicti.keys()) + ';'

extractddicti = {i[1:-1]:dicti[i[1:-1]] for sublist in 
                [re.findall(';'+k+';', joined_keys) for k in regex_list] 
                for i in sublist}
10个循环的Timeit结果:

╔════════════╦════════════════════╗
║ Algorithm  ║     Time (sec)     ║
╠════════════╬════════════════════╣
║ Mine       ║ 2.116118321594911  ║
║ E. Gordon  ║ 20.956314717412653 ║
╚════════════╩════════════════════╝
更新 正如@E.Gordon所建议的,您应该将分隔符更改为换行符
\n
,这样您就可以在
re.MULTILINE
中使用^and$特殊运算符

regex_list=['.*TH', 'AN.*'] 

joined_keys = '\n'.join(dicti.keys())   
all_regex = "^" + "$|^".join(regex_list) + "$" 
matched_keys = re.findall(all_regex, joined_keys, re.MULTILINE)

dicti_match = {k:dicti[k] for k in matched_keys}

你的问题看起来很像广告阻塞问题。与正则表达式列表类似,但包含url和url模式

一般来说,正则表达式匹配相对较快。但是,正如您已经看到的,它的速度不如不运行它的速度快,而且常见的正则表达式库也与您的一样

您的正则表达式词典也包含完整的单词。或者,如果内存是一个问题,则A将非常适合匹配正则表达式字典的完整单词部分

剩下的问题是将一个大的输入匹配到一个大的正则表达式集,并且预过滤方法在这种情况下非常有效:首先检查是否存在一个合理的匹配,并且仅当存在一个合理的匹配时,才运行完整的正则表达式匹配。i、 e.为了使“an?”正则表达式匹配,字符串中必须有“an”。非常适合搜索这样的子字符串。如果正则表达式总是完全匹配的,则可能需要添加字符串开始和字符串结束标记。当然,您也可以构建固定字符串集的trie或dawg,并在每个字符处开始新的搜索。使用强大的自动机库的确定性有限状态自动机具有相同的效果


希望这种方法能够消除对大部分输入运行正则表达式匹配的需要。一旦您必须运行正则表达式匹配,您可以在其他答案中使用python正则表达式实现。

您的问题看起来很像广告阻塞问题。与正则表达式列表类似,但包含url和url模式

一般来说,正则表达式匹配相对较快。但是,正如您已经看到的,它的速度不如不运行它的速度快,而且常见的正则表达式库也与您的一样

您的正则表达式词典也包含完整的单词。或者,如果内存是一个问题,则A将非常适合匹配正则表达式字典的完整单词部分

剩下的问题是将一个大的输入匹配到一个大的正则表达式集,并且预过滤方法在这种情况下非常有效:首先检查是否存在一个合理的匹配,并且仅当存在一个合理的匹配时,才运行完整的正则表达式匹配。i、 e.为了使“an?”正则表达式匹配,字符串中必须有“an”。非常适合搜索这样的子字符串。如果正则表达式总是完全匹配的,则可能需要添加字符串开始和字符串结束标记。当然,您也可以构建固定字符串集的trie或dawg,并在每个字符处开始新的搜索。使用强大的自动机库的确定性有限状态自动机具有相同的效果


希望这种方法能够消除对大部分输入运行正则表达式匹配的需要。一旦必须运行正则表达式匹配,您可以在其他答案中使用python正则表达式实现。

在完整版本中,您是否编译列表中的正则表达式?如果没有,请这样做。这就是compile选项存在的确切原因。实际上,每次将每个正则表达式与字符串进行比较时,都会对其进行编译。使用
dict.iteritems()
将提供一个小的改进(除非这是python3)。但更重要的是,确保你的正则表达式不会受到影响。我将继续假设你真正的正则表达式更复杂。因为使用正则表达式搜索“the”可能不是最快的方法。例如,如果您正在搜索使用str.findAlso的简单字符串,请确保在正则表达式定义中使用原始字符串文字,否则您可能会遇到令人惊讶的行为(
r'the'
,而不是
'the'
re.match
意味着字符串的开头,因此
^
是不必要的(除非它是多行的)。如果你在Python 3上,你可以考虑< C