Более быстрый способ удаления стоп-слов в 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 миллионов таких строк, поэтому скорость важна. Профилирование моего кода, самая медленная часть строки выше, есть ли лучший способ сделать это? Я думаю об использовании чего-то вроде регулярных выражений re.sub
но я не знаю, как написать шаблон для набора слов. Может ли кто-нибудь помочь мне, и я также рад услышать другие, возможно, более быстрые методы.
Примечание: я пытался предложить чью-то обертку stopwords.words('english')
с set()
но это не имело значения.
Спасибо.
6 ответов
Попробуйте кэшировать объект стоп-слов, как показано ниже. Создание этого каждый раз, когда вы вызываете функцию, кажется узким местом.
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 cumulative test.py. Соответствующие строки размещены ниже.
nCalls совокупное время
10000 7,723 words.py:7(testFuncOld)
10000 0.140 words.py:11(testFuncNew)
Таким образом, кэширование экземпляра стоп-слов дает ускорение в ~70 раз.
Извините за поздний ответ. Будет полезным для новых пользователей.
- Создать словарь стоп-слов с использованием библиотеки коллекций
Используйте этот словарь для очень быстрого поиска (время = 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 stopwords_dict[word]==0])
Используйте регулярное выражение, чтобы удалить все слова, которые не совпадают:
import re
pattern = re.compile(r'\b(' + r'|'.join(stopwords.words('english')) + r')\b\s*')
text = pattern.sub('', text)
Вероятно, это будет намного быстрее, чем зацикливание, особенно для больших строк ввода.
Если последнее слово в тексте будет удалено из-за этого, у вас может быть завершающий пробел. Я предлагаю справиться с этим отдельно.
Сначала вы создаете стоп-слова для каждой строки. Создайте это один раз. Набор был бы здесь действительно хорош.
forbidden_words = set(stopwords.words('english'))
Позже избавимся от []
внутри join
, Вместо этого используйте генератор.
' '.join([x for x in ['a', 'b', 'c']])
заменить на
' '.join(x for x in ['a', 'b', 'c'])
Следующая вещь, с которой нужно иметь дело, это сделать .split()
выдавать значения вместо возврата массива. я верю regex
будет хорошей заменой здесь. Посмотри на этот вопрос, почему s.split()
на самом деле быстро.
Наконец, выполняйте такую работу параллельно (удаляя стоп-слова в строках длиной 6 м). Это совсем другая тема.
Попробуйте использовать это, избегая цикла и вместо этого используя регулярное выражение для удаления стоп-слов:
import re
from nltk.corpus import stopwords
cachedStopWords = stopwords.words("english")
pattern = re.compile(r'\b(' + r'|'.join(cachedStopwords) + r')\b\s*')
text = pattern.sub('', text)
Использование обычного диктофона кажется самым быстрым решением.
Превосходит даже решение Counter примерно на 10%
from nltk.corpus import stopwords
stopwords_dict = {word: 1 for word in stopwords.words("english")}
text = 'hello bye the the hi'
text = " ".join([word for word in text.split() if word not in stopwords_dict])
Протестировано с помощью профилировщика cProfile
Вы можете найти тестовый код, используемый здесь: https://gist.github.com/maxandron/3c276924242e7d29d9cf980da0a8a682 .
РЕДАКТИРОВАТЬ:
Вдобавок ко всему, если мы заменим понимание списка циклом, мы получим еще 20% прирост производительности.
from nltk.corpus import stopwords
stopwords_dict = {word: 1 for word in stopwords.words("english")}
text = 'hello bye the the hi'
new = ""
for word in text.split():
if word not in stopwords_dict:
new += word
text = new