4.6.13 분할 함수 hsplit()과 vsplit()

① np.hsplit(ary)

[비유로 이해하기: 샌드위치용 식빵 자르기] 식빵을 잘라 샌드위치를 만드는 것처럼 데이터를 옆으로(좌우 열 단위) 수직하게 자릅니다. 실무 데이터 분석에서는 거대한 2차원 표(엑셀 시트 등) 하나에서 머신러닝 입력 문제 데이터(X)정답 데이터(y)를 좌우로 분리할 때 아주 널리 사용하는 방식입니다.

배열을 수평으로 분할하는 np.hsplit(a)는 1차원 배열을 제외하면 np.split(a, axis=1)과 같다.

다음 10개 원소의 벡터 a가 있다.

a = np.arange(10)
a

출력:

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

다음 코드로 벡터를 2개의 부분 벡터로 분할한다.

np.hsplit(a, 2)

출력:

[array([0, 1, 2, 3, 4]), array([5, 6, 7, 8, 9])]

다음 코드로 배열을 a[:2], a[2:5], a[5:]의 3개 슬라이싱의 배열 목록으로 분할한다.

np.hsplit(a, [2, 5])

출력:

[array([0, 1]), array([2, 3, 4]), array([5, 6, 7, 8, 9])]

다음 모양 (2, 12)의 2차원 배열 a가 있다.

a = np.arange(24).reshape(2, 12)

배열 a를 축 1 방향으로 3개로 분리하면 12/3 = 4이므로 4개의 열로 구성된 분할 배열 목록인 다음 결과가 나온다.

np.hsplit(a, 3)

출력:

[array([[ 0,  1,  2,  3],
        [12, 13, 14, 15]]),
 array([[ 4,  5,  6,  7],
        [16, 17, 18, 19]]),
 array([[ 8,  9, 10, 11],
        [20, 21, 22, 23]])]

마찬가지로, 배열 a를 축 1 방향으로 4개로 분리하면 다음 결과가 나온다.

np.hsplit(a, 4)

출력:

[array([[ 0,  1,  2],
        [12, 13, 14]]),
 array([[ 3,  4,  5],
        [15, 16, 17]]),
 array([[ 6,  7,  8],
        [18, 19, 20]]),
 array([[ 9, 10, 11],
        [21, 22, 23]])]

그러나, 배열 a를 축 1 방향으로 5개로 분리하면 12/5가 떨어지지 않으므로 오류가 발생한다.

np.hsplit(a, 5)

오류:

ValueError: array split does not result in an equal division

참고로 np.hsplit(a, [3, 5, 8])np.split(a, [3, 5, 8], axis=1)과 같다.

np.hsplit(a, [3, 5, 8])

출력:

[array([[ 0,  1,  2],
        [12, 13, 14]]),
 array([[ 3,  4],
        [15, 16]]),
 array([[ 5,  6,  7],
        [17, 18, 19]]),
 array([[ 8,  9, 10, 11],
        [20, 21, 22, 23]])]

② np.vsplit(ary)

[비유로 이해하기: 케이크 층 떼어내기] 층이 겹겹이 쌓인 맛있는 케이크를 층별로 하나하나 떼어내는 것과 같습니다. 데이터를 위아래 수평(행 단위)으로 자릅니다. 인공지능 학습 시 수천 장의 이미지를 메모리 폭주의 우려 없이 한 번에 100장씩 잘라내서 훈련 미니 배치(Batch)로 먹여줄 때 이 분할법이 매우 유용하게 쓰입니다.

3차원 이상 배열에서 수직으로 분할하는 np.vsplit(a)np.split(a, axis=0)과 같다.

다음 10개 원소의 벡터 a가 있다.

a = np.arange(10)
a

출력:

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

다음 코드로 벡터를 수직으로 2개의 부분 벡터로 분할되지 못한다. 수직으로의 분할은 2차원 이상의 배열에서만 가능하므로 오류가 발생한다.

np.vsplit(a, 2)

오류:

ValueError: vsplit only works on arrays of 2 or more dimensions

다음 모양 (6, 3)의 2차원 배열 a가 있다.

a = np.arange(18).reshape(6, 3)
a

출력:

array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14],
       [15, 16, 17]])

배열 a를 축 0 방향으로 3개로 분리하면 6(전체 행수)/3 = 2이므로 2개의 행으로 구성된 3개의 분할 배열 목록인 다음 결과가 나온다.

np.vsplit(a, 3)

출력:

[array([[ 0,  1,  2],
        [ 3,  4,  5]]),
 array([[ 6,  7,  8],
        [ 9, 10, 11]]),
 array([[12, 13, 14],
        [15, 16, 17]])]

마찬가지로, 배열 a를 축 0 방향으로 4개로 분리하면 6(전체 행수)/4가 정수가 아니므로 오류가 발생한다.

np.vsplit(a, 4)

오류:

ValueError: array split does not result in an equal division

다음 코드는 축 0 방향의 슬라이싱 인자 (1, 4, 6)으로 a[:1, :], a[1:4, :], a[4:6, :], a[6:, :]의 슬라이싱 결과의 2차원 배열로 분할한 목록을 반환한다.

np.vsplit(a, (1, 4, 6))

출력:

[array([[ 0,  1,  2]]),
 array([[ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]]),
 array([[12, 13, 14],
        [15, 16, 17]]),
 array([], shape=(0, 3), dtype=int32)]

참고로 직접 슬라이싱 a[:1, :]의 결과는 위 결과 첫 항목과 같다.

a[:1, :]

출력:

array([[ 0,  1,  2]])

