무효 클릭 IP 추적 중...
머신러닝,딥러닝/파이썬, 넘파이 구현

[넘파이 기초] 차원 추가/축소/ 변경 완벽 정리

꼬예 2021. 5. 12.

이번 포스팅에서는 ndarray의 size는 유지하면서 차원을 변경하는 법에 대해 알아보도록 할게요. 

 

 

reshape을 이용한 차원 확대

1. 1차원 --> 2차원

 

이전시간에 언급했듯이 reshape은 말그대로 shape을 바꿔주는 api입니다.

 

내가 확장하고 싶은 차원의 위치에 1를 넣어주면 되는데요.

import numpy as np

a = np.arange(9)

b = a.reshape((1,9)) 
c = a.reshape((9,1))

print(f"a: {a.shape}\n {a}\n")

print(f"b: {b.shape}\n {b}\n")
print(f"c: {c.shape}\n {c}\n")

output :

b는 row 위치에 1를 위치 시킴으로써 (9,)를 1개 가진 형태의 행렬을 만들어줍니다.. 

물론 출력 결과를 확인해보시면 a와 모양은 거의 같으나 대괄호 한개가 더 추가된것으로 미루어 보아 한차원 높아진것을 유추 할 수 있죠.

 

반면 c는 column 자리에 1을 넣은 형태입니다. 

이건 (1,)짜리가 9개 있는 행렬 형태로 column 벡터 형태인것을 알 수 있습니다. 

 

 

위 코드에서는 내가 확장하고 싶은 차원에 1을 넣고 나머지 차원에 원래 원소의 갯수를 넣음으로써 size는 유지하되 차원을 확장했습니다.

 

하지만 실제 데이터를 다룰때는 원래 원소의 갯수를 알기가 까다로울때가 많습니다. 이럴땐 해당 위치에 -1을 넣어주면 알아서 원소의 갯수를 계산하고, 1을 넣은 위치에 따라 차원을 확장하게 되죠.

import numpy as np

a = np.arange(9)

b = a.reshape((1,-1)) 
c = a.reshape((-1,1))

print(f"a: {a.shape}\n {a}\n")

print(f"b: {b.shape}\n {b}\n")
print(f"c: {c.shape}\n {c}\n")

output :

 

 

 

2. 1차원 --> 3차원

앞서 2차원으로 변경 할때랑 같은 원리가 적용됩니다.  우리가 추가 하고싶은 차원에 1을 넣어주면 되는데요.

다만 차이가 있다면 1차원에서 3차원은 2개 차원의  차이가 있기 때문에, 여기선 1을 두번 넣어주는 형태가 됩니다.

 

import numpy as np

a = np.arange(9)

b = a.reshape((1,1, 9)) 
c = a.reshape((1,9, 1)) 
d = a.reshape((9,1, 1))  

print(f"a: {a.shape}\n {a}\n")

print(f"b: {b.shape}\n {b}\n")
print(f"c: {c.shape}\n {c}\n")
print(f"d: {d.shape}\n {d}\n")

output :

 

최초값인 a는 (9,) 형태의 1차원 row 벡터입니다. 

(numpy에서는 디폴트로 1차원 값을 row 벡터로 간주합니다. 즉  (1,9)의 shape을 가진 2차원 row 벡터와 차원은 다르나 모양은 같습니다. )

 

b는 (1,9)의 shape을 가진 row 벡터 행렬을 1개 가지고 있다는 말입니다. 

원래 값인 a와 모양은 같으나 대괄호 갯수가 2개 더 많습니다. 즉 2차원이 높아졌음을 의미합니다. 

 

c는 (9,1)의 shape을 가진 column 벡터 행렬이 1개 라는 의미 입니다. 

 

d는 (1,1)의 shape을 가진 행렬이 9개의 차원으로 구성되어 있음을 의미합니다. 

 

 

3. 2차원 --> 3차원

이전까지는 1차원값을 다차원으로 만드는 형태였다면,

