Python - Оптимальный код для поиска предшествующих и следующих пяти слов в заданной точке - PullRequest
3 голосов
/ 24 марта 2011

Я пытаюсь написать код, чтобы найти 5 слов с каждой стороны определенной фразы. Это достаточно просто, но я должен сделать это для огромного объема данных, поэтому код должен быть оптимальным!

for file in listing:
    file2 = open('//home/user/Documents/Corpus/Files/'+file,'r')
    for line in file2:
        linetrigrams = trigram_split(line)
        for trigram in linetrigrams:
            if trigram in trigrams:
                line2 = line.replace(trigram,'###').split('###')
                window = (line2[0].split()[-5:] + line2[1].split()[:5])
                for item in window:
                    if item in mostfreq:
                        matrix[trigram][mostfreq[item]] += 1

Есть предложения, чтобы сделать это намного быстрее? Возможно, я здесь работаю с совершенно неправильными структурами данных. trigram_split () просто дает все триграммы из линии (для которых нужны единицы измерения, для которых я должен создавать векторы). «Триграммы» - это, по сути, список из миллиона триграмм, для которых я занимаюсь созданием векторов. Окно получает 5 слов, предшествующих и следующих за триграммой (если эта триграмма находится в списке), а затем проверяет, есть ли они в списке MostFreq (который представляет собой словарь из 1000 слов в качестве ключей, каждое из которых соответствует целому числу [ 0-100] как сохраненное значение). Затем он используется для обновления матрицы (словарь со списками ([0] * 1000) в качестве сохраненных значений). Соответствующее значение в псевдоматрице увеличивается таким образом.

Ответы [ 5 ]

3 голосов
/ 24 марта 2011

Несколько важных факторов, которые следует учитывать при взвешивании различных подходов:

  • Многострочный по сравнению с одной строкой
  • Длина строк
  • Длина шаблона поиска
  • Частота поиска, совпадающая с
  • Что делать, если до / после осталось менее 5 слов
  • Как обрабатывать несловные, непробельные символы (символы новой строки и пунктуации)
  • Без учета регистра?
  • Что делать с перекрывающимися совпадениями?Например, если текст We are the knights who say NI! NI NI NI NI NI NI NI NI и вы ищете NI, что вы возвращаете?Это случится с вами?
  • Что, если в ваших данных будет ###?
  • Вы бы предпочли пропустить некоторые или выдать дополнительные неверные результаты?Там могут быть некоторые компромиссы, особенно с грязными данными реального мира.

Вы можете попробовать регулярное выражение ...

import re
zen = """Beautiful is better than ugly. \
Explicit is better than implicit. \
Simple is better than complex. \
Complex is better than complicated. \
Flat is better than nested. \
Sparse is better than dense. \
Readability counts. \
Special cases aren't special enough to break the rules. \
Although practicality beats purity. \
Errors should never pass silently. \
Unless explicitly silenced. \
In the face of ambiguity, refuse the temptation to guess. \
There should be one-- and preferably only one --obvious way to do it. \
Although that way may not be obvious at first unless you're Dutch. \
Now is better than never. \
Although never is often better than *right* now. \
If the implementation is hard to explain, it's a bad idea. \
If the implementation is easy to explain, it may be a good idea. \
Namespaces are one honking great idea -- let's do more of those!"""

searchvar = 'Dutch'
dutchre = re.compile(r"""((?:\S+\s*){,5})(%s)((?:\S+\s*){,5})""" % searchvar, re.IGNORECASE | re.MULTILINE)
print dutchre.findall(zen)
#[("obvious at first unless you're ", 'Dutch', '. Now is better than ')]

Альтернативный подход, который приводит к худшим результатам IMO...

def splitAndFind(text, phrase):
    text2 = text.replace(phrase, "###").split("###")
    if len(text2) > 1:
        return ((text2[0].split()[-5:], text2[1].split()[:5]))
print splitAndFind(zen, 'Dutch')
#(['obvious', 'at', 'first', 'unless', "you're"],
# ['.', 'Now', 'is', 'better', 'than'])

В iPython вы можете легко рассчитать время:

timeit dutchre.findall(zen)
1000 loops, best of 3: 814 us per loop

timeit 'Dutch' in zen
1000000 loops, best of 3: 650 ns per loop

timeit zen.find('Dutch')
1000000 loops, best of 3: 812 ns per loop

timeit splitAndFind(zen, 'Dutch')
10000 loops, best of 3: 18.8 us per loop
2 голосов
/ 24 марта 2011

Вы можете использовать модуль re

import re

f = open('filepath', 'r')
txt = f.read()
# 'hey' is the search phrase
phrase = 'hey'
matches = re.findall(r'(\w+\s+\w+\s+\w+\s+\w+\s+\w+)\s+%s\s+(\w+\s+\w+\s+\w+\s+\w+\s+\w+)' % phrase, txt)

Это должно дать вам все совпадения для файла. Вам понадобится os.walk, чтобы получить все файлы.

1 голос
/ 24 марта 2011

Обычно массовые данные хранятся в файлах; загрузка таких файлов в память только для их поиска может замедлить поиск - поэтому ищите файл на диске. Однако для поиска 5 слов, предшествующих фразе, вам понадобится произвольный доступ, помните об этом.

Что касается поиска, алгоритм поиска строк Бойера-Мура обычно быстрее, чем наивный подход. Если вы работаете с файлами произвольного доступа, это также может исключить необходимость считывания всего файла в память.

Особенно, если ваша фраза часто меняется, но данные не меняются, тогда рассмотрите возможность использования какой-либо системы полнотекстового поиска, как в Система полнотекстового поиска для Python - однако, это может быть излишним для этого вида домашнего задания.

1 голос
/ 24 марта 2011

Я пробовал этот скрипт на 18 МБ файле:

for line in f:
    if phrase in line:
        line2 = line.replace(phrase,'###').split('###')
        result.append(line2[0].split()[-5:] + line2[1].split()[:5])

Прошедшее время: 1.12602651429 s

0 голосов
/ 24 марта 2011

Если бы скорость была действительно важна, я бы написал модуль расширения Python C , чтобы выполнить тяжелую работу. Это может дать огромные улучшения скорости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...