Наиболее эффективный способ индексирования слов в документе?

это возникло в другом вопросе, но я решил, что лучше задать это как отдельный вопрос. Дать большой список предложений (порядка 100 тысяч):

[
"This is sentence 1 as an example",
"This is sentence 1 as another example",
"This is sentence 2",
"This is sentence 3 as another example ",
"This is sentence 4"
]

каков наилучший способ кодирования следующей функции?

def GetSentences(word1, word2, position):
    return ""

где дано два слова,word1, word2 и position функция должна возвращать список всех предложений, удовлетворяющих этим ограничением. Например:

GetSentences("sentence", "another", 3)

должен возвращать предложений 1 и 3 как индекс предложения. Мой текущий подход использовал такой словарь:

Index = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: [])))

for sentenceIndex, sentence in enumerate(sentences):
    words = sentence.split()
    for index, word in enumerate(words):
        for i, word2 in enumerate(words[index:):
            Index[word][word2][i+1].append(sentenceIndex)

но это быстро взрывает все непропорционально на наборе данных, который составляет около 130 МБ в размере, поскольку мой 48GB RAM исчерпан менее чем за 5 минут. Я почему-то чувствую, что это общая проблема, но не могу найти никаких ссылок на то, как это эффективно решить. Есть предложения, как к этому подойти?

2 ответов


используйте базу данных для хранения значений.

  1. первый добавить все предложения в одну таблицу (у них должны быть идентификаторы). Вы можете назвать его, например. sentences.
  2. во-вторых,создать таблицу со словами содержится во всех предложениях (назовите его, например. words, дайте каждому слову идентификатор), сохраняя связь между записями таблицы предложений и записями таблицы слов в отдельной таблице (назовите ее, например. sentences_words, он должен иметь два столбца, предпочтительно word_id и sentence_id).
  3. при поиске предложений, содержащих все упомянутые слова, ваша работа будет упрощена:

    1. вы должны сначала найти записи из words стол, где слова именно те, которые вы ищете. Запрос может выглядеть так:

      SELECT `id` FROM `words` WHERE `word` IN ('word1', 'word2', 'word3');
      
    2. во-вторых, вы должны найти sentence_id значения из таблицы sentences что требуется word_id значения (соответствует словам из words таблица). Первоначальный запрос может выглядеть так:

      SELECT `sentence_id`, `word_id` FROM `sentences_words`
      WHERE `word_id` IN ([here goes list of words' ids]);
      

      который можно было бы упростить до этого:

      SELECT `sentence_id`, `word_id` FROM `sentences_words`
      WHERE `word_id` IN (
          SELECT `id` FROM `words` WHERE `word` IN ('word1', 'word2', 'word3')
      );
      
    3. отфильтровать результат в Python возвратить только sentence_id значения, которые имеют все необходимые word_id IDs вам нужно.

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

EDIT:

  1. если вы будете искать только два слова, вы можете сделать еще больше (почти все) на СУБД стороны.
  2. учитывая, что вам также нужна разница в позиции, вы должны сохранить позицию слова в третьем столбце sentences_words таблица (назовем ее просто position) и при поиске соответствующих слов, вы должны рассчитать разницу этого значения, связанного с обоими словами.

вот как я это сделал в Python. Хотя, предполагая, что это нужно сделать более одного раза, СУБД является правильным инструментом для работы. Однако это, кажется, работает довольно хорошо для меня с миллионом строк.

sentences = [
    "This is sentence 1 as an example",
    "This is sentence 1 as another example",
    "This is sentence 2",
    "This is sentence 3 as another example ",
    "This is sentence 4"
    ]

sentences = sentences * 200 * 1000

sentencesProcessed = []

def preprocess():
    global sentences
    global sentencesProcessed
    # may want to do a regex split on whitespace
    sentencesProcessed = [sentence.split(" ") for sentence in sentences]

    # can deallocate sentences now
    sentences = None


def GetSentences(word1, word2, position):
    results = []
    for sentenceIndex, sentence in enumerate(sentencesProcessed):
        for wordIndex, word in enumerate(sentence[:-position]):
            if word == word1 and sentence[wordIndex + position] == word2:
                results.append(sentenceIndex)
    return results

def main():
    preprocess()
    results = GetSentences("sentence", "another", 3)
    print "Got", len(results), "results"

if __name__ == "__main__":
    main()