順序を維持しながら、リストから重複をどのように削除しますか?


770

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

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

(そのコードサンプルほどいてくれてありがとう。)

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

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

回答:


762

ここにいくつかの選択肢があります: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.addseen_addだけではなく、呼び出しのseen.add?Pythonは動的言語であり、seen.add各反復の解決はローカル変数の解決よりもコストがかかります。seen.add反復間で変更された可能性があり、ランタイムはそれを除外するほどスマートではありません。安全にプレイするには、毎回オブジェクトをチェックする必要があります。

この関数を同じデータセットで何度も使用することを計画している場合は、順序付きセットを使用したほうがよいでしょう。http//code.activestate.com/recipes/528878/

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

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


20
@JesseDhillon seen.addは反復間で変更されている可能性があり、ランタイムはそれを除外するほどスマートではありません。安全にプレイするには、毎回オブジェクトをチェックする必要があります。-バイトコードをdis.dis(f)で見ると、各反復でメンバーLOAD_ATTRに対して実行されることがわかりaddます。ideone.com/tz1Tll
Markus Jarderot 2013年

5
リストのリストでこれを試してみると、TypeError:unhashable type: 'list'
Jens Timmerman

7
あなたのソリューションは最速のものではありません。Python 3(テスト2ではありませんでした)では、これはより高速です(300kエントリリスト-0.045秒(自分のもの)対0.035秒(これのもの)):seen = set(); return x [x for in in not in not in not and not seen.add(x)]。あなたが見たseen_add行の速度効果を見つけることができませんでした
user136036

3
@ user136036テストにリンクしてください。それらを何回実行しましたか?seen_add改善ですが、タイミングはその時点のシステムリソースの影響を受ける可能性があります。完全なタイミングを確認したいと
思います

2
Pythonコードを書いている人にとっては、可読性と一般的に認められているPythonの規則を犠牲にする前に、ループごとにさらに数ナノ秒を絞るために、よく考えなければなりません。seen_add = seen.add歩留まりありとなしのテストでは、速度が1%だけ向上します。それはほとんど重要ではありません。
sleblanc

343

2016年を編集

レイモンドが指摘したようにOrderedDictCで実装されているpython 3.5以降では、リストの理解のアプローチはOrderedDict(実際にリストが実際に必要な場合を除き、さらには入力が非常に短い場合のみ)より遅くなります。したがって、3.5以上の場合の最適なソリューションはOrderedDictです。

重要な編集2015

@abarnertノート、more_itertoolsライブラリーは、( pip install more_itertools)が含まunique_everseenいかなるせずにこの問題を解決するために構築されている機能読めないnot seen.addの変異リストの内包表記では。これも最速のソリューションです。

>>> from  more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]

