7. Structural indexing tools
저번 글에 이어서 np.newaxis에 대한 내용인데 일단 보기 쉽게 저번 출력값을 먼저 적어놓자면:
x = np.arange(12).reshape(4, 3)
# [[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]
y = x[np.newaxis, :]
# [[[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]]
z = x[:, np.newaxis]
# [[[ 0 1 2]]
#
# [[ 3 4 5]]
#
# [[ 6 7 8]]
#
# [[ 9 10 11]]]
참고로 x의 차원은 2차원, y는 (1, 4, 3)형태 3차원 텐서, z는 (4, 1, 3)형태의 3차원 텐서이다.
np.newaxis가 행을 나타내는 자리에 나오느냐 열을 나타내는 자리에 나오느냐로 축(1)이 어디에 추가되는지 형태를 보면 알 수 있다.
이에 대해서 선배에게 대체 얜 뭐 하는 애냐 물으니 한 번도 안 써봐서 모른다고.
하지만 np.newaxis를 쓰는 것이 귀찮아서 다들 None을 쓴다는 얘기를 들었다. 아래와 같이 쓰면 된다.
print(x[None, :]) #y와 같은 출력값
print(x[:, None]) #z와 같은 출력값
어쨌든 하던 것 마저 더 하자면,
print(x+y) #print(y+x)와 같음
# [[[ 0 2 4]
# [ 6 8 10]
# [12 14 16]
# [18 20 22]]]
print(x+z) #print(z+x)와 같음
# [[[ 0 2 4]
# [ 3 5 7]
# [ 6 8 10]
# [ 9 11 13]]
#
# [[ 3 5 7]
# [ 6 8 10]
# [ 9 11 13]
# [12 14 16]]
#
# [[ 6 8 10]
# [ 9 11 13]
# [12 14 16]
# [15 17 19]]
#
# [[ 9 11 13]
# [12 14 16]
# [15 17 19]
# [18 20 22]]]
print(y+z) #print(z+y)와 같음
# [[[ 0 2 4]
# [ 3 5 7]
# [ 6 8 10]
# [ 9 11 13]]
#
# [[ 3 5 7]
# [ 6 8 10]
# [ 9 11 13]
# [12 14 16]]
#
# [[ 6 8 10]
# [ 9 11 13]
# [12 14 16]
# [15 17 19]]
#
# [[ 9 11 13]
# [12 14 16]
# [15 17 19]
# [18 20 22]]]
보면 알겠지만 print(x+z)와 print(y+z)도 같은 값이 나온다.
x의 각 행을 x전체에 각각 더한 것이 행렬로 추가되어 (4, 4, 3)형태의 3차원 텐서가 된다.
당연한 말이지만 이걸 1차원 배열에서 하면 더 보기 쉽다.
x = np.arange(5) #[0 1 2 3 4]
y = x[np.newaxis, :] #[[0 1 2 3 4]]
z = x[:, np.newaxis]
# [[0]
# [1]
# [2]
# [3]
# [4]]
print(y+z)
# [[0 1 2 3 4]
# [1 2 3 4 5]
# [2 3 4 5 6]
# [3 4 5 6 7]
# [4 5 6 7 8]]
어쨌든 np.newaxis는 축을 하나 추가해서 차원을 늘리는데 그 위치에 따라 추가되는 축의 위치가 다르다는 것을 알았다. 정확한 원리?에 대해선 좀 보류..
다음으로 ... 을 이용해 인덱싱 하는 방법이 나오는데 그 전에 먼저 높은 차원의 텐서에서의 슬라이싱이 어떻게 이루어지는지 보고 가는 것이 나을 것 같다.
h=np.arange(81).reshape(3, 3, 3, 3)
# [[[[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]]
#
# [[ 9 10 11]
# [12 13 14]
# [15 16 17]]
#
# [[18 19 20]
# [21 22 23]
# [24 25 26]]]
#
#
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
#
#
# [[[54 55 56]
# [57 58 59]
# [60 61 62]]
#
# [[63 64 65]
# [66 67 68]
# [69 70 71]]
#
# [[72 73 74]
# [75 76 77]
# [78 79 80]]]]
h[1]
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
h[1, 1]
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
h[1, 1, 1] #[39 40 41]
h[1, 1, 1, 1] #40
텐서, 행, 열의 모든 정보를 지정해주고 싶지 않을 때, 예를 들면 열만 빼오고 싶다던가 할 때에는 ... 를 사용할 수 있다.
print(h[1, ...]) #print(x[1])과 같음
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
print(h[..., 1])
# [[[ 1 4 7]
# [10 13 16]
# [19 22 25]]
#
# [[28 31 34]
# [37 40 43]
# [46 49 52]]
#
# [[55 58 61]
# [64 67 70]
# [73 76 79]]]
print(h[1, ..., 1])
# [[28 31 34]
# [37 40 43]
# [46 49 52]]
여기서 주의해야 할 점은 ... 을 한 번만 써야 한다는 것이다. 그렇지 않으면 IndexError를 일으킨다.
print(h[1, ..., 1, ...])
# IndexError: an index can only have a single ellipsis ('...')
그럼 행은 어떻게 빼 오느냐:
print(h[..., 1, :])
# [[[ 3 4 5]
# [12 13 14]
# [21 22 23]]
#
# [[30 31 32]
# [39 40 41]
# [48 49 50]]
#
# [[57 58 59]
# [66 67 68]
# [75 76 77]]]
print(h[1, :, 1, :])
# [[30 31 32]
# [39 40 41]
# [48 49 50]]
: 를 사용하면 된다.
numpy 인덱싱에서 ... 는 : 와 같은 역할을 하기 때문이다.
8. Assigning values to indexed arrays
이미 만들어진 텐서의 요소 값을 바꿔주는 것도 가능하다.
i = np.arange(10) #[0 1 2 3 4 5 6 7 8 9]
i[2:7] = 1 #[0 1 1 1 1 1 1 7 8 9]
i[2:7] = np.arange(5) #[0 1 0 1 2 3 4 7 8 9]
하지만 float를 int로 바꾼다던가 complex를 float이나 int로 바꾸는 것은 불가능하다.
가령 a[1] = 1.2 를 a[1] = 1.2j로 변경하려 하면 다음과 같은 에러가 뜬다.
<type 'exceptions.TypeError'>: can't convert complex to long; use long(abs(z))
j = np.arange(0, 50, 10)
j[np.array([1, 1, 3, 1])] += 1 #[0 11 20 31 40]
위와 같은 코드에서 배열 [1 1 3 1]로 j의 요소를 바꾼다고 하면 인덱스 1에 해당하는 요소만 1을 3번 더해서 13이 될 것 같지만 그렇지 않다. 그 이유는 인덱싱에 사용한 배열의 숫자는 j에서 가져온 것이기 때문에 j[1]+1 자체가 3번 적용되는 것이지 +1이 3번 적용되는 것은 아니기 때문이다.
말로 잘 설명은 못하겠지만 어쨌든 이렇다 한다.
만약 아래와 같이 지정 인덱스가 중복될 경우:
k = np.arange(5) #[0 1 2 3 4]
k[[0, 0, 2, 3]] = [1, 2, 5, 6]
print(k) #[2 1 5 6 4]
더 나중 값으로 변환된다. 즉, 위의 경우에서는 0이 1이 된 후 다시 2가 되었기 때문에 최종적으로 인덱스 0에는 2가 들어가는 것이다.
9. Dealing with variable numbers of indices within programs
지금까지 인덱싱이나 슬라이싱을 리스트를 가지고만 했었는데 튜플로도 할 수 있다.
indices = (1, 1, 1, slice(0, 2)) #[1, 1, 1, 0:2]와 같음
print(h[indices]) #[39 40]
ell = (1, Ellipsis, 1) #[1, ..., 1]과 같음
print(h[ell])
# [[28 31 34]
# [37 40 43]
# [46 49 52]]
하지만 인덱스를 적을 때 리스트형으로 적느냐 튜플로 적느냐에 따라 결과는 완전히 달라지니 주의해야 한다.
print(h[[1, 1, 1, 1]])
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
#
#
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
#
#
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]
#
#
# [[[27 28 29]
# [30 31 32]
# [33 34 35]]
#
# [[36 37 38]
# [39 40 41]
# [42 43 44]]
#
# [[45 46 47]
# [48 49 50]
# [51 52 53]]]]
print(h[(1, 1, 1, 1)]) #40
'파이썬3 노트' 카테고리의 다른 글
모듈/함수 공부: [__hash__()][Collections-Counter] (0) | 2018.08.11 |
---|---|
numpy 공부: [선형대수] (0) | 2018.08.10 |
numpy 공부: [Indexing] (2) (0) | 2018.08.08 |
numpy 공부: [indexing] (1) (0) | 2018.08.07 |
모듈/함수 공부: [데이터 모델] ① (0) | 2018.06.15 |