안녕하세요. 지난 포스팅의 넘파이 알고 쓰자 - Random Module에서는 넘파이에서 자주 사용되는 랜덤 모듈에 대해서 알아보았습니다. 오늘 포스팅에서는 2개 이상의 넘파이 객체를 합치는 방법에 대해서 알아보겠습니다. 합치는 함수의 종류가 굉장히 많기 때문에 이번 포스팅에서는 가장 많이 쓰이는 concatenate에서 넘파이 객체 합치기의 기본 원리와 사용 방법에 대해서 설명하고 다음 포스팅에서 다른 합치는 함수들에 대해서 정리해보겠습니다.
np.concatenate((a1, a2, ...), axis=0)
Parameters
1). (a1, a2, ...) : a1, a2, ... 들은 각각 넘파이 객체입니다. 이들을 합치기 위해서는 각 객체의 합칠 axis의 shape을 제외하고 나머지 shape이 전부 동일해야합니다.
2). axis=0 : 합치는 방향입니다. ndim-1까지 정할 수 있습니다.
Outputs : 합쳐진 넘파이 배열
이 함수는 넘파이 객체들을 하나로 합칠 때 가장 많이 사용되는 함수일 거 같습니다. 예를 들어서 코드를 보도록 하겠습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6]]) # b.shape=(1, 2)
np.concatenate((a, b), axis=0)
# array([[1, 2],
# [3, 4],
# [5, 6]])
concatenate 함수를 사용하기 위한 조건을 다시 한번 상기해보도록 하겠습니다. 합치려는 모든 배열들의 shape이 합치려는 axis를 제외하고 전부 동일해야한다고 하였습니다. 이 조건을 위의 코드에 대입해보면 a의 shape은 (2, 2), b의 shape은 (1, 2)입니다. 이때, axis=0, 즉 a의 다음 행에 이어붙입니다. 따라서, axis=0은 서로 달라도 상관없지만 axis=1의 값은 동일해야합니다. 실제로 axis=1에서는 두 배열의 shape의 값이 2이기 때문에 동일합니다!! 그렇기 때문에 concatenate를 할 수 있습니다. 이를 그림으로 그려보도록 하겠습니다.
항상 말씀드렸듯이 각 행이 axis=0, 각 열이 axis=1임을 꼭 기억해주시길 바랍니다. 그림으로 보면 앞에 있던 a 배열 아래에 뒤의 b 배열을 행(axis=0) 방향으로 합치는 것을 볼 수 있습니다. 여기서 axis=0 방향의 shape 값이 굳이 동일할 필요가 없다는 것을 금방 알아채실겁니다. 어차피 행 방향으로 합치기 때문에 값이 뭐가 되는 열의 개수만 맞으면 합칠 수 있기 때문입니다.
이번에는 동일한 배열들을 이용해서 열(axis=1) 방향으로 합쳐보겠습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6]]) # b.shape=(1, 2)
np.concatenate((a, b), axis=1)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-917c07b21668> in <module>
2 b = np.array([[5, 6]]) # b.shape=(1, 2)
3
----> 4 np.concatenate((a, b), axis=1)
5 # array([[1, 2],
6 # [3, 4],
<__array_function__ internals> in concatenate(*args, **kwargs)
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 1
concatenate의 조건에 의해서 합치는 것이 불가능합니다. 왜냐하면 axis=1을 제외한 모든 axis의 shape값이 일치해야하는 데 a의 axis=0에 대한 shape 값은 2, b는 1이기 때문이죠. 이를 그림으로 그려보면 애초에 합칠 수가 없어보입니다!!
하지만 저희는 종종 열 방향으로 합쳐야할 때가 있습니다. 이를 위해서 넘파이 객체가 가지고 있는 함수인 transpose 함수를 이용하면 됩니다. transpose는 선형대수에서 중요한 연산으로 간단하게 말하면 가상의 주대각성분을 기준으로 뒤집는다고 보면 될 거 같습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6]]) # b.shape=(1, 2)
np.concatenate((a, b.T), axis=1)
# array([[1, 2, 5],
# [3, 4, 6]])
간단하게 넘파이 객체에 .T를 하면 shape이 (2, 1)로 변하게 됩니다. 이 transpose 연산은 나중에 선형 대수 모듈에서 더 자세하게 알아보도록 하겠습니다. 이제 이를 열 방향으로 합치는 것을 그림으로 그려보겠습니다.
이와 같이 매우 쉽게 열 방향으로도 합칠 수 있습니다!! 물론 애초에 axis=1의 shape이 일치하면 굳이 transpose를 취하지 않아도 concatenate를 할 수 있습니다. 하지만 아래의 예시에서는 axis=0 방향으로 연결하면 위에서 언급한 동일한 이유로 오류를 얻습니다. 이를 해결하려면? transpose 연산을 취해주면 쉽게 해결 할 수 있습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5], [6]]) # b.shape=(2, 1)
np.concatenate((a, b), axis=1)
# array([[1, 2, 5],
# [3, 4, 6]])
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5], [6]]) # b.shape=(2, 1)
np.concatenate((a, b), axis=0)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-12-4503d9b15ea3> in <module>
2 b = np.array([[5], [6]]) # b.shape=(1, 2)
3
----> 4 np.concatenate((a, b), axis=0)
5 # array([[1, 2, 5],
6 # [3, 4, 6]])
<__array_function__ internals> in concatenate(*args, **kwargs)
ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 2 and the array at index 1 has size 1
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5], [6]]) # b.shape=(2, 1)
np.concatenate((a, b.T), axis=0)
# array([[1, 2],
# [3, 4],
# [5, 6]])
이번에는 3차원 넘파이 배열에 적용해보도록 하겠습니다. a 배열의 shape은 (3, 3, 3), b 배열의 shape은 (3, 3, 1)이기 때문에 저희가 유일하게 합칠 수 있는 방향은 axis=2 방향입니다. 나머지 방향(axis=0, axis=1)은 concatenate의 조건에 의해서 오류가 나겠죠.
a = np.arange(1, 28).reshape(3, 3, 3)
b = np.arange(29, 38).reshape(3, 3, 1)
np.concatenate((a, b), axis=2)
# array([[[ 1, 2, 3, 29],
# [ 4, 5, 6, 30],
# [ 7, 8, 9, 31]],
# [[10, 11, 12, 32],
# [13, 14, 15, 33],
# [16, 17, 18, 34]],
# [[19, 20, 21, 35],
# [22, 23, 24, 36],
# [25, 26, 27, 37]]])
이번 예제는 살~짝 더 어려워졌습니다. 하지만 아직은 3차원이기 때문에 그림으로 그려보면 쉽게 이해할 수 있습니다.
이번에는 3개의 배열을 한번에 합쳐보도록 하겠습니다. 몇 개가 되는 상관없습니다. 오직 concatenate의 조건만 맞으면 함수가 실행됩니다. 하지만 살짝 꼬아보도록 하겠습니다.
a = np.arange(1, 7).reshape(3, 2)
b = np.arange(7, 10).reshape(1, 3)
c = np.arange(10, 19).reshape(3, 3)
위와 같은 배열 3개가 있다고 가정했을 때 concatenate를 하려면 어떻게 해야할까요? 저희는 2가지 방법으로 접근할 수 있습니다. 일단 위 배열은 어느 방향으로 concatenate를 하려해도 항상 합치려는 axis를 제외한 다른 axis의 shape 값이 다르기 때문에 오류가 발생합니다. 따라서 오류가 발생하지 않게 합치는 방법은 a 배열에 transpose를 취하여 axis=0 방향으로 합치거나 b 배열에 transpose를 취하여 axis=1 방향으로 합치는 방법입니다. 이때, 각 방법은 합치는 방향이 다르기 때문에 당연하지만 그 결과도 다릅니다.
먼저, axis=0 방향으로 합치는 방법입니다.
np.concatenate((a.T, b, c), axis=0)
# array([[ 1, 3, 5],
# [ 2, 4, 6],
# [ 7, 8, 9],
# [10, 11, 12],
# [13, 14, 15],
# [16, 17, 18]])
그 다음으로 axis=1 방향으로 합치는 방법입니다.
np.concatenate((a, b.T, c), axis=1)
# array([[ 1, 2, 7, 10, 11, 12],
# [ 3, 4, 8, 13, 14, 15],
# [ 5, 6, 9, 16, 17, 18]])
오늘은 배열을 합치는 concatenate 함수에 대해서 알아보았습니다. 아마도 4차원, 5차원과 같이 더 고차원 넘파이 배열을 합치려면 그림을 그려서 생각하는 것 자체가 불가능할 것입니다. 하지만, concatenate 함수를 이용해서 넘파이 배열들을 합칠 수 있냐 없냐는 항상 shape을 보고 합칠 방향(axis) 결정하면 된다는 것을 알았습니다. 다음 포스팅에서 볼 다른 넘파이 배열들을 합치는 함수들 모두 이 방식을 따라가니 꼭 숙지하시는 것을 당부드립니다.
'Programming > Python' 카테고리의 다른 글
넘파이 알고 쓰자 - split (0) | 2020.08.28 |
---|---|
넘파이 알고 쓰자 - stack, hstack, vstack, dstack, column_stack (7) | 2020.08.26 |
넘파이 알고 쓰자 - Random Module (0) | 2020.08.22 |
넘파이 알고 쓰자 - 브로드캐스팅 (0) | 2020.08.19 |
넘파이 알고 쓰자 - 넘파이 인덱싱과 슬라이싱 (0) | 2020.08.18 |