web-dev-qa-db-ja.com

numpy.arrayで一意の行を見つける

numpy.arrayで一意の行を見つける必要があります。

例えば:

>>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

私はセットを作成して配列をループできることを知っていますが、効率的な純粋なnumpyソリューションを探しています。データ型をvoidに設定する方法があり、numpy.uniqueを使用することはできると思いますが、それを機能させる方法がわかりませんでした。

174
Akavall

NumPy 1.13の時点では、任意のN次元配列で一意の値を選択するための軸を簡単に選択できます。一意の行を取得するには、次のようにします。

unique_rows = np.unique(original_array, axis=0)

83
aiwabdn

さらに別の可能な解決策

np.vstack({Tuple(row) for row in a})
135

構造化配列を使用する別のオプションは、行全体を単一のアイテムに結合するvoid型のビューを使用することです。

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

EDIT @sebergの推奨に従ってnp.ascontiguousarrayを追加しました。これにより、配列が連続していない場合、メソッドの速度が低下します。

EDIT上記をわずかに高速化することができます。

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

また、少なくとも私のシステムでは、パフォーマンスの面では、lexsortメソッドと同等かそれ以上のパフォーマンスです。

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop
109
Jaime

一連のタプルまたは別の同様のデータ構造に変換するメモリの費用を回避したい場合、numpyの構造化配列を活用できます。

トリックは、元の配列を、各項目が元の配列の行に対応する構造化配列として表示することです。これはコピーを作成せず、非常に効率的です。

簡単な例として:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

何が起こっているのかを理解するには、中間結果をご覧ください。

構造化配列として物事を見ると、配列の各要素は元の配列の行になります。 (基本的に、タプルのリストに似たデータ構造です。)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

numpy.uniqueを実行すると、構造化配列が返されます。

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

次に、「通常の」配列として表示する必要があります(_は、最後の計算の結果をipythonに保存するため、_.view...が表示されます)。

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

そして、2D配列に形状を戻します(-1は、numpyに正しい行数を計算し、列数を与えるように指示するプレースホルダーです):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

明らかに、もっと簡潔にしたい場合は、次のように書くことができます。

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

結果:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
29
Joe Kington

np.random.random(100).reshape(10,10)で実行すると、np.uniqueは一意の個々の要素をすべて返しますが、一意の行が必要なので、最初にタプルに入れる必要があります。

array = #your numpy array of lists
new_array = [Tuple(row) for row in array]
uniques = np.unique(new_array)

それはあなたが望むことをするためにタイプを変更する唯一の方法であり、タプルに変更するリストの反復があなたの「ループしない」で大丈夫かどうかわかりません

19
Ryan Saxe

np.uniqueは、平坦化された配列をソートし、各アイテムが前のアイテムと等しいかどうかを調べることで機能します。これは、平坦化せずに手動で実行できます。

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

このメソッドはタプルを使用せず、ここで指定された他のメソッドよりもはるかに高速で単純でなければなりません。

注:これの以前のバージョンでは、a [の直後にindがありませんでした。これは、誤ったインデックスが使用されたことを意味します。また、Joe Kingtonは、このdoesがさまざまな中間コピーを作成することを指摘しています。次のメソッドは、ソートされたコピーを作成してからそのビューを使用することにより、より少なくなります。

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

これは高速で、使用するメモリが少なくなります。

また、ndarrayで一意の行を検索する場合は、配列内の次元数をとにかくにすると、次のように機能します。

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=Tuple(range(1,a.ndim)))))]

興味深い残りの問題は、任意の次元の配列の任意の軸に沿って並べ替え/一意にしたい場合です。これはもっと難しいことです。

編集:

