파이썬 넘파이(Numpy) 함수 총정리2 : 배열 모양 변경 및 구조 조작 (reshape, vstack, split 등)

데이터 분석을 하다보면 수집된 데이터의 형태가 내가 사용하려는 데이터 구조와 맞지 않는 경우가 많습니다. 이때 필요한 기능이 바로 넘파이 배열의 모양을 변경하고 구조를 조작하는 함수들입니다. 

이 글에서는 넘파이 함수 총정리 제 2탄으로, 데이터 가공을 위해 자주 사용되는 넘파이 배열의 차원 변경, 배열 결합 및 분할 함수 등을 정리해 보도록 하겠습니다. 

넘파이 배열 모양 변경 및 구조조작 함수
넘파이(Numpy) 배열의 모양 변경 및 구조조작 함수

01. 배열 모양 변경 함수

1) .reshape(newshape)

배열의 구조를 지정한 형태로 변경해 주는 함수입니다. 데이터의 개수(size)는 반드시 일치해야 하며 일치 하지 않을 경우 ValueError가 발생합니다. 
  1. newshape : 새로운 배열의 모양을 지정합니다. 
    • -1 옵션 : -1을 넣으면 나머지 차원의 숫자를 보고 자동으로 크기를 계산합니다.
      (ex) a.reshape(2, 4) : 배열 a를 2 x 4 형태의 배열로 변경합니다.
      (ex) a.reshape(-1, 4) : 배열 a의 size에 맞게 ? x 4 형태의 배열로 변경합니다. 
  • 변경 전/후 원소의 개수는 동일해야합니다.
  • 원본 배열은 그대로 두고 "보여주는 모양"만 바꿉니다
  • 뷰(View) 반환 : 변경된 배열 값 수정시 원본 배열의 값도 함께 바뀝니다. 
    (단, 메모리가 연속적이지 않은 경우 복사본을 반환할 수도 있습니다)
org = np.array([0, 1, 2, 3, 4, 5, 6, 7])
a = org.reshape(2, 4)
b = org.reshape(-1, 4)

print(org)       # 결과: [0 1 2 3 4 5 6 7]
print(a)         # 결과: [[0 1 2 3][4 5 6 7]]
print(b)         # 결과: [[0 1 2 3][4 5 6 7]]

a[(1, 1)] = 99

print(org)       # 결과: [0 1 2 3 4 99 6 7]     (원본 변경됨)
print(a)         # 결과: [[0 1 2 3][4 99 6 7]]
print(b)         # 결과: [[0 1 2 3][4 99 6 7]]  

2) .flatten( ) 

다차원 배열을 1차원 배열로 변경하는 함수입니다. 
  • 복사본 반환 : 변경된 배열의 값을 수정해도 원본 배열과는 무관합니다.
org = np.array([[1, 2],[3, 4], [5, 6]])
a = org.flatten()

print(org)       # 결과: [[1 2][3 4][5 6]]
print(a)         # 결과: [1 2 3 4 5 6]

a[2] = 99

print(org)       # 결과: [[1 2][3 4][5 6]]   (원본은 그대로임)
print(a)         # 결과: [1 2 99 4 5 6]


3) .ravel( ) 

다차원 배열을 1차원 배열로 변경하는 함수입니다. 앞서 설명한 .flatten() 함수와 차이점은 복사본 대신 뷰(View)를 반환합니다. 
  • 뷰(View) 반환 :  변경된 배열의 값을 수정하면 원본도 수정됩니다. 
org = np.array([[1, 2],[3, 4], [5, 6]])
a = org.ravel()

print(org)       # 결과: [[1 2][3 4][5 6]]
print(a)         # 결과: [1 2 3 4 5 6]

a[2] = 99

print(org)       # 결과: [[1 2][99 4][5 6]]   (원본 변경됨)
print(a)         # 결과: [1 2 99 4 5 6]


4) .transpose( ) 또는 .T

배열의 행과 열을 바꾸어 주는 함수입니다. 
  • .transpose()와 .T는 동일한 역할을 합니다. 
a = np.array([[1, 2],[3, 4], [5, 6]])

print(a.transpose())   # 결과: [[1 3 5][2 4 6]]
print(a.T)             # 결과: [[1 3 5][2 4 6]]


02. 배열 병합 함수 (합치기)

1) .vstack(tup)