지금부터는 2차원 값을 확장 하는 형태를 알아보겠습니다.

 

import numpy as np

a = np.random.normal(size=(100, 200))

b = a.reshape((1, 100, 200))
c = a.reshape((100, 200, 1))

print(b.shape)
print(c.shape)

 

a는 (100, 200) 형태의 2차원 행렬입니다. 

코드를 보시면 아시겠지만 앞서 배운 원리는 같습니다. 내가 추가하고싶은 차원에 1을 추가하는 형태이죠.

1을 제외한 다른 위치에는 a의 shape이 그대로 들어간걸 확인할 수 있습니다.

 

output :

 

위 코드에서는 a가 2차원이지만,  차원이 더욱 확장되어 5차, 6차 고차원이 되다보면, 확장시 해당 shape을 정확하게 입력을 해줘야하는 번거로움이 발생합니다.

 

뿐만아니라 a의 shape을 바꿔야 하는 상황이 왔을때 연이어 b , c 모두 일일이 값을 변경해줘야 하고요.

 

import numpy as np

a = np.random.normal(size=(100, 200))

b = a.reshape((1, *a.shape))
c = a.reshape((*a.shape, 1))

print(f"a: {a.shape}\n")

print(f"b: {b.shape}")
print(f"c: {c.shape}")

 

이럴때 효과적으로 사용할 수 있는것이 *(asterisk) 입니다.

해당 asterisk사용법에 대해선 이전 포스팅에서 다룬적이 있으니, 사용법은 생략하고 바로 사용을 해보겠습니다!

 

보시는것처럼 우리가 추가 하고싶은 차원에 1을 넣어주고 , 기존 값인 a의 shape 값을 *로 unpacking해주면됩니다. 

 

물론 (100,1, 200)와 같이 1이 가운데 오는 형태는 *를 사용할 수 없습니다만, 

대부분의 차원을 확장을 할때는 1이 제일 앞에 또는 뒤에 오니 유용하게 사용할 수 있겠죠?

output :

 

 

슬라이싱을 이용한 차원 확대

1. 1차원 -> 2차원

앞서 reshape을 이용할때는 새롭게 추가할 차원에 1을 넣는 형태였다면

슬라이싱을 이용할때는 아래와 같이 np.newaxis 또는 None을 넣어주면 됩니다. 

 

import numpy as np

a = np.arange(9)
# 원래 차원은 : 로 표시하고 새로 추가할것을 np.newaxis 또는 None 사용
row_vec1 = a[np.newaxis, :] # 내부적으로 np.newaixs는 None과 같음 가시성을 위한거임
row_vec2 = a[None,:]
# reshape((1, -1))과 같음
col_vec1 = a[:, np.newaxis]
col_vec2 = a[:, None]
# reshape((-1,1))과 같음

print(f"row_vec1.shape: {row_vec1.shape}")
print(f"row_vec2.shape: {row_vec2.shape}")

print(f"col_vec1.shape: {col_vec1.shape}")
print(f"col_vec2.shape: {col_vec2.shape}")

또 차이가 있다면 reshape에서 1을 제외한 곳에 기존 차원의 shape을 그대로 넣었던 반면, 슬라이싱에서 기존 그대로 차원을 적는 방법으로 콜론을 사용합니다. 

output :

 

2. 1차원 -> 3차원

이번에는 1차원에서 3차원으로 확장 해보겠습니다. 

앞서 reshape을 사용했던 방식과 같습니다. 

 

차이가 있다면 1대신 np.newaxis, None을 넣어주는것과, 2개의 차원이 커짐으로 np.newaxis 가 두개 들어 가겠죠?

import numpy as np

a = np.arange(9)

b = a[np.newaxis, np.newaxis, :]
c = a[np.newaxis, : , np.newaxis]
d = a[:, np.newaxis, np.newaxis]

print(f"a.shape: {a.shape}")
print(f"b.shape: {b.shape}")

