list1 = ["happy new year", "game over", "a happy story", "hold on"]
list2 = ["happy", "new", "hold"]
假设我有两个字符串列表,我想使用一个新列表来存储这两个列表的匹配对,如下所示:
list3=[["happy new year","happy"],["happy new year","new"],["a happy story","happy"],["hold on","hold"]]
这意味着我需要在一个列表中获取所有字符串对,而在另一个列表中获取其子字符串。
实际上,这是关于一些中国古代文字数据的。第一个列表包含10至13世纪的人物姓名,第二个列表包含该时期所有诗歌的标题。中国古代人经常在作品标题中记录他们的社会关系。例如,某人可能写了一首题为“给我的朋友王安石的诗”。在这种情况下,第一个列表中的人“王安石”应与此标题匹配。他们的案件还包括“给我的朋友王安石和苏石”,其中有不止一个人的头衔。因此基本上,这是一项巨大的工作,涉及30,000人和16万首诗。
以下是我的代码:
list3 = []
for i in list1:
for j in list2:
if str(i).count(str(j)) > 0:
list3.append([i,j])
我使用str(i)是因为python总是将我的中文字符串当作float。这段代码确实有效,但是太慢了。我必须找出另一种方法。谢谢!
答案 0 :(得分:2)
使用regular expression通过re
module进行搜索。正则表达式引擎可以比嵌套for
循环更好地搜索文本中的匹配元素。
我将在这里使用更好的变量名,以更清楚地知道列表的位置。 titles
是您要搜索的诗歌标题,names
是您要匹配的事物。 matched
是您要产生的(title, name)
对:
import re
titles = ["happy new year", "game over", "a happy story", "hold on"]
names = ["happy", "new", "hold"]
by_reverse_length = sorted(names, key=len, reverse=True)
pattern = "|".join(map(re.escape, by_reverse_length))
any_name = re.compile("({})".format(pattern))
matches = []
for title in titles:
for match in any_name.finditer(title):
matches.append((title, match.group()))
以上内容会产生您所需的输出:
>>> matches
[('happy new year', 'happy'), ('happy new year', 'new'), ('a happy story', 'happy'), ('hold on', 'hold')]
名称是按长度相反的顺序排列的,以便在具有相同前缀的短名称之前找到长名称;例如在Hollander
之前找到Holland
。
根据您的姓名创建Holl
字符串,以形成pattern
替代模式,其中任何一种模式都可以匹配,但是regex引擎会找到较早列出的模式顺序放在后面的顺序上,因此需要按长度反向排序。整个名称模式的...|...|...
括号告诉正则表达式引擎以组的形式捕获。然后,循环中的(...)
调用可以提取匹配的文本。
使用re.escape()
function调用可以防止名称中的“元字符”,具有特殊含义的字符,例如match.group()
,^
,$
,{{1} }等被解释为特殊的正则表达式含义。
re.finditer()
function(以及编译模式的方法)然后按从左到右的顺序查找不重叠的匹配项,因此它将永远不会匹配较短的子字符串,并为我们提供了提取{{3} }。如果您想了解match object和其他元数据,则可以给您更多选择。否则,这里也可以使用starting positions of the matches。
如果您要在带有西方字母的文本上而不是中文上使用以上内容,那么您可能还希望添加单词边界标记(
:
)
否则,可以匹配较大单词的子字符串部分。由于中文没有单词边界字符(例如空格和标点符号),因此您不想在此类文本中使用\b
。
答案 1 :(得分:0)
如果列表较长,可能值得为给定单词出现的句子建立某种“索引”。创建索引所需的时间与在所有list2
中找到第一个单词所需的时间相同。 list1
中的句子(它必须遍历所有句子中的所有单词),创建之后,您可以在O(1)中更快地获得包含单词的句子。
list1 = ["happy new year", "game over", "a happy story", "hold on"]
list2 = ["happy", "new", "hold"]
import collections
index = collections.defaultdict(list)
for sentence in list1:
for word in sentence.split():
index[word].append(sentence)
res = [[sentence, word] for word in list2 for sentence in index[word]]
结果:
[['happy new year', 'happy'],
['a happy story', 'happy'],
['happy new year', 'new'],
['hold on', 'hold']]
这使用str.split
在空格处分割单词,但是如果句子更复杂,例如如果它们包含标点符号,则可以使用带有单词边界\b
的正则表达式,并且可以对句子进行规范化(例如,转换为小写字母或应用词干分析器,但不确定是否适用于中文)。>
答案 2 :(得分:0)
这可以以绝对直接的方式轻松完成。
选项 A:查找“所有”可能的组合: 要在一个列表中查找包含来自另一个列表的子字符串的所有字符串,请遍历 set potato = NULLIF(potato,'')
的所有字符串(要评估)并检查每个元素是否包含 list1
的子字符串:
list2
(不过,我确实认为您的问题的标题有点误导,因为您不仅要求包含另一个列表的子字符串的列表元素,而且根据您的代码示例,您正在寻找 'all可能的组合'。)
因此选项 B:查找“任意”组合:更简单、更快捷,如果您真的只需要问题所说的内容,则可以通过仅查找“任意”匹配来提高性能: >
list1 = ["happy new year", "game over", "a happy story", "hold on"]
list2 = ["happy", "new", "hold"]
[(string, substring) for string in list1 for substring in list2 if substring in string]
>>> [('happy new year', 'happy'), ('happy new year', 'new'), ('a happy story', 'happy'), ('hold on', 'hold')]
选项 B 还可让您提高性能。如果列表很长,您可以先运行 B,创建一个子集(只有实际会与子字符串匹配的字符串),然后再次扩展以捕获“所有”而不是任何。