【问题标题】:Pythonic way to count occurrences from a list in a string从字符串中的列表中计算出现次数的 Pythonic 方法
【发布时间】:2017-05-10 13:49:01
【问题描述】:

从目标字符串的列表中查找字符串出现次数的最佳方法是什么?具体来说,我有一个清单:

string_list = [
    "foo",
    "bar",
    "baz"
]

target_string = "foo bar baz bar"

# Trying to write this function!
count = occurrence_counter(target_string) # should return 4

我想优化以最小化速度和内存使用量,如果这会有所不同的话。在大小方面,我预计string_list 最终可能包含数百个子字符串。

【问题讨论】:

  • 使用后缀尝试。使用您的模式创建一个 trie,然后在探索树的出现时迭代文本。想象一下,您的模式之一是aaa,而您的文本是aaaaa。解决方案应该是 3,您可以使用后缀尝试来实现。
  • 你的字符串总是用空格分隔的吗?
  • 另外,["foo", "bar", "foobar"]"foobar" 的结果应该是什么?
  • 感谢您提出的好问题 - 我今天的用例是一个以空格分隔的单词列表,其中需要匹配整个单词(即,没有类似 'foobar' 的问题)

标签: python algorithm


【解决方案1】:

使用collelctions.Counter的另一种方式:

from collections import Counter
word_counts = Counter(target_string.split(' '))
total = sum(word_counts.get(w, 0)) for w in string_list)

【讨论】:

  • 虽然没有回答这个问题!它说无处只考虑完整的空格分隔标记。字符串中的出现似乎是一个更复杂的问题。
  • @schwobaseggl OP 也没有提及复杂性 :) 它回答了问题中的问题。
【解决方案2】:

这行得通!

def occurrence_counter(target_string):
    return sum(map(lambda x: x in string_list, target_string.split(' ')))

字符串被拆分为标记,然后每个标记在列表中时转换为 1,否则为 0。 sum 函数最终将这些值相加。

编辑:也:

def occurrence_counter(target_string):
    return len(list(filter(lambda x: x in string_list, target_string.split(' '))))

【讨论】:

  • 在这种情况下,1 if x if string_list else 0x in string_list 相同。但无论如何,这不计算出现次数。
  • 你说得对,已编辑。为什么不计算出现次数?
  • 如果一个字符串在目标列表中出现几次,OP希望它每次都被计数(因此示例中的预期结果是4,尽管string_list只包含3个元素)。
  • 它确实返回 4 :) 如果每个令牌在 string_list 中,则它将为 1。在示例中为[1, 1, 1, 1],因此总和为 4
【解决方案3】:

这个 Python3 应该可以工作:

In [4]: string_list = [
   ...:     "foo",
   ...:     "bar",
   ...:     "baz"
   ...: ]
   ...: 
   ...: set_of_counted_word = set(string_list)
   ...: 
   ...: def occurrence_counter(target_str, words_to_count=set_of_counted_word):
   ...:     return sum(1 for word in target_str.strip().split()
   ...:                if word in words_to_count)
   ...: 
   ...: 
   ...: for target_string in ("foo bar baz bar", " bip foo bap foo dib baz   "):
   ...:     print("Input: %r -> Count: %i" % (target_string, occurrence_counter(target_string)))
   ...: 
   ...: 
Input: 'foo bar baz bar' -> Count: 4
Input: ' bip foo bap foo dib baz   ' -> Count: 3

In [5]:

【讨论】:

  • 很好地处理额外的空格和填充。
【解决方案4】:

您可以使用变量来存储运行计数,如果您像这样遍历列表:

def occurence_counter(x):
    count = 0
    for y in x:
        count +=1
    return count

【讨论】:

    【解决方案5】:

    另一种解决方案:

    def occurrence_counter(target_string, string_list):
        target_list = target_string.split(' ')
        return len([w for w in target_list if w in string_list])
    

    【讨论】:

      【解决方案6】:

      sumstring.count 的组合:

      def counter(s, lst)
          return sum(s.count(sub) for sub in lst)
      

      这不会计算相同模式的重叠出现次数。

      【讨论】:

      • @SembeiNorimaki:目前还不清楚重叠模式的预期结果是什么。
      • @SembeiNorimaki 是的。不过,这感觉像是一个边缘案例。然而,它是 Python 式的、内置的、简洁的,并且可能比遭受相同问题的其他建议更高效;)
      • 为什么它应该比其他解决方案性能更高?没有优化,所以它是O(m*n),在lst 中有n 字符和m 元素。如果元素用空格分隔,Counter 解决方案要快得多。如果没有,Trie 解决方案会快得多。
      • @EricDuminil 没错,但是,不建议使用 trie 解决方案(作为答案),并且令牌 Counter 几乎没有考虑尽可能多的子字符串。事实上,这里没有解决真正的子字符串出现次数的解决方案。
      • 对于我的用例,重叠模式目前不是问题。另外,我没有说明这一点(对不起!),但我需要匹配确切的单词边界(即匹配“foo”,但不匹配“food”)
      【解决方案7】:

      您可以使用Trie 将您的子字符串转换为正则表达式模式(例如(?:ba[rz]|foo))并解析您的target_string

      import re
      from trie import Trie
      
      trie = Trie()
      
      substrings = [
          "foo",
          "bar",
          "baz"
      ]
      for substring in substrings:
          trie.add(substring)
      print(trie.pattern())
      # (?:ba[rz]|foo)
      
      target_string = "foo bar baz bar"
      print(len(re.findall(trie.pattern(), target_string)))
      # 4
      

      所需的库在这里:trie.py

      它应该比为每个 substring 解析整个 target_string 快得多,但它可能不会返回重叠子字符串的所需结果。它为["foo", "bar", "foobar"]"foobar" 返回2

      一个相关的问题是:“Speed up millions of regex replacements in Python 3”:这是answer with setsone with a trie regex

      【讨论】:

      • 如果substrings 包含 2-300 个字,这有意义吗?
      • @KevinBedell:用两个词,肯定不是。有几十个或几百个,是的。链接的答案有数千个,trie 正则表达式比正则表达式联合快 1000。
      • @KevinBedell:接受的答案效率不高,只适用于完整的单词,用空格分隔。
      • 我目前只需要处理用空格分隔的完整单词。它的优点是除了内置函数之外不需要任何东西,简洁并且几乎不需要时间来适应我的需求。完成工作编码后,我选择了对解决我的问题最有用的答案。我赞成这个答案,因为我觉得这也是一个可靠的答案。
      • @KevinBedell:对不起,我不想听起来粗鲁。我现在更了解您的需求。在这种情况下,您可能需要使用Counter 解决方案,它应该是最快和最干净的解决方案。
      【解决方案8】:

      我不确定这是最pythonic的方式,但你可以试试:

      string_list_B = target_string.split(" ")
      commonalities = set(string_list) - (set(string_list) - set(string_list_B))
      

      【讨论】:

      • 我不认为这会计算出现次数,但它确实会找到列表中的单词。
      • @Kevin Bedell 我同意,我想可以通过集合的长度找到“唯一”的出现
      猜你喜欢
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 2014-06-19
      • 2014-08-22
      • 2016-01-03
      • 2020-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多