単純なライブラリのインポートは1つだけで、ハックはありません。これは、unique_everseen次のようなitertoolsレシピの実装によるものです。

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 filterfalse(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

Pythonでは2.7+、この用途で受け入れられている一般的なイディオム(機能しますが、速度が最適化されていません。今はを使用しますunique_everseencollections.OrderedDict

ランタイム:O(N)

>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]

これは次のものよりはるかに良く見えます:

seen = set()
[x for x in seq if x not in seen and not seen.add(x)]

そして醜いハックを利用していません:

not seen.add(x)

これはset.add、常に返されるインプレースメソッドであるという事実に依存するNoneため、にnot None評価されTrueます。

ただし、実行時の複雑さは同じO(N)ですが、ハックソリューションの方が高速であることに注意してください。


5
キーを取るためだけにカスタムの種類の辞書に変換しますか?ただの松葉杖。
ナキロン2013年

3
@Nakilon私はそれが松葉杖である方法を本当に見ていない。変更可能な状態を公開しないため、その意味では非常にクリーンです。内部的には、Pythonセットはdict()(stackoverflow.com/questions/3949310/…)で実装されているので、基本的には、とにかくインタプリタが行うことを行っているだけです。
イムラン2013年

副作用を使用して実行するか[seen.add(x) for x in seq if x not in seen]、理解が苦手な場合はforループを使用してください:(for x in seq: seen.add(x) if x not in seen else Noneまだワンライナーですが、この場合、ワンライナーネスは、ソリューション
2013

@EMS順序が保持されません。あなたもそうすることができますseen = set(seq)
flornquake 2013

1
事実上、それが原因スーパー非常に低い最悪の場合には、ほとんどの場合、O(n)のだが、私は、同意@CommuSoft
jamylak

110

Python 2.7では、元の順序を維持しながら反復可能オブジェクトから重複を削除する新しい方法は次のとおりです。

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

Python 3.5では、OrderedDictにC実装があります。私のタイミングは、これがPython 3.5のさまざまなアプローチの中で最速かつ最短であることを示しています。

Python 3.6では、通常の辞書が秩序あるコンパクトなものになりました。(この機能はCPythonとPyPyに適用されますが、他の実装には存在しない場合があります)。これにより、順序を維持しながら重複排除を行う新しい最速の方法が得られます。

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

Python 3.7では、通常のdictはすべての実装で順序付けされることが保証されています。 したがって、最短かつ最速のソリューションは次のとおりです。

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

@maxへの対応:3.6または3.7に移行してOrderedDictの代わりに通常の辞書を使用すると、他の方法ではパフォーマンスを打ち負かすことはできません。辞書は密度が高く、ほとんどオーバーヘッドのないリストにすぐに変換されます。ターゲットリストはlen(d)にプリサイズされており、リスト内包で発生するすべてのサイズ変更が保存されます。また、内部キーリストは密集しているため、ポインタのコピーはリストコピーとほぼ同じくらい高速です。


最後にリストに変換しない限り、私のマシン(python 3.5)の他のどのアプローチよりも高速ですOrderedDict。それをリストに変換する必要がある場合は、小さな入力の場合でも、リストの理解アプローチは最大1.5倍高速です。とはいえ、このソリューションはよりクリーンです。
最大

7
唯一の落とし穴は、反復可能な「要素」がハッシュ可能でなければならないことです-任意の要素(リストのリストとして)を持つ反復可能なものと同等のものがあると便利です
Mr_and_Mrs_D

dictに対する挿入順序の反復は、重複を削除するよりも多くのユースケースに対応する機能を提供します。たとえば、科学的分析は、非決定論的なdict反復がサポートしていない再現可能な計算に依存しています。再現性は、計算科学モデリングにおける現在の主要な目的であるため、この新しい機能を歓迎します。確定的ディクショナリで構築するのは簡単なことですが、高性能で確定的でset()あれば、より単純なユーザーが再現可能なコードを開発するのに役立ちます。
Arthur

41
sequence = ['1', '2', '3', '3', '6', '4', '5', '6']
unique = []
[unique.append(item) for item in sequence if item not in unique]

ユニーク→ ['1', '2', '3', '6', '4', '5']


28
これが実行されることは注目に値しますn^2
goncalopp

25
いや。2ストライキ:メンバーシップテストのリストの使用(遅い、O(N))および副作用のリスト理解の使用(Noneプロセスでの参照の別のリストの作成!)
Martijn Pieters

1
私は@MartijnPietersに同意します。副作用を伴うリスト理解の理由はまったくありませんfor代わりにループを使用してください
jamylak

31

死んだ馬を蹴らないでください(この質問は非常に古く、すでに多くの良い答えがあります)が、ここでは、多くの状況で非常に高速で使いやすい、パンダを使用した解決策を示します。

import pandas as pd

my_list = [0, 1, 2, 3, 4, 1, 2, 3, 5]

>>> pd.Series(my_list).drop_duplicates().tolist()
# Output:
# [0, 1, 2, 3, 4, 5]

27
from itertools import groupby
[ key for key,_ in groupby(sortedList)]

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

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

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


1
しかし、これは秩序を維持しません!

1
Hrm、これは問題です。リストを1回ループせずに同じ値がグループ化されることを保証できないため、その時点で重複を排除できた可能性があります。
ジョシュグローバー、

「順序を維持する」とは、リストが実際に順序付けられていることを意味すると想定しました。
ラファウDowgird

1
たぶん、入力リストの仕様は少し不明確です。値をグループ化する必要すらありません:[2、1、3、1]。では、保持する値と削除する値はどれですか。

1
@igorkfペアの2番目の要素を無視します。
ラファウDowgird

24

秩序を維持したいなら

あなたはこれを試すことができます:

list1 = ['b','c','d','b','c','a','a']    
list2 = list(set(list1))    
list2.sort(key=list1.index)    
print list2

または同様に、これを行うことができます:

list1 = ['b','c','d','b','c','a','a']  
list2 = sorted(set(list1),key=list1.index)  
print list2 

これを行うこともできます:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
for i in list1:    
    if not i in list2:  
        list2.append(i)`    
print list2

次のように書くこともできます:

list1 = ['b','c','d','b','c','a','a']    
list2 = []    
[list2.append(i) for i in list1 if not i in list2]    
print list2 

3
最初の2つの答えは、ソート関数を使用してリストの順序を再構築できることを前提としていますが、そうではない場合があります。
Richard

5
ほとんどの回答はパフォーマンスに焦点を当てています。パフォーマンスを心配するほど大きくないリストの場合、sorted(set(list1)、key = list1.index)は私が見た中で最高のものです。追加のインポート、追加の関数、追加の変数はなく、かなりシンプルで読みやすいです。
Derek Veit

23

Python 3.7以上と、辞書がされている保証はそのキー挿入順番を覚えておくこと。この質問に対する答えは、現在の状況を要約したものです。

OrderedDictしたがって、このソリューションは時代遅れになり、importステートメントがなければ、次のように簡単に発行できます。

>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> list(dict.fromkeys(lst))
[1, 2, 3, 4]

12

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

itertoolsレシピは使用して、これを行い機能を持つseenセット技術を、しかし:

  • 標準key関数を処理します。
  • 見苦しいハッキングを使用していません。
  • seen.addN回検索するのではなく、事前バインディングによってループを最適化します。(f7これも行いますが、一部のバージョンでは行いません。)
  • を使用してループを最適化するifilterfalseため、すべての要素ではなく、Pythonの一意の要素をループするだけで済みます。(ifilterfalseもちろん、内部でそれらすべてを繰り返し処理しますが、それはC言語で行われ、はるかに高速です。)

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

すべてのレシピと同様に、でも使用できます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

more-itertoolsはこれを完全に見落としました。これが明らかに最良の答えです。シンプルfrom more_itertools import unique_everseen list(unique_everseen(items))私よりはるかに速く、受け入れられた答えよりもはるかに優れたアプローチですが、ライブラリのダウンロードは価値があると思います。私は私の答えのwikiコミュニティに行くと、これを追加しています。
jamylak

12

そのような機能の別の(非常にパフォーマンスの高い)実装を外部モジュール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免責事項:私はそのパッケージの作成者です。


私はこの行の必要性を理解していません:seen_add = seen.add-これはベンチマークに必要ですか?
Alex

@Alexこれは、この回答で示されているアプローチです。そこで質問するほうが理にかなっています。私はその答えからのアプローチを使ってタイミングを比較しました。
MSeifert

dict.fromkeys()チャートにメソッドを追加できますか?
ボリス

私が同じタイミングですぐにタイミングを計れるかどうかは本当にわかりません。あなたはそれがよりもはるかに速いと思いますordereddict.fromkeysか?
MSeifert

「このiteration_utilities.unique_everseen関数は、入力内のハッシュ不可能な値も処理できます」-はい、これは本当に重要です。dicts of dicts of dictsなどのリストがある場合、小規模であっても、これが唯一の方法です。
ロコ・ミヒッチ

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

3

nubリストに対するHaskellの関数の定義に使用される再帰的なアイデアを借りると、これは再帰的なアプローチになります。

def unique(lst):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: x!= lst[0], lst[1:]))

例えば:

In [118]: unique([1,5,1,1,4,3,4])
Out[118]: [1, 5, 4, 3]

私は、データサイズの増加に対して試してみましたが、線形ではない時間の複雑さを確認しました(決定的ではありませんが、これは通常のデータでは問題ないことを示唆しています)。

In [122]: %timeit unique(np.random.randint(5, size=(1)))
10000 loops, best of 3: 25.3 us per loop

In [123]: %timeit unique(np.random.randint(5, size=(10)))
10000 loops, best of 3: 42.9 us per loop

In [124]: %timeit unique(np.random.randint(5, size=(100)))
10000 loops, best of 3: 132 us per loop

In [125]: %timeit unique(np.random.randint(5, size=(1000)))
1000 loops, best of 3: 1.05 ms per loop

In [126]: %timeit unique(np.random.randint(5, size=(10000)))
100 loops, best of 3: 11 ms per loop

また、これが他の操作によって一意性に容易に一般化できることも興味深いと思います。このような:

import operator
def unique(lst, cmp_op=operator.ne):
    return [] if lst==[] else [lst[0]] + unique(filter(lambda x: cmp_op(x, lst[0]), lst[1:]), cmp_op)

たとえば、次のように、一意の目的で「等しい」かのように同じ整数に丸めるという概念を使用する関数を渡すことができます。

def test_round(x,y):
    return round(x) != round(y)

次に、unique(some_list、test_round)は、リストの一意の要素を提供します。一意性は、従来の同等性を意味しなくなりました(これは、この問題に対して、あらゆる種類のセットベースまたはdictキーベースのアプローチを使用することによって暗示されます)代わりに、要素が丸める可能性のある整数KごとにKに丸める最初の要素のみ。例:

In [6]: unique([1.2, 5, 1.9, 1.1, 4.2, 3, 4.8], test_round)
Out[6]: [1.2, 5, 1.9, 4.2, 3]

1
一意の要素の数が要素の総数に比べて非常に多い場合、パフォーマンスが低下することに注意してください。後続の再帰呼び出しのそれぞれの使用はfilter、前の呼び出しからほとんど利益を得ません。しかし、一意の要素の数が配列のサイズに比べて少ない場合、これはかなりうまくいくはずです。
2013

3

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

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

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]

2
また、それはO(n ^ 2)操作になることに注意してください。ここでは、(ルックアップタイムが一定の)セット/ディクトを作成し、以前に表示されていない要素のみを追加すると線形になります。
2013

これはPython 2.6だけだと思います。そして、はい、それはO(N ^ 2)です
jamylak

2

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

これは私が声を出して考えている間に思いついたものです:

mylist = [x for i,x in enumerate(mylist) if x not in mylist[i+1:]]

あなたの解決策は素晴らしいですが、それは各要素の最後の出現を取ります。最初の外観を使用するには、次のようにします。[x for i、x in enumerate(mylist)if x not in mylist [:i]]
Rivka

7
リストの検索はO(n)操作であり、各アイテムに対して実行するため、結果としてソリューションの複雑さはになりますO(n^2)。これは、そのようなささいな問題には受け入れられません。
Nikita Volkov

2

ここにそれを行う簡単な方法があります:

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

それは出力を与えます:

["hello", " ", "w", "o", "r", "l", "d"]

1

あなたは一種の醜いリスト内包ハックをすることができます。

[l[i] for i in range(len(l)) if l.index(l[i]) == i]

を参照i,e in enumerate(l)してくださいl[i] for i in range(len(l))
Evpok 2015

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

1
l = [1,2,2,3,3,...]
n = []
n.extend(ele for ele in l if ele not in set(n))

O(1)を使用するジェネレータ式は、新しいリストに要素を含めるかどうかを決定するためにセットを検索します。


1
extend拡張されるもの(+1)に依存set(n)するが、各ステージ(線形)で再計算されるジェネレーター式の巧妙な使用。これにより、全体的なアプローチが2次式になります。実際、これはほぼ間違いなく単にを使用するよりも悪いですele in n。単一のメンバーシップテストのセットを作成することは、セットの作成に費用をかける価値はありません。それでも、それは興味深いアプローチです。
John Coleman

1

単純な再帰的ソリューション:

def uniquefy_list(a):
    return uniquefy_list(a[1:]) if a[0] in a[1:] else [a[0]]+uniquefy_list(a[1:]) if len(a)>1 else [a[0]]

1

シーケンス内の重複する値を削除しますが、残りのアイテムの順序は保持します。汎用ジェネレーター機能の使用。

# for hashable sequence
def remove_duplicates(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

a = [1, 5, 2, 1, 9, 1, 5, 10]
list(remove_duplicates(a))
# [1, 5, 2, 9, 10]



# for unhashable sequence
def remove_duplicates(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)

a = [ {'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 1, 'y': 2}, {'x': 2, 'y': 4}]
list(remove_duplicates(a, key=lambda d: (d['x'],d['y'])))
# [{'x': 1, 'y': 2}, {'x': 1, 'y': 3}, {'x': 2, 'y': 4}]

1

パンダのユーザーはチェックアウトする必要がありpandas.uniqueます。

>>> import pandas as pd
>>> lst = [1, 2, 1, 3, 3, 2, 4]
>>> pd.unique(lst)
array([1, 2, 3, 4])

関数はNumPy配列を返します。必要に応じて、メソッドを使用してリストに変換できますtolist


1
良いですね。私はそのためにパンダを使用することを決して想像しませんが、それはうまく
いき

0

ライナーが1つ必要な場合は、おそらくこれが役立ちます。

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

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


それはだ条件式ので、それの良い
code22

0

を日常的に使用していてpandas、パフォーマンスよりも美学の方が望ましい場合は、組み込み関数を検討してpandas.Series.drop_duplicatesください。

    import pandas as pd
    import numpy as np

    uniquifier = lambda alist: pd.Series(alist).drop_duplicates().tolist()

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

    alist = np.random.randint(low=0, high=1000, size=10000).tolist()

    print uniquifier(alist) == f7(alist)  # True

タイミング:

    In [104]: %timeit f7(alist)
    1000 loops, best of 3: 1.3 ms per loop
    In [110]: %timeit uniquifier(alist)
    100 loops, best of 3: 4.39 ms per loop

0

これにより、順序が保持され、O(n)時間で実行されます。基本的には、重複が見つかった場所に穴を作成し、それを底に沈めるという考え方です。読み取りおよび書き込みポインタを使用します。重複が見つかると、読み取りポインタのみが進み、書き込みポインタは重複エントリにとどまって上書きします。

def deduplicate(l):
    count = {}
    (read,write) = (0,0)
    while read < len(l):
        if l[read] in count:
            read += 1
            continue
        count[l[read]] = True
        l[write] = l[read]
        read += 1
        write += 1
    return l[0:write]

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

これはO(N ** 2)の複雑度+毎回のリストスライスです。
ジャン=フランソワ・ファーブル

0

インプレースメソッド

リストのすべての要素についてリストへの線形ルックアップがあるため、この方法は2次式です(delsのためにリストを再配置するコストを追加する必要があります)。

とは言っても、リストの最後から開始して、左側にあるサブリストにある各用語を削除して元の方向に進むと、適切に動作する可能性があります。

コード内のこのアイデアは単純です

for i in range(len(l)-1,0,-1): 
    if l[i] in l[:i]: del l[i] 

実装の簡単なテスト

In [91]: from random import randint, seed                                                                                            
In [92]: seed('20080808') ; l = [randint(1,6) for _ in range(12)] # Beijing Olympics                                                                 
In [93]: for i in range(len(l)-1,0,-1): 
    ...:     print(l) 
    ...:     print(i, l[i], l[:i], end='') 
    ...:     if l[i] in l[:i]: 
    ...:          print( ': remove', l[i]) 
    ...:          del l[i] 
    ...:     else: 
    ...:          print() 
    ...: print(l)
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5, 2]
11 2 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4, 5]
10 5 [6, 5, 1, 4, 6, 1, 6, 2, 2, 4]: remove 5
[6, 5, 1, 4, 6, 1, 6, 2, 2, 4]
9 4 [6, 5, 1, 4, 6, 1, 6, 2, 2]: remove 4
[6, 5, 1, 4, 6, 1, 6, 2, 2]
8 2 [6, 5, 1, 4, 6, 1, 6, 2]: remove 2
[6, 5, 1, 4, 6, 1, 6, 2]
7 2 [6, 5, 1, 4, 6, 1, 6]
[6, 5, 1, 4, 6, 1, 6, 2]
6 6 [6, 5, 1, 4, 6, 1]: remove 6
[6, 5, 1, 4, 6, 1, 2]
5 1 [6, 5, 1, 4, 6]: remove 1
[6, 5, 1, 4, 6, 2]
4 6 [6, 5, 1, 4]: remove 6
[6, 5, 1, 4, 2]
3 4 [6, 5, 1]
[6, 5, 1, 4, 2]
2 1 [6, 5]
[6, 5, 1, 4, 2]
1 5 [6]
[6, 5, 1, 4, 2]

In [94]:                                                                                                                             

投稿する前に、回答の本文で「場所」を検索しましたが、役に立ちませんでした。他の人が同様の方法で問題を解決した場合は、私に警告してください。私はできるだけ早く回答を削除します。
gboffi

l[:] = <one of the the faster methods>インプレース操作が必要な場合にのみ使用できますか?
timgeb

@timgebはい、いいえ…私がそうするときa=[1]; b=a; a[:]=[2]b==[2]価値はTrueそれであり、それをインプレースで行っていると言えます。それにもかかわらず、新しいスペースを使用して新しいリストを作成し、古いデータを新しいデータに置き換えて、ガベージコレクションの古いデータは何からも参照されていないため、インプレースで動作していると言うことは、私が示した概念が少し拡張されていることを示しています。はい、しかし私は前もってそれを話しました。
gboffi

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

0

1つのライナーリストの理解:

values_non_duplicated = [value for index, value in enumerate(values) if value not in values[ : index]]

条件を追加して、が前の位置にないことを確認します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.