print(f"c.shape: {c.shape}")
print(f"d.shape: {d.shape}")

output :

 

3. 2차원 -> 3차원

이번에는 2차원의 a를 3차원으로 변경 해보겠습니다. 

원리는 같습니다. np.newaxis or None 을 새롭게 추가할 차원에 적어주면 됩니다. 

다만 a가 2차원이기 때문에 유지해야 차원이 2개를 의미하고, 다시 말해 콜론을 두개 적어 줘야 합니다.

import numpy as np

a =  np.random.normal(size=(100,200)) 

b = a[np.newaxis, :, :]
c = a[:, :, np.newaxis] 
d = a[:, np.newaxis, :]
print(f"a.shape: {a.shape}")
print(f"b.shape: {b.shape}")

print(f"c.shape: {c.shape}")
print(f"d.shape: {d.shape}")

output :

 

:, : 를 두번이나 입력하니 조금 불편하지 않으셨나요?

앞서 reshape에서 설명드렸듯이 a가 차원이 많아지면 : 를 계속해서 적어줘야 하는 번거로움이 발생합니다. 

 

reshape에서 *asterisk를 이용한것처럼 슬라이싱에서도 유용하게 사용할 수 있는 방법이 있는데요 

바로 ' ... ' 입니다. 

 

'...'도 마찬가지로 a[:, np.newaxis, :] 와 같이 np.newaxis가 가운데 있는 형태는 쓸수 없다는 단점이 있지만, 앞서 언급드린대로 새롭게 추가하는 차원은 처음과 끝에 경우가 많기 때문에 대부분 사용가능합니다. 

 

사용법은 아래와 같이 사용해 주시면 됩니다

import numpy as np

a =  np.random.normal(size=(100,200)) 

b = a[np.newaxis, ...] 
c = a[..., np.newaxis] 

print(f"a.shape: {a.shape}")
print(f"b.shape: {b.shape}")

print(f"c.shape: {c.shape}")

output :

 

 

expand_dims api를 이용한 차원 확대

이어서 알아볼  api는 expand_dims 입니다. expand dimension을 그대로 의미하는 api명이 매우 직관적인데요!

 

사용법은 아래 코드와 같습니다. 

1. 1차원 --> 2차원

import numpy as np

a = np.arange(9)

b = np.expand_dims(a, axis=0) # 첫번째차원에 새로운 축 만들겠다.
c = np.expand_dims(a, axis=1) # 두번째 차원에 새로운 축 만들겠다. 

print(f"a.shape: {a.shape}")
print(f"b.shape: {b.shape}")

print(f"c.shape: {c.shape}")

output :

첫번째 인자로는 확장할 값을 넣어주고, 두번째 인자로는 axis = (변경할 축) 을 넣어주면 됩니다. 

 

output 결과를 보시면 아시겠지만 axis = 0 적으면  row위치에 1이 추가 됨으로써 차원이 확장되는 것을 알 수있고 반대로 axis =1 인 경우에는 column위치에 1이 추가된 것을 확인할 수 있습니다. 

 

2. 1차원 --> 3차원

이번에는 3차원으로 확장을 해볼건데요.

1차원에서 3차원은 2차원이 차이가 납니다. 즉  axis 에도 변경할 차원을 2개 적어줘야겠지요.

넣어줄때는 튜플형태로 넣어주시면 됩니다. 

import numpy as np

a = np.arange(9)

b = np.expand_dims(a, axis= (0,1))
c = np.expand_dims(a, axis= (0,2))
d = np.expand_dims(a, axis= (1,2))


print(f"a.shape: {a.shape}")
print(f"b.shape: {b.shape}")

print(f"c.shape: {c.shape}")
print(f"d.shape: {d.shape}")

output :

ouput 결과를 보시면 아시겠지만 axis에 적어준 차원 값들이 1로 바뀐걸 알수가 있습니다.

 

d를 예로 들어보자면 axis =(1,2) 을 넣었기때문에 row 와 column을 나타내는 차원이 1로 바뀐것을 알 수 있습니다. 

