[python] 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는 있지만 사용할 수있는 방법을 알 수 없었습니다.



답변

NumPy 1.13부터 N-dim 배열에서 고유 한 값을 선택할 축을 간단히 선택할 수 있습니다. 고유 한 행을 얻으려면 다음을 수행하십시오.

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


답변

또 다른 가능한 해결책

np.vstack({tuple(row) for row in a})


답변

구조적 배열을 사용하는 또 다른 옵션 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]])

EDITnp.ascontiguousarray @seberg의 추천에 따라
추가되었습니다 . 배열이 아직 인접하지 않은 경우 메소드 속도가 느려집니다.

편집
위의 내용은 명확성을 희생하여 약간 속도를 높일 수 있습니다.

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


답변

일련의 튜플 또는 다른 유사한 데이터 구조로 변환하는 데 드는 메모리 비용을 피하려면 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]]


답변

np.unique실행할 때 np.random.random(100).reshape(10,10)고유 한 개별 요소를 모두 반환하지만 고유 한 행을 원하므로 먼저 튜플에 넣어야합니다.

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

이것이 내가 원하는 것을하기 위해 유형을 변경하는 유일한 방법이며, 튜플로 변경하는 목록 반복이 “반복되지 않음”으로 괜찮은지 확실하지 않습니다.


답변

np.unique는 평평한 배열을 정렬 한 다음 각 항목이 이전 항목과 같은지 확인하여 작동합니다. 이 작업은 병합하지 않고 수동으로 수행 할 수 있습니다.

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

이 방법은 튜플을 사용하지 않으며 여기에 제공된 다른 방법보다 훨씬 빠르고 간단해야합니다.

참고 : 이것의 이전 버전에는 a [바로 뒤에 표시가 없었습니다. 이는 잘못된 인덱스가 사용되었음을 의미합니다. 또한, 조 킹톤이 그 좋은 점하게 수행 중간 사본의 다양성을 확인합니다. 다음 방법은 정렬 된 사본을 작성한 후보기를 사용하여 더 적게 만듭니다.

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

흥미로운 나머지 문제는 임의의 차원 배열의 임의의 축을 따라 정렬 / 고유하게하려는 경우 더 어려울 것입니다.

편집하다:

속도 차이를 보여주기 위해 ipython에서 답변에 설명 된 세 가지 다른 방법 중 몇 가지 테스트를 실행했습니다. 으로 당신 이 버전이 조금 더 빠르다 불구하고 정확한 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


답변

@Greg pythonic answer의 또 다른 변형입니다.

np.vstack(set(map(tuple, a)))