【问题标题】:Searching a list based on values in another list根据另一个列表中的值搜索列表
【发布时间】:2012-12-16 18:33:36
【问题描述】:

我有一个名字列表,我试图从字符串列表中提取出来。我不断收到误报,例如部分匹配。另一个需要注意的是,我希望它还可以在适用的情况下获取姓氏。

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kimberly','Christmas is here', 'CHRIS']

desired_output = ['Chris Smith', 'Kimberly', 'CHRIS']

我试过这段代码:

[i for e in names for i in target if i.startswith(e)]

这可以预见地返回克里斯史密斯,圣诞节到了,金伯利。

我将如何最好地解决这个问题?使用正则表达式还是可以使用列表推导来完成?性能可能是个问题,因为实名列表的长度约为 880,000 个。

(python 2.7)

编辑:我已经意识到我在这个例子中的标准是不切实际的,因为在排除圣诞节的同时想要包括 Kimberly 是不可能的要求。为了缓解这个问题,我找到了一个更完整的名单,其中包括变体(包括 Kim 和 Kimberly)。

【问题讨论】:

  • 为什么输出中有CHRIS,而不是I hijacked this thread?
  • 到目前为止你尝试过什么方法?
  • 如果'Kimberly' 在输出中,那么为什么'Christmas is here' 会丢失?
  • @Ashwini 实际上,据我们所知,我认为整个目标是可行的
  • 对不起,扩大了描述

标签: python regex list


【解决方案1】:

完全猜测(再次),因为我看不出在给定任何合理标准的情况下您怎么不能拥有Christmas is here:

这将匹配任何以名称中的单词开头的单词的目标...

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kimberly','Christmas is here', 'CHRIS']

import re
matches = [targ for targ in target if any(re.search(r'\b{}'.format(name), targ, re.I) for name in names)]
print matches
# ['Chris Smith', 'Kimberly', 'Christmas is here', 'CHRIS']

如果您将其更改为\b{}\b' - then you'll get ['Chris Smith', 'CHRIS'],那么您将失去Kim...

【讨论】:

  • 这似乎是最简洁优雅的解决方案。我刚刚根据我的 OP 中的编辑更改的标准在搜索模式的末尾添加了另一个 \b
【解决方案2】:

这行得通吗?

names = ['Chris', 'Jack', 'Kim']
target = ['Chris Smith', 'I hijacked this thread', 'Kimberly','Christmas is here', 'CHRIS']

res = []
for tof in target:
    for name in names:
        if tof.lower().startswith(name.lower()):
            res.append(tof)
            break
print res

【讨论】:

    【解决方案3】:

    根据你的描述,我得到的规则是:

    1. 忽略大小写;
    2. 目标词必须以关键字开头。
    3. 如果目标词不完全是关键词,那么目标词必须是句子中的唯一词。

    试试这个:

    names = ['Chris', 'Jack', 'Kim']
    target = ['Chris Smith', 'I hijacked this thread', 'Kimberly','Christmas is here', 'CHRIS']
    desired_output = ['Chris Smith', 'Kimberly', 'CHRIS']
    
    actual_output = []
    for key in names:
        for words in target:
            for word in words.split():
                if key.lower() == word.lower():
                    actual_output.append(words)
                elif key.lower() == word.lower()[:len(key)] and len(words.split()) == 1:
                    actual_output.append(words)
    print(actual_output)
    

    它会输出 EXACTLY 作为你想要的输出(顺便说一句,你确定你真的想要这个吗?)。不要对 3 层循环感到沮丧。如果你有 N 个名字和 M 个句子,并且每个句子的单词数是有限的,那么这段代码的复杂度是 O(mn) 再好不过了。

    【讨论】:

      【解决方案4】:

      没有确定的方式来删除匹配“圣诞节在这里”,因为系统可能无法确定圣诞节是一个名字还是其他什么。 相反,如果你想加快这个过程,你可以试试这个 O(n) 方法。 我没有计时,但绝对比您或建议的解决方案快。

      from difflib import SequenceMatcher
      names = ['Chris', 'Jack', 'Kim']
      target = ['Chris Smith', 'I hijacked this thread', 'Kimberly','Christmas is here', 'CHRIS']
      def foo(names, target):
          #Create a generator to search the names
          def bar(names, target):
                  #which for each target
              for t in target:
                          #finds the matching blocks, a triplet, (i, j, n), and means that a[i:i+n] == b[j:j+n]
                  match = SequenceMatcher(None,names, t).get_matching_blocks()[0]
                          #match.size == 0 means no match
                          #and match.b > 0 means match does not happens at the start
                  if match.size > 0 and match.b == 0:
                                  #and generate the matching target
                      yield t
          #Join the names to create a single string
          names = ','.join(names)
          #and call the generator and return a list of the resultant generator
          return list(bar(names, target))
      
      >>> foo(names, target)
      ['Chris Smith', 'Kimberly', 'Christmas is here', 'CHRIS']
      

      【讨论】:

        猜你喜欢
        • 2021-12-31
        • 2013-06-25
        • 1970-01-01
        • 1970-01-01
        • 2023-02-23
        • 1970-01-01
        • 1970-01-01
        • 2021-05-13
        • 2019-08-10
        相关资源
        最近更新 更多