速度の違いを示すために、回答に記載されている3つの異なる方法のipythonでいくつかのテストを実行しました。 your exact aを使用すると、あまり違いはありませんが、このバージョンは少し高速です。

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [Tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

ただし、aを大きくすると、このバージョンははるかに高速になります。

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [Tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop
16
cge

@Greg Pythonic answerの別のバリエーションがあります

np.vstack(set(map(Tuple, a)))
9
divenex

提案された速度の代替案を比較したところ、驚くべきことに、void view uniqueソリューションは、unique引数を持つnumpyのネイティブaxisよりも少し高速であることがわかりました。速度を探しているなら、あなたは望むでしょう

numpy.unique(
    a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
    ).view(a.dtype).reshape(-1, a.shape[1])

enter image description here


プロットを再現するコード:

import numpy
import perfplot


def unique_void_view(a):
    return numpy.unique(
        a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
        ).view(a.dtype).reshape(-1, a.shape[1])


def lexsort(a):
    ind = numpy.lexsort(a.T)
    return a[ind[
        numpy.concatenate((
            [True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
            ))
        ]]


def vstack(a):
    return numpy.vstack({Tuple(row) for row in a})


def unique_axis(a):
    return numpy.unique(a, axis=0)


perfplot.show(
    setup=lambda n: numpy.random.randint(2, size=(n, 20)),
    kernels=[unique_void_view, lexsort, vstack, unique_axis],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    xlabel='len(a)',
    equality_check=None
    )
7
Nico Schlömer

線形代数またはベクトル空間の意味で浮動小数点配列を処理するものはないため、これらの回答はどれも好きではありませんでした。 https://stackoverflow.com/a/26867764/500207 という許容しきい値を持つ1つの答えは、しきい値を要素単位とdecimal精度の両方にすることで、これは場合によっては機能しますが、真のベクトル距離ほど数学的に一般的ではありません。

これが私のバージョンです。

from scipy.spatial.distance import squareform, pdist

def uniqueRows(arr, thresh=0.0, metric='euclidean'):
    "Returns subset of rows that are unique, in terms of Euclidean distance"
    distances = squareform(pdist(arr, metric=metric))
    idxset = {Tuple(np.nonzero(v)[0]) for v in distances <= thresh}
    return arr[[x[0] for x in idxset]]

# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
    return uniqueRows(arr.T, *args, **kwargs)

上記のパブリックドメイン関数は scipy.spatial.distance.pdist を使用して、行の各ペア間のユークリッド(カスタマイズ可能)距離を見つけます。次に、それぞれの距離をthresholdと比較して、お互いのthresh内にある行を見つけ、各thresh- clusterから1行だけを返します。

示唆されているように、距離metricはユークリッドである必要はありません。pdistは、cityblock(マンハッタンノルム)およびcosine(ベクトル間の角度)などの雑多な距離を計算できます。

thresh=0(デフォルト)の場合、「一意」と見なされるには行がビット正確でなければなりません。 threshの他の適切な値は、スケーリングされたマシン精度、つまりthresh=np.spacing(1)*1e3を使用します。

7
Ahmed Fasih

numpy_indexed パッケージ(免責事項:私はその著者です)は、Jaimeによって投稿されたソリューションを、ニースでテスト済みのインターフェースに加え、さらに多くの機能をラップしています。

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default
3

パンダのdrop_duplicatesを使用しない理由:

>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop

>>> timeit np.vstack({Tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop
3
kalu

このページの答えに基づいて、MATLABのunique(input,'rows')関数の機能を複製する関数を作成し、一意性をチェックするための許容範囲を追加する機能を追加しました。また、c = data[ia,:]およびdata = c[ic,:]などのインデックスを返します。矛盾やエラーが見つかった場合は報告してください。

def unique_rows(data, prec=5):
    import numpy as np
    d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
    b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
    _, ia = np.unique(b, return_index=True)
    _, ic = np.unique(b, return_inverse=True)
    return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic
1
Arash_D_B

@Jaimeの優れた答えを超えて、行を折りたたむ別の方法は、a.strides[0]に等しいa.dtype.itemsize*a.shape[0]aがC連続であると仮定)を使用することです。さらに、void(n)dtype((void,n))のショートカットです。最終的にこの最短バージョンに到達します。

a[unique(a.view(void(a.strides[0])),1)[1]]

For

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
1
B. M.

np.uniqueはタプルのリストを与えられて動作します:

>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]: 
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

リストのリストでは、TypeError: unhashable type: 'list'が発生します

1
codeape

3D以上の多次元のネストされた配列のような一般的な目的のために、これを試してください:

import numpy as np

def unique_nested_arrays(ar):
    Origin_shape = ar.shape
    Origin_dtype = ar.dtype
    ar = ar.reshape(Origin_shape[0], np.prod(Origin_shape[1:]))
    ar = np.ascontiguousarray(ar)
    unique_ar = np.unique(ar.view([('', Origin_dtype)]*np.prod(Origin_shape[1:])))
    return unique_ar.view(Origin_dtype).reshape((unique_ar.shape[0], ) + Origin_shape[1:])

あなたの2Dデータセットを満たします:

a = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
unique_nested_arrays(a)

与える:

array([[0, 1, 1, 1, 0, 0],
   [1, 1, 1, 0, 0, 0],
   [1, 1, 1, 1, 1, 0]])

しかし、次のような3D配列も:

b = np.array([[[1, 1, 1], [0, 1, 1]],
              [[0, 1, 1], [1, 1, 1]],
              [[1, 1, 1], [0, 1, 1]],
              [[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)

与える:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
0
Tara

Numpyマトリックス全体をリストとして取得し、このリストから重複を削除して、最後に一意のリストをnumpyマトリックスに戻します。

matrix_as_list=data.tolist() 
matrix_as_list:
[[1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0]]

uniq_list=list()
uniq_list.append(matrix_as_list[0])

[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]

unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])
0
Mahdi Ghelichi

これらの答えはどれも私にとってはうまくいきませんでした。私のユニークな行には数字ではなく文字列が含まれていると仮定しています。しかし、別のスレッドからのこの答えはうまくいきました:

ソース: https://stackoverflow.com/a/38461043/5402386

.count()および.index()リストのメソッドを使用できます

coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_Tuple = [Tuple(x) for x in coor]
unique_coor = sorted(set(coor_Tuple), key=lambda x: coor_Tuple.index(x))
unique_count = [coor_Tuple.count(x) for x in unique_coor]
unique_index = [coor_Tuple.index(x) for x in unique_coor]
0
mjp

実際に、m x n数値numpy配列をm x 1 numpy文字列配列に変換できます。次の関数を使用してみてください。numpy.uniqueと同様に、countinverse_idxなどを提供します。

import numpy as np

def uniqueRow(a):
    #This function turn m x n numpy array into m x 1 numpy array storing 
    #string, and so the np.unique can be used

    #Input: an m x n numpy array (a)
    #Output unique m' x n numpy array (unique), inverse_indx, and counts 

    s = np.chararray((a.shape[0],1))
    s[:] = '-'

    b = (a).astype(np.str)

    s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)

    n = a.shape[1] - 2    

    for i in range(0,n):
         s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)

    s3, idx, inv_, c = np.unique(s2,return_index = True,  return_inverse = True, return_counts = True)

    return a[idx], inv_, c

例:

A = np.array([[ 3.17   9.502  3.291],
  [ 9.984  2.773  6.852],
  [ 1.172  8.885  4.258],
  [ 9.73   7.518  3.227],
  [ 8.113  9.563  9.117],
  [ 9.984  2.773  6.852],
  [ 9.73   7.518  3.227]])

B, inv_, c = uniqueRow(A)

Results:

B:
[[ 1.172  8.885  4.258]
[ 3.17   9.502  3.291]
[ 8.113  9.563  9.117]
[ 9.73   7.518  3.227]
[ 9.984  2.773  6.852]]

inv_:
[3 4 1 0 2 4 0]

c:
[2 1 1 1 2]
0
Ting On Chan