2013-05-01 91 views
1

重複的元素假設我有以下列表蟒蛇:刪除基於分數

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

排序有多重要?輸入字典中是否還有其他鍵或僅有'id'和's'鍵? – 2013-05-01 11:34:01

+0

@MartijnPieters我不關心輸出列表的排序。 – Shai 2013-05-01 11:34:39

+0

此清單來自哪裏? – Aya 2013-05-01 11:35:25

回答

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

使用.items(),而是如果你使用Python 3

結果(順序改變,因爲一個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()] 
+0

不過,我很希望有一種解決方案,不需要「l」的「重新生成」,因爲在我的情況下,許多領域的每個元素,而不僅僅是''''''和''''... – Shai 2013-05-01 11:39:04

+0

第二種解決方案似乎適用於我。 – Shai 2013-05-01 12:11:10

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

這裏是我的解決方案,使用GROUPBY從itertools

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

回覆:阿什維尼

我已經做了performance test,比較不同的解決方案。這裏的結果,以圖表形式:

enter image description here

我只用10個不同的值,爲'id'關鍵在這裏,你可以與自己的代碼看lst成分如何影響結果玩。更改id值的數量列表中的項目數量的一半,使阿什維尼明確的勝利者,並集中使我們的休息:

enter image description here

這是當你比較的O(n)它的外觀解決方案在雙對數圖的O(n*log(n))解決方案:

enter image description here

所以,我不太清楚有關於大O參數得出什麼結論。

+1

排序使它成爲'O(NLogN)'解決方案,但是這可以在'O(N)'中完成。 – 2013-05-01 11:47:20

+0

@Ashwini你確定嗎?使用defaultdict解決方案,您必須爲每個ID創建一個列表,並在末尾遍歷該列表以查找最大值。這不就是一個僞裝的O(NLogN)解決方案嗎? – 2013-05-01 11:51:13

+0

我只循環一次'list'並根據條件更新'dic',然後'dic.values()'也是一個循環。 – 2013-05-01 12:04:41

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

@jamylak - 我不確定這個解決方案是否有效(或者我錯過了什麼?)。 'dic'在任何階段都沒有鍵's',所以'dic.get('s',float(' - inf'))'將始終是'-inf' ... – Shai 2013-05-01 11:54:22

+0

@Shai不是我的解決方案但是這肯定是一個錯字,應該是'idx' – jamylak 2013-05-01 11:55:44

+0

@Shai你是對的,我解決了這個問題。 – 2013-05-01 11:58:27

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

你可能會決定首先排序,以避免額外的空間以觸摸輸入爲代價。

所有這些在時間和空間複雜性方面可能都不是最佳的,但它非常優雅。