(참고로 axis = (2,1)을 넣어도 값은 같습니다.  어떤 숫자를 넣었는지가 중요하지 튜플상에서 위치가 중요하진 않다는것도 챙겨가시길 바래요 :) )

 

 

+추가 

혹시 axis 에 튜플을 넣는 형태를 넣었을때 오류가 뜨시는분은

print(numpy.__version__)  을 통해 버젼을 확인하시기 바랍니다.

해당기능은 NumPy 1.18. 에서 추가된 기능입니다. 

 

 

reshape | flatten을 이용한 차원 축소 

지금까지는 차원을 확장하는 내용을 알아보았다면 이제 차원을 죽이는 작업을 알아 보겠습니다. 차원을 확장하는 것만큼 차원을 죽이는것도 많이 사용되는 기술인데요.

 

위에서 reshape을 이용하여 차원을 확장할때는 확장하고 싶은 차원 위치에 1을 넣어주고, 다른곳에 기존의 원소 갯수를 넣어주면 되었죠.

 

차원을 없앨때는 반대로 1을 빼주면됩니다. 

import numpy as np

a = np.ones(shape=(1, 10)) 

# 차원을 죽이는 다양한 방법. 
b = a.reshape((10,)) 
c = a.reshape((-1,))
d = a.flatten()

print(f"a.shape: {a.shape}\n")

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")
print(f"d.shape: {d.shape}\n")

output :

b를 기준으로 설명하겠습니다.  reshape의 인자로 튜플 (10,)를 넣었네요.

차원 확대와 다르게 들어가는 값자체가 한차원 낮은 shape의 값을 넣었고, 1을 뺀것을 알 수 있습니다. 

 

무엇보다 여기서 확인하셔야할것은 어떤 값을 넣었는가입니다. 

1이 아닌 원소의갯수를 의미하는 10을 넣었다는걸 알 수 있습니다. 

 

다음으로 c는 -1를 넣었습니다. 

앞선 포스팅을 읽어보셨으면 아시겠지만 -1이란 친구는 자동으로 기존 원소의 갯수를 계산하여 넣는 값인데요. b처럼 직접 원소의 갯수를 넣을 필요가 없다는 점이 메리트가 있죠.

 

d는 flatten() 입니다. 이미 flatten() vs ravel() 비교 포스팅에서 설명 드린 기능인데요.

어떤 차원이든 1차원으로 만들어버리는 api 입니다. ( flatten은 모두 1차원으로 만든다는점에서 3차원 -> 2차원으로 만들지는 못하는 단점이 있습니다.)

 

reshape을 통한 차원 확장을 할때도 다뤘던 내용인데요.

 

최초 변경전인 값인 a의 차원이 많아질경우 번거로운(?) 일이 발생했었죠.

차원을 없앨때도 똑같이 그러한 문제가 발생합니다. 

 

이때도  *asterisk를 사용하면 그 문제를 해결할 수 있습니다. 

최초값의 shape을 뽑아 1을 제외한 값의 shape을 뽑아낸 후 그값을 *를 통해 unpacking 하면 되는데요.

1을 제외할때는 슬라이싱을 이용하시면 됩니다:)

import numpy as np


a = np.ones(shape=(1,3,4))
b = np.ones(shape=(3,4,1))

c = a.reshape(*a.shape[1:])
d = b.reshape(*b.shape[:-1]) # (3,4,5,6,1) 이렇게 차원이 많아지면  -1이 더 효율적

print(f"a.shape: {a.shape}\n")

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")
print(f"d.shape: {d.shape}\n")

output :

 

슬라이싱을 통한 차원 축소 

앞서 슬라이싱을 통한 차원을 확대할때는 확장할 차원에 np.newaxis 나 None을 넣었고 나머지 위치에는 : 를 넣었는데요.

 

축소의 경우에는 특정 인덱스를 지정 해줌으로써 차원을 축소시킵니다.

