例えば:
>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]
リスト要素がハッシュ可能であると想定します。
明確化:結果は、リストの最初の重複を保持する必要があります。たとえば、[1、2、3、2、3、1]は[1、2、3]になります。
def unique(items):
found = set()
keep = []
for item in items:
if item not in found:
found.add(item)
keep.append(item)
return keep
print unique([1, 1, 2, 'a', 'a', 3])
使用:
lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
timeit モジュールを使用すると、次のようになります。
$ python -m timeit -s 'import uniquetest' 'uniquetest.etchasketch(uniquetest.lst)'
その他のさまざまな機能 (ポスターにちなんで名付けました) についても、次の結果が得られました (私の第 1 世代の Intel MacBook Pro で)。
Allen: 14.6 µs per loop [1]
Terhorst: 26.6 µs per loop
Tarle: 44.7 µs per loop
ctcherry: 44.8 µs per loop
Etchasketch 1 (short): 64.6 µs per loop
Schinckel: 65.0 µs per loop
Etchasketch 2: 71.6 µs per loop
Little: 89.4 µs per loop
Tyler: 179.0 µs per loop
[1] Allen がその場でリストを変更していることに注意してください。timeit
モジュールがコードを 100000 回実行し、そのうちの 99999 回が重複のないリストであるという点で、これが時間を歪めていると思います。
まとめ: セットを使用した簡単な実装は、混乱を招くワンライナーに勝ります :-)
更新: Python3.7+ で:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
古い答え:
これまでのところ最速のソリューションです(次の入力の場合):
def del_dups(seq):
seen = {}
pos = 0
for item in seq:
if item not in seen:
seen[item] = True
seq[pos] = item
pos += 1
del seq[pos:]
lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18,
13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1,
5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9,
9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
del_dups(lst)
print(lst)
# -> [8, 9, 7, 15, 2, 20, 13, 24, 6, 11, 12, 4, 10, 18, 23, 3, 5, 22, 19, 14,
# 21, 1, 0, 16, 17]
辞書検索は、Python 3 のセットの検索よりもわずかに高速です。
何が最速になるかは、リストの何パーセントが重複しているかによって異なります。ほとんどすべてが重複しており、一意のアイテムがほとんどない場合は、新しいリストを作成する方がおそらく高速です。ほとんどが一意のアイテムである場合は、元のリスト (またはコピー) から削除する方が高速です。
リストをその場で変更するための例を次に示します。
def unique(items):
seen = set()
for i in xrange(len(items)-1, -1, -1):
it = items[i]
if it in seen:
del items[i]
else:
seen.add(it)
インデックスを逆方向に反復することで、アイテムの削除が反復に影響しないことが保証されます。
これは、私が見つけた最速のインプレース メソッドです (大部分の重複があると仮定します)。
def unique(l):
s = set(); n = 0
for x in l:
if x not in s: s.add(x); l[n] = x; n += 1
del l[n:]
これは、ベースとなっている Allen の実装よりも 10% 高速です (timeit.repeat でタイミングを合わせ、psyco によってコンパイルされた JIT)。複製の最初のインスタンスを保持します。
repton-infinity: 私のタイミングを確認していただければ幸いです。
必須のジェネレータベースのバリエーション:
def unique(seq):
seen = set()
for x in seq:
if x not in seen:
seen.add(x)
yield x
これが最も簡単な方法かもしれません:
list(OrderedDict.fromkeys(iterable))
Python 3.5以降、OrderedDictはCで実装されるようになったため、これが最短、最クリーン、最速になりました。
http://www.peterbe.com/plog/uniqifiers-benchmarkから取得
def f5(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result
一発ギャグ:
new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, [])
これは、このベンチマークを参照して、この長い議論からのすべてのものとここで与えられた他の回答を比較して、最速のものです。これは、議論からの最速の関数よりもさらに 25% 高速です。アイデアを提供してくれた David Kirby に感謝します。f8
def uniquify(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
いくつかの時間比較:
$ python uniqifiers_benchmark.py
* f8_original 3.76
* uniquify 3.0
* terhorst 5.44
* terhorst_localref 4.08
* del_dups 4.76
このためのインプレースワンライナー:
>>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3]
これを解決するために、実際にPythonで本当にクールなことをすることができます。構築中に自分自身を参照するリスト内包表記を作成できます。次のように:
# remove duplicates...
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]'].__self__]
編集: 「自己」を削除しました。Mac OS X、Python 2.5.1 で動作します。
_[1] は、新しいリストへの Python の「秘密の」参照です。もちろん、上記は少し面倒ですが、必要に応じてニーズに合わせて調整できます。たとえば、内包表記への参照を返す関数を実際に作成できます。次のようになります。
return [x for x in my_list if x not in this_list()]
重複は最初からリストに含まれている必要がありますか? 要素を検索する限りオーバーヘッドはありませんが、要素を追加する際のオーバーヘッドが少し増えます (ただし、オーバーヘッドは O(1) である必要があります)。
>>> x = []
>>> y = set()
>>> def add_to_x(val):
... if val not in y:
... x.append(val)
... y.add(val)
... print x
... print y
...
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>>
重複を削除して順序を維持する:
これは、リスト内包表記と辞書の組み込み機能を活用する高速な 2 ライナーです。
x = [1, 1, 2, 'a', 'a', 3]
tmpUniq = {} # temp variable used below
results = [tmpUniq.setdefault(i,i) for i in x if i not in tmpUniq]
print results
[1, 2, 'a', 3]
dict.setdefaults() 関数は、値を返すだけでなく、それをリスト内包表記の一時辞書に直接追加します。組み込み関数と dict のハッシュを使用すると、プロセスの効率を最大化できます。
itertoolsドキュメントからの2つのレシピは次のとおりです。
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return imap(next, imap(itemgetter(1), groupby(iterable, key)))
Python の has_key は O(1) です。ハッシュからの挿入と取得も O(1) です。n 個の項目を 2 回ループするので、O(n) になります。
def unique(list):
s = {}
output = []
for x in list:
count = 1
if(s.has_key(x)):
count = s[x] + 1
s[x] = count
for x in list:
count = s[x]
if(count > 0):
s[x] = 0
output.append(x)
return output
dict が hash の場合は O(n)、dict が tree の場合は O(nlogn)、simple、fixed です。提案をしてくれたマシューに感謝します。申し訳ありませんが、基になる型はわかりません。
def unique(x):
output = []
y = {}
for item in x:
y[item] = ""
for item in x:
if item in y:
output.append(item)
return output
ここには、優れた効率的なソリューションがいくつかあります。ただし、絶対的な最も効率的なO(n)
ソリューションに関心がない人には、単純なワンライナーO(n^2*log(n))
ソリューションを使用します。
def unique(xs):
return sorted(set(xs), key=lambda x: xs.index(x))
または、より効率的な 2 行のO(n*log(n))
ソリューション:
def unique(xs):
positions = dict((e,pos) for pos,e in reversed(list(enumerate(xs))))
return sorted(set(xs), key=lambda x: positions[x])
>>> def unique(list):
... y = []
... for x in list:
... if x not in y:
... y.append(x)
... return y
私はpythonの経験がありませんが、アルゴリズムはリストをソートし、次に重複を削除し(リスト内の前のアイテムと比較して)、最後に古いリストと比較して新しいリストの位置を見つけます。
より長い答え: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
Terhost の回答で set() の呼び出しから空のリストを取り出すと、速度が少し向上します。
変更: found = set([])
から: found = set()
ただし、セットはまったく必要ありません。
def unique(items):
keep = []
for item in items:
if item not in keep:
keep.append(item)
return keep
timeit を使用すると、次の結果が得られました。
set([]) あり -- 4.97210427363
set() あり -- 4.65712377445
set なし -- 3.44865284975
x = [] # Your list of items that includes Duplicates
# Assuming that your list contains items of only immutable data types
dict_x = {}
dict_x = {item : item for i, item in enumerate(x) if item not in dict_x.keys()}
# Average t.c. = O(n)* O(1) ; furthermore the dict comphrehension and generator like behaviour of enumerate adds a certain efficiency and pythonic feel to it.
x = dict_x.keys() # if you want your output in list format
これが速いかどうかはわかりませんが、少なくとも単純です。
単純に、最初にセットに変換してから、もう一度リストに変換します
def unique(container):
return list(set(container))
>>> x=[1,1,2,'a','a',3]
>>> y = [ _x for _x in x if not _x in locals()['_[1]'] ]
>>> y
[1, 2, 'a', 3]
"locals()['_[1]']" は、作成中のリストの「秘密の名前」です。
ワンパス。
a = [1,1,'a','b','c','c']
new_list = []
prev = None
while 1:
try:
i = a.pop(0)
if i != prev:
new_list.append(i)
prev = i
except IndexError:
break
a=[1,2,3,4,5,7,7,8,8,9,9,3,45]
def unique(l):
ids={}
for item in l:
if not ids.has_key(item):
ids[item]=item
return ids.keys()
print a
print unique(a)
要素を挿入すると、要素が終了しているかどうかを取得するのに theta(n) がかかります。すべての項目をテストするのに一定の時間がかかります。Python の辞書はハッシュ テーブルによって実装されることに注意してください。
私はテストを行っていませんが、考えられるアルゴリズムの 1 つは、2 番目のリストを作成し、最初のリストを反復処理することです。項目が 2 番目のリストにない場合は、2 番目のリストに追加します。
x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
if each not in y:
y.append(each)