2016-09-28 132 views
2

假設我們有一個未知大小的列表,列表中有一個元素與其他元素不同,但我們不知道元素的索引。該列表僅包含數字並從遠程服務器獲取,並且每次都更改列表的長度和不同元素的索引。什麼是找到不同元素的最pythonic方式? 我試過這個,但我不確定它是否是最好的解決方案。在列表中找到與其他元素不同的元素時,最常見的pythonic方法是什麼?

a = 1 
different_element = None 
my_list = fetch_list() 

b = my_list[0] - a 

for elem in my_list[1::]: 
    if elem - a != b: 
     different_element = elem 

print(different_element) 
+0

如果列表大小爲1或2,該怎麼辦? – depperm

+0

所以這個列表實際上是一堆重複的一個獨特的? –

+0

是啊@vishes_shell – Juggernaut

回答

2

這是numpy

一個偉大的使用考慮到它的一些隨機均勻列表與單個唯一不同的數字:

>>> li=[1]*100+[200]+[1]*250 

如果均勻值是已知的(在這種情況下,1和未知值是200),你可以使用np.where陣列上獲得該值:

>>> import numpy as np 
>>> a=np.array(li) 
>>> a[a!=1] 
array([200]) 

如果均勻值是不知道,你可以使用np.uniques獲得不重複計數:

>>> np.unique(a, return_counts=True) 
(array([ 1, 200]), array([350, 1])) 

對於一個純Python的解決方案,使用發電機next獲得的第一個值比所有其他人不同:

>>> next(e for i, e in enumerate(li) if li[i]!=1) 
200 

或者,你可以使用dropwhile從itertools:

>>> from itertools import dropwhile 
>>> next(dropwhile(lambda e: e==1, li)) 
200 

如果你不知道的均勻值是什麼,用一個計數器上大切片足夠得到它:

>>> uniform=Counter(li[0:3]).most_common()[0][0] 
>>> uniform 
1 
>>> next(e for i, e in enumerate(li) if li[i]!=uniform) 
200 

在這些情況下,next會短路,在滿足條件的第一個值。

+0

numpy的解決方案是相當驚人的。謝謝 – Juggernaut

2

這會適合您嗎?

In [6]: my_list = [1,1,1,2,1,1,1] 
In [7]: different = [ii for ii in set(my_list) if my_list.count(ii) == 1] 
In [8]: different 
Out[8]: [2] 
+1

這可能不是最有效的方法,因爲它從列表創建一個集合(我猜測它是O(N)),然後統計每個集合的出現次數可能的值(再次,O(N))。對於短名單它會做的很好,並會找到所有不同的值 – Jblasco

+0

是的,這個工程,但運行在二次時間 – timgeb

2

您可以使用Countercollections

from collections import Counter 

a = [1,2,3,4,3,4,1] 
b = Counter(a) # Counter({1: 2, 2: 1, 3: 2, 4: 2}) 
elem = list(b.keys())[list(b.values()).index(1)] # getting elem which is key with value that equals 1 
print(a.index(elem)) 

另一種可能的解決方案,只是不同的計算elem

a = [1,2,3,4,3,4,1] 
b = Counter(a) # Counter({1: 2, 2: 1, 3: 2, 4: 2}) 
elem = (k for k, v in b.items() if v == 1) 
print(a.index(next(elem))) 

UPDATE

鈦我的消費:

正如@Jblasco所提到的,Jblasco的方法並不是真正有效的方法,我很想好好衡量它。

所以最初的數據是200-400個元素的數組,只有一個唯一值。生成該數組的代碼是。在剪斷到底有證明,它具有100個第一要素一個獨特

import random 
from itertools import chain 
f = lambda x: [x]*random.randint(2,4) 
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100))) 
a[random.randint(1, 100)] = 101 
print(a[:100]) 
# [5, 5, 5, 84, 84, 84, 46, 46, 46, 46, 6, 6, 6, 68, 68, 68, 68, 38, 
# 38, 38, 44, 44, 61, 61, 15, 15, 15, 15, 36, 36, 36, 36, 73, 73, 73, 
# 28, 28, 28, 28, 6, 6, 93, 93, 74, 74, 74, 74, 12, 12, 72, 72, 22, 
# 22, 22, 22, 78, 78, 17, 17, 17, 93, 93, 93, 12, 12, 12, 23, 23, 23, 
# 23, 52, 52, 88, 88, 79, 79, 42, 42, 34, 34, 47, 47, 1, 1, 1, 1, 71, 
# 71, 1, 1, 45, 45, 101, 45, 39, 39, 50, 50, 50, 50] 

就是這樣告訴我們的結果,我選擇用10000個處決執行3次代碼:

from timeit import repeat 


s = """\ 
import random 
from itertools import chain 
f = lambda x: [x]*random.randint(2,4) 
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100))) 
a[random.randint(1, 100)] = 101 
""" 

print('my 1st method:', repeat(stmt="""from collections import Counter 
b=Counter(a) 
elem = (k for k, v in b.items() if v == 1) 
a.index(next(elem))""", 
      setup=s, number=10000, repeat=3) 

print('my 2nd method:', repeat(stmt="""from collections import Counter 
b = Counter(a) 
elem = list(b.keys())[list(b.values()).index(1)] 
a.index(elem)""", 
      setup=s, number=10000, repeat=3)) 

print('@Jblasco method:', repeat(stmt="""different = [ii for ii in set(a) if a.count(ii) == 1] 
different""", setup=s, number=10000, repeat=3)) 

# my 1st method: [0.303596693000145, 0.27322746600111714, 0.2701447969993751] 
# my 2nd method: [0.2715420649983571, 0.28590541199810104, 0.2821485950007627] 
# @Jblasco method: [3.2133491599997797, 3.488262927003234, 2.884892332000163] 
+0

請添加我的兩個功能。它們比您在此發佈的速度快4到10倍。 – dawg

+0

@dawg對不起,但我在列表上只有一個唯一值和其他重複項(不限於只有一個數字)。而你只在兩個數字上工作(一個唯一,另一個不是)。 –

1

我會嘗試可能是這樣的:

newList = list(set(my_list)) 
print newList.pop() 

假設只有1個不同的值,其餘都是一樣的。 在你的問題中有一點含糊不清,這使得難以回答,但這就是我能想到的最佳方式。

+1

爲什麼'pop()'返回唯一元素而不是其中一個重複元素? – Barmar

+0

集合是無序的,所以你不知道你是否得到唯一的或統一的編號。 – dawg

相關問題