무슨말인지는 코드를 통해 확인해보죠

import numpy as np

a = np.arange(9).reshape((3,3))

b, c = a[1, :], a[:, 1]

print(f"a.shape: {a.shape}\n")

print(f"row.shape: {b.shape}\n")
print(f"col.shape: {c.shape}\n")

output :

우선 b 를 기준으로 설명을 해보겠습니다.

a[1, :] 를 통해 차원 축소작업을 하는데요.

 

여기서 우리가 원하는건 row 차원의 값을 없애버리고 싶습니다. 

이럴때 row 차원에 인덱싱을 지정 하면 됩니다.

우리 예에서는 1이라고 지정을 했죠?

물론 a가 (3,3) 행렬이기 때문에 0 또는 2를 해도 상관없습니다. 

여기서 중요한건 특정 인덱스를 지정하는 순간 해당 지정된 차원이 죽는다는걸 아시면 됩니다!

 

 

이해를 돕기 다른 예를 통해 연습해보죠

import numpy as np

a = np.arange(9).reshape((1, -1))
b = np.arange(9).reshape((-1, 1))

c = a[0, :]

d = b[:, 0]

print(f"a.shape: {a.shape}\n")

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")
print(f"d.shape: {d.shape}\n")

output :

a는 2차원 row 벡터, b는 2차원 column 벡터를 의미합니다. 

앞선 예에서 (3,3) 같은 경우에는 인덱스를 0,1,2 다양하게 사용가능했으나 

여기선 한쪽이 1이니까 인덱스 0 하나만 사용가능하겠네요.

 

우선 c는 a의 row에 0이라는 인덱스를 지정해줬기 때문에 1이 적혀있던 row 차원이 날라가면서 (9,)가 됩니다. 

마찬가지로

d는 column에 0이라는 인덱스를 지정해줬기때문에 b 기준으로 1이 적혀있던 column 차원이 날라가면서 (9,)가 됩니다. 



슬라이싱을 이용한 차원 축소에서도 a, b 가 고차원이 되면 c, d 에서 콜론을 여러번 써줘야 하는 문제가 발생하죠

이럴땐 '...' 를 통해 반복되는 코드를 줄일 수 있습니다. 

 

코드 설명은 앞과 같은 원리이기때문에 생략할게요

import numpy as np

a = np.ones(shape=(1, 3, 4))
b = np.ones(shape=(3, 4, 1))

c = a[0, ...]
d = b[...,0]

print(f"a.shape: {a.shape}\n")

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")
print(f"d.shape: {d.shape}\n")

output :

 

 

squeeze api

마지막으로 차원 축소 api인 squeeze api 에 대해 알아보겠습니다. 

앞서 flatten() 같은경우는 다차원 -> 1차원만 가능했습니다.

 

왜냐하면 무조건 1차원 벡터 형태로만 바꾸는 기능을 하기 때문이죠.

만약 3차원 이상일 경우는 기존 shape을 다 망가뜨리기 때문에 문제가 발생합니다. 

 

반면 squeeze는 1이 아닌 값들의 shape은 유지하면서 1인값들은 다 없애버리는 녀석이죠.

 

사용방법은 아래와 같습니다. 

import numpy as np

a = np.ones(shape=(1, 3, 4))

b = np.squeeze(a)
c = a.squeeze()

print(f"a.shape: {a.shape}\n")

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")

output :

np.squeeze(a) 형태로 써도되고 a.squeeze() 형태로 써도 무방합니다. 사용방법은 코드를 보시면 충분히 이해되실테니 생략하겠습니다.

 

차원이 아무리 높아지고 복잡해져도 1만 쏙쏙 빼서 없애버리는 편리한 api입니다 

import numpy as np

a = np.ones(shape=(1,1,4,1,3,1)) # 6차원

b = np.squeeze(a)

c = a.squeeze()
print(f"a.shape: {a.shape}\n") 

