我正在尝试从文本字符串中删除停用词:
1
2
3from 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'))])
我正在处理600万这种字符串,因此速度很重要。 分析我的代码,最慢的部分是上面的几行,是否有更好的方法来做到这一点? 我正在考虑使用正则表达式的re.sub之类的东西,但是我不知道如何为一组单词写模式。 有人可以帮我吗,我也很高兴听到其他可能更快的方法。
注意:我尝试过有人建议用set()包装stopwords.words('english'),但这没什么区别。
谢谢。
stopwords.words(english)有多大?
@SteveBarnes 127个单词的列表
您将其包装在列表理解内还是外部? 尝试添加stw_set = set(stopwords.words(english))并使用此对象代替
@alko我以为我将其包装在外面并没有任何效果,但是我只是再次尝试了一下,现在我的代码运行速度至少快了10倍!!!
您是逐行处理文本还是一起处理文本?
@glasslion逐行
NLTK可能会删除停用词
尝试缓存停用词对象,如下所示。每次调用函数时都要构造它,这似乎是瓶颈。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from 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()
我通过探查器运行了此命令:python -m cProfile -s累积test.py。相关行如下。
nCalls累积时间
10000 7.723个单词.py:7(testFuncOld)
10000 0.140个单词。py:11(testFuncNew)
因此,缓存停用词实例可以使速度提高约70倍。
同意性能的提高来自缓存停用词,而不是真正创建set。
当然,您不必每次都从磁盘读取列表,因此会得到极大的提升,因为那是最耗时的操作。但是,如果您现在将"已缓存"列表变成一组(当然只有一次),您将获得另一个提升。
谁能告诉我这是否支持日语?
它给了我这个UnicodeWarning:Unicode相等比较无法将两个参数都转换为Unicode-将它们解释为不相等的text = .join([如果不是stop_words中的单词,则为text.split()中的单词)] Salomone为我提供了解决方案这个
它也可以与pandas数据帧一起使用吗(加速)df[column]=df[column].apply(lambda x: [item for item in x if item not in cachedStopWords])
使用正则表达式删除所有不匹配的单词:
1
2
3import re
pattern = re.compile(r'\b(' + r'|'.join(stopwords.words('english')) + r')\b\s*')
text = pattern.sub('', text)
这可能比循环您自己快得多,特别是对于大型输入字符串。
如果由此删除了文本中的最后一个单词,则您可能会有尾随空格。我建议分开处理。
知道这有多复杂吗?如果w =我的文本中的单词数,而s =停止列表中的单词数,我认为循环将以w log s的顺序进行。在这种情况下,w约为s,因此其w log w。 grep会不会慢一些,因为它(必须)逐个字符地匹配?
实际上,我认为O(...)含义的复杂性是相同的。两者都是O(w log s),是的。但是,正则表达式是在较低的级别上实现的,并且进行了优化。单词拆分已经导致复制所有内容,创建字符串列表以及列表本身,所有这一切都花费了宝贵的时间。
首先,您要为每个字符串创建停用词。创建一次。集合确实在这里很棒。
1forbidden_words = set(stopwords.words('english'))
稍后,摆脱join内部的[]。请改用生成器。
1' '.join([x for x in ['a', 'b', 'c']])
替换为
1' '.join(x for x in ['a', 'b', 'c'])
接下来要处理的是使.split()产生值,而不是返回数组。 我相信regex在这里将是很好的替代品。 s>有关为什么s.split()确实很快的原因,请参见此内容。
最后,并行执行此作业(删除6m字符串中的停用词)。那是一个完全不同的话题。
我怀疑使用正则表达式会有所改进,请参见stackoverflow.com/questions/7501609/python-re-split-vs-split/
刚刚发现它。 :)
谢谢。 set至少使速度提高了8倍。为什么使用生成器有帮助? RAM对我来说不是问题,因为每段文本都非常小,大约100-200个字。
实际上,我已经看到join在列表理解方面的性能要优于等效的生成器表达式。
抱歉回复晚了。
将会对新用户有用。
使用集合库创建停用词词典
使用该字典进行非常快速的搜索(时间= O(1)),而不要在列表上进行搜索(时间= O(停用词))
1
2
3
4from 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])
与基于正则表达式的方法相比,确实确实可以大大加快速度。