906

順序を維持しながら、Pythonのリストから重複を削除する組み込み機能はありますか?セットを使用して重複を削除できることは知っていますが、それによって元の順序が破棄されます。私はまた、私がこのように自分自身を転がすことができることを知っています:

def uniq(input):
  output = []
  for x in input:
    if x not in output:
      output.append(x)
  return output

(そのコードサンプルをお楽しみいただきありがとうございます。)

ただし、可能であれば、組み込みまたはよりPythonicのイディオムを利用したいと思います。

関連する質問: Pythonで、順序を維持しながらすべての要素が一意になるようにリストから重複を削除するための最速のアルゴリズムは何ですか?

4

33 に答える 33

855

ここにいくつかの代替手段があります: http://www.peterbe.com/plog/uniqifiers-benchmark

最速のもの:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

seen.addを呼び出すだけでseen_addなく、に割り当てるのはなぜseen.addですか? Python は動的言語であり、seen.add各反復の解決はローカル変数の解決よりもコストがかかります。seen.add繰り返しの間に変更された可能性があり、ランタイムはそれを除外するほど賢くありません。安全にプレイするには、毎回オブジェクトをチェックする必要があります。

この関数を同じデータセットで頻繁に使用する予定がある場合は、順序付きセットを使用したほうがよいでしょう: http://code.activestate.com/recipes/528878/

O (1) 操作ごとの挿入、削除、メンバーチェック。

(小さな追加メモ:seen.add()常に を返すNoneため、or上記は論理テストの不可欠な部分としてではなく、セットの更新を試みる方法としてのみ存在します。)

于 2009-01-26T15:47:01.490 に答える
27
from itertools import groupby
[ key for key,_ in groupby(sortedList)]

リストはソートする必要さえありません。十分な条件は、等しい値がグループ化されることです。

編集:「順序を維持する」とは、リストが実際に順序付けられていることを意味すると思いました。そうでない場合は、MizardX のソリューションが適切です。

コミュニティの編集: ただし、これは「重複する連続した要素を単一の要素に圧縮する」ための最もエレガントな方法です。

于 2009-01-26T15:47:14.217 に答える
15

外部モジュールからそのような機能の別の(非常にパフォーマンスの高い)実装を追加するだけです1 : iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen
>>> lst = [1,1,1,2,3,2,2,2,1,3,4]

>>> list(unique_everseen(lst))
[1, 2, 3, 4]

タイミング

私はいくつかのタイミング(Python 3.6)を実行しましたが、これらは、テストした他のすべての代替手段よりも高速であることを示してOrderedDict.fromkeysf7ますmore_itertools.unique_everseen

%matplotlib notebook

from iteration_utilities import unique_everseen
from collections import OrderedDict
from more_itertools import unique_everseen as mi_unique_everseen

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if not (x in seen or seen_add(x))]

def iteration_utilities_unique_everseen(seq):
    return list(unique_everseen(seq))

def more_itertools_unique_everseen(seq):
    return list(mi_unique_everseen(seq))

def odict(seq):
    return list(OrderedDict.fromkeys(seq))

from simple_benchmark import benchmark

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: list(range(2**i)) for i in range(1, 20)},
              'list size (no duplicates)')
b.plot()

ここに画像の説明を入力

そして、それが違いを生むかどうかを確認するためだけに、より多くの重複を使用してテストも行ったことを確認するために:

import random

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(1, 20)},
              'list size (lots of duplicates)')
b.plot()

ここに画像の説明を入力

そして、値を 1 つだけ含むもの:

b = benchmark([f7, iteration_utilities_unique_everseen, more_itertools_unique_everseen, odict],
              {2**i: [1]*(2**i) for i in range(1, 20)},
              'list size (only duplicates)')
b.plot()

ここに画像の説明を入力

これらのすべてのケースで、iteration_utilities.unique_everseen関数は (私のコンピューター上で) 最速です。


このiteration_utilities.unique_everseen関数は、入力内のハッシュ不可能な値も処理できます (ただし、値がハッシュ可能である場合O(n*n)は、パフォーマンスではなくパフォーマンスを使用O(n)します)。

>>> lst = [{1}, {1}, {2}, {1}, {3}]

>>> list(unique_everseen(lst))
[{1}, {2}, {3}]

1免責事項: 私はそのパッケージの作成者です。

于 2017-01-10T19:55:48.137 に答える
12

別の非常に古い質問に対する別の非常に遅い回答については:

itertoolsレシピseenには、 set テクニックを使用してこれを行う関数がありますが、次のとおりです。

  • 標準key関数を処理します。
  • 見苦しいハックは使用しません。
  • seen.addN 回ルックアップするのではなく、事前にバインドすることでループを最適化します。(f7これも行いますが、一部のバージョンでは行いません。)
  • を使用してループを最適化するifilterfalseため、Python のすべての要素ではなく、一意の要素のみをループする必要があります。(もちろん、内部でそれらすべてを繰り返し処理しますがifilterfalse、それは C であり、はるかに高速です。)