또한, 슬라이싱 a[4:6, :]의 결과는 위 세 번째 결과 항목과 같다.

a[4:6, :]

출력:

array([[12, 13, 14],
       [15, 16, 17]])

마지막 슬라이싱 코드는 빈 배열이다.

a[6:, :]

출력:

array([], shape=(0, 3), dtype=int32)

③ 3차원 배열의 hsplit()

3차원 이상에서의 hsplit()은 두 번째 축인 axis=1로 분할한다. 즉 np.hsplit(a)np.split(a, axis=1)과 같다.

다음 모양 (4, 4, 2)의 3차원 배열 x가 있다.

x = np.arange(32).reshape(4, 4, 2)
x

출력:

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]],

       [[24, 25],
        [26, 27],
        [28, 29],
        [30, 31]]])
np.hsplit(x, 2)

출력:

[array([[[ 0,  1],
         [ 2,  3]],

        [[ 8,  9],
         [10, 11]],

        [[16, 17],
         [18, 19]],

        [[24, 25],
         [26, 27]]]),
 array([[[ 4,  5],
         [ 6,  7]],

        [[12, 13],
         [14, 15]],

        [[20, 21],
         [22, 23]],

        [[28, 29],
         [30, 31]]])]

결과적으로, 위 구문은 다음 코드 np.split(x, 2, axis=1)과 같다.

np.split(x, 2, axis=1)

출력:

[array([[[ 0,  1],
         [ 2,  3]],

        [[ 8,  9],
         [10, 11]],

        [[16, 17],
         [18, 19]],

        [[24, 25],
         [26, 27]]]),
 array([[[ 4,  5],
         [ 6,  7]],

        [[12, 13],
         [14, 15]],

        [[20, 21],
         [22, 23]],

        [[28, 29],
         [30, 31]]])]

분할된 2개의 배열 중 첫 번째 배열은 다음 슬라이싱 배열과 같다.

분할된 2개의 배열 중 첫 번째 배열은 다음 슬라이싱 배열과 같다.

x[:, :2, :]

출력:

array([[[ 0,  1],
        [ 2,  3]],

       [[ 8,  9],
        [10, 11]],

       [[16, 17],
        [18, 19]],

       [[24, 25],
        [26, 27]]])

분할된 2개의 배열 중 두 번째 배열은 다음 슬라이싱 배열과 같다.

x[:, 2:, :]

출력:

array([[[ 4,  5],
        [ 6,  7]],

       [[12, 13],
        [14, 15]],

       [[20, 21],
        [22, 23]],

       [[28, 29],
        [30, 31]]])

물론, 다음 슬라이싱 구간 [1, 3]으로도 분할 가능하다.

np.hsplit(x, [1, 3])

출력:

[array([[[ 0,  1],
         [ 8,  9],
         [16, 17],
         [24, 25]]]),
 array([[[ 2,  3],
         [ 4,  5],
         [10, 11],
         [12, 13],
         [18, 19],
         [20, 21],
         [26, 27],
         [28, 29]]]),
 array([[[ 6,  7],
         [14, 15],
         [22, 23],
         [30, 31]]])]

③ 3차원 배열의 vsplit()

수직으로 분할하는 np.vsplit()은 무조건 첫 번째 축 axis=0으로 분할한다. 그러므로 np.vsplit(x)np.split(x, axis=0)과 같다.

다음 모양 (4, 4, 2)의 3차원 배열 x가 있다.

import numpy as np
x = np.arange(32).reshape(4, 4, 2)
x

출력:

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]],

       [[24, 25],
        [26, 27],
        [28, 29],
        [30, 31]]])

다음 코드로 첫 번째 축 axis=0으로 2개를 분할한다.

np.vsplit(x, 2)

출력:

[array([[[ 0,  1],
         [ 2,  3],
         [ 4,  5],
         [ 6,  7]],

        [[ 8,  9],
         [10, 11],
         [12, 13],
         [14, 15]]]),
 array([[[16, 17],
         [18, 19],
         [20, 21],
         [22, 23]],

        [[24, 25],
         [26, 27],
         [28, 29],
         [30, 31]]])]

결과적으로, 위 코드는 다음 np.split(x, 2, axis=0)과 같다.

np.split(x, 2, axis=0)

출력:

[array([[[ 0,  1],
         [ 2,  3],
         [ 4,  5],
         [ 6,  7]],

        [[ 8,  9],
         [10, 11],
         [12, 13],
         [14, 15]]]),
 array([[[16, 17],
         [18, 19],
         [20, 21],
         [22, 23]],

        [[24, 25],
         [26, 27],
         [28, 29],
         [30, 31]]])]

분할된 2개의 배열 중 첫 번째 배열은 다음 슬라이싱 배열과 같다.

x[:2, :, :]

출력:

array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5],
        [ 6,  7]],

       [[ 8,  9],
        [10, 11],
        [12, 13],
        [14, 15]]])

분할된 2개의 배열 중 두 번째 배열은 다음 슬라이싱 배열과 같다.

x[2:, :, :]

출력:

array([[[16, 17],
        [18, 19],
        [20, 21],
        [22, 23]],

       [[24, 25],
        [26, 27],
        [28, 29],
        [30, 31]]])

물론, 다음 슬라이싱 구간 [1, 3]으로도 분할 가능하다.

np.vsplit(x, [1, 3])

출력:

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

        [[16, 17],
         [18, 19],
         [20, 21],
         [22, 23]]]),
 array([[[24, 25],
         [26, 27],
         [28, 29],
         [30, 31]]])]
서브목차