Более быстрый способ удаления стоп-слов в 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
Другие вопросы по тегам