45

例えば:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]

リスト要素がハッシュ可能であると想定します。

明確化:結果は、リストの最初の重複を保持する必要があります。たとえば、[1、2、3、2、3、1]は[1、2、3]になります。

4

27 に答える 27

33
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])
于 2008-09-18T01:41:18.170 に答える
19

使用:

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 回が重複のないリストであるという点で、これが時間を歪めていると思います。


まとめ: セットを使用した簡単な実装は、混乱を招くワンライナーに勝ります :-)

于 2008-09-18T05:14:19.750 に答える
18

更新: 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 のセットの検索よりもわずかに高速です。

于 2008-11-12T00:04:22.647 に答える
15

何が最速になるかは、リストの何パーセントが重複しているかによって異なります。ほとんどすべてが重複しており、一意のアイテムがほとんどない場合は、新しいリストを作成する方がおそらく高速です。ほとんどが一意のアイテムである場合は、元のリスト (またはコピー) から削除する方が高速です。

リストをその場で変更するための例を次に示します。

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)

インデックスを逆方向に反復することで、アイテムの削除が反復に影響しないことが保証されます。

于 2008-09-18T01:33:44.333 に答える
10

これは、私が見つけた最速のインプレース メソッドです (大部分の重複があると仮定します)。

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: 私のタイミングを確認していただければ幸いです。

于 2008-09-18T10:17:44.777 に答える
7

必須のジェネレータベースのバリエーション:

def unique(seq):
  seen = set()
  for x in seq:
    if x not in seen:
      seen.add(x)
      yield x
于 2008-09-27T15:54:03.440 に答える
7

これが最も簡単な方法かもしれません:

list(OrderedDict.fromkeys(iterable))

Python 3.5以降、OrderedDictはCで実装されるようになったため、これが最短、最クリーン、最速になりました。

于 2011-10-21T01:10:05.853 に答える
5

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
于 2008-09-18T01:30:50.410 に答える
5

一発ギャグ:

new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, [])
于 2008-09-18T01:59:05.833 に答える
4

これは、このベンチマークを参照して、この長い議論からのすべてのものとここで与えられた他の回答を比較して、最速のものです。これは、議論からの最速の関数よりもさらに 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
于 2013-12-26T15:38:50.320 に答える
4

このためのインプレースワンライナー:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3]
于 2010-04-09T13:11:48.107 に答える
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()]

于 2008-09-18T01:43:03.703 に答える
2

重複は最初からリストに含まれている必要がありますか? 要素を検索する限りオーバーヘッドはありませんが、要素を追加する際のオーバーヘッドが少し増えます (ただし、オーバーヘッドは 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])
>>> 
于 2008-09-18T02:06:40.700 に答える
2

重複を削除して順序を維持する:

これは、リスト内包表記と辞書の組み込み機能を活用する高速な 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 のハッシュを使用すると、プロセスの効率を最大化できます。

于 2010-12-29T17:11:51.973 に答える
1

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)))
于 2011-10-21T01:07:11.860 に答える
1

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
于 2008-09-18T04:07:21.347 に答える
1

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
于 2008-09-18T01:35:33.130 に答える
1

ここには、優れた効率的なソリューションがいくつかあります。ただし、絶対的な最も効率的な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])
于 2008-09-18T13:23:05.673 に答える
0
>>> def unique(list):
...   y = []
...   for x in list:
...     if x not in y:
...       y.append(x)
...   return y
于 2008-09-18T01:32:47.457 に答える
0

私はpythonの経験がありませんが、アルゴリズムはリストをソートし、次に重複を削除し(リスト内の前のアイテムと比較して)、最後に古いリストと比較して新しいリストの位置を見つけます。

より長い答え: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560

于 2008-09-18T01:29:06.903 に答える
0

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

于 2008-09-19T09:42:20.657 に答える
0
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 
于 2014-09-21T11:26:53.927 に答える
-1

これが速いかどうかはわかりませんが、少なくとも単純です。

単純に、最初にセットに変換してから、もう一度リストに変換します

def unique(container):
  return list(set(container))
于 2008-09-18T08:51:32.660 に答える
-1
>>> 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]']" は、作成中のリストの「秘密の名前」です。

于 2008-09-18T01:54:40.027 に答える
-2

ワンパス。

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
于 2008-09-18T05:08:13.367 に答える
-2
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 の辞書はハッシュ テーブルによって実装されることに注意してください。

于 2008-11-11T00:32:18.847 に答える
-2

私はテストを行っていませんが、考えられるアルゴリズムの 1 つは、2 番目のリストを作成し、最初のリストを反復処理することです。項目が 2 番目のリストにない場合は、2 番目のリストに追加します。

x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
    if each not in y:
        y.append(each)
于 2008-09-18T01:29:11.820 に答える