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