수직으로 쌓는 개념으로 데이터를 합치는 함수입니다. 2차원 배열의 경우 행(row) 방향으로 쌓아서 아래로 길게 데이터를 합치게됩니다. 기존의 데이터셋에 새로운 데이터를 추가할 때 주로 사용합니다.
  1. tup : 결합하려는 배열들을 튜플이나 리스트 형태로 전달합니다.
  • 1차원 배열 결합 : 2차원 배열이 됨 (아래로 데이터를 쌓는 개념 적용) *
  • 다차원 배열 결합 : 첫번째 축(axis 0)을 기준으로 결합합니다.
    • 2차원 배열 결합 : 행(axis 0)만 아래로 추가됨 (의 개수는 동일해야 함)
    • 3차원 배열 결합 : 깊이(axis 0)만 추가됨 (행과 열의 개수는 동일해야 함
a1 = np.array([1, 2])
a2 = np.array([3, 4])
a3 = np.array([[1, 2], [3, 4]])
a4 = np.array([[5, 6], [7, 8]])
a5 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
a6 = np.array([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])

print(np.vstack([a1, a2]))   # 결과:  [[ 1  2]
                             #         [ 3  4]]

print(np.vstack([a3, a4]))   # 결과:  [[ 1  2]
                             #         [ 3  4]
                             #         [ 5  6]
                             #         [ 7  8]]

print(np.vstack([a5, a6]))   # 결과: [[[ 1  2]
                             #         [ 3  4]]
                             #        [[ 5  6]
                             #         [ 7  8]]
                             #        [[ 9 10]
                             #         [11 12]]
                             #        [[13 14]
                             #         [15 16]]]


2) .hstack(tup)

수평으로 붙이는 개념으로 데이터를 합치는 함수입니다. 2차원 배열의 경우 열(col) 방향인 옆으로 길게 데이터를 합치게 됩니다. 기존의 데이터에 새로운 특징(Feature)을 추가할 때 사용됩니다. 
  1. tup : 결합하려는 배열들을 튜플이나 리스트 형태로 전달합니다.
  • 1차원 배열 결합 : 하나의 긴 1차원 배열이 됨 (옆으로 데이터를 붙이는 개념 적용) *
  • 다차원 배열 결합 : 두번째 축(axis 1)을 기준으로 결합합니다. 
    • 2차원 배열 결합 : 열(axis 1)만 옆으로 추가됨 (의 개수는 동일해야 함)
    • 3차원 배열 결합 : 행(axis 1)만 추가됨 (깊이와 열의 개수는 동일해야 함
a1 = np.array([1, 2])
a2 = np.array([3, 4])
a3 = np.array([[1, 2], [3, 4]])
a4 = np.array([[5, 6], [7, 8]])
a5 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
a6 = np.array([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])

print(np.hstack([a1, a2]))   # 결과: [1 2 3 4]

print(np.hstack([a3, a4]))   # 결과: [[1 2 5 6]
                             #        [3 4 7 8]]

print(np.hstack([a5, a6]))   # 결과: [[[ 1  2]
                             #         [ 3  4]
                             #         [ 9 10]
                             #         [11 12]]
                             #        [[ 5  6]
                             #         [ 7  8]
                             #         [13 14]
                             #         [15 16]]]


3) .concatenate(arrays, axis=0)

