2011-12-05 9 views
9

Mam problemy z uzyskaniem moich danych w formie, którą chciałbym w python.zagnieżdżanie z namedtuple

Zasadniczo mam program, który odczytuje dane binarne i zapewnia funkcje do drukowania i analizy tych danych.

Moje dane mają główne nagłówki, a następnie podtytuły, które mogą być dowolną liczbą różnych typów danych.

Chciałbym być w stanie uzyskać dostęp do swoich danych, jak na przykład:

>>> a = myDatafile.readit() 
>>> a.elements.hydrogen.distributionfunction 
(a big array) 
>>> a.elements.hydrogen.mass 
1 
>>> a.elements.carbon.mass 
12 

ale nie znam nazwy atomów aż do czasu wykonywania.

Próbowałem za pomocą namedtuple, na przykład po czytałem wszystkich nazw Atom:

self.elements = namedtuple('elements',elementlist) 

Gdzie elementlist jest listą ciągów na przykład wodoru („”, „”) węgla. Ale problemem jest to, że nie mogą one gniazdo stosując na przykład:

for i in range(0,self.nelements): 
    self.elements[i] = namedtuple('details',['ux','uy','uz','mass','distributionfunction']) 

a następnie być w stanie uzyskać dostęp do wartości poprzez np

self.elements.electron.distributionfunction. 

Może robię to całkowicie błędne. Jestem dość niedoświadczony w python. Wiem, że byłoby to łatwe, gdybym nie miał problemu z dynamicznym nazywaniem zmiennych.

Mam nadzieję, że jasno wyjaśniłem, co próbuję osiągnąć!

+0

Czy możesz pokazać nam przykładowe dane? – Abhijit

+0

@abhijit Jest to dość skomplikowane i jest odczytywane jako dane binarne. liczba pierwiastków jest zmienna z pliku do pliku, ale każdy z elementów posiada: „nazwę (ciąg) masowe (podwójnej precyzji) ładowania (podwójnej precyzji) 3d prędkość siatki (3 * n * podwójnej precyzji) Siatka fazowa (n^6 * podwójna precyzja) ' Mam klasę, która odczytuje plik binarny i wczytuje go do zmiennych, ale mam problem z utworzeniem struktury danych, do której można uzyskać dostęp w sposób opisany przeze mnie. –

Odpowiedz

5

Bez znajomości danych możemy podać tylko ogólne rozwiązanie.

Rozpatrując pierwsze dwa wiersze zawiera nagłówki i nagłówek poddziału, jakoś ustalono hierarchię. Wszystko, co musisz zrobić, to stworzyć hierarchiczny słownik.

Na przykład, rozszerzając swoją przykład

data.elements.hydrogen.distributionfunction 
data.elements.nitrogen.xyzfunction 
data.elements.nitrogen.distributionfunction 
data.compound.water.distributionfunction 
data.compound.hcl.xyzfunction 

Mamy więc do stworzenia słownika jako taki

{'data':{'elements':{'hydrogen':{'distributionfunction':<something>} 
        'nitrogen':{'xyzfunction':<something>, 
          'distributionfunction':<something>} 
       } 
     compound:{'water':{'distributionfunction':<something>} 
       'hcl':{'xyzfunction':<something>} 
       } 
     } 
} 

jak będzie zapełnić słownika zależy od danych, które trudno teraz powiedzieć. Ale klucze do słownika należy wypełnić z nagłówków, i jakoś trzeba zmapować dane do odpowiedniej wartości w pustych slotach słownika.

Gdy mapa jest pusta, można uzyskać do niego dostęp, jak

yourDict['data']['compound']['hcl']['xyzfunction'] 
+0

Dzięki za szybką reakcję! Czy to zadziała dla dowolnego typu danych? na przykład, jeśli niektóre pola są łańcuchami, a niektóre pola są numpy tablicami? Więc czy mogę zrobić "distributionfunction": self.dist1 gdzie self.dist1 to 3d numpy array? Jeszcze raz dziękuję! W pełni doceniam twój czas! –

+0

Jak widzisz w tym przykładzie, mam na myśli tylko klucze do ciągów (które są zdolne do haszowania). Tak długo, jak klucze są nieosiągalne, możesz przechowywać wszystko w wartości, dowolne typy danych, funkcje, obiekty, to, co kiedykolwiek planujesz. – Abhijit

2

Jeśli nazwa elementu są dynamiczne, a uzyskane z danych w czasie wykonywania, można przypisać je do dict i dostęp jak ten

