본문 바로가기
파이썬/Numpy(넘파이)

[NumPy] 4. 넘파이 차원 변환(Shape Manipulation)

by 쿠킷리스트 2021. 10. 13.

학습내용

#1 : 넘파이 차원 변환(Shape Manipulation)
#2 : 데이터 쌓기(Stacking together diffrent arrays)
#3 : 데이터 쪼개기(Splitting one array into several smaller ones)

#1 : 넘파이 차원 변환(Shape Manipulation)

 np.ndarray의 shape를 다양한 방법으로 변경 가능하다. .ravel().flatten() 1차원(행 or 열)으로, .reshape()는 지정한 차원으로, .T는 전치(Transpos) 변환이 가능하다. 이 때, 데이터 원본은 변경되지 않고 복사하여 연산된 결과가 반환된다.

import numpy as np
import numpy.random as npr

a = np.floor(10*npr.random((3,4)))
a
# array([[8., 8., 7., 9.],
#        [6., 6., 0., 4.],
#        [2., 9., 1., 8.]])

print(a.shape)
# (3, 4)

1) .ravel() , .flatten , .reshape() , .T

import numpy as np
import numpy.random as npr

a = np.floor(10*npr.random((3,4)))
a
# array[[9. 9. 2. 5.]
#       [0. 7. 0. 4.]
#       [8. 9. 8. 7.]]

a.shape
# (3, 4)

a.ravel()
# array([9. 9. 2. 5. 0. 7. 0. 4. 8. 9. 8. 7.])
a.flatten()
# array([9. 9. 2. 5. 0. 7. 0. 4. 8. 9. 8. 7.])
a.reshape(-1)
# array([9. 9. 2. 5. 0. 7. 0. 4. 8. 9. 8. 7.])

a.reshape(2,6)
# array([[9. 9. 2. 5. 0. 7.],
#        [0. 4. 8. 9. 8. 7.]])

a.T
# array([[9. 0. 8.]
#        [9. 7. 9.]
#        [2. 0. 8.]
#        [5. 4. 7.]])

a.T.shape
# (4, 3)

- .raver(order = 'C')는 행 방향으로, .ravel(order = 'F') 열 방향으로 변환.


2) .reshape(-1)

- .reshape()을 할 때 차원값에 -1을 입력하면 -1 부분은 자동으로 차원을 채워준다. 여러 차원에서 -1은 하나만 사용할 수 있으며, 나머지가 지정된 결과를 바탕으로 자동으로 계산한다.

d = np.floor(10*npr.random((3,4)))
d
# array([[6., 3., 4., 6.],
#        [9., 8., 8., 5.],
#        [4., 4., 1., 7.]])

d.reshape(2,-1)
# array([[6., 3., 4., 6., 9., 8.],
#        [8., 5., 4., 4., 1., 7.]])
d.reshape(-1,4)
# array([[6., 3., 4., 6.],
#        [9., 8., 8., 5.],
#        [4., 4., 1., 7.]])

#2 : 데이터 쌓기(Stacking together diffrent arrays)

1).vstack() , .hstack()

.vstack().hstack()을 통해 데이터를 합칠 수 있다.

  • .vstack() : axis = 0 기준으로 쌓는다. 수직 확장.
  • .hstack() : axis = 1 기준으로 쌓는다. 수평 확장.
a = np.floor(10*npr.random((2,2)))
a
# array([[7. 5.],
#        [7. 4.]])
b = np.floor(10*npr.random((2,2)))
b
# array([[5., 2.],
#        [5., 0.]])

np.vstack((a,b))
# array([[7., 5.],
#        [7., 4.],
#        [5., 2.],
#        [5., 0.]])
np.hstack((a,b))
# array([[7., 5., 5., 2.],
#        [7., 4., 5., 0.]])

2) column_stack()

column_stack() 함수는 1차원 배열을 2차원 배열에 열(column)로 쌓는다. 2차원 배열의 경우에만 hstack()과 동일. 

a
# array([[7. 5.],
#        [7. 4.]])
b
# array([[5., 2.],
#        [5., 0.]])
np.column_stack((a,b))
# array([[7., 5., 5., 2.],
#        [7., 4., 5., 0.]])

a = np.array([4., 2.])
b = np.array([3., 8.])
np.column_stack((a,b))
# array([[4., 3.],
#        [2., 8.]])
np.hstack((a,b))
#array([4., 2., 3., 8.])

3) newaxis

newaxis는 새로운 축을 만드는 것으로 1차원을 2차원으로, 2차원을 3차원으로, ... 만들 수 있다.

a = np.array([1,2,3,4])
a
# array([1, 2, 3, 4])
a[:, newaxis]
# array([[1],
#        [2],
#        [3],
#        [4]])
from numpy import newaxis
a = np.array([4., 2.])
b = np.array([3., 8.])
np.column_stack((a,b))
# array([[4., 3.],
#        [2., 8.]])
a[:, newaxis]
# array([[4.],
#        [2.]])
b[:, newaxis]
# array([[3.],
#        [8.]])
np.column_stack((a[:, newaxis], b[:, newaxis]))
# array([[4., 3.],
#        [2., 8.]])
np.hstack((a[:, newaxis], b[:, newaxis]))
# array([[4., 3.],
#        [2., 8.]])

4) row_stack은 vstack

