4.6.14 분할 함수 array_split()

① np.array_split() 개요

[비유로 이해하기: 유연한 사탕 나누기] np.split은 공평하게 똑같이 나누어 떨어지지 않으면 에러를 뿜지만, np.array_split은 에러 없이 최대한 비슷하게 눈치껏 나눠줍니다. (예: 사탕 7개를 3명에게 나누면 3개, 2개, 2개로 앞쪽부터 남은 자투리를 배분합니다.) 실전 데이터는 갯수가 딱 떨어지지 않는 경우가 흔하므로 훨씬 안전한 분할법입니다.

함수 np.array_split(ary) 인자인 ary를 여러 개의 부분 배열로 분리한다. 반환 값은 분리한 배열의 목록(list)이다. 이미 알아본 np.split()와의 차이는 두 번째 인자가 정수인 경우, 등분이 안 되더라도 오류가 발생하지 않고 분할이 가능하다.

numpy.array_split(ary, indices_or_sections, axis=0): indices_or_sections가 축을 동일하게 나누지 않는 정수가 될 경우, 오류가 발생하지 않고 마지막 남은 배열을 부분 배열로 반환한다. l개의 섹션으로 분할되어야 하는 길이가 len인 배열의 경우, len % l 크기의 하위 배열 len // l + 1개와 나머지 크기 len // l을 반환함.

  • ary: 분할 대상 배열
  • indices_or_sections: 분할할 수나 첨자 구간
  • axis=0: 기본은 0이며, 분할하는 축

다음 9개 원소로 구성된 벡터 x가 있다.

import numpy as np

x = np.arange(9.0)
x

출력:

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

배열 x는 원소가 9개이므로 2등분은 안 된다. 그러나 다음 코드로 배열 x를 2개로 나눌 수 있다.

np.array_split(x, 2)

출력:

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

3등분은 가능하다. 다음 코드는 배열 x를 3개로 분할한다.

np.array_split(x, 3)

출력:

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

4등분은 안 되나 다음 코드로 배열 x를 4개로 나눌 수 있다.

np.array_split(x, 4)

출력:

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

또한, 5등분은 안 되나 다음 코드는 배열 x를 5개로 나눌 수 있다.

np.array_split(x, 5)

출력:

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

다음 코드의 첨자 목록 [2, 4, 8]로 배열 x를 4개로 분할할 수 있다.

np.array_split(x, [2, 4, 8])

출력:

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

② 2차원 np.array_split() 분할

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

import numpy as np

a = np.arange(35).reshape(7, 5)
a

출력:

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, 32, 33, 34]])

배열 x를 수직으로 2개로 나누면 다음과 같다.

np.array_split(a, 2, axis=0)

출력:

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

배열 x를 수평으로 2개로 나누면 다음과 같다.

np.array_split(a, 2, axis=1)

출력:

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

배열 x를 수직으로 4개로 나누면 다음과 같다.

np.array_split(a, 4, axis=0)

출력:

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

배열 x를 수평으로 4개로 나누면 다음과 같다.

np.array_split(a, 4, axis=1)

출력:

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

다음으로 수직으로 첨자 [2, 4]로 슬라이싱한 3개의 배열의 목록을 반환한다.

np.array_split(a, [2, 4], axis=0)

출력:

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

다음으로 수평으로 첨자 [2, 4]로 슬라이싱한 3개의 배열의 목록을 반환한다.

np.array_split(a, [2, 4], axis=1)

출력:

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

③ 3차원 이상의 axis=2로 배열 분할

np.array_split(a, axis=2): 3차원에서 axis는 0, 1, 2를 사용할 수 있다. 일반적으로 3차원에서 axis=2는 깊이(depth) 축이라고 한다. numpy에서 표시된 2차원 배열이 여러 개 쌓은 3차원 배열(다음의 왼쪽 그림)에서 보면 깊이 축인 axis=2는 다음의 검은색 가로 방향의 축이다. 3차원으로 상상하면 오른쪽 그림으로 이해하면 쉽다.

음 왼쪽 그림은 모양 (2, 2, 4)인 3차원 배열을 나타내며, 오른쪽 그림은 모양 (3, 3, 4)인 3차원 배열을 표시한다. 일반적으로 오른쪽 모양의 3차원 배열을 아래 방향으로 펼쳐서 2차원 배열 형태를 여러 개 그리면 왼쪽 그림처럼 그려진다. 4차원 이상으로 가도 같은 절차에 따라 상상하면 이해하기 쉽다.

