![](/img/trans.png)
[英]What is the Pythonic way of reordering a list consisting of dicts?
[英]What is a Pythonic way of doing the following transformation on a list of dicts?
我有一个像这样的dicts列表:
l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
我想获得这种形式的输出:
>>> [('foo', 'bar'), ([1,2,3,4], [5,6,7,8])]
但是, for
缺乏促销和append
我没有看到解决方案。 有比这更聪明的方法吗?
names = []
values = []
for d in l:
names.append(d['name'])
values.append(d['values'])
使用生成器表达:
l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
v = [tuple(k["name"] for k in l), tuple(k["values"] for k in l)]
print(v)
输出:
[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
如果我正在编写此代码供公众使用,我会使用列表理解(很像eyllanesc的)。 但只是为了好玩,这里是不使用任何一个班轮for
秒。
>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> list(zip(*map(dict.values, l)))
[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
(请注意,只有在字典保留插入顺序时才能可靠地工作,但在所有版本的Python中都不是这样.CPython 3.6将其作为实现细节,但它仅保证3.7的行为。)
快速细分过程:
dict_values
返回一个dict_values
对象,它是一个包含dict所有值的iterable。 map
接受l
每个字典并在其上调用dict.values,返回可迭代的dict_values对象。 zip(*thing)
是一个经典的“转置”配方,它采用可迭代的迭代,并有效地对角翻转它。 例如[[a,b],[c,d]]变为[[a,c],[b,d]]。 这会将所有名称放入一个元组,将所有值放入另一个元组中。 list
将zip对象转换为列表。 您可以使用operator.itemgetter
来保证值的排序:
from operator import itemgetter
fields = ('name', 'values')
res = list(zip(*map(itemgetter(*fields), L)))
print(res)
[('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
如果,假设Python 3.6+,您无法保证在输入列表中对字典进行适当的插入排序,则需要如上所述明确定义顺序。
性能
虽然“元组理解”列表有效,但在查询多个字段时会变得难以理解且效率低下:
from operator import itemgetter
n = 10**6
L = [{'name': 'foo', 'values': [1,2,3,4], 'name2': 'zoo', 'name3': 'xyz',
'name4': 'def'}, {'name': 'bar', 'values': [5,6,7,8], 'name2': 'bart',
'name3': 'abc', 'name4': 'ghi'}] * n
%timeit [tuple(k["name"] for k in L), tuple(k["values"] for k in L),\
tuple(k["name2"] for k in L), tuple(k["name3"] for k in L),
tuple(k["name4"] for k in L)]
%timeit fields = ('name', 'values', 'name2', 'name3' ,'name4');\
list(zip(*map(itemgetter(*fields), L)))
1 loop, best of 3: 1.25 s per loop
1 loop, best of 3: 1.04 s per loop
这可能与您的想法不完全相同,但对于像这样的表格数据,我发现pandas
通常是长期运行的最佳解决方案:
>>> import pandas as pd
>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> df = pd.DataFrame(l)
name values
0 foo [1, 2, 3, 4]
1 bar [5, 6, 7, 8]
通常,您可以直接使用数据框来执行任何操作,但您也可以将其转换为基于列表的数据结构:
>>> df['name'].tolist(), df['values'].tolist()
(['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]])
不确定性能,但这是另一个使用zip()
和解包:
list(zip(*[tuple(i.values()) for i in l]))
# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
编辑:正如@DeepSpace指出的那样,它可以进一步减少到:
list(zip(*(i.values() for i in l)))
如果您想自己定义订单,这里有一个更长但更明确的答案:
list(zip(*(tuple(map(lambda k: i.get(k), ('name', 'values'))) for i in l)))
# [('foo', 'bar'), ([1, 2, 3, 4], [5, 6, 7, 8])]
使用地图
names = tuple(map(lambda d: d['name'], l))
values = tuple(map(lambda d: d['values'], l))
result = [names, values]
第一:你的代码很好,可读和高效,这对我来说听起来像Pythonic。 请注意,您可能不需要列表元组。 元组是不可变的 ,因此您无法在名称中附加其他names
。
如果names
是唯一的,您可以将您的dicts列表转换为大型dict:
>>> l = [{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}]
>>> data = {d['name']:d['values'] for d in l}
>>> data
{'foo': [1, 2, 3, 4], 'bar': [5, 6, 7, 8]}
您可以直接获得所需信息:
>>> data.keys()
dict_keys(['foo', 'bar'])
>>> data.values()
dict_values([[1, 2, 3, 4], [5, 6, 7, 8]])
如果你真的想要一个列表列表:
>>> [list(data.keys()), list(data.values())]
[['foo', 'bar'], [[1, 2, 3, 4], [5, 6, 7, 8]]]
如果你正在使用大量的dicts,你可能想要考虑一下pandas
。
您可以直接初始化DataFrame
:
>>> import pandas as pd
>>> df = pd.DataFrame([{'name': 'foo', 'values': [1,2,3,4]}, {'name': 'bar', 'values': [5,6,7,8]}])
>>> df
name values
0 foo [1, 2, 3, 4]
1 bar [5, 6, 7, 8]
如果您需要将名称作为可迭代,则可以获取相应的列:
>>> df['name']
0 foo
1 bar
Name: name, dtype: object
如果你真的需要一个名单:
>>> list(df['name'])
['foo', 'bar']
要将名称和值组合在一起:
>>> df.values.T
array([['foo', 'bar'],
[list([1, 2, 3, 4]), list([5, 6, 7, 8])]], dtype=object)
这是一种递归方式:
def trans(l):
if l:
res = trans(l[1:])
res[0], res[1] = (l[0]['name'],) + res[0], (l[0]['values'],) + res[1]
return res
return [(),()]
像这样:
(lambda f:
lambda l, r=[(), ()]: f(f, l, r)
)(lambda g, l, r:
r if len(l) == 0 else g(g, l[1:], [r[0]+(l[0]['name'],), r[1]+(l[0]['values'],)])
)([
{'name': 'foo', 'values': [1, 2, 3, 4]},
{'name': 'bar', 'values': [5, 6, 7, 8]},
{'name': 'baz', 'values': [9, 9, 9, 9]}
])
结果:
[('foo', 'bar', 'baz'), ([1, 2, 3, 4], [5, 6, 7, 8], [9, 9, 9, 9])]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.