Комбинированное понимание списков и диктов
У меня есть какая-то многословная логика, которую я хотел бы сжать с некоторыми пониманиями.
По сути, у меня есть объект dict, из которого я читаю, в котором есть 16 значений, которые меня интересуют. Я получаю нужные мне ключи со следующим пониманием:
["I%d" % (i,) for i in range(16)]
Исходный словарь выглядит примерно так:
{ "I0": [0,1,5,2], "I1": [1,3,5,2], "I2": [5,9,10,1], ... }
Я хотел бы по существу сопоставить этот словарь, чтобы быть примерно таким:
[
{ "I0": 0, "I1": 1, "I2": 5, ... }
{ "I0": 1, "I1": 3, "I2": 9, ... }
...
]
Как я могу сопоставить вещи со списком и словарным пониманием, чтобы преобразовать мой исходный словарь в список словарей назначения?
3 ответа
Это полностью функциональное решение, которое может быть применено к произвольному размеру.
d = { "I0": [0,1,5,2], "I1": [1,3,5,2], "I2": [5,9,10,1]}
map(dict, zip(*map(lambda (k, v): map(lambda vv: (k, vv), v), d.iteritems())))
уточнять: (Я пользуюсь ipython
и подчеркивание _
означает предыдущий вывод)
In [1]: d = {'I0': [0, 1, 5, 2], 'I1': [1, 3, 5, 2], 'I2': [5, 9, 10, 1]}
In [2]: map(lambda (k, v): map(lambda vv: (k, vv), v), _.iteritems())
Out[2]:
[[('I1', 1), ('I1', 3), ('I1', 5), ('I1', 2)],
[('I0', 0), ('I0', 1), ('I0', 5), ('I0', 2)],
[('I2', 5), ('I2', 9), ('I2', 10), ('I2', 1)]]
In [3]: zip(*_)
Out[3]:
[(('I1', 1), ('I0', 0), ('I2', 5)),
(('I1', 3), ('I0', 1), ('I2', 9)),
(('I1', 5), ('I0', 5), ('I2', 10)),
(('I1', 2), ('I0', 2), ('I2', 1))]
In [4]: map(dict, _)
Out[4]:
[{'I0': 0, 'I1': 1, 'I2': 5},
{'I0': 1, 'I1': 3, 'I2': 9},
{'I0': 5, 'I1': 5, 'I2': 10},
{'I0': 2, 'I1': 2, 'I2': 1}]
Как бы я решил это:
Во-первых, я получу для каждого ключа список, содержащий кортежи, где первый элемент будет ключом, а второй - одним из значений в списке:
>>> [ [ (k, i) for i in l] for k, l in d.items() ]
[[('I1', 1), ('I1', 3), ('I1', 5), ('I1', 2)],
[('I0', 0), ('I0', 1), ('I0', 5), ('I0', 2)],
[('I2', 5), ('I2', 9), ('I2', 10), ('I2', 1)]]
Затем я бы пересек этот список, создав список кортежей, содержащих каждый соответствующий ключ, используя функцию zip:
>>> list(zip(*[ [ (k, i) for i in l] for k, l in d.items() ]))
[(('I1', 1), ('I0', 0), ('I2', 5)),
(('I1', 3), ('I0', 1), ('I2', 9)),
(('I1', 5), ('I0', 5), ('I2', 10)),
(('I1', 2), ('I0', 2), ('I2', 1))]
Эти подсписки могут передаваться в качестве параметра конструктору dict:
>>> [dict(lp) for lp in zip(*[ [ (k, i) for i in l] for k, l in d.items() ])]
[{'I0': 0, 'I1': 1, 'I2': 5},
{'I0': 1, 'I1': 3, 'I2': 9},
{'I0': 5, 'I1': 5, 'I2': 10},
{'I0': 2, 'I1': 2, 'I2': 1}]
На практике, однако, я бы никогда не рекомендовал делать такую вещь только в одной строке:
>>> pairs = [ [ (k, i) for i in l] for k, l in d.items() ]
>>> transversed = zip(*pairs)
>>> ds = [dict(t) for t in transversed]
>>> pprint(ds)
[{'I0': 0, 'I1': 1, 'I2': 5},
{'I0': 1, 'I1': 3, 'I2': 9},
{'I0': 5, 'I1': 5, 'I2': 10},
{'I0': 2, 'I1': 2, 'I2': 1}]
На самом деле, я бы сказал, что я опубликовал этот ответ в основном, чтобы предложить вам разбить свое решение более чем на одну строку.
Существует короткое и простое решение:
keys = data.keys()
values = data.values()
transformed = [dict(zip(keys, t)) for t in zip(*values)]
Ключевым моментом здесь является транспонирование матрицы значений, что делается с zip(*values)
тогда мы просто восстанавливаем диктанты.