Python распаковывает 2-мерный список именованных кортежей - PullRequest
2 голосов
/ 09 августа 2011

У меня есть двумерный список именованных кортежей (скажем, у каждого кортежа есть N значений), и я хочу распаковать их в N разных двумерных списков, где каждый распакованный двумерный список полностью состоит из одногоатрибут из исходного списка.Например, если у меня есть этот двумерный список:

>>> 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')]]

Я бы хотел, чтобы 3 результата были такими:

[[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']]

Если бы у меня был только одномерный список кортежейЯ бы использовал zip(*mylist), например:

>>> 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')]

И я могу расширить это до моей ситуации, просто вложив:

>>> 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'))]

Но это не дает мне списки, которые яразыскиваемые и вложенные функции zip(*) не читаются.У кого-нибудь есть идеи для более питонического решения?Бонусные баллы, если вы можете обработать имена атрибутов кортежей там где-нибудь в конечном результате.

На самом деле, теперь, когда я думаю об этом, было бы идеально , если бы я мог иметьдикт, который сопоставил имя атрибута кортежа с соответствующей матрицей, например:

{'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']]}

Ответы [ 4 ]

3 голосов
/ 09 августа 2011

Функциональное программирование на помощь?По сути, это более чистая версия вложения молний:

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)

Использование:

>>> 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]]}
2 голосов
/ 09 августа 2011
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
1 голос
/ 09 августа 2011

Это не более читабельно, чем вложенные zip(*) функции, но оно выполняет свою работу:

>>> 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]]}

Полагаю, вы могли бы немного легче читать, например:

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 голосов
/ 09 августа 2011

Это может быть просто достигнуто путем транспонирования массива с использованием transpose ().

Ссылка: http://www.astro.ufl.edu/~warner/prog/python.html (поиск по слову "транспонировать")

...