NumPy 2d 배열의 슬라이스 또는 nxn 배열에서 mxm 하위 행렬을 어떻게 추출합니까 (n> m)?


174

NumPy nxn 배열을 슬라이스하고 싶습니다. 해당 배열의 m 행과 열을 임의로 선택 하여 추출합니다 (즉, 행 / 열 수에 패턴이 없음). 새로운 mxm 배열이됩니다. 이 예제에서는 배열이 4x4라고 가정하고 2x2 배열을 추출하고 싶습니다.

배열은 다음과 같습니다.

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

제거 할 행과 열이 동일합니다. 가장 쉬운 경우는 시작 또는 끝에있는 2x2 하위 행렬을 추출하려는 경우입니다.

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

그러나 다른 행 / 열 혼합을 제거해야하는 경우 어떻게해야합니까? 첫 번째와 세 번째 줄 / 행을 제거하여 하위 행렬을 추출해야하는 경우 어떻게해야 [[5,7],[13,15]]합니까? 행 / 라인의 구성이있을 수 있습니다. 어딘가에서 행과 열 모두에 대한 배열 / 인덱스 목록을 사용하여 배열을 색인화 해야하는 곳을 읽었지만 작동하지 않는 것 같습니다.

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

한 가지 방법을 찾았습니다.

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

이것의 첫 번째 문제는 읽을 수는 없지만 읽을 수는 없다는 것입니다. 누군가가 더 나은 해결책을 가지고 있다면 분명히 듣고 싶습니다.

또 다른 것은 포럼 에서 배열을 사용하여 배열을 인덱싱하면 NumPy가 원하는 배열의 사본을 만들도록 강요하므로 큰 배열로 처리 할 때 문제가 될 수 있다는 것입니다. 그 이유는 무엇입니까 /이 메커니즘은 어떻게 작동합니까?

답변:


62

Sven이 언급했듯이 x[[[0],[2]],[1,3]]1 및 3 열과 일치하는 0 및 2 행을 x[[0,2],[1,3]]반환하고 배열의 값 x [0,1] 및 x [2,3]을 반환합니다.

내가 준 첫 번째 예제를 수행하는 데 유용한 기능이 있습니다 numpy.ix_. 을 사용하여 첫 번째 예제와 동일한 작업을 수행 할 수 있습니다 x[numpy.ix_([0,2],[1,3])]. 이렇게하면 추가 대괄호를 모두 입력하지 않아도됩니다.


111

이 질문에 답하기 위해 Numpy에서 다차원 배열의 색인 생성 방식을 살펴 봐야합니다. 먼저 x질문 에서 배열이 있다고 가정 해 봅시다 . 할당 된 버퍼 x는 0에서 15까지 16 개의 오름차순 정수를 포함합니다. 만약 당신이 하나의 요소에 접근한다면 x[i,j], NumPy는 버퍼의 시작과 관련된이 요소의 메모리 위치를 알아 내야합니다. 이것은 실제로 계산 i*x.shape[1]+j하고 실제 메모리 오프셋을 얻기 위해 int 크기를 곱하여 수행됩니다 .

와 같은 기본 슬라이싱으로 하위 배열을 추출 y = x[0:2,0:2]하면 결과 객체는 기본 버퍼를와 공유합니다 x. 그러나 당신이 액세스하면 어떻게됩니까 y[i,j]? NumPy는 i*y.shape[1]+j속한 데이터 y가 메모리에서 연속적이지 않기 때문에 배열에 대한 오프셋을 계산하는 데 사용할 수 없습니다 .

NumPy는 보폭 을 도입하여이 문제를 해결합니다 . 에 액세스하기위한 메모리 오프셋을 계산할 때 x[i,j]실제로 계산되는 것은 실제로 i*x.strides[0]+j*x.strides[1]int 크기에 대한 요소를 포함합니다.

x.strides
(16, 4)

경우 y위와 같이 추출하고, NumPy와 새로운 버퍼를 생성하지 않지만 않는 동일한 완충액 (다르게 참조 새로운 어레이 오브젝트 생성 y단지 동일 할 것이다 x그럼.) 새로운 어레이 오브젝트가 다른 형상을 가질 것이다 x어쩌면 다른 시작 버퍼로 오프셋되지만 보폭을 x(이 경우 적어도) 공유합니다 .

y.shape
(2,2)
y.strides
(16, 4)

이런 식으로 메모리 오프셋을 계산 y[i,j]하면 올바른 결과를 얻을 수 있습니다.

그러나 NumPy는 무엇을해야 z=x[[1,3]]합니까? strides 메커니즘은 원본 버퍼가에 사용되는 경우 올바른 색인 생성을 허용하지 않습니다 z. NumPy는 이론적으로 보폭보다 더 정교한 메커니즘을 추가 할 수 있지만, 이는 요소 액세스를 상대적으로 비싸게 만들어 어쨌든 배열의 전체 아이디어를 무시합니다. 또한 뷰는 더 이상 가벼운 물체가 아닙니다.

