要素を繰り返すことができるpythonリストがあります。
>>> a = [1,2,2,3,3,4,5,6]
リストから最初のn
一意の要素を取得したい。したがって、この場合、最初の5つの一意の要素が必要な場合、次のようになります。
[1,2,3,4,5]
私は発電機を使用した解決策を考え出しました:
def iterate(itr, upper=5):
count = 0
for index, element in enumerate(itr):
if index==0:
count += 1
yield element
Elif element not in itr[:index] and count<upper:
count += 1
yield element
使用中で:
>>> i = iterate(a, 5)
>>> [e for e in i]
[1,2,3,4,5]
これが最も最適なソリューションであることに疑問があります。よりPythonicで効率的な方法で記述するために実装できる代替戦略はありますか?
set
を使用して、見たものを記憶し、seen
が十分にあるときにジェネレーターから戻るようにします。
a = [1,2,2,3,3,4,5,6]
def get_unique_N(iterable, N):
"""Yields (in order) the first N unique elements of iterable.
Might yield less if data too short."""
seen = set()
for e in iterable:
if e in seen:
continue
seen.add(e)
yield e
if len(seen) == N:
return
k = get_unique_N([1,2,2,3,3,4,5,6], 4)
print(list(k))
出力:
[1,2,3,4]
PEP-479 によると、raise StopIteration
ではなく、ジェネレーターからreturn
を使用する必要があります- @ khelwood & @ iBug に感謝そのコメントのために-人は決して学びません。
3.6では非推奨の警告が表示され、3.7ではRuntimeErrorsが表示されます。 Transition Planraise StopIteration
を使用している場合
Elif element not in itr[:index] and count<upper:
を使用するソリューションは、O(k)
ルックアップを使用します-k
はスライスの長さです-セットを使用すると、これはO(1)
ルックアップに減少しますが、セットは同様に保管してください。これは速度とメモリのトレードオフです-アプリケーション/データ依存性の方が優れています。
[1,2,3,4,4,4,4,5]
対[1]*1000+[2]*1000+[3]*1000+[4]*1000+[5]*1000+[6]
を検討してください:
6つの一意の場合(長いリスト):
O(1)+O(2)+...+O(5001)
のルックアップがあります5001*O(1)
ルックアップ+ set( {1,2,3,4,5,6})
のメモリがあります人気の itertools
unique_everseen
レシピ を適応させることができます:
def unique_everseen_limit(iterable, limit=5):
seen = set()
seen_add = seen.add
for element in iterable:
if element not in seen:
seen_add(element)
yield element
if len(seen) == limit:
break
a = [1,2,2,3,3,4,5,6]
res = list(unique_everseen_limit(a)) # [1, 2, 3, 4, 5]
または、@ Chris_Randsが提案するように、 itertools.islice
を使用して、制限のないジェネレーターから一定数の値を抽出できます。
from itertools import islice
def unique_everseen(iterable):
seen = set()
seen_add = seen.add
for element in iterable:
if element not in seen:
seen_add(element)
yield element
res = list(islice(unique_everseen(a), 5)) # [1, 2, 3, 4, 5]
unique_everseen
レシピは、サードパーティライブラリで more_itertools.unique_everseen
または toolz.unique
から入手できます。 、あなたは使用できる:
from itertools import islice
from more_itertools import unique_everseen
from toolz import unique
res = list(islice(unique_everseen(a), 5)) # [1, 2, 3, 4, 5]
res = list(islice(unique(a), 5)) # [1, 2, 3, 4, 5]
オブジェクトが hashable (int
sはhashable)の場合、 fromkeys
method of collections.OrderedDict
class (またはPython3.7単純なdict
、 公式に 順序付けられたため、
from collections import OrderedDict
def nub(iterable):
"""Returns unique elements preserving order."""
return OrderedDict.fromkeys(iterable).keys()
そして、iterate
の実装は、
from itertools import islice
def iterate(itr, upper=5):
return islice(nub(itr), upper)
または、常に出力としてlist
が必要な場合
def iterate(itr, upper=5):
return list(nub(itr))[:upper]
@Chris_Randsが述べたように、このソリューションはコレクション全体をウォークスルーし、他の人がすでにやったようにnub
ユーティリティを generator の形式で書くことでこれを改善できます:
def nub(iterable):
seen = set()
add_seen = seen.add
for element in iterable:
if element in seen:
continue
yield element
add_seen(element)
itertools.takewhile()
を使用したPythonのアプローチは次のとおりです。
In [95]: from itertools import takewhile
In [96]: seen = set()
In [97]: set(takewhile(lambda x: seen.add(x) or len(seen) <= 4, a))
Out[97]: {1, 2, 3, 4}
OrderedDict
を使用できます。または、Python 3.7以降、通常のdict
を使用できます。これらは、挿入順序を保持するために実装されているためです。これはセットでは機能しないことに注意してください。
N = 3
a = [1, 2, 2, 3, 3, 3, 4]
d = {x: True for x in a}
list(d.keys())[:N]
この質問には本当に驚くべき答えがあります。それは速くてコンパクトで素晴らしいです!ここにこのコードを入れている理由は、1マイクロ秒の時間の緩みを気にかけない場合や、単純なタスクを一度だけ解決するためにコードに追加のライブラリが必要な場合が多いと思うからです。
a = [1,2,2,3,3,4,5,6]
res = []
for x in a:
if x not in res: # yes, not optimal, but doesnt need additional dict
res.append(x)
if len(res) == 5:
break
print(res)
指定
import itertools as it
a = [1, 2, 2, 3, 3, 4, 5, 6]
コード
単純なリストの理解(@cdlaneの答えに似ています)。
[k for k, _ in it.groupby(a)][:5]
# [1, 2, 3, 4, 5]
または、Python 3.6+で:
list(dict.fromkeys(a))[:5]
# [1, 2, 3, 4, 5]
set
をsorted+ key
とともに使用する
sorted(set(a), key=list(a).index)[:5]
Out[136]: [1, 2, 3, 4, 5]
要素が示されているように順序付けられていると仮定すると、これはitertoolsのgroupby
関数を楽しむ機会です。
from itertools import groupby, islice
def first_unique(data, upper):
return islice((key for (key, _) in groupby(data)), 0, upper)
a = [1, 2, 2, 3, 3, 4, 5, 6]
print(list(first_unique(a, 5)))
@ juanpa.arrivillagaごとにislice
の代わりにenumerate
を使用するように更新されました。重複を追跡するためにset
も必要ありません。
このようなものを使用してみませんか?
>>> a = [1, 2, 2, 3, 3, 4, 5, 6]
>>> list(set(a))[:5]
[1, 2, 3, 4, 5]
a = [1,2,2,3,3,4,5,6]
from collections import defaultdict
def function(lis,n):
dic = defaultdict(int)
sol=set()
for i in lis:
try:
if dic[i]:
pass
else:
sol.add(i)
dic[i]=1
if len(sol)>=n:
break
except KeyError:
pass
return list(sol)
print(function(a,3))
出力
[1, 2, 3]
a = [1, 2, 2, 3, 3, 4, 5, 6]
1番目の引数-動作するリスト、2番目の引数(オプション)-一意のアイテムの数(デフォルト-なし-すべての一意の要素が返されることを意味します)
def unique_elements(lst, number_of_elements=None):
return list(dict.fromkeys(lst))[:number_of_elements]
以下に動作例を示します。リスト名は「a」であり、2つの一意の要素を取得する必要があります。
print(unique_elements(a, 2))