2016-11-27 1 views
0

Я ищу, сколько раз все слова в сумке слов находятся в статье. Меня не интересует частота каждого слова, но общее количество раз все они найдены в статье. Мне нужно проанализировать сотни статей, поскольку я извлекаю их из Интернета. Мой алгоритм занимает много времени, так как каждая статья составляет около 800 слов.Самый быстрый способ подсчета списка слов в статье с использованием python

Вот что я (где количество этого количество раз слова были найдены в одной статье, статьи содержит строку со всеми словами формирования содержания статьи, и я использую NLTK к разметить)

bag_of_words = tokenize(bag_of_words) 
tokenized_article = tokenize(article) 

occurrences = [word for word in tokenized_article 
        if word in bag_of_words] 

amount = len(occurrences) 

Где tokenized_article выглядит следующим образом:.

[u'sarajevo', u'bosnia', u'herzegovi', u'war', ...] 

и так же bag_of_words.

Мне было интересно, есть ли более эффективный/быстрый способ сделать это, например, с помощью функций NLTK или лямбда.

+0

Я не уверен, как NTLK может помочь вам здесь - вы делаете сравнения строк, и это все. Теперь есть несколько способов сделать это немного более эффективно: сделайте 'bag_of_words'' set', так как это имеет проверку членства в постоянном времени (вместо линейного времени в размере списка). Теперь вы можете подсчитать вхождения N слов в любом наборе в O (N), который вы не можете победить (насколько я могу судить). –

ответ

0

Я предлагаю использовать set для слов, которые вы считаете - set имеет постоянный критерий членства и, следовательно, быстрее, чем использование списка (который имеет тест на членство в линейном времени).

Например:

occurrences = [word for word in tokenized_article 
        if word in set(bag_of_words)] 

amount = len(occurrences) 

Некоторые тесты синхронизации (с искусственно созданного списка, повторяется десять раз):

In [4]: words = s.split(' ') * 10 

In [5]: len(words) 
Out[5]: 1060 

In [6]: to_match = ['NTLK', 'all', 'long', 'I'] 

In [9]: def f(): 
    ...:  return len([word for word in words if word in to_match]) 

In [13]: timeit(f, number = 10000) 
Out[13]: 1.0613768100738525 

In [14]: set_match = set(to_match) 

In [15]: def g(): 
    ...:  return len([word for word in words if word in set_match]) 

In [18]: timeit(g, number = 10000) 
Out[18]: 0.6921310424804688 

Некоторые другие тесты:

In [22]: p = re.compile('|'.join(set_match)) 

In [23]: p 
Out[23]: re.compile(r'I|all|NTLK|long') 

In [24]: p = re.compile('|'.join(set_match)) 

In [28]: def h(): 
    ...:  return len(filter(p.match, words)) 

In [29]: timeit(h, number = 10000) 
Out[29]: 2.2606470584869385 
0

Использовать наборы для членства te жала.

Другим методом проверки может быть подсчет вхождения каждого слова и добавление вхождения, если слово существует, при условии, что статьи содержат некоторую частоту повторяющихся слов и если статья не очень короткая. Скажем, статья содержит 10 «the», теперь мы проверяем только членство один раз вместо 10 раз.

from collections import Counter 
def f(): 
    return sum(c for word, c in Counter(check).items() if word in words) 
0

Если вы не хотите считать, это больше не «мешок слов», а набор слов. Поэтому конвертируйте свой документ в set, если это действительно так.

Избегайте использования петель и лямбда-функций, в частности вложенных. Для этого требуется много переводчик, и он работает медленно.Вместо этого попробуйте использовать оптимизированные вызовы, такие как intersection (для работы, библиотеки, такие как numpy также очень хорошо, потому что они делают свою работу в низкоуровневый C/Fortran/Cython код)

т.е.

count = len(bag_of_words_set.intersection(set(tokenized_article))) 

где word_set - это слова, которые вас интересуют, как set.

Если вы хотите посчитать классическое слово вместо этого использовать collections.Counter:

from collections import Counter 
counter = Counter() 
... 
counter.update(tokenized_article) 

Это будет считать все слова, хотя, в том числе тех, кто не в списке. Вы можете попробовать это, но это может оказаться медленнее из цикла:

bag_of_words_set = set(bag_of_words) 
... 
for w in tokenized_article: 
    if w in bag_of_words_set: # use a set, not a list! 
     counter[w] += 1 

Немного сложнее, но потенциально быстрее, является использование двух Counter с. Один общий и один для документов.

doc_counter.clear() 
doc_counter.update(tokenized_article) 
for w in doc_counter.keys(): 
    if not w in bag_of_words_set: del doc_counter[w] 
counter.update(doc_counter) # untested. 

Использование счетчика для документа полезно, если у вас есть много дубликатов нежелательных слов, где вы можете сохранить поиск. Это также лучше для многопоточной операции (упрощенная синхронизация)

+0

Спасибо. Выполнение явно быстрее, но количество слов, подсчитанных, неверно. Кроме того, я использовал len (word_set.intersection (set (document))), потому что нет атрибута suck как 'intersection_size'. – Nil

+0

Эта версия учитывает только каждое слово один раз за документ, потому что это, по-видимому, было тем, что вы описали. используйте 'from collections import Counter' вместо наборов для подсчета каждого события. –

+0

Извините, я думал, что у python был оптимизированный 'intersection_size', но, по-видимому, это не так. Тогда len является обходным решением, но медленнее. Но, по-видимому, вы все равно не хотите, чтобы набор был на этом уровне. –

Смежные вопросы