python:根据分数删除重复的元素

时间:2013-05-01 11:32:13

标签: python list

假设我有以下列表

l = [ {'id':1, 's':1.0 }, {'id':3, 's': 0.6}, {'id':1, 's': 1.5} ]

我想根据'id'值删除重复's'值的元素 在前一个例子中,我想丢弃第一个元素,因为第一个和第三个元素都有'id'==1,而l[0]['s'] < l[2]['s']我希望l[0]被丢弃。

因此我期望的输出是(我关心输出列表中元素的顺序)

[ {'id':1, 's':1.5}, {'id':3, 's':0.6} ]

6 个答案:

答案 0 :(得分:5)

我会使用映射来跟踪ID及其分数:

from collections import defaultdict

id_to_scores = defaultdict(list)

for entry in l:
    id_to_scores[entry['id']].append(entry['s'])

output = [{'id': k, 's': max(v)} for k, v in id_to_scores.iteritems()]

如果您使用的是Python 3,请使用.items()

结果(由于dict没有固定排序,订单已更改):

>>> [{'id': k, 's': max(v)} for k, v in id_to_scores.iteritems()]
[{'s': 1.5, 'id': 1}, {'s': 0.6, 'id': 3}]

这会重建词典。如果涉及其他键,则需要为每个id存储整个字典,而不仅仅是分数:

per_id = defaultdict(list)

for entry in l:
    per_id[entry['id']].append(entry)

output = [max(v, key=lambda d: d['s']) for v in per_id.itervalues()]

答案 1 :(得分:4)

使用collections.defaultdict

In [58]: dic=defaultdict(dict)

In [59]: for x in lis:
    idx=x['id']
    if dic[idx].get('s',float('-inf')) < x ['s']:
        dic[idx]=x
   ....:         

In [60]: dic.values()
Out[60]: [{'id': 1, 's': 1.5}, {'id': 3, 's': 0.6}]

使用简单的dict

In [71]: dic={}

In [72]: for x in lis:
    idx=x['id']
    if dic.get(idx, {'s': float('-inf')}) ['s'] < x['s']:
        dic[idx]=x
   ....:         

In [73]: dic.values()
Out[73]: [{'id': 1, 's': 1.5}, {'id': 3, 's': 0.6}]

答案 2 :(得分:3)

这是我的解决方案,使用来自itertools的groupby。

>>> l = [ {'id':1, 's':1.0 }, {'id':3, 's': 0.6}, {'id':1, 's': 1.5} ]
>>> from itertools import groupby
>>> key = lambda dct: dct['id']
>>> l.sort(key=key)
>>> for key, group in groupby(l, key=key):
...     print max(group, key=lambda dct: dct['s'])
... 
{'s': 1.5, 'id': 1}
{'s': 0.6, 'id': 3}

回复:Ashwini

我做了performance test,比较了不同的解决方案。这是结果,以图表形式:

enter image description here

我在这里只使用了'id'键的10个不同值,您可以自己使用代码来查看lst的组合如何影响结果。将id值的数量更改为列表中项目数量的一半,使得Ashwini成为明确的胜利者,让我们其余的人陷入困境:

enter image description here

这是将O(n)解决方案与O(n*log(n))解决方案在日志日志图表中进行比较时的样子:

enter image description here

所以,我不太确定关于大O论证的结论是什么。

答案 3 :(得分:0)

>>> L = [ {'id':1, 's':1.0 }, {'id':3, 's': 0.6}, {'id':1, 's': 1.5} ]
>>> res = {}
>>> for d in L:
        id_ = d['id']
        res[id_] = max(res.get(id_, {}), d, key=lambda x: x.get('s', float('-inf')))


>>> res.values()
[{'s': 1.5, 'id': 1}, {'s': 0.6, 'id': 3}]

答案 4 :(得分:0)

>>> l2={}
>>> for y in l:
        l2.setdefault(y['id'],[]).append(y['s'])
>>> l3=[{'id':k,'s':max(v)} for k,v in l2.items()]
>>> print l3

给出:

[{'id': 1, 's': 1.5}, {'id': 3, 's': 0.6}]

答案 5 :(得分:0)

按降序s排序,因此对于每个id,最高s排在第一位。然后只选择第一次出现的id

seen = set()
output = [d for d in sorted(l, key=lambda d: d['s'], reverse=True)
          if d['id'] not in seen and not seen.add(d['id'])]

您可以决定先进行排序,以避免额外的空间,但需要以触摸输入为代价。

所有这些在时间和空间复杂性方面可能都不是最佳的,但它非常优雅。