繁体   English   中英

从另一个列表中搜索任何子字符串的字符串列表

[英]Search a list of strings for any sub-string from another list

鉴于这3个数据列表和关键字列表:

good_data1 = ['hello, world', 'hey, world']
good_data2 = ['hey, man', 'whats up']
bad_data = ['hi, earth', 'sup, planet']
keywords = ['world', 'he']

我正在尝试编写一个简单的函数来检查是否存在任何关键字作为数据列表中任何单词的子字符串。 它应该返回True为good_data列表和虚假的bad_data

我知道如何以一种效率低下的方式做到这一点:

def checkData(data):
  for s in data:
    for k in keywords:
      if k in s:
        return True
  return False

你在找吗?

any( k in s for k in keywords )

它更紧凑,但效率可能更低。

在你的例子中,只有很少的项目,这并不重要。 但如果您有几千个项目的列表,这可能会有所帮助。

由于您不关心列表中的哪个元素包含关键字,因此您可以扫描整个列表(作为一个字符串),而不是一次扫描一个项目。 为此,您需要一个您知道在关键字中不会出现的连接字符,以避免误报。 我在这个例子中使用换行符。

def check_data(data):
    s = "\n".join(data);
    for k in keywords:
        if k in s:
            return True

    return False

在我完全不科学的测试中,我的版本在大约30秒内检查了5000个项目的列表100000次。 我在3分钟后停止了你的版本 - 厌倦了等待发布=)

如果您有许多关键字,则可能需要尝试后缀树[1]。 插入三个数据列表中的所有单词,在其终止节点中存储每个单词来自哪个列表。 然后,您可以非常快速地在树上为每个关键字执行查询。

警告:后缀树实现起来非常复杂!

[1] http://en.wikipedia.org/wiki/Suffix_tree

您可以通过将关键字列表构建为正则表达式来改进问题。

这可能允许它们并行测试,但很大程度上取决于关键字是什么(例如,某些工作可以重复使用测试“hello”和“hell”,而不是从每个单词的开头搜索每个短语。

您可以执行以下操作:

import re
keyword_re = re.compile("|".join(map(re.escape, keywords)))

然后:

>>> bool(keyword_re.search('hello, world'))
True
>>> bool(keyword_re.search('hi, earth'))
False

(它实际上将返回找到的匹配对象,如果没有找到则返回None - 如果您需要知道哪个关键字匹配,这可能很有用)

但是,这取得多少(如果有的话)将取决于关键字。 如果您只有一两个,请保持当前的方法。 如果你有一个大的列表,可能值得进行分析和分析,看看哪个表现更好。

[编辑]作为参考,以下是这些方法对您的示例的作用:

               good1   good2  good3   bad1   bad2
original     : 0.206   0.233  0.229   0.390   63.879
gnud (join)  : 0.257   0.347  4.600   0.281    6.706
regex        : 0.766   1.018  0.397   0.764  124.351
regex (join) : 0.345   0.337  3.305   0.481   48.666

显然,对于这种情况,你的方法比正则表达式要好得多。 这种情况是否总是如此取决于关键字的数量和复杂性以及将要检查的输入数据。 对于大量关键字,冗长列表或很少匹配的短语,正则表达式可能效果更好,但确实可以获得计时信息,并且可能首先尝试更简单的优化(例如将最常用的单词移到关键字列表的前面)。 有时最简单的方法确实是最好的。

[Edit2]使用gnud的解决方案更新了表格,并在应用正则表达式之前采用了类似的方法。 我还添加了2个新测试:

good_data3 = good_data2 * 500  # 1000 items, the first of which matches.
bad_data2 = bad_data * 500     # 1000 items, none of which matches.

其中显示了各种优点和缺点。 当立即找到匹配时,加入确实会更糟(因为加入列表时总是付费的,前期成本 - 这是线性搜索方法的最佳情况),但是对于非匹配列表,它执行更好。 当有在list.case大量的项目) 好得多。

我认为这是非常有效和清晰的,尽管你可以使用map()来避免许多巢。 对于更大的列表,我同意罗斯对字典的看法。

暂无
暂无

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

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