Python: Как обновить значение пары ключ-значение во вложенном словаре? - PullRequest
2 голосов
/ 22 февраля 2011

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

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

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

вывод должен в конечном итоге выглядеть следующим образом:

word1, {doc1 : freq}, {doc2 : freq} <br>
word2, {doc1 : freq}, {doc2 : freq}, {doc3:freq}
etc....

Проблема в том, что я не могу обновить переменную словаря. При попытке сделать это я получаю сообщение об ошибке:

  File "scriptV3.py", line 45, in main
    freq = dictionary[keyword][filename] + 1
TypeError: unsupported operand type(s) for +: 'AutoVivification' and 'int'

Я думаю, мне нужно каким-то образом привести экземпляр AutoVivification к int ....

Как ехать?

заранее спасибо

мой код:

<code>#!/usr/bin/env python 
# encoding: utf-8

import sys
import os
import re
import glob
import string
import sets

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

def main():
    pad = 'temp/'
    dictionary  = AutoVivification()
    docID = 0
    for files in glob.glob( os.path.join(pad, '*.html') ):  #for all files in specified folder:
        docID = docID + 1
        filename = "doc_"+str(docID)
        text = open(files, 'r').read()                      #returns content of file as string
        text = extract(text, '<pre>', '
') # функция извлечения вызовов для извлечения текста из
tags
        text = text.lower()                                 #all words to lowercase
        exclude = set(string.punctuation)                   #sets list of all punctuation characters
        text = ''.join(char for char in text if char not in exclude) # use created exclude list to remove characters from files
        text = text.split()                                 #creates list (array) from string
        uniques = set(text)                                 #make list unique (is dat handig? we moeten nog tellen)

        for keyword in uniques:                             #For every unique word do   
            for word in text:                               #for every word in doc:
                if (word == keyword and dictionary[keyword][filename] is not None): #if there is an occurence of keyword increment counter 
                    freq = dictionary[keyword][filename]    #here we fail, cannot cast object instance to integer.
                    freq = dictionary[keyword][filename] + 1
                    print(keyword,dictionary[keyword])
                else:
                    dictionary[word][filename] = 1

#extract text between substring 1 and 2 
def extract(text, sub1, sub2): 
    return text.split(sub1, 1)[-1].split(sub2, 1)[0]    

if __name__ == '__main__':
    main()
</code>

Ответы [ 9 ]

6 голосов
/ 22 февраля 2011

Можно использовать Python Collections.defaultdict вместо создания класса AutoVivification и затем создания словаря в качестве объекта этого типа.

import collections
dictionary = collections.defaultdict(lambda: collections.defaultdict(int))

Это создаст словарь словарей со значением по умолчанию 0.Если вы хотите увеличить запись, используйте:

dictionary[keyword][filename] += 1
2 голосов
/ 04 сентября 2014

Я согласен, что вам следует избегать дополнительных занятий, особенно __getitem__. (Небольшие концептуальные ошибки могут сделать __getitem__ или __getattr__ довольно болезненным для отладки.)

Python dict кажется достаточно сильным для того, что вы делаете.

А как же просто dict.setdefault

    for keyword in uniques:                             #For every unique word do   
        for word in text:                               #for every word in doc:
            if (word == keyword):
                dictionary.setdefault(keyword, {})
                dictionary[keyword].setdefault(filename, 0)
                dictionary[keyword][filename] += 1

Конечно, это будет где dictionary это просто dict, а не что-то из collections или вашего собственного класса.

Опять же, разве это не просто:

        for word in text:                               #for every word in doc:
            dictionary.setdefault(word, {})
            dictionary[word].setdefault(filename, 0)
            dictionary[word][filename] += 1

Нет причин изолировать уникальные экземпляры, так как в любом случае dict использует уникальные ключи.

0 голосов
/ 22 февраля 2011
<code>#!/usr/bin/env python
# encoding: utf-8
from os.path import join
from glob import glob as glob_
from collections import defaultdict, Counter
from string import punctuation

WORKDIR  = 'temp/'
FILETYPE = '*.html'
OUTF     = 'doc_{0}'.format

def extract(text, startTag='<pre>', endTag='
'): "" "Извлечь текст между начальным тегом и конечным тегом. Начать с первого символа после первого появления startTag. Если его нет, начать с начала текста.текста "" "вернуть text.split (startTag, 1) [- 1] .split (endTag, 1) [0] def main (): DocWords = defaultdict (dict) infnames = glob_ (join (WORKDIR, FILETYPE))для docId, infname в перечислении (infnames, 1): outfname = OUTF (docId) с открытым (infname) как inf: text = inf.read (). lower () words = extract (text) .strip (пунктуация) .split() для wd, num в Counter (слова) .iteritems (): DocWords [wd] [outfname] = num if __name__ == '__main__': main ()
0 голосов
/ 22 февраля 2011

Этот класс AutoVivification не та магия, которую вы ищете.

Извлечение collections.defaultdict из стандартной библиотеки. Ваши внутренние дикты должны быть defaultdicts, которые по умолчанию являются целочисленными значениями, а ваши внешние dict будут тогда defaultdicts, которые по умолчанию принимают значения inner-dict.

0 голосов
/ 22 февраля 2011

Было бы лучше выгнать AutoVivification все вместе, потому что это ничего не добавляет.

Следующая строка:

if (word == keyword and dictionary[keyword][filename] is not None):

Не работает должным образом, из-за того, как работает ваш класс, dictionary[keyword] всегда будет возвращать экземпляр AutoVivification, как и dictionary[keyword][filename].

0 голосов
/ 22 февраля 2011

В классе AutoVivification вы определяете

value = self[item] = type(self)()
return value

, который возвращает экземпляр себя, который в этом контексте является автовивификацией. Ошибка становится понятной.

Вы уверены, что хотите вернуть автовивификацию для любого недостающего ключевого запроса? Из кода я бы предположил, что вы хотите вернуть обычный словарь со строковым ключом и значениями int.

Кстати, может быть, вас заинтересует класс defaultdict .

0 голосов
/ 22 февраля 2011

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

идентификатор документа -> [word_ids]

и обратное отображение индекса

word_id -> [document_ids]

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

0 голосов
/ 22 февраля 2011

Я думаю, что вы пытаетесь добавить 1 к словарной записи, которой еще не существует.Ваш метод getitem по какой-то причине возвращает новый экземпляр класса AutoVivification в случае сбоя поиска.Поэтому вы пытаетесь добавить 1 к новому экземпляру класса.

Я думаю, что ответ состоит в том, чтобы обновить метод getitem, чтобы он устанавливал счетчик на 0, если он еще не существует.

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            self[item] = 0
            return 0

Надеюсь, это поможет.

0 голосов
/ 22 февраля 2011
if (word == keyword and dictionary[keyword][filename] is not None): 

это неправильное использование, я думаю, вместо этого попробуйте это:

if (word == keyword and filename in dictionary[keyword]): 

Потому что, проверка значения несуществующего ключа вызывает KeyError.Вы должны проверить, существует ли ключ в словаре ...

...