更快地删除Python中的停用词

mch*_*gun 35 python regex stop-words

我试图从一串文本中删除停用词:

from nltk.corpus import stopwords
text = 'hello bye the the hi'
text = ' '.join([word for word in text.split() if word not in (stopwords.words('english'))])
Run Code Online (Sandbox Code Playgroud)

我正在处理6密耳的这种弦,所以速度很重要.分析我的代码,最慢的部分是上面的行,有没有更好的方法来做到这一点?我正在考虑使用像正则表达式这样的东西,re.sub但我不知道如何为一组单词编写模式.有人可以帮助我,我也很高兴听到其他可能更快的方法.

注意:我试过有人建议包装stopwords.words('english'),set()但没有区别.

谢谢.

小智 80

尝试缓存stopwords对象,如下所示.每次调用函数时构造它似乎都是瓶颈.

    from nltk.corpus import stopwords

    cachedStopWords = stopwords.words("english")

    def testFuncOld():
        text = 'hello bye the the hi'
        text = ' '.join([word for word in text.split() if word not in stopwords.words("english")])

    def testFuncNew():
        text = 'hello bye the the hi'
        text = ' '.join([word for word in text.split() if word not in cachedStopWords])

    if __name__ == "__main__":
        for i in xrange(10000):
            testFuncOld()
            testFuncNew()
Run Code Online (Sandbox Code Playgroud)

:我通过探查跑这蟒蛇-m CPROFILE -s累计test.py.相关行列在下面.

n呼叫累计时间

10000 7.723 words.py:7(testFuncOld)

10000 0.140 words.py:11(testFuncNew)

因此,缓存停用词实例可提供~70倍的加速.

  • 当然,每次都不必从磁盘读取列表,从而获得了巨大的推动,因为这是最耗时的操作.但是如果你现在把你的"缓存"列表变成一个集合(当然只有一次),你将获得另一个提升. (5认同)

Gul*_*gid 16

抱歉回复晚了。证明对新用户有用。

  • 使用集合库创建停用词字典
  • 使用该字典进行非常快速的搜索(时间 = O(1)),而不是在列表中进行搜索(时间 = O(停用词))

    from collections import Counter
    stop_words = stopwords.words('english')
    stopwords_dict = Counter(stop_words)
    text = ' '.join([word for word in text.split() if word not in stopwords_dict])
    
    Run Code Online (Sandbox Code Playgroud)

  • 我相信使用这个 (`collections.Counter(stopwords.words('english')`) 不会比使用 `set(stopwords.words('english'))` 更快。collections.Counter 方法只会不必要地使用更多内存。 (3认同)
  • 上面的代码速度快的主要原因是我们正在字典中搜索,字典基本上是一个哈希图。在 hashmap 中,搜索时间为 O(1)。除此之外,Counter 是集合库的一部分,并且库是用 C 编写的,并且由于 C 比 python 快得多,因此 Counter 比用 python 编写的类似代码更快 (2认同)

Alf*_*lfe 14

使用正则表达式删除所有不匹配的单词:

import re
pattern = re.compile(r'\b(' + r'|'.join(stopwords.words('english')) + r')\b\s*')
text = pattern.sub('', text)
Run Code Online (Sandbox Code Playgroud)

这大概是这样比你自己的循环,特别是对于大的输入字符串更快.

如果文本中的最后一个单词被删除,则可能有尾随空格.我建议分开处理.

  • 实际上我认为O(...)含义的复杂性是相同的.两者都是"O(w log s)",是的.**但是**regexp在更低的级别上实现并且进行了大量优化.已经分裂的单词将导致复制所有内容,创建字符串列表和列表本身,所有这些都需要宝贵的时间. (2认同)

Krz*_*arz 7

首先,您为每个字符串创建停用词。创建一次。在这里设置确实很棒。

forbidden_words = set(stopwords.words('english'))
Run Code Online (Sandbox Code Playgroud)

[]后来,把里面的东西干掉join。使用发电机代替。

代替

' '.join([x for x in ['a', 'b', 'c']])
Run Code Online (Sandbox Code Playgroud)

' '.join(x for x in ['a', 'b', 'c'])
Run Code Online (Sandbox Code Playgroud)

接下来要处理的事情是生成.split()收益值而不是返回数组。我相信regex这里会是一个很好的替代品。请参阅此线程以了解为什么s.split()实际上很快。

最后,并行做这样的工作(去除6m字符串中的停用词)。那是一个完全不同的话题。

  • 实际上,我已经看到“join”在列表理解方面比等效的生成器表达式表现得更好。 (2认同)