Более быстрый способ удаления стоп-слов в Python
Я пытаюсь удалить стоп-слова из строки текста:
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'))])
Я обрабатываю 6 мил таких строк, поэтому скорость важна. Профилирование моего кода, самая медленная часть - это строки выше, есть ли лучший способ сделать это? Я думаю использовать что-то вроде regex re.sub
, но я не знаю, как написать шаблон для набора слов. Может кто-то дать мне руку, и я также рад услышать другие, возможно, более быстрые методы.
Примечание. Я попробовал, чтобы кто-то предложил обернуть stopwords.words('english')
с помощью set()
, но это не имело значения.
Спасибо.
Ответы
Ответ 1
Попробуйте кэшировать объект стоп-слов, как показано ниже. Построение этого каждый раз, когда вы вызываете функцию, кажется узким местом.
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()
Я провел это через профайлер: python -m cProfile -s кумулятивный test.py. Соответствующие строки размещены ниже.
nCallulative Time
10000 7.723 words.py:7(testFuncOld)
10000 0.140 words.py:11(testFuncNew)
Итак, кеширование экземпляра стоп-слов дает ускорение ~ 70x.
Ответ 2
Используйте regexp для удаления всех слов, которые не соответствуют:
import re
pattern = re.compile(r'\b(' + r'|'.join(stopwords.words('english')) + r')\b\s*')
text = pattern.sub('', text)
Это, вероятно, будет быстрее, чем цикл, особенно для больших входных строк.
Если последнее слово в тексте удаляется этим, у вас может быть пробел в пробелах. Я предлагаю обработать это отдельно.
Ответ 3
Сначала вы создаете стоп-слова для каждой строки. Создайте его один раз. Множество было бы здорово здесь.
forbidden_words = set(stopwords.words('english'))
Позже избавьтесь от []
внутри join
. Вместо этого используйте генератор.
' '.join([x for x in ['a', 'b', 'c']])
заменить на
' '.join(x for x in ['a', 'b', 'c'])
Следующая вещь, с которой нужно иметь дело, заключалась в том, чтобы сделать значения .split()
yield вместо возвращения массива. Я считаю, что regex
будет хорошей заменой здесь. См. thist hread, почему s.split()
на самом деле быстро.
Наконец, выполните такое задание параллельно (удаление стоп-слов в строках длиной 6 м). Это совершенно другая тема.
Ответ 4
Извините за поздний ответ. Будет полезным для новых пользователей.
- Создать словарь стоп-слов с использованием библиотеки коллекций
-
Используйте этот словарь для очень быстрого поиска (время = O (1)), а не в списке (время = O (стоп-слова))
from collections import Counter
stop_words = stopwords.words('english')
stopwords_dict = Collections.counter(stop_words)
text = ' '.join([word for word in text.split() if word not in stopword_dict])