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