【问题标题】:Count occurrences of a couple of specific words计算几个特定单词的出现次数
【发布时间】:2020-11-28 11:43:43
【问题描述】:

我有一个单词列表,比如说:["foo", "bar", "baz"] 和一个可能出现这些单词的大字符串。

我现在对列表中的每个单词使用 "string".count("word") 方法。这工作正常,但似乎相当低效。对于添加到列表中的每个额外单词,必须在额外的时间内迭代整个字符串。

他们是否有更好的方法来做到这一点,或者我应该实现一个自定义方法,对大字符串进行一次迭代,检查每个字符是否已达到列表中的单词之一?

要明确:

  • 我想要列表中每个单词的出现次数。
  • 每次搜索的字符串都不一样,大约由10000个字符组成
  • 单词列表是不变的
  • 单词列表中的单词可以包含空格

【问题讨论】:

  • 单词是用空格分隔的吗?如果是,那么from collections import Counter
  • “似乎效率很低。对于添加到列表中的每个额外单词” ...“要清楚:单词列表是恒定的”..??

标签: python


【解决方案1】:

为您的单词制作一个dict-typed 频率表,然后遍历您的字符串中的单词。

vocab = ["foo", "bar", "baz"]
s = "foo bar baz bar quux foo bla bla"

wordcount = dict((x,0) for x in vocab)
for w in re.findall(r"\w+", s):
    if w in wordcount:
        wordcount[w] += 1

编辑:如果列表中的“单词”包含空格,您可以用它们构建一个 RE:

from collections import Counter

vocab = ["foo bar", "baz"]
r = re.compile("|".join(r"\b%s\b" % w for w in vocab))
wordcount = Counter(re.findall(r, s))

解释:这会从词汇表中构建 RE r'\bfoo bar\b|\bbaz\b'findall 然后找到列表 ['baz', 'foo bar']Counter (Python 2.7+) 计算其中每个不同元素的出现次数。 注意,您的单词列表不应包含 RE 特有的字符,例如 ()[]\

【讨论】:

    【解决方案2】:

    假设需要单独查找单词(即您要按str.split()制作的单词计数):

    编辑:正如 cmets 中所建议的,此处计数器是一个不错的选择:

    from collections import Counter
    
    def count_many(needles, haystack):
        count = Counter(haystack.split())
        return {key: count[key] for key in count if key in needles}
    

    运行如下:

    count_many(["foo", "bar", "baz"], "testing somefoothing foo bar baz bax foo foo foo bar bar test bar test")
    {'baz': 1, 'foo': 4, 'bar': 4}
    

    请注意,在 Python return dict((key, count[key]) for key in count if key in needles)。

    当然,另一种选择是简单地返回整个 Counter 对象,并仅在需要时获取所需的值,因为根据情况,拥有额外的值可能不是问题。

    旧答案:

    from collections import defaultdict
    
    def count_many(needles, haystack):
        count = defaultdict(int)
        for word in haystack.split():
            if word in needles:
                count[word] += 1
        return count
    

    结果:

    count_many(["foo", "bar", "baz"], "testing somefoothing foo bar baz bax foo foo foo bar bar test bar test")
    defaultdict(<class 'int'>, {'baz': 1, 'foo': 4, 'bar': 4})
    

    如果您非常反对返回 defaultdict(您不应该这样做,因为它在访问时的功能与 dict 完全相同),那么您可以使用 return dict(count) 来获取普通字典。

    【讨论】:

      【解决方案3】:

      您的字符串有多长,我知道它不会像您的字符串列表那样不断变化?

      一个好主意是遍历字符串中的单词并为单词创建字典并增加每个单词的计数。有了这个。然后,您可以在字典的列表中查找单词并输出它的值,即出现次数。

      【讨论】:

        【解决方案4】:

        Counter 方法不适用于大型词汇表。在下面的示例中,CountVectorizer 的速度要快很多倍:

        import time
        import random
        
        longstring = ["foo", "bar", "baz", "qux", "thud"] * 100000
        random.shuffle(longstring)
        longstring = " ".join(longstring)
        vocab = ["foo bar", "baz"] + ["nothing"+str(i) for i in range(100000)]
        

        测试:

        import re
        from collections import Counter
        
        tic = time.time()
        r = re.compile("|".join(r"\b%s\b" % w for w in vocab))
        wordcount = Counter(re.findall(r, longstring))
        print(time.time() - tic)
        

        870 秒

        from sklearn.feature_extraction.text import CountVectorizer
        from numpy import array
        
        tic = time.time()
        vectorized = CountVectorizer(vocabulary=vocab, ngram_range=(1, 2)).fit([longstring])  # list strings contains 1 to 2 words
        counts = vectorized.transform([longstring])
        counts = array(counts.sum(axis=0))[0]
        wordcount = {vocab[i]: counts[i] for i in range(len(vocab))}
        print(time.time() - tic)
        

        1.17 秒

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-04
          • 2020-09-09
          • 1970-01-01
          相关资源
          最近更新 更多