3차원에서 축 0, 1, 2

다음 모양 (2, 2, 6)의 3차원 배열 x가 있다. 즉, 2행 6열의 배열 2개가 있는 3차원 배열이다.

import numpy as np

x = np.arange(24).reshape(2, 2, 6)
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]]])

다음 코드는 깊이 축 axis=2로 배열을 4개로 나눈다.

np.array_split(x, 4, axis=2)

출력:

[array([[[ 0,  1],
         [ 6,  7]],

        [[12, 13],
         [18, 19]]]),
 array([[[ 2,  3],
         [ 8,  9]],

        [[14, 15],
         [20, 21]]]),
 array([[[ 4],
         [10]],

        [[16],
         [22]]]),
 array([[[ 5],
         [11]],

        [[17],
         [23]]])]

배열 x의 모양이 (2, 2, 6)이므로 깊이 축의 길이는 마지막 값인 6이다. 그러므로 4등분은 되지 않는다. 다음 코드와 같이 array_split()에서 첨자 목록 [2, 4, 5]를 사용하고 axis=2로 지정하면 위 결과와 같다.

np.split(x, [2, 4, 5], axis=2)

출력:

[array([[[ 0,  1],
         [ 6,  7]],

        [[12, 13],
         [18, 19]]]),
 array([[[ 2,  3],
         [ 8,  9]],

        [[14, 15],
         [20, 21]]]),
 array([[[ 4],
         [10]],

        [[16],
         [22]]]),
 array([[[ 5],
         [11]],

        [[17],
         [23]]])]

위에서 분할된 마지막 배열은 다음 슬라이싱 코드로도 확인할 수 있다.

x[:, :, 5:6]

출력:

array([[[ 5],
        [11]],

       [[17],
        [23]]])

④ np.dsplit()

함수 np.dsplit(a)는 배열 a를 무조건 axis=2로만 분리하는 함수이다. 그러므로 3차원 이상의 배열에만 사용이 가능하다. 함수 np.hsplit(a)는 배열 a를 무조건 axis=1로만 분리하며 함수 np.vsplit(a)는 배열 a를 무조건 axis=0으로만 분리한다.

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

import numpy as np
x = np.arange(24).reshape(2, 2, 6)
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]]])

다음 코드로 깊이 축으로 2개를 분할한다.

np.dsplit(x, 2)

출력:

[array([[[ 0,  1,  2],
         [ 6,  7,  8]],

        [[12, 13, 14],
         [18, 19, 20]]]),
 array([[[ 3,  4,  5],
         [ 9, 10, 11]],

        [[15, 16, 17],
         [21, 22, 23]]])]

위 예의 2분할은 등분이 되므로 위 코드는 다음 코드 np.split(x, 2, axis=2)로도 가능하다.

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

출력:

[array([[[ 0,  1,  2],
         [ 6,  7,  8]],

        [[12, 13, 14],
         [18, 19, 20]]]),
 array([[[ 3,  4,  5],
         [ 9, 10, 11]],

        [[15, 16, 17],
         [21, 22, 23]]])]

다음 코드로 깊이 축으로 3개로 분할한다.

np.dsplit(x, 3)

출력:

[array([[[ 0,  1],
         [ 6,  7]],

        [[12, 13],
         [18, 19]]]),
 array([[[ 2,  3],
         [ 8,  9]],

        [[14, 15],
         [20, 21]]]),
 array([[[ 4,  5],
         [10, 11]],

        [[16, 17],
         [22, 23]]])]

배열 x의 모양이 (2, 2, 6)이므로 깊이 축의 길이는 마지막 값인 6이다. 그러므로 4등분은 되지 않으므로 오류가 발생한다.

np.dsplit(x, 4)

오류:

ValueError: array split does not result in an equal division

다음 코드와 같이 np.array_split()에서 첨자 목록 [2, 4, 5]를 사용하면 가능하다.

np.dsplit(x, [2, 4, 5])

출력:

[array([[[ 0,  1],
         [ 6,  7]],

        [[12, 13],
         [18, 19]]]),
 array([[[ 2,  3],
         [ 8,  9]],

        [[14, 15],
         [20, 21]]]),
 array([[[ 4],
         [10]],

        [[16],
         [22]]]),
 array([[[ 5],
         [11]],

        [[17],
         [23]]])]

위에서 분할된 세 번째 배열은 다음 슬라이싱 코드로도 확인할 수 있다.

x[:, :, 4:5]

출력:

array([[[ 4],
        [10]],

       [[16],
        [22]]])
서브목차