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

时间:2009-04-14 20:56:08

标签: python

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

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

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

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

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

5 个答案:

答案 0 :(得分:36)

您在寻找

吗?
any( k in s for k in keywords )

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

答案 1 :(得分:17)

在你的例子中,由于项目很少,所以并不重要。但如果你有几千个项目的清单,这可能会有所帮助。

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

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

    return False

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

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:2)

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

这可能允许它们并行测试,但很大程度上取决于关键字是什么(例如,某些工作可能会重复使用测试“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's solution更新了表格,并在应用正则表达式之前采用了类似的方法。我还添加了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中有大量项目时更好。

答案 4 :(得分:0)

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