이것에 대해서는 NumPy 문서 색인 에서 자세히 다루고 있습니다.

아, 그리고 실제 질문에 대해 거의 잊어 버렸습니다. 여러 목록으로 인덱싱을 예상대로 작동시키는 방법은 다음과 같습니다.

x[[[1],[3]],[1,3]]

이는 인덱스 배열이 공통 형태 로 브로드 캐스트 되기 때문 입니다. 물론,이 특정 예제의 경우 기본 슬라이싱을 수행 할 수도 있습니다.

x[1::2, 1::2]

"slcie-view"객체가 인덱스를 원래 배열로 다시 매핑 할 수 있도록 배열을 서브 클래 싱하는 것이 가능합니다. 아마도 OP의 요구 충족 수
jsbueno

@jsbueno : 파이썬 코드에서는 작동하지만 Scipy / Numpy가 감싸는 C / Fortran 루틴에서는 작동하지 않습니다. 랩핑 된 루틴은 Numpy의 힘이있는 곳입니다.
Dat Chu

So .. x [[[1], [3]], [1,3]]와 x [[1,3], :] [:, [1,3]]의 차이점은 무엇입니까? 다른 것보다 사용하기에 더 좋은 변형이 있습니까?
levesque

1
@JC : x[[[1],[3]],[1,3]]새 배열을 하나만 만들고 x[[1,3],:][:,[1,3]]두 번 복사하므로 첫 번째 배열을 사용하십시오.
Sven Marnach

@ JC : 또는 Justin의 대답에서 나온 방법을 사용하십시오.
Sven Marnach

13

나는 그것이 x[[1,3]][:,[1,3]]거의 읽을 수 없다고 생각합니다 . 의도를 더 명확하게하려면 다음을 수행하십시오.

a[[1,3],:][:,[1,3]]

나는 슬라이싱 전문가가 아니지만 일반적으로 배열로 슬라이스하려고 시도하고 값이 연속적이라면 보폭 값이 변경된 곳을 다시 볼 수 있습니다.

예를 들어 입력 33 및 34에서 2x2 배열을 얻더라도 보폭은 4입니다. 따라서 다음 행을 인덱싱하면 포인터가 메모리의 올바른 위치로 이동합니다.

분명히,이 메커니즘은 다양한 인덱스의 경우에는 잘 적용되지 않습니다. 따라서 numpy는 복사본을 만들어야합니다. 결국, 많은 다른 행렬 연산 함수는 크기, 보폭 및 연속 메모리 할당에 의존합니다.


10

다른 모든 행과 다른 모든 열을 건너 뛰려면 기본 슬라이싱으로 할 수 있습니다.

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

배열의 복사본이 아닌 뷰를 반환합니다.

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

z=x[(1,3),:][:,(1,3)]고급 인덱싱 을 사용하므로 사본을 반환합니다.

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

참고 x변경되지 않습니다 :

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

임의의 행과 열을 선택하려면 기본 슬라이싱을 사용할 수 없습니다. x[rows,:][:,columns], where rowscolumnsare 시퀀스 를 사용하여 고급 인덱싱을 사용해야 합니다. 물론 이것은 원래 배열의 사본이 아닌 사본을 제공합니다. numpy 배열은 연속 메모리를 사용하여 연속 메모리를 사용하기 때문에 임의의 행과 열로 뷰를 생성 할 수있는 방법이 없기 때문에 일정하지 않은 보폭이 필요하기 때문입니다.


5

numpy를 사용하면 인덱스의 각 구성 요소에 대해 슬라이스를 전달할 수 있으므로 x[0:2,0:2]위 의 예가 작동합니다.

열이나 행을 균등하게 건너 뛰려면 세 가지 구성 요소 (예 : 시작, 중지, 단계)로 슬라이스를 전달할 수 있습니다.

다시, 위의 예를 들어 :

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

기본적으로 인덱스 1에서 시작하여 인덱스가 4보다 크거나 같을 때 중지하고 각 패스의 인덱스에 2를 추가합니다. 두 번째 차원에서도 동일합니다. 다시 : 이것은 일정한 단계에서만 작동합니다.

내부적으로는 상당히 다른 구문이 필요합니다. x[[1,3]][:,[1,3]]실제로 원래 배열에서 행 1과 3 만 포함하는 새 배열을 x[[1,3]]만든 다음 (부분으로 수행 ) 다시 세분화하여 세 번째 배열을 생성합니다. 이전 배열의 열 1과 3


1
이 솔루션은 추출하려는 행 / 열에 따라 작동하지 않습니다. 50x50 행렬에서 동일한 것을 상상해보십시오. 행 / 열 5,11,12,32,39,45를 추출하려고 할 때 간단한 조각으로는 그렇게 할 수있는 방법이 없습니다. 내 질문에 명확하지 않으면 죄송합니다.
levesque


0

이것이 얼마나 효율적인지 잘 모르겠지만 range ()를 사용하여 두 축에서 슬라이스 할 수 있습니다

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.