elements['hydrogen'].mass 

ale jeśli chcesz notacji kropkowanej można tworzyć atrybuty w czasie wykonywania, np.

from collections import namedtuple 

class Elements(object): 
    def add_element(self, elementname, element): 
     setattr(self, elementname, element) 

Element = namedtuple('Element', ['ux','uy','uz','mass','distributionfunction']) 

elements = Elements() 
for data in [('hydrogen',1,1,1,1,1), ('helium',2,2,2,2,2), ('carbon',3,3,3,3,3)]: 
    elementname = data[0] 
    element = Element._make(data[1:]) 
    elements.add_element(elementname, element) 

print elements.hydrogen.mass 
print elements.carbon.distributionfunction 

Oto jestem przy założeniu, że dane masz, ale z danych w innym formacie można zrobić podobne sztuczki

1

Oto metoda rekursywnie tworząc namedtuples z danych zagnieżdżonych.

from collections import Mapping, namedtuple 


def namedtuplify(mapping, name='NT'): # thank you https://gist.github.com/hangtwenty/5960435 
    """ Convert mappings to namedtuples recursively. """ 
    if isinstance(mapping, Mapping): 
     for key, value in list(mapping.items()): 
      mapping[key] = namedtuplify(value) 
     return namedtuple_wrapper(name, **mapping) 
    elif isinstance(mapping, list): 
     return [namedtuplify(item) for item in mapping] 
    return mapping 

def namedtuple_wrapper(name, **kwargs): 
    wrap = namedtuple(name, kwargs) 
    return wrap(**kwargs) 


stuff = {'data': {'elements': {'hydrogen': {'distributionfunction': 'foo'}, 
    'nitrogen': {'xyzfunction': 'bar', 
    'distributionfunction': 'baz'} 
    }, 
    'compound': {'water': {'distributionfunction': 'lorem'}, 
    'hcl': {'xyzfunction': 'ipsum'}}} 
} 

example = namedtuplify(stuff) 

example.data.elements.hydrogen.distributionfunction # 'foo' 
+0

To jest świetne. Jedynym problemem jest to, że nie będzie serializować z marynatą, ponieważ marynowanie nie może śledzić struktury. Zrobiłem ulepszoną wersję, która ma. Opublikuję poniżej. – BobbyG

0

miałem ten sam problem z zagnieżdżonego json ale potrzebne, aby móc serialise wyjście z zalewy, która nie podoba Ci się tworzenia obiektów w locie.

Wziąłem odpowiedź @ bren i udoskonaliłem ją, aby powstała struktura mogła być szeregowana z marynarką. Musisz zapisać odniesienia do każdej ze struktur, które utworzysz, do globali, tak aby marynowany mógł mieć na nich karty.

############################################## 
class Json2Struct: 
    ''' 
    Convert mappings to nested namedtuples 

    Usage: 
     jStruct = Json2Struct('JS').json2Struct(json) 
    ''' 
############################################## 

    def __init__(self, name): 
     self.namePrefix = name 
     self.nameSuffix = 0 


    def json2Struct(self, jsonObj): # thank you https://gist.github.com/hangtwenty/5960435 
     """ 
     Convert mappings to namedtuples recursively. 
     """ 
     if isinstance(jsonObj, Mapping): 
      for key, value in list(jsonObj.items()): 
       jsonObj[key] = self.json2Struct(value) 
      return self.namedtuple_wrapper(**jsonObj) 
     elif isinstance(jsonObj, list): 
      return [self.json2Struct(item) for item in jsonObj] 
     return jsonObj 


    def namedtuple_wrapper(self, **kwargs): 
     self.nameSuffix += 1 
     name = self.namePrefix + str(self.nameSuffix) 

     Jstruct = namedtuple(name, kwargs) 
     globals()[name] = Jstruct 

     return Jstruct(**kwargs) 

Poniższy przykład powinien działać następująco a także być serialisable:

stuff = {'data': {'elements': {'hydrogen': {'distributionfunction': 'foo'}, 
    'nitrogen': {'xyzfunction': 'bar', 
    'distributionfunction': 'baz'} 
    }, 
    'compound': {'water': {'distributionfunction': 'lorem'}, 
    'hcl': {'xyzfunction': 'ipsum'}}} 
} 

example = Json2Struct('JS').json2Struct(stuff) 

example.data.elements.hydrogen.distributionfunction # 'foo'