2011-08-08 2 views
2

J'ai une liste bidimensionnelle de tuples nommés (disons que chaque tuple a N valeurs), et je veux les décompresser en N différentes listes bidimensionnelles où chaque non empaqueté 2 -D liste est entièrement composée d'un seul attribut de la liste d'origine. Par exemple, si je cette liste 2-D:Python décompresser la liste bidimensionnelle des tuples nommés

>>> combo = namedtuple('combo', 'i, f, s') 
>>> combo_mat = [[combo(i + 3*j, float(i + 3*j), str(i + 3*j)) for i in range(3)] 
       for j in range(3)] 
>>> combo_mat 
[[combo(i=0, f=0.0, s='0'), combo(i=1, f=1.0, s='1'), combo(i=2, f=2.0, s='2')], 
[combo(i=3, f=3.0, s='3'), combo(i=4, f=4.0, s='4'), combo(i=5, f=5.0, s='5')], 
[combo(i=6, f=6.0, s='6'), combo(i=7, f=7.0, s='7'), combo(i=8, f=8.0, s='8')]] 

Je aimerais que les 3 résultats soient:

[[0, 1, 2], 
[3, 4, 5], 
[6, 7, 8]] 

[[0.0, 1.0, 2.0], 
[3.0, 4.0, 5.0], 
[6.0, 7.0, 8.0]] 

[['0', '1', '2'], 
['3', '4', '5'], 
['6', '7', '8']] 

Si je viens d'avoir une liste 1 dimensions de tuples j'utiliser zip(*mylist), comme:

>>> zip(*[combo(i=0, f=0.0, s='0'), combo(i=1, f=1.0, s='1'), combo(i=2, f=2.0, s='2')]) 
[(0, 1, 2), (0.0, 1.0, 2.0), ('0', '1', '2')] 

Et je peux l'étendre à ma situation juste en imbrication:

>>> zip(*[zip(*combs) for combs in combo_mat]) 
[((0, 1, 2), 
    (3, 4, 5), 
    (6, 7, 8)), 
((0.0, 1.0, 2.0), 
    (3.0, 4.0, 5.0), 
    (6.0, 7.0, 8.0)), 
(('0', '1', '2'), 
    ('3', '4', '5'), 
    ('6', '7', '8'))] 

Mais cela ne me donne pas les listes que je voulais, et imbriqué déballage zip(*) fonctions n'est pas lisible. Quelqu'un a des idées pour une solution plus pythonique? Points bonus si vous pouvez travailler les noms des attributs des tuples là-bas quelque part dans le résultat final.

En fait, maintenant que je pense, ce serait idéal si je pouvais avoir un dict qui a cartographié le nom de l'attribut tuple à sa matrice respective, comme:

{'i': [[0, 1, 2], 
     [3, 4, 5], 
     [6, 7, 8]], 
'f': [[0.0, 1.0, 2.0], 
     [3.0, 4.0, 5.0], 
     [6.0, 7.0, 8.0]] 
's': [['0', '1', '2'], 
     ['3', '4', '5'], 
     ['6', '7', '8']]} 

Répondre

3

Programmation fonctionnelle à la rescousse? Il est essentiellement une version plus propre d'imbrication des fermetures éclair:

def fmap_element(f, el): 
    return f(el) 

def fmap_list(f, l): 
    return [fmap_element(f, el) for el in l)] 

def fmap_lol(f, lol): 
    return [fmap_list(f,l) for l in lol] 

def split_nt_lol(nt_lol): 
    return dict((name, fmap_lol(lambda nt: getattr(nt, name), nt_lol)) 
       for name in nt_lol[0][0]._fields) 

Utilisation:

>>> split_nt_lol(combo_mat) 
{'i': [[0, 1, 2], [3, 4, 5], [6, 7, 8]], 
's': [['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8']], 
'f': [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]} 
+0

Excellent, j'aime l'utilisation de 'getattr()' plutôt que d'énumérer la variable privée '_fields'. Cela semble plus propre. –

+0

À l'origine, j'utilisais 'getattr' dans le mien, mais j'avais l'impression que' getattr' n'était pas aussi propre.:-) – kindall

+0

J'utilise toujours '_fields'. pas sûr s'il y a un moyen d'obtenir la liste des noms de champs d'un tuple nommé sans lui – Claudiu

2
matrices = {} 

for num, name in enumerate(combo._fields): 
    matrix = [] 
    for i in combo_mat: 
     row = [] 
     for j in i: 
      row.append(j[num]) 
     matrix.append(row) 
    matrices[name] = matrix 
+0

Aha bon, je ne savais pas que namedtuples avait un attribut '_fields'. –

+0

Mais existe-t-il un moyen plus direct d'accéder au nom de l'attribut, aux paires de valeurs? Peut-être que je devrais stocker dans un dictionnaire. –

+0

ah oui, plus pythonique que le mien .. – Claudiu

1

Ce n'est plus lisible que zip(*) imbriqués fonctions, mais il fait le travail:

>>> dict((key, [[c._asdict()[key] for c in combos] for combos in combo_mat]) 
     for key in ['i', 'f', 's']) 
{'i': [[0, 1, 2], [3, 4, 5], [6, 7, 8]], 
's': [['0', '1', '2'], ['3', '4', '5'], ['6', '7', '8']], 
'f': [[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]} 

Je suppose que vous pourriez le rendre un peu plus facile à lire comme ça:

def get_mat(combo_mat, key): 
    return [[c._asdict()[key] for c in combos] for combos in combo_mat] 

def get_mat_dict(combo_mat, keys): 
    return dict((key, get_mat(combo_mat, key)) for key in keys) 
+0

Hmm je l'aime mais tu as raison car ce n'est pas particulièrement lisible. –

+0

Excellente révision, j'aurais +1 de plus si je le pouvais. –

Questions connexes