我在Python3.5.2中有一个字典列表,我正在尝试“去复制”。所有的字典都是唯一的,但有一个特定的键我想要去重复,保持字典的非空值最多。
例如,我有以下字典列表:
d1 = {"id":"a", "foo":"bar", "baz":"bat"}
d2 = {"id":"b", "foo":"bar", "baz":None}
d3 = {"id":"a", "foo":"bar", "baz":None}
d4 = {"id":"b", "foo":"bar", "baz":"bat"}
l = [d1, d2, d3, d4]我想将l过滤到只有唯一的id键的字典中,保留具有最少空值的字典。在这种情况下,函数应该保持d1和d4。
我试图为“值计数”创建一个新的键val对,如下所示:
for d in l:
d['val_count'] = len(set([v for v in d.values() if v]))现在,我要坚持的是如何过滤我的字典列表中唯一的ids,其中val_count键是更大的值。
我对其他方法持开放态度,但由于资源限制,我无法在这个项目中使用pandas。
预期产出:
l = [{"id":"a", "foo":"bar", "baz":"bat"},
{"id":"b", "foo":"bar", "baz":"bat"}]发布于 2018-05-22 01:06:08
我会使用群比,从每个组中选择第一个:
1)首先按键对列表进行排序(以创建组)并对空值进行降序计数(您声明的目标):
>>> l2=sorted(l, key=lambda d: (d['id'], -sum(1 for v in d.values() if v))) 2)然后按id分组,并在排序列表上的groupby中获取每个迭代器的第一个元素作为d:
>>> from itertools import groupby
>>> [next(d) for _,d in groupby(l2, key=lambda _d: _d['id'])]
[{'id': 'a', 'foo': 'bar', 'baz': 'bat'}, {'id': 'b', 'foo': 'bar', 'baz': 'bat'}]如果您想要一个“领带断路器”来选择第一个dict,否则它们具有相同的空计数,您可以添加一个枚举装饰器:
>>> l2=sorted(enumerate(l), key=lambda t: (t[1]['id'], t[0], -sum(1 for v in t[1].values() if v)))
>>> [next(d)[1] for _,d in groupby(l2, key=lambda t: t[1]['id'])]但是,我怀疑额外的步骤实际上是必要的,因为Python的排序(和sorted)是一个稳定排序,并且序列只会根据键数和空计数改变列表顺序。所以,使用第一个版本,除非您确定需要使用第二个版本。
发布于 2018-05-21 17:01:19
您可以使用max
d1 = {"id":"a", "foo":"bar", "baz":"bat"}
d2 = {"id":"b", "foo":"bar", "baz":None}
d3 = {"id":"a", "foo":"bar", "baz":None}
d4 = {"id":"b", "foo":"bar", "baz":"bat"}
l = [d1, d2, d3, d4]
max_none = max(sum(c is None for c in i.values()) for i in l)
new_l = [i for i in l if sum(c is None for c in i.values()) < max_none]输出:
[{'foo': 'bar', 'baz': 'bat', 'id': 'a'}, {'foo': 'bar', 'baz': 'bat', 'id': 'b'}]发布于 2018-05-22 00:20:31
如果您愿意使用第三方库,则可以按None值的数目进行排序,然后输入toolz.unique。
from toolz import unique
from operator import itemgetter
l_sorted = sorted(l, key=lambda x: sum(v is None for v in x.values()))
res = list(unique(l_sorted, key=itemgetter('id')))
[{'baz': 'bat', 'foo': 'bar', 'id': 'a'},
{'baz': 'bat', 'foo': 'bar', 'id': 'b'}]如果您不能使用toolz,那么源代码就足够小,可以自己实现。
性能基准测试
我只包含了每个id都给出一个结果的解决方案。许多解决方案不适合重复的字典。
l = [d1, d2, d3, d4]*1000
%timeit dawg(l) # 11.4 ms
%timeit jpp(l) # 7.91 ms
%timeit tsw(l) # 4.23 s
from operator import itemgetter
from itertools import groupby
from toolz import unique
def dawg(l):
l2=sorted(enumerate(l), key=lambda t: (t[1]['id'], -sum(1 for v in t[1].values() if v), t[0]))
return [next(d)[1] for _,d in groupby(l2, key=lambda t: t[1]['id'])]
def jpp(l):
l_sorted = sorted(l, key=lambda x: sum(v is None for v in x.values()))
return list(unique(l_sorted, key=itemgetter('id')))
def tsw(l):
for d in l:
d['val_count'] = len(set([v for v in d.values() if v]))
new = [d for d in l if d['val_count'] == max([d_other['val_count'] for d_other in l if d_other['id'] == d['id']])]
return [x for i, x in enumerate(new) if x['id'] not in {y['id'] for y in new[:i]}]https://stackoverflow.com/questions/50453416
复制相似问题