2015년 7월 6일 월요일

넘파이(numpy)의 팬시 색인

 넘파이 배열(ndarray) 의 팬시 색인은 정수 리스트(혹은 배열)를 이용하여 여러 개를 동시에 선택하는 방식이다. 인덱싱은 항상 0으로 시작한다는 것에 유의하자. MATLAB/SCILAB/OCTAVE 에 익숙한 사용자는 인덱스가 0에서 시작된다는 것에 주의해야 한다. 이후 예제에서는 넘파이가 다음과 같이 임포트되었다고 가정한다.
>>> import numpy as np
 예를 들어서 다음과 같은 a 행렬을 고려하자.

>>> a=np.arange(24).reshape((6,4))
>>> a
array([[ 0,  1,  2,  3],
      [ 4,  5,  6,  7],
      [ 8,  9, 10, 11],
      [12, 13, 14, 15],
      [16, 17, 18, 19],
      [20, 21, 22, 23]])
여기서 특정한 행들을 선택하고 싶다면 원하는 순서대로 행번호를 적은 리스트나 배열로 인덱싱하면 된다.
>>> a[[3,0,5]]
array([[12, 13, 14, 15],
      [ 0,  1,  2,  3],
      [20, 21, 22, 23]])

>>> a[[-1,-3,2]]
array([[20, 21, 22, 23],
      [12, 13, 14, 15],
      [ 8,  9, 10, 11]])

 한 가지 주의할 점은 슬라이싱과 다르게 이런 식으로 반환되는 배열은 복사된 것이라는 점이다. 즉, 원본과 연결되지 않은 복사 객체가 반환된다. 따라서 원본의 갱신과 무관하게 된다.
 또 한 가지 유의할 점이 있는데 다음과 같이 다중으로 인덱싱을 할 경우이다.
>>> a[:,[2,3]] #(1)
array([[ 2,  3],
      [ 6,  7],
      [10, 11],
      [14, 15],
      [18, 19],
      [22, 23]])

>>>  a[[0,1],[2,3]] # (2)
array([2, 7])

여기서 (1)에서와 같이 한 쪽이 모든 요소를 나타내는 콜론(:) 이 쓰인 경우는 해석이 쉽다. 즉, a 배열의 0번 열과 1번 열을 골라내는 것이다. 그런데 (2)의 경우는 MATLAB에 익숙하던 사용자라면 좀 의아할 것이다. 다음 표와 같이 0행, 1행과 2열, 3열이 겹치는 부분인 2x2 행렬이 반환된다고 오해하기 쉽기 때문이다.
2열
3열
0행
0
1
2
3
1행
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
실제 동작은 (0,2) 요소와 (1,3)요소 두 개를 반환한다.
 MATLAB과 같이 그리드 점에 있는 요소들의 집합인 2x2 행렬을 반환하게 하려면 np.ix_() 함수에 팬시 색인을 넘겨주어야 한다.
>>> a[np.ix_([0,1],[2,3])]
array([[2, 3],
      [6, 7]])

이것은 사실 a[ [0,1] ][ :, [2,3] ] 과 같은 동작을 취한다.
 그리고 a[1,2] 와 a[1][2] 는 같은 요소를 가리킨다.
>>>  a[1,2]
6

>> a[1][2]
6

하지만 이것으로 두 인덱싱이 같은 표현법이라고 오해하면 안 된다. 사실은 전혀 다르기 때문이다. 예를 들면 다음과 같다.
>>> a[:,2] # 2번 열만 뽑아낸다.
array([ 2,  6, 10, 14, 18, 22])

>>> a[:][2] # 이것은 a[2]와 같다.
Out[41]: array([ 8,  9, 10, 11])

>>> a[ [-1,-2], 1 ]
array([21, 17])

>>> a[[-1,-2]][1]
array([16, 17, 18, 19])

마지막 예에서 a[[-1,-2]][1] 은 a[[-1,-2]] 행렬의 1번째 행을 뽑아내는 것이다. a[[-1,-2], 1] 과는 완전히 다르다.


댓글 없음:

댓글 쓰기