배열을 특정 축(axis)을 기준으로 합치는 함수입니다. 
  1. arrays : 결합하려는 배열들을 튜플이나 리스트 형태로 전달합니다.
  2. axis : 배열 결합 시 기준이 되는 축(axis)을 지정함 (기본값 : 0)
    • axis=0인 경우 : vstack()과 유사 (1차원 배열은 결과가 다름. 주의!)
    • axis=1인 경우 : hstack()과 유사 (1차원 배열은 결과가 다름. 주의!) 
    a1 = np.array([1, 2])
    a2 = np.array([3, 4])
    a3 = np.array([[1, 2], [3, 4]])
    a4 = np.array([[5, 6], [7, 8]])
    a5 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    a6 = np.array([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])
    
    print(np.concatenate([a1, a2], axis=0))   # 결과: [1 2 3 4]
    
    print(np.concatenate([a3, a4], axis=0))   # 결과: [[1 2]
                                              #        [3 4]
                                              #        [5 6]
                                              #        [7 8]]
    
    print(np.concatenate([a5, a6], axis=0))   # 결과: [[[ 1  2]
                                              #         [ 3  4]
                                              #        [[ 5  6]
                                              #         [ 7  8]]
                                              #        [[ 9 10]
                                              #         [11 12]]
                                              #        [[13 14]
                                              #         [15 16]]]
    
    #print(np.concatenate([a1, a2], axis=1))   # Error (axis=1 없음)
    
    print(np.concatenate([a3, a4], axis=1))   # 결과: [[1 2 5 6]
                                              #        [3 4 7 8]]
    
    print(np.concatenate([a5, a6], axis=1))   # 결과: [[[ 1  2]
                                              #         [ 3  4]
                                              #         [ 9 10]
                                              #         [11 12]]
                                              #        [[ 5  6]
                                              #         [ 7  8]
                                              #         [13 14]
                                              #         [15 16]]]
    


    03. 배열 분할 함수 (나누기)

    1) .vsplit(array, indices_or_sections)

    수직 방향(위 아래)으로 배열을 나누는 개념의 함수입니다. 2차원 배열의 경우 행(row)을 기준으로 나누게 됩니다. 
    1. array : 분할하려는 배열을 지정합니다.  
    2. indices_or_sections : 정수(N) 또는 1차원 배열(리스트)을 지정합니다. 
      • 정수(N) : 배열을 같은 크기의 N개로 분할 합니다. 
      • 배열(index) : 배열(리스트)의 인덱스 위치를 기준으로 분할합니다.
        (ex) [2, 5]를 지정하면 [:2], [2:5], [5:] 세 부분으로 분할합니다.
    • 1차원 배열 분할 : Error 발생 (수직 방향, 위 아래 개념이 없음) *
    • 다차원 배열 분할 : 첫번째 축(axis 0)을 기준으로 분할합니다. 
    a1 = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    a2 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    
    r1 = np.vsplit(a1, 2)
    r2 = np.vsplit(a1, [2,3])
    r3 = np.vsplit(a2, 2)
    r4 = np.vsplit(a2, [1,2])
    
    print(r1)     # 결과: [array([[1, 2],[3, 4]]), 
                  #        array([[5, 6],[7, 8]])]
    
    print(r2)     # 결과: [array([[1, 2],[3, 4]]),
                  #        array([[5, 6]]),
                  #        array([[7, 8]])]
    
    print(r3)     # 결과: [array([[[1, 2],[3, 4]]]),
                  #        array([[[5, 6],[7, 8]]])]
    
    print(r4)     # 결과: [array([[[1, 2],[3, 4]]]),
                  #        array([[[5, 6],[7, 8]]]),
                  #        array([], shape=(0, 2, 2), dtype=int64)]
    


    2) .hsplit(array, indices_or_sections)

    수평 방향(좌우)으로 배열을 나누는 개념의 함수입니다. 2차원 배열의 경우 열(col)을 기준으로 나누게 됩니다. 
    1. array : 분할하려는 배열을 지정합니다. 
    2. indices_or_sections : 정수(N) 또는 1차원 배열(리스트)을 지정합니다. 
      • 정수(N) : 배열을 같은 크기의 N개로 분할 합니다. 
      • 배열(index) : 배열(리스트)의 인덱스 위치를 기준으로 분할합니다.
        (ex) [2, 5]를 지정하면 [:2], [2:5], [5:] 세 부분으로 분할합니다.
    • 1차원 배열 분할 : 좌/우로 데이터를 나눔 *
    • 다차원 배열 분할 : 두번째 축(axis 1)을 기준으로 분할합니다. 
    a1 = np.array([1, 2, 3, 4, 5, 6, 7, 8])
    a2 = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    a3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    
    r1 = np.hsplit(a1, 2)
    r2 = np.hsplit(a1, [2,3])
    r3 = np.hsplit(a2, 2)
    r4 = np.hsplit(a2, [1,2])
    r5 = np.hsplit(a3, 2)
    r6 = np.hsplit(a3, [1,2])
    
    print(r1)  # 결과: [array([1, 2, 3, 4]), 
               #        array([5, 6, 7, 8])]
    
    print(r2)  # 결과: [array([1, 2]), 
               #        array([3]),
               #        array([4, 5, 6, 7, 8])]
    
    print(r3)  # 결과: [array([[1],[3],[5],[7]]),
               #        array([[2],[4],[6],[8]])]
    
    print(r4)  # 결과: [array([[1],[3],[5],[7]]),
               #        array([[2],[4],[6],[8]]),
               #        array([], shape=(4, 0), dtype=int64)]
    
    print(r5)  # 결과: [array([[[1, 2]], [[5, 6]]]),
               #        array([[[3, 4]], [[7, 8]]])]
    
    print(r6)  # 결과: [array([[[1, 2]], [[5, 6]]]),
               #        array([[[3, 4]], [[7, 8]]]),
               #        array([], shape=(2, 0, 2), dtype=int64)]
    


    3) .split(array, indices_or_sections, axis=0)

    배열을 특정 축(axis)을 기준으로 분할하는 함수입니다. 
    1. array : 분할하려는 배열을 지정합니다.  
    2. indices_or_sections : 정수(N) 또는 1차원 배열(리스트)을 지정합니다. 
      • 정수(N) : 배열을 같은 크기의 N개로 분할 합니다. 
      • 배열(index) : 배열(리스트)의 인덱스 위치를 기준으로 분할합니다.
        ex) [2, 5]를 지정하면 [:2], [2:5], [5:] 세 부분으로 분할합니다.
    3. axis : 배열 분할 시 기준이 되는 축(axis)을 지정합니다. (기본값 : 0)
      • axis=0인 경우 : vsplit()과 유사 (1차원 배열은 결과가 다름. 주의!)
      • axis=1인 경우 : hsplit()과 유사 (1차원 배열은 결과가 다름. 주의!)

    [axis=0 인 경우]

    a1 = np.array([1, 2, 3, 4, 5, 6, 7, 8])
    a2 = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    a3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    
    r1 = np.split(a1, 2, axis=0)
    r2 = np.split(a1, [2,3], axis=0)
    r3 = np.split(a2, 2, axis=0)
    r4 = np.split(a2, [2,3], axis=0)
    r5 = np.split(a3, 2, axis=0)
    r6 = np.split(a3, [1,2], axis=0)
    
    print(r1)  # 결과: [array([1, 2, 3, 4]), 
               #        array([5, 6, 7, 8])]
    
    print(r2)  # 결과: [array([1, 2]), 
               #        array([3]), 
               #        array([4, 5, 6, 7, 8])]
    
    print(r3)  # 결과: [array([[1, 2],[3, 4]]),
               #        array([[5, 6],[7, 8]])]
    
    print(r4)  # 결과: [array([[1, 2],[3, 4]]),
               #        array([[5, 6]]),
               #        array([[7, 8]])]
    
    print(r5)  # 결과: [array([[[1, 2],[3, 4]]]),
               #        array([[[5, 6],[7, 8]]])]
    
    print(r6)  # 결과: [array([[[1, 2],[3, 4]]]),
               #        array([[[5, 6],[7, 8]]]),
               #        array([], shape=(0, 2, 2), dtype=int64)]

    [axis=1 인 경우]

    a1 = np.array([1, 2, 3, 4, 5, 6, 7, 8])
    a2 = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    a3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
    
    # r1 = np.split(a1, 2, axis=1)      # Error (axis=1 없음)
    # r2 = np.split(a1, [2,3], axis=1)  # Error (axis=1 없음)
    r3 = np.split(a2, 2, axis=1)
    r4 = np.split(a2, [1,2], axis=1)
    r5 = np.split(a3, 2, axis=1)
    r6 = np.split(a3, [1,2], axis=1)
    
    print(r3)  # 결과: [array([[1],[3],[5],[7]]), 
               #        array([[2],[4],[6],[8]])]
    
    print(r4)  # 결과: [array([[1],[3],[5],[7]]),
               #        array([[2],[4],[6],[8]]),
               #        array([], shape=(4, 0), dtype=int64)]
    
    print(r5)  # 결과: [array([[[1, 2]],[[5, 6]]]),
               #        array([[[3, 4]],[[7, 8]]])]
    
    print(r6)  # 결과: [array([[[1, 2]],[[5, 6]]]),
               #        array([[[3, 4]],[[7, 8]]]),
               #        array([], shape=(2, 0, 2), dtype=int64)]
    


    04. 마치며

    배열 조작은 데이터 전처리의 80%를 차지합니다. 위에서 살펴본 모양 변경(reshape), 병합(concatenate), 분할(split) 함수들은 복잡한 데이터를 필요에 맞게 다듬는 필수 도구들이므로 잘 알아둘 필요가 있습니다. 

    또한, 뷰(view)와 복사본(copy)의 차이를 명확히 이해하고 사용한다면, 원본 데이터를 실수로 손상시키는 일을 막을 수 있습니다. 


    Previous Post
    No Comment
    Add Comment
    comment url