print(f"b.shape: {b.shape}\n")
print(f"c.shape: {c.shape}\n")

output :

편리하긴하지만 기존의 차원이 죽으면서 전혀 새로운 형태가 나오게 됩니다. 

그 말은 처음 이 shape 보게되면 각 차원이 무슨 의미인지 모를수있는 경우가 있기 때문에 주석을 통해 남겨두는 센스가 필요합니다 :)

 

 

 

앞서 차원 축소/확대 같은 경우는 차원을 높이거나 낮추는 경우였다면 

지금부터 알아볼것은 차원의 높고 낮음이 아니라 형태를 바꾸는 코드를 알아보겠습니다.

 

 

np.swapaxes

import numpy as np

a = np.random.normal(size=(3,4,5,6)) 

b = np.swapaxes(a, 0, 1) # 서로 바꿔줄 index를 넣는다. 
c = np.swapaxes(a, 0, 2)
d = np.swapaxes(a, 0, 3)

print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")
print(f"c.shape : {c.shape}\n")
print(f"d.shape : {d.shape}\n")
# 두가지 차원을 바꿀때 효율적임

output :

 

api 명 그대로 axes간의 위치를 변경 해주는 api 입니다. 

 

첫번째 인자로는 바꿀값을 넣습니다.

두번째 세번째는 서로 바꿔줄 차원의 index를 넣어주면 됩니다.

 

c를 예를 들어 보자면,

0, 2를 넣었다는 말은 

최초 (3,4,5,6) 중 3 과 5의 위치를 바꿔주겠다는 의미 입니다. 

출력 결과도 (5,4,3,6)이 나오는것을 알 수 있습니다. 

 

 

숙달을 위해 또다른 예를 볼까요?

이번에는 첫번째 차원과 마지막 차원을 변경해주는 코드입니다. 

첫번째 차원과 마지막 차원을 바꾸는경우가 제일 많이 쓰이는데요.

일반적으로 마지막 차원은 -1로 적으니까 참고하시면 되겠습니다. 

 

import numpy as np

a = np.random.normal(size=(3, 200, 100))

b = np.swapaxes(a, 0, -1) # 첫번째 차원과 마지막 차원을 바꿔라 이게 제일 많이 쓰임
print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")

output :

 

 

 

np.moveaxis

다음으로는 moveaxis api에 대해서 알아보겠습니다. swapaxes는 두 인덱스의 위치를 바뀌는 개념이라면, moveaxis(axes가 아님) 는 "특정위치에서 -> 특정 위치"로 위치를 변경 시키는 느낌이 강하다고 보시면 됩니다.

코드를 보실까요?

 

import numpy as np
a = np.random.normal(size=(3,4,5,6)) 

b = np.moveaxis(a, source=0, destination=1) # 0을 1위치로 옮겨라 
c = np.moveaxis(a, source=0, destination=2)
d = np.moveaxis(a, source=0, destination=-1)
print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")
print(f"c.shape : {c.shape}\n")
print(f"d.shape : {d.shape}\n")

output :

 

b를 기준으로 설명을 해보겠습니다.

첫번째 인자로는 바꿀값을 넣고, 두번째 인자에  source 라는 키워드명을 보면 알 수 있겠지만 최초 바꿀 차원의 인덱스, destination은 옮길 인덱스를 말합니다.

 

즉 b로 예를 들자면 0번째 인덱스에 위치한 값을 1번째 인덱스 위치로 옮겨라 라는 말이 됩니다. 

결과를 보시면 (3,4,5,6) -> (4,3,5,6) 이 되었네요.

얼핏보면 마치 swapaxes와 같아보이는데요.

 

c를 보실까요? 

0번째 인덱스에 위치한 값을 2번째 인덱스 위치로 옮겨라 말인데요.

(3, 4, 5, 6) -> (4, 5, 3, 6)

0번째 인덱스에 위치한 3이 destination에 적혀있는 인덱스 2에 위치하게되는데요.

