web-dev-qa-db-ja.com

配列内の重複値を判別する

私は配列を持っているとします

_a = np.array([1, 2, 1, 3, 3, 3, 0])
_

(効率的に、Python的に)aのどの要素が重複(つまり、一意でない値)であるかを見つけるにはどうすればよいですか?この場合、結果はarray([1, 3, 3])になるか、効率的な場合はarray([1, 3])になります。

私はうまくいくように見えるいくつかの方法を考え出しました:

マスキング

_m = np.zeros_like(a, dtype=bool)
m[np.unique(a, return_index=True)[1]] = True
a[~m]
_

セット操作

_a[~np.in1d(np.arange(len(a)), np.unique(a, return_index=True)[1], assume_unique=True)]
_

これはかわいいですが、おそらく違法です(aは実際には一意ではないため):

_np.setxor1d(a, np.unique(a), assume_unique=True)
_

ヒストグラム

_u, i = np.unique(a, return_inverse=True)
u[np.bincount(i) > 1]
_

仕分け

_s = np.sort(a, axis=None)
s[:-1][s[1:] == s[:-1]]
_

Pandas

_s = pd.Series(a)
s[s.duplicated()]
_

見逃したことはありますか?私は必ずしもnumpyのみのソリューションを探しているわけではありませんが、numpyデータ型で動作し、中規模データセット(最大1,000万個)で効率的でなければなりません。


結論

1000万サイズのデータ​​セット(2.8 GHz Xeonで)を使用したテスト:

_a = np.random.randint(10**7, size=10**7)
_

最速は1.1秒でのソートです。疑わしい_xor1d_は2.6秒で2番目で、その後にマスクとPandas _Series.duplicated_が3.1秒、bincountが5.6秒、_in1d_が続きます。センダルの_setdiff1d_両方とも7.3秒。スティーブンのCounterは10.5秒で少し遅くなります。後ろに続くのは、110秒でのBurhanの_Counter.most_common_と360秒でのDSMのCounter減算です。

パフォーマンスのために並べ替えを使用しますが、パフォーマンスは許容可能であり、feelsより明確でよりPythonicなので、Stevenの答えを受け入れます。

編集:Pandasソリューションを発見しました。 Pandasが利用可能な場合、それは明確であり、うまく機能します。

46
ecatmur

これはnumpyの外で最も明確に行われると思います。速度が心配な場合は、numpyソリューションに対して時間を計る必要があります。

>>> import numpy as np
>>> from collections import Counter
>>> a = np.array([1, 2, 1, 3, 3, 3, 0])
>>> [item for item, count in Counter(a).iteritems() if count > 1]
[1, 3]

注:これはBurhan Khalidの答えに似ていますが、条件に添え字を付けずにiteritemsを使用する方が高速です。

21

Numpyバージョン1.9.0の時点で、 np.unique には引数return_countsがあり、タスクを大幅に簡素化します。

u, c = np.unique(a, return_counts=True)
dup = u[c > 1]

これは Counter を使用するのと似ていますが、マッピングの代わりに配列のペアを取得する点が異なります。それらが互いにどのように機能するかを知りたいです。

18
Mad Physicist

すでにCounterのバリエーションが提案されていますが、listcompを使用しないものは次のとおりです。

>>> from collections import Counter
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> (Counter(a) - Counter(set(a))).keys()
[1, 3]

[投稿されたのは、それが効率的だからではなく、そうではないからです。しかし、Counterインスタンスを減算できるのはかわいいと思うからです。]

12
DSM

Python 2.7+

>>> import numpy
>>> from collections import Counter
>>> n = numpy.array([1,1,2,3,3,3,0])
>>> [x[1] for x in Counter(n).most_common() if x[0] > 1]
[3, 1]
7
Burhan Khalid

集合演算を使用する別のアプローチは、あなたが提供するものよりも少し簡単だと思います:

>>> indices = np.setdiff1d(np.arange(len(a)), np.unique(a, return_index=True)[1])
>>> a[indices]
array([1, 3, 3])

numpyのみのソリューションを求めていると思います。そうでない場合は、代わりにCounterを使用するだけでは議論が非常に難しいからです。ただし、その要件を明確にする必要があると思います。

5
senderle

aが小さな整数で構成されている場合、numpy.bincountを直接使用できます。

import numpy as np

a = np.array([3, 2, 2, 0, 4, 3])
counts = np.bincount(a)
print np.where(counts > 1)[0]
# array([2, 3])

これは「ヒストグラム」メソッドに非常に似ています。これは、aが小さな整数で構成されていない場合に使用する方法です。

4
Bi Rico

配列がソートされたnumpy配列の場合、次のようにします。

a = np.array([1, 2, 2, 3, 4, 5, 5, 6])
rep_el = a[np.diff(a) == 0]
3
Thomas Antony

Numpy以外にlibを使用したり使用したりするソリューションに適合するソリューションはないため、この3年前の質問の山にソリューションを追加しています。このメソッドは、重複のインデックスとdistinct重複セットの値の両方を見つけます。

import numpy as np

A = np.array([1,2,3,4,4,4,5,6,6,7,8])

# Record the indices where each unique element occurs.
list_of_dup_inds = [np.where(a == A)[0] for a in np.unique(A)]

# Filter out non-duplicates.
list_of_dup_inds = filter(lambda inds: len(inds) > 1, list_of_dup_inds)

for inds in list_of_dup_inds: print inds, A[inds]
# >> [3 4 5] [4 4 4]
# >> [7 8] [6 6]
3
Matt Hancock
>>> import numpy as np

>>> a=np.array([1,2,2,2,2,3])

>>> uniques, uniq_idx, counts = np.unique(a,return_index=True,return_counts=True)
>>> duplicates = a[ uniq_idx[counts>=2] ]  # <--- Get duplicates

孤児も取得したい場合:

>>> orphans = a[ uniq_idx[counts==1] ] 
0
user563325