それは実際よりも速いですf7か?これはデータに依存するため、テストして確認する必要があります。最後にリストが必要な場合f7は、listcomp を使用しますが、ここではそれを行う方法はありません。( ingappendの代わりに直接実行することも、ジェネレーターを関数にフィードすることもできますが、どちらも listcomp 内の LIST_APPEND ほど高速にはなりません。) いずれにせよ、通常、数マイクロ秒を絞り出すことはそれほど速くはありません。装飾したいときにDSUを必要としない、簡単に理解でき、再利用可能な、すでに作成された関数を持つことが重要です。yieldlist

すべてのレシピと同様に、more-iterools.

no- keycase のみが必要な場合は、次のように単純化できます。

def unique(iterable):
    seen = set()
    seen_add = seen.add
    for element in itertools.ifilterfalse(seen.__contains__, iterable):
        seen_add(element)
        yield element
于 2013-10-09T18:27:09.767 に答える
6

MizardX に基づく、ハッシュ可能なタイプ (リストのリストなど) がない場合:

def f7_noHash(seq)
    seen = set()
    return [ x for x in seq if str( x ) not in seen and not seen.add( str( x ) )]
于 2011-08-21T20:04:12.327 に答える
4

5 倍高速な reduce バリアントですが、より洗練されています

>>> l = [5, 6, 6, 1, 1, 2, 2, 3, 4]
>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

説明:

default = (list(), set())
# use list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

>>> reduce(reducer, l, default)[0]
[5, 6, 1, 2, 3, 4]
于 2015-04-27T14:47:21.623 に答える
4

これを行う簡単な方法は次のとおりです。

list1 = ["hello", " ", "w", "o", "r", "l", "d"]
sorted(set(list1 ), key=list1.index)

それは出力を与える:

["hello", " ", "w", "o", "r", "l", "d"]
于 2020-04-04T06:03:27.893 に答える
3

シンボル '_[1]' によって構築されているリスト内包表記を参照できます。
たとえば、次の関数は、リスト内包表記を参照することにより、順序を変更せずに要素のリストを一意化します。

def unique(my_list): 
    return [x for x in my_list if x not in locals()['_[1]']]

デモ:

l1 = [1, 2, 3, 4, 1, 2, 3, 4, 5]
l2 = [x for x in l1 if x not in locals()['_[1]']]
print l2

出力:

[1, 2, 3, 4, 5]
于 2012-11-07T03:21:34.133 に答える
1

配列を使用し_sorted_た比較的効果的なアプローチ:numpy

b = np.array([1,3,3, 8, 12, 12,12])    
numpy.hstack([b[0], [x[0] for x in zip(b[1:], b[:-1]) if x[0]!=x[1]]])

出力:

array([ 1,  3,  8, 12])
于 2013-10-02T11:23:31.650 に答える
1
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))

セットの O(1) ルックアップを使用して、要素を新しいリストに含めるかどうかを決定するジェネレータ式。

于 2014-11-07T05:02:54.830 に答える
1

1 つのライナーが必要な場合は、これが役立つかもしれません。

reduce(lambda x, y: x + y if y[0] not in x else x, map(lambda x: [x],lst))

...動作するはずですが、間違っている場合は修正してください

于 2011-08-05T17:06:25.247 に答える
1

一種の醜いリスト理解ハックを行うことができます。

[l[i] for i in range(len(l)) if l.index(l[i]) == i]
于 2014-04-25T01:28:31.997 に答える
1

MizardX の答えは、複数のアプローチの優れたコレクションを提供します。

色々と考えた結果、以下のようになりました。

mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]
于 2011-10-09T14:16:00.440 に答える
0

インポートされたモジュールまたはセットを使用しないソリューション:

text = "ask not what your country can do for you ask what you can do for your country"
sentence = text.split(" ")
noduplicates = [(sentence[i]) for i in range (0,len(sentence)) if sentence[i] not in sentence[:i]]
print(noduplicates)

出力を与えます:

['ask', 'not', 'what', 'your', 'country', 'can', 'do', 'for', 'you']
于 2016-01-27T13:08:08.430 に答える
0

zmk のアプローチは、非常に高速でありながら順序を自然に保つリスト内包表記を使用します。大文字と小文字を区別する文字列に適用する場合は、簡単に変更できます。これも元のケースを保持します。

def DelDupes(aseq) :
    seen = set()
    return [x for x in aseq if (x.lower() not in seen) and (not seen.add(x.lower()))]

密接に関連する機能は次のとおりです。

def HasDupes(aseq) :
    s = set()
    return any(((x.lower() in s) or s.add(x.lower())) for x in aseq)

def GetDupes(aseq) :
    s = set()
    return set(x for x in aseq if ((x.lower() in s) or s.add(x.lower())))
于 2019-06-27T14:01:30.720 に答える