2015-04-30 3 views
3

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

Например,

{"a": [[1, 2],[2,1]]} 
The word "a" appears twice in document 1 and once in document 2. 

Я практикую с двумя маленькими файлами.

file1.txt:

Where should I go 
    When I want to have 
    A smoke, 
    A pancake, 
    and a nap. 

file2.txt:

I do not know 
Where my pancake is 
I want to take a nap. 

Вот мой код программы:

def cleanData(myFile): 
    file = open(myFile, "r") 

    data = file.read() 
    wordList = [] 

    #All numbers and end-of-sentence punctuation 
    #replaced with the empty string 
    #No replacement of apostrophes 
    formattedData = data.strip().lower().replace(",","")\ 
       .replace(".","").replace("!","").replace("?","")\ 
       .replace(";","").replace(":","").replace('"',"")\ 
       .replace("1","").replace("2","").replace("3","")\ 
       .replace("4","").replace("5","").replace("6","")\ 
       .replace("7","").replace("8","").replace("9","")\ 
       .replace("0","") 

    words = formattedData.split() #creates a list of all words in the document 
    for word in words: 
     wordList.append(word)  #adds each word in a document to the word list 
    return wordList 

def main(): 

fullDict = {} 

files = ["file1.txt", "file2.txt"] 
docNumber = 1 

for file in files: 
    wordList = cleanData(file) 

    for word in wordList: 
     if word not in fullDict: 
      fullDict[word] = [] 
      fileList = [docNumber, 1] 
      fullDict[word].append(fileList) 
     else: 
      listOfValues = list(fullDict.values()) 
      for x in range(len(listOfValues)): 
       if docNumber == listOfValues[x][0]: 
        listOfValues[x][1] +=1 
        fullDict[word] = listOfValues 
        break 
      fileList = [docNumber,1] 
      fullDict[word].append(fileList) 

    docNumber +=1 
return fullDict 

То, что я пытаюсь сделать, это генерировать что-то вроде этого :

{"a": [[1,3],[2,1]], "nap": [[1,1],[2,1]]} 

Что я получаю это:

{"a": [[1,1],[1,1],[1,1],[2,1]], "nap": [[1,1],[2,1]]} 

Он записывает все вхождения каждого слова во всех документах, но он записывает повторы отдельно. Я не могу понять это. Любая помощь будет оценена! Заранее спасибо. :)

+1

Возможно, счетчик может быть полезным https://docs.python.org/3.4/library/collections.html#collections.Counter – Marein

ответ

2

В коде есть две основные проблемы.

Задача 1

 listOfValues = list(fullDict.values()) 
     for x in range(len(listOfValues)): 
      if docNumber == listOfValues[x][0]: 

Здесь вы берете все значения словаря, независимо от текущего слова и приращения счета, но вы должны быть увеличивающимся счетчик в списках соответствующий к текущему слову. Таким образом, вы должны изменить его на

listOfValues = fullDict[word] 

Задачи 2

 fileList = [docNumber,1] 
     fullDict[word].append(fileList) 

кроме приращения счетчика для всех слов, вы добавляете новое значение в fullDict всегда. Но вы должны добавить его, только если docNumber еще не существует в listOfValues. Таким образом, вы можете использовать else с петлей for, как этот

for word in wordList: 
     if word not in fullDict: 
      .... 
     else: 
      listOfValues = fullDict[word] 
      for x in range(len(listOfValues)): 
       .... 
      else: 
       fileList = [docNumber, 1] 
       fullDict[word].append(fileList) 

После этих двух изменений, я получил следующий вывод

{'a': [[1, 3], [2, 1]], 
'and': [[1, 1]], 
'do': [[2, 1]], 
'go': [[1, 1]], 
'have': [[1, 1]], 
'i': [[1, 2], [2, 2]], 
'is': [[2, 1]], 
'know': [[2, 1]], 
'my': [[2, 1]], 
'nap': [[1, 1], [2, 1]], 
'not': [[2, 1]], 
'pancake': [[1, 1], [2, 1]], 
'should': [[1, 1]], 
'smoke': [[1, 1]], 
'take': [[2, 1]], 
'to': [[1, 1], [2, 1]], 
'want': [[1, 1], [2, 1]], 
'when': [[1, 1]], 
'where': [[1, 1], [2, 1]]} 

Есть несколько предложений, чтобы улучшить свой код ,

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

  2. Вместо подсчета вручную вы можете использовать collections.Counter.

  3. Вместо использования нескольких Заменяет, вы можете использовать простой регулярное выражение, как этот

    formattedData = re.sub(r'[.!?;:"0-9]', '', data.strip().lower()) 
    

Если бы я был очистить cleanData, я бы сделал это так

import re 
def cleanData(myFile): 
    with open(myFile, "r") as input_file: 
     data = input_file.read() 
    return re.sub(r'[.!?;:"0-9]', '', data.strip().lower()).split() 

В петле main вы можете использовать улучшения, предложенные Брэдом Будлоном, например

def main(): 
    fullDict = {} 
    files = ["file1.txt", "file2.txt"] 
    for docNumber, currentFile in enumerate(files, 1): 
     for word in cleanData(currentFile): 
      if word not in fullDict: 
       fullDict[word] = [[docNumber, 1]] 
      else: 
       for x in fullDict[word]: 
        if docNumber == x[0]: 
         x[1] += 1 
         break 
       else: 
        fullDict[word].append([docNumber, 1]) 
    return fullDict 
+0

Спасибо за ваши предложения! Я принял ваш ответ :) – AbigailB

1

Моя предпочтительная реализация циклов for не выполняет итерацию с использованием функций len и range. Поскольку это все изменяемые списки, вам не нужно знать индекс, вам просто нужно иметь каждый из списков, а затем его можно изменить без индекса. Я заменил цикл for следующим и получил тот же результат, что и thefourtheye.

for word in wordList: 
    if word not in fullDict: 
     fullDict[word] = [[docNumber, 1]] 
    else: 
     for val in fullDict[word]: 
      if val[0] == docNumber: 
       val[1] += 1 
       break 
     else: 
      fullDict[word].append([docNumber, 1])