2011-12-05 24 views
9

Ho problemi a ottenere i miei dati nel formato che mi piacerebbe in python.nidificazione con namedtuple

Fondamentalmente ho un programma che legge in dati binari e fornisce funzioni per la stampa e l'analisi dei dati su detto.

i miei dati sono titoli principali e poi sottotitoli che potrebbero essere qualsiasi numero di tipi di dati diverse.

mi piacerebbe essere in grado di accedere ai miei dati, come ad esempio:

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

ma io non conosco i nomi degli atomi fino a runtime.

Ho provato con namedtuple, ad esempio dopo ho letto in tutti i nomi atomo:

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

Dove elementlist è una lista di stringhe, per esempio ('idrogeno', 'carbon'). Ma il problema è che non posso nido questi usando per esempio:

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

e quindi in grado di accedere ai valori tramite ad esempio

self.elements.electron.distributionfunction. 

Forse sto facendo questo completamente sbagliato. Sono abbastanza inesperto con Python. So che sarebbe facile farlo se non mi fossi preoccupato di nominare le variabili dinamicamente.

spero di stato chiaro con quello che sto cercando di realizzare!

+0

Puoi per favore ci mostrano i dati di esempio? – Abhijit

+0

@abhijit È piuttosto complicato e viene letto come dati binari. Il numero di elementi chimici è variabile da un file all'altro ma ogni elemento ha: 'un nome (stringa) massa (doppia precisione) carica (doppia precisione) griglia velocità 3d (3 * n * doppia precisione) griglia di sfasamento (n^6 * doppia precisione) ' Ho una classe che legge il file binario e lo legge in variabili, ma ho problemi a creare la struttura dati a cui è possibile accedere nel modo in cui ho descritto. –

risposta

5

Senza conoscere i dati, possiamo solo dare una soluzione generica.

Considerando le prime due righe contiene le intestazioni e sotto-voce di lettura in qualche modo si è determinato la gerarchia. Tutto quello che devi fare è creare un dizionario gerarchico.

Per esempio, estendendo il vostro esempio

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

Quindi dobbiamo creare un dizionario come tale

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

come si sarà popolare il dizionario dipende dai dati che è difficile da dire ora. Ma le chiavi del dizionario dovresti compilare dalle intestazioni e in qualche modo devi mappare i dati al rispettivo valore negli slot vuoti del dizionario.

Una volta che la mappa è popolato, è possibile accedere come

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

Grazie per la rapida risposta! Quindi funzionerà per qualsiasi tipo di dati? per esempio se alcuni campi sono stringhe e alcuni campi sono array numpy? Quindi potrei fare 'distributionfunction': self.dist1 dove self.dist1 è una matrice numpy 3d? Ancora una volta grazie! Apprezzo pienamente il tuo tempo! –

+0

Come si vede nell'esempio, ho solo intenzione che le chiavi siano stringhe (che sono hash-able). Finché le chiavi sono lavabili, puoi memorizzare qualsiasi cosa nel valore, qualsiasi tipo di dati, funzioni, oggetti, qualsiasi cosa sulla terra che intendi. – Abhijit

2

Se il nome di un elemento sono dinamici ed ottenuto dai dati in fase di esecuzione, è possibile assegnare ad un dict e l'accesso come questo

elements['hydrogen'].mass 

ma se si vuole notazione con punti è possibile creare attributi in fase di esecuzione per esempio

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 

Qui io parto dal presupposto i dati che hai, ma con i dati in qualsiasi altro formato si può fare i trucchi simili

1

Ecco un metodo per creare in modo ricorsivo namedtuples dai dati nidificate.

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

Questo è fantastico. L'unico problema è che non serializzerà con pickle come pickle non può tenere traccia della struttura. Ho fatto una versione migliorata che fa. Inserirò qui sotto. – BobbyG

0

ho avuto lo stesso problema con JSON nidificato ma aveva bisogno di essere in grado di puntate l'uscita con salamoia che non piace si creazione di oggetti al volo.

Ho preso la risposta di @ bren e l'ho migliorata in modo che la struttura risultante sia serializzabile con pickle. Devi salvare i riferimenti a ciascuna delle strutture che crei a livello globale in modo che pickle possa tenerle sotto controllo.

############################################## 
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) 

L'esempio che segue dovrebbe funzionare come segue e anche essere 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'