row_stack 함수는 입력 배열의 vstack과 동일하다. 실제로 row_stack은 vstack의 별칭이다. 일반적으로 3차원 이상 배열의 경우 hstack은 두 번째 축을 따라 쌓고, vstack은 첫 번째 축을 따라 쌓는다. concatenate는 연결이 발생해야하는 축의 수를 제공하는 선택적 인수를 허용한다.

np.column_stack is np.hstack
# False
np.row_stack is np.vstack
# True

5) 축을 따라 숫자를 쌓아 배열 만들기

r_ 및 c_ 는 축을 따라 숫자를 쌓아 배열을 만들 때 유용하다. 슬라이싱을 활용할 수 있다.

np.r_[1:4, 0, 4]
# array([1, 2, 3, 0, 4])

- r_[1:4, 0, 4]는 1부터 4미만, 0, 4를 하나의 가로로 배열을 만들라는 의미.

np.c_[1:4]
# array([[1],
#        [2],
#        [3]])

- r_[1:4]는 1부터 4미만의 값을 세로로 배열을 만들라는 의미.


#3 : 데이터 쪼개기(Splitting one array into several smaller ones)

 

1) np.hsplit

 np.hsplit()을 통해 숫자 1개가 들어갈 경우 x개로 등분, 리스트로 넣을 경우 axis = 1 기준 인덱스로 데이터를 분할할 수 있다.

import numpy.random as npr
a = np.floor(10*npr.random((2,12)))
a
# array([[8., 5., 3., 3., 7., 5., 0., 2., 4., 9., 5., 7.],
#        [7., 8., 8., 2., 5., 5., 7., 4., 8., 7., 1., 5.]])
np.hsplit(a, 3) # a를 3등분 하라
# [array([[8., 5., 3., 3.],
#         [7., 8., 8., 2.]]), array([[7., 5., 0., 2.],
#         [5., 5., 7., 4.]]), array([[4., 9., 5., 7.],
#         [8., 7., 1., 5.]])]

- a는 24개의 요소를 가지고 있는데, 이를 3등분하면 8개로 나뉘고. 그 8개를 2차원으로 만들어야 하기에 4개씩 나눠지게 되는 것.

import numpy.random as npr
a = np.floor(10*npr.random((2,12)))
a
# array([[4., 5., 7., 6., 6., 8., 5., 6., 0., 2., 9., 6.],
#        [6., 7., 0., 1., 0., 1., 9., 4., 8., 7., 8., 2.]])

np.hsplit(a, (3, 4))
# [array([[4., 5., 7.],
#         [6., 7., 0.]]), array([[6.],
#         [1.]]), array([[6., 8., 5., 6., 0., 2., 9., 6.],
#         [0., 1., 9., 4., 8., 7., 8., 2.]])]

- 위 코드의 경우에는 (3, 4)로 분할 경계를 설정한 것이다. 3전에, 4전에, 나머지.(012ㅣ3ㅣ...)


2) np.vsplit

np.vsplit()은 수직 축을 따라 분할하고 np.array_split()을 사용하면 분할 할 축을 지정할 수 있다.

a = np.arange(16.0).reshape(4,4)
a
# array([[ 0.,  1.,  2.,  3.],
#        [ 4.,  5.,  6.,  7.],
#        [ 8.,  9., 10., 11.],
#        [12., 13., 14., 15.]])

np.vsplit(a, 2)
# [array([[0., 1., 2., 3.],
#         [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
#         [12., 13., 14., 15.]])]

a = np.arange(9)
a
# array([0, 1, 2, 3, 4, 5, 6, 7, 8])
np.array_split(a, 4)
# [array([0, 1, 2]), array([3, 4]), array([5, 6]), array([7, 8])]

- array_split()은 남는 값을 앞쪽으로 붙여준다.


# 4 : 데이터 복사

1) 데이터가 복사되지 않는 경우(No Copy at All)

 아래와 같이 np.array를 변수에 넣으면 단순히 레퍼런스를 참조할 뿐 복사가 되진 않는다.

a = np.array([[ 0, 1, 2, 3],
              [ 4, 5, 6, 7],
              [ 8, 9, 10, 11]])
b = a
b is a
# True
print(id(a),id(b))
# 139689684167456 139689684167456

 


2) 얕은 복사(View or Shallow Copy)

 다른 배열 객체는 동일한 데이터를 공유 할 수 있다. view()메소드는 동일한 데이터를 참조하는 새로운 배열 객체를 생성한다.

import numpy as np
import numpy.random as npr
a = np.floor(10*npr.random((6,2)))
c = a.view()
c is a
# False
c.base is a
# True
c.flags.owndata
# False
c = c.reshape((2,6))
a.shape
# (3, 4)
c[0,4] = 1234
a
# array([[   0,    1,    2,    3],
#        [1234,    5,    6,    7],
#        [   8,    9,   10,   11]])
s = a[:, 1:3]
s[:] = 10
a
# array([[   0,   10,   10,    3],
#        [1234,   10,   10,    7],
#        [   8,   10,   10,   11]])

- s는 a의 뷰이며 s[:] 뷰의 전체를 수정하는 것.


3) 깊은 복사(Deep Copy)

 Python의 del 키워드를 이용하면 메모리를 반환할 수 있다.

a = np.arange(int(1e8))
b = a[:100].copy()
del a
print(a)
#NameError: name 'a' is not defined
print(b)
# [ 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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#  96 97 98 99]

- del a를 사용하면 더이상 a의 넘파이 배열을 사용할 수 없다.

댓글