보시는것처럼 3 인덱스 2에 위치하게 되었죠.

반면 다른 값들은 그대로 밀려난 것을 알 수 있습니다. 

 

transpose api

다음으론 transpose api 사용법에 대해 알아보겠습니다. 

transpose는 기본적으로 기존 shape을 정반대로 바꾼다고 생각하시면 됩니다. 

코드를 보시죠.

 

import numpy as np

a = np.random.normal(size=(3,4))

b = np.transpose(a) 
c = a.T

print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")
print(f"c.shape : {c.shape}\n")

# (3,4,5,6)을 (6,5,4,3)으로 바꾸는데 좋은 api임

output :

tranpose 함수안에 바꿀값을 넣으면 됩니다. (3,4) -> (4,3) 형태로 뒤집혀진것을 알 수 있죠.

사실 ndarray method 인 .T 과 같은 기능이다라고 볼 수도 있습니다. 

 

a의 차원을 높여볼까요?

import numpy as np

a = np.random.normal(size=(3,4,5,6,7))

b = np.transpose(a) 
c = a.T

print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")
print(f"c.shape : {c.shape}\n")

output :

output을 보시면 아시겠지만 모든 차원이 반대로 되는걸 알 수 있습니다. 

 

 

앞서 제가 transpose 가 .T가 기본적으로 같다고 볼수 있다고 하였는데요.

이 말은 다를 수도 있다라는 걸 의미하죠.

엄밀하게 말하면 transpose의 디폴트 상태는 .T와 같습니다.

 

하지만 디폴트 상태가 아닌 인자를 수정하게 되면 .T와는 다른 기능을 하게 되는데요.

이 부분에 대해서 알아보겠습니다.

 

import numpy as np
# axes가 있으면 커스터마이징 가능.
a = np.random.normal(size=(3,4,5))

b = np.transpose(a, axes=(0,1,2)) 
c = np.transpose(a, axes=(1,2,0)) 
d = np.transpose(a, axes=(2,0,1)) 
e = np.transpose(a, axes=(2,1,0))  # 3번째를 1번째로, 두번째를, 두번째로, 첫번째를 세번째로

print(f"a.shape : {a.shape}\n")

print(f"b.shape : {b.shape}\n")
print(f"c.shape : {c.shape}\n")
print(f"d.shape : {d.shape}\n")
print(f"e.shape : {e.shape}\n")

output :

transpose에는 숨어있는 인자로 axes 가 존재하는데요. 

튜플 형태로 값을 넘겨주게 됩니다. 

 

앞서 다뤘던 axes랑 조금 느낌이 다르기때문에 헷갈리지 않도록 주의하세요!

 

e를 예를 들어보죠

axes = (2,1,0)

 

axes 안에 있는 2a의 인덱스를 의미하는데요. a에서 해당하는 값은 5이죠.

5값을 axes 튜플상 인덱스 0 에 위치 시켰는데 이 말은 우리가 바꿀 최종 shape 위치에서 인덱스 0에 위치시키라는 말이 됩니다. 

 

다음으로 axes 안에 있는 1은  a에서 4를 의미하고 이 4를 axes 튜플상 인덱스 1에 위치해 있기 때문에 최종 shape에서 인덱스 1에 위치할 거구요.

 

마지막으로 axes안에 있는 0은 a에서 5를 의미하고 이 5를 axes 튜플상 인덱스 0 에 위치해 있기 때문에 최종 shape에서 인덱스 2에 위치하게 됩니다. 

 

output을 보시면 제가 말씀드린대로 (5,4,3) 으로 shape이 변경 된것을 알 수가 있습니다. 

 

다차원의 값이면서 여러값들의 위치를 동시에 바꾸고싶다면 꾀나 유용한 api겠죠?

 

  • 트위터 공유하기
  • 페이스북 공유하기
  • 카카오톡 공유하기
이 컨텐츠가 마음에 드셨다면 커피 한잔(후원) ☕

댓글