2013-05-05 14 views
6

Mój kod obecnie musi policzyć rzeczy w mocno zagnieżdżonym dict w innym. Mam przedmioty, które muszą być indeksowane przez 3 wartości, a następnie policzone. Tak więc, przed moim pętli zainicjować zagnieżdżony defaultdict tak:Więcej Pythoniczny sposób liczenia rzeczy w mocno zagnieżdżonym defaultdict

from collections import defaultdict 

type_to_count_dic = defaultdict(
     lambda: defaultdict(
      lambda: defaultdict(int) 
     ) 
    ) 

który pozwala mi policzyć elementy wewnątrz pętli napięty tak:

for a in ...: 
    for b in ...: 
     for c in ...: 
      type_to_count_dic[a][b][c] += 1 

czuję jak inicjowanie wszystkie te defaultdict s Czuje się jak tworzenie deklaracji typu w języku Java. Czy istnieje bardziej idiomatyczny/Pythoniczny sposób robienia czegoś takiego?

Odpowiedz

8
from collections import defaultdict 

class _defaultdict(defaultdict): 
    def __add__(self, other): 
     return other 

def CountTree(): 
    return _defaultdict(CountTree) 

>>> t = CountTree() 
>>> t['a'] 
defaultdict(<function CountTree at 0x9e5c3ac>, {}) 
>>> t['a']['b']['c'] += 1 
>>> print t['a']['b']['c'] 
1 
3

Ponieważ liczą rzeczy, należy użyć licznika do wewnętrznej skrajnej dict:

import collections 
defaultdict = collections.defaultdict 
Counter = collections.Counter 

x = defaultdict(lambda: defaultdict(Counter)) 

for a in A: 
    for b in B: 
     x[a][b].update(C) 

pomocą licznika da Ci dostęp do przydatnych metod, takich jak most_common.

W zależności od tego, co zamierzasz zrobić z tym dyktando, możesz nie potrzebować głębokiego zagnieżdżenia. Zamiast tego możesz użyć krotki dla klucza. Na przykład,

import collections 
import itertools as IT 

A = range(2) 
B = 'XYZ' 
C = 'abc' 
x = collections.Counter(IT.product(A, B, C)) 
print(x) 

daje

A = range(2) 
B = 'XYZ' 
C = 'abc' 
x = collections.Counter(IT.product(A, B, C)) 
print(x) 

daje

Counter({(0, 'X', 'c'): 1, (0, 'Z', 'a'): 1, (1, 'Z', 'a'): 1, (1, 'X', 'c'): 1, (1, 'Z', 'b'): 1, (0, 'X', 'b'): 1, (0, 'Y', 'a'): 1, (1, 'Y', 'a'): 1, (0, 'Z', 'c'): 1, (1, 'Z', 'c'): 1, (0, 'X', 'a'): 1, (0, 'Y', 'b'): 1, (1, 'X', 'a'): 1, (1, 'Y', 'b'): 1, (0, 'Z', 'b'): 1, (1, 'Y', 'c'): 1, (1, 'X', 'b'): 1, (0, 'Y', 'c'): 1}) 
2

jestem przy założeniu, że tylko dodanie do każdego licznika gdy pewna con dows są spełnione, lub ewentualnie dodanie różnych wartości w zależności od warunków? W przeciwnym razie na pewno wartość każdego licznika zawsze będzie wynosić 1?

To powiedziawszy, najprostszym rozwiązaniem, jakie mogę wymyślić, jest stworzenie pojedynczego dyktafonu wpisanego na krotce trzech wartości pętli. Na przykład coś takiego:

dict(((a,b,c),1) for a in A for b in B for c in C) 

Ale jak powiedziałem, to po prostu da ci 1 w każdym liczniku. Musisz wymienić w wypowiedzi powyżej z warunków lub wywołania funkcji, która zwraca coś bardziej odpowiedni w zależności od wartości , b i c.

0

miałem podobną potrzebę i stworzył następujące:

import json 

from collections import defaultdict 


class NestedDefaultDict(defaultdict): 
    def __init__(self, depth, default=int, _root=True): 
     self.root = _root 
     self.depth = depth 
     if depth > 1: 
      cur_default = lambda: NestedDefaultDict(depth - 1, 
                default, 
                False) 
     else: 
      cur_default = default 
     defaultdict.__init__(self, cur_default) 

    def __repr__(self): 
     if self.root: 
      return "NestedDefaultDict(%d): {%s}" % (self.depth, 
                defaultdict.__repr__(self)) 
     else: 
      return defaultdict.__repr__(self) 


# Quick Example 
core_data_type = lambda: [0] * 10 
test = NestedDefaultDict(3, core_data_type) 
test['hello']['world']['example'][5] += 100 
print test 
print json.dumps(test) 

# Code without custom class. 
test = defaultdict(lambda: defaultdict(lambda: defaultdict(core_data_type))) 
test['hello']['world']['example'][5] += 100 
print test 
print json.dumps(test) 

Jeśli skończę uaktualnianie Ja również stworzył istotę: https://gist.github.com/KyleJamesWalker/8573350

Powiązane problemy