【问题标题】:How to efficiently loop regex searches from set python如何有效地从 set python 循环正则表达式搜索
【发布时间】:2015-09-12 05:46:20
【问题描述】:

我正在评估一长串句子,看看它们是否包含州名并用字典映射它们,这就是我想出的代码——它非常慢。这应该如何正确完成?

       for sent in sentences: #set of sentences.upper()
            for state in stateset: #set of state abbrev's and names in .upper()
                boundst = re.compile(r'\b%s\b' % state, re.I)
                if re.search(boundst, sent):
                    sentstatedict[sent] = state
                    break

我不知道如何提前创建绑定版本 - 我可以创建一组并使用它吗?

为了清楚起见,我想为我拥有的每个句子找出该句子中包含的最多一个匹配的州名或缩写。我的困难在于不知道如何预先组装状态字符串的可用“绑定”版本列表以进行“整个单词”匹配。这导致我在内部循环中有re.compile

【问题讨论】:

    标签: python regex loops dictionary


    【解决方案1】:

    您一遍又一遍地编译所有正则表达式(N 次!其中 N 是句子的数量)。 re.compile 不是一个快速的操作,所以这就是造成痛苦的原因。您可以初始化它们的字典,以便按状态查找它们:

    re_lookup = {
        state: re.compile(r'\b%s\b' % state, re.I)
        for state in stateset
    }
    
    for sent in sentences:
        for state in stateset:
            if re.search(re_lookup[state], sent):
                sentstatedict[sent] = state
                break
    

    【讨论】:

    • re.compile 缓存您传递给它的模式(无论您是显式调用它,还是依赖于对顶级搜索函数之一的隐式编译,例如 re.search 与字符串模式)。
    • 出于某种原因(称为我的 unpythonic),这里的字典对我来说比函数中的循环更有意义。
    • 这会留下正确的中断。 stateset 中只有 100 个元素,因此初始循环不会受到影响。
    • @Blckknght:嗯,缓存有多大? (可能至少 50 个 :))@Xodarapp777:现在慢得不那么痛苦了吗?
    • @BrianO:查看源代码,缓存大小在 Python 2 中为 100,在 Python 3 的最新版本中为 512。因此,提问者的代码运行速度可能与您的完全相同。实际上,如果所有句子都包含stateset 中的前几个状态之一,那么提问者的代码可能会更快。在那种特定情况下,您的代码会做更多的工作(因为它编译所有状态,而不仅仅是匹配的状态)。请注意,虽然有一个错误提示 explicit calls to re.compile shouldn't be cached.
    【解决方案2】:

    我建议您构建一个匹配所有状态的单个正则表达式。然后,您可以对每个句子进行一次更复杂的正则表达式搜索,并从结果中提取匹配状态:

    pattern = r"\b({})\b".format("|".join(stateset))
    for sent in sentences:
        match = re.search(pattern, sent, re.I)
        if match:
            sentstatedict[sent] = match.group(1)
    

    我不介意re.compile,因为所有采用字符串模式的正则表达式方法都会在内部缓存已编译的模式。所以使用相同的模式字符串搜索应该和自己调用compile然后使用编译模式的方法一样快。

    【讨论】:

    • 请修改if match is not None: 为:if match: - 它伤害了我的眼睛...:D
    • 你的意思是if matchif not match 是我最初写它的方式,但我认为这很难看(证明口味可能不同),所以我在颠倒测试逻辑之前的某个时候将其更改为 if match is None(当我意识到我没有需要对失败的比赛做任何事情)。我再次更改了它,因为我同意 if matchif match is not None 更好(假设 match 没有其他有效值是错误的)。
    • 是的if match - 告诉它它伤害了我的眼睛...... :)
    • 这是创新的。但我怀疑它不会更快:通过状态的内部循环仍然存在,隐藏在大正则表达式的交替中。 re.search 必须通过运行其状态机模拟(AK?不,好的,AL?不,... CA?是的,中断)来执行循环直到找到匹配,这会引入一些额外的开销。但这只是一种预感。也许 OP 可以对这些替代方案进行基准测试?
    • 是的,我不知道它是否真的会更快。可能是,因为 re 模块完成的实际搜索是用 C 而不是 Python 实现的,但如果有其他开销,它可能不是。
    【解决方案3】:

    因为您不想一遍又一遍地编译正则表达式(n^2 次而不是 n 次...)。更改 for 循环的顺序并在中间编译正则表达式会更有效:

    for state in stateset:             
        boundst = re.compile(r'\b%s\b' % state, re.I) # compile the regex once (at most) per each state
            for sent in sentences: 
                if re.search(boundst, sent):
                    sentstatedict[sent] = state
                    break
    

    【讨论】:

    • 你的稍微好一点 :) 但不是因为你说的原因!您仍将为每个州编译一个正则表达式。
    • @BrianO 请教育我 :) 为什么?
    • 因为外部循环将遍历每个状态,对吗?中断只是中断到下一个句子迭代。
    • 假设有 N 个句子。我的在一个单独的循环中执行 50 re.compiles,然后执行循环体 N * 50 次。你的只有一个循环体 N * 50 次,你的循环体等于我的循环体加上re.compile。所以我的还有一个循环设置,并且创建了一个真正需要的 dict(不是一个大的,但它仍然是绒毛)。
    • 哦不,不:现在您的答案完全错误,不再是正确答案。它只会设置sentstatedict[sent] = state *对于第一句话 sent 包含一个状态,然后它返回这意味着完全不同的东西。 OP,你在听吗? :)
    猜你喜欢
    • 2016-11-17
    • 1970-01-01
    • 2013-08-16
    • 1970-01-01
    • 2016-06-06
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多