Повторяющиеся фразы в тексте Python

У меня есть проблема, и я не знаю, как ее решить. Пожалуйста, дайте совет.

У меня есть текст. Большой, большой текст. Задача состоит в том, чтобы найти в тексте все повторяющиеся фразы, длина которых равна 3(содержит три слова).

4 ответа

У вас, как мне кажется, две проблемы.

Первый - это эффективный способ нормализации ввода. Вы говорите, что хотите найти все фразы из трех слов во входных данных, но из чего состоит фраза? Например, являются the black dog а также The black, dog? та же фраза?

Способ сделать это, как предполагает Marcog, - использовать что-то вроде re.findall, Но это довольно неэффективно: он обходит весь ваш ввод и копирует слова в список, а затем вам нужно обработать этот список. Если ваш вводимый текст очень длинный, это будет расточительно как во времени, так и в пространстве.

Лучшим подходом было бы рассматривать входные данные как поток и создать генератор, который выводит по одному слову за раз. Вот пример, который использует пробелы в качестве разделителя между словами, затем удаляет из слов не-буквенные символы и преобразует их в нижний регистр:

>>> def words(text):
       pattern = re.compile(r"[^\s]+")
       non_alpha = re.compile(r"[^a-z]", re.IGNORECASE)
       for match in pattern.finditer(text):
           nxt = non_alpha.sub("", match.group()).lower()
           if nxt:  # skip blank, non-alpha words
               yield nxt


>>> text
"O'er the bright blue sea, for Sir Joseph Porter K.C.B."
>>> list(words(text))
['oer', 'the', 'bright', 'blue', 'sea', 'for', 'sir', 'joseph', 'porter', 'kcb']

Вторая проблема - группировка нормализованных слов в трехсловные фразы. Опять же, вот место, где генератор будет работать эффективно:

>>> def phrases(words):
        phrase = []
        for word in words:
            phrase.append(word)
            if len(phrase) > 3:
                phrase.remove(phrase[0])
            if len(phrase) == 3:
                yield tuple(phrase)

>>> list(phrases(words(text)))
[('oer', 'the', 'bright'), ('the', 'bright', 'blue'), ('bright', 'blue', 'sea'), ('blue', 'sea', 'for'), ('sea', 'for', 'sir'), ('for', 'sir', 'joseph'), ('sir', 'joseph', 'porter'), ('joseph', 'porter', 'kcb')]

Почти наверняка возможна более простая версия этой функции, но она эффективна, и ее нетрудно понять.

Важно отметить, что объединение генераторов в цепочку происходит только один раз, и оно не создает больших временных структур данных в памяти. Вы можете использовать результат, чтобы построить defaultdict ключевая фраза:

>>> import collections
>>> counts = collections.defaultdict(int)
>>> for phrase in phrases(words(text)):
        counts[phrase] += 1

Это делает один проход text как он считает фразы. Когда это будет сделано, найдите в словаре каждую запись, значение которой больше единицы.

Самый грубый способ - читать текст в виде строки. Сделайте string.split() и получите отдельные слова в списке. Затем вы можете нарезать список на три слова и использовать collection.defaultdict(int) для сохранения количества.

d = collection.defaultdict(int)

д [фраза]+=1

как я уже сказал, это очень грубо. Но, конечно, вы должны начать

Я бы посоветовал взглянуть на инструментарий NLTK. Это открытый исходный код и предназначен для обучения естественному языку. Наряду с высокоуровневыми функциями НЛП, он имеет много типов функций и коллекций.

Вот примерное решение O(n), которое должно работать с довольно большими входными текстами. Если он слишком медленный, вы, вероятно, захотите использовать Perl, который был разработан для обработки текста, или C++ для чистой производительности.

>>> s = 'The quick brown fox jumps over the lazy dog'
>>> words = string.lower(s).split()
>>> phrases = collections.defaultdict(int)
>>> for a, b, c in zip(words[:-3], words[1:-2], words[2:]):
...     phrases[(a, b, c)] += 1
... 
>>> phrases
defaultdict(<type 'int'>, {('over', 'the', 'lazy'): 1, ('quick', 'brown', 'fox'): 1, ('the', '
quick', 'brown'): 1, ('jumps', 'over', 'the'): 1, ('brown', 'fox', 'jumps'): 1, ('fox', 'jumps
', 'over'): 1})
>>> [phrase for phrase, count in phrases.iteritems() if count > 1]
>>> []
Другие вопросы по тегам