안녕하세요. 지난 포스팅의 넘파이 알고 쓰자 - concatenate에 이어서 다른 합치는 함수들에 대해서 알아보도록 하겠습니다. 혹시, 이전 포스팅을 보지 않으신 분은 꼭!! 보고 와주시는 것을 추천드립니다. 오늘 알아볼 함수들 전부 유사한 방식이기 때문에 보고 오시면 굉장히 수월하게 이해하실 수 있습니다.
1. np.stack(arrays, axis=0)
이 함수는 concatenate 함수에 비해서 제약이 많은 함수입니다. concatenate 함수는 합칠 axis를 제외한 shape 값을 제외하고 일치하면 되지만 stack 함수는 그런거 필요없이 합치려는 배열들의 shape이 전부 동일해야합니다.
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)
# array([[1, 2],
# [3, 4],
# [5, 6]])
np.stack((a, b), axis=0)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-21-0d830c0a0dbb> in <module>
----> 1 np.stack((a, b), axis=0)
2 # array([[1, 2],
3 # [3, 4],
4 # [5, 6]])
<__array_function__ internals> in stack(*args, **kwargs)
~/anaconda3/lib/python3.7/site-packages/numpy/core/shape_base.py in stack(arrays, axis, out)
424 shapes = {arr.shape for arr in arrays}
425 if len(shapes) != 1:
--> 426 raise ValueError('all input arrays must have the same shape')
427
428 result_ndim = arrays[0].ndim + 1
ValueError: all input arrays must have the same shape
그래서 shape이 동일하지 않은 두 배열 a, b를 이용해서 stack 함수를 호출하면 오류가 발생합니다. 그에 반해 concatenate는 오류가 발생하지 않고 이전 포스팅과 동일한 결과를 주게 됩니다.
이제 동일한 shape을 가지는 두 배열 a, b를 선언해서 비교해보도록 하겠습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6], [7, 8]]) # b.shape=(2, 2)
np.concatenate((a, b), axis=0) # shape=(4, 2)
# array([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
np.stack((a, b), axis=0) # shape=(2, 2, 2)
# array([[[1, 2],
# [3, 4]],
# [[5, 6],
# [7, 8]]])
오류는 발생하지 않았지만 concatenate 함수와 stack 함수의 결과를 비교해보면 결과 shape부터 다른 것을 알 수 있습니다. 즉, 기능이 다르다는 것이죠. stack 함수는 지정한 axis를 완전히 새로운 axis로 생각하게 됩니다. 그 다음에 합치는 것이라고 볼 수 있습니다. 실제로 stack 함수를 이용해서 합치게 되면 합치기 전 배열 a, b에는 없던 새로운 축 axis=2가 생기는 것을 볼 수 있습니다. 이를 단계적으로 그림으로 설명하면 아래와 같습니다.
먼저, concatenate 함수를 보도록 하겠습니다. 이전 포스팅과 완전히 동일합니다!!
그 다음으로 stack 함수를 보도록 하겠습니다.
concatenate 함수와 비교해보면 1개의 과정이 추가되었습니다. 바로 reshape 과정이죠. reshape은 지정한 axis의 shape을 1로 설정하게 됩니다. 따라서 위의 예제의 경우에는 reshape을 (1, 2, 2)로 하게 되겠죠. 이제 이것을 axis=0 방향으로 합칩니다!! 그러면 shape이 (2, 2, 2)로 변하겠죠? 그냥 concatenate 함수는 이 reshape 과정이 없기 때문에 차원이 증가하지 않고 단순히 (4, 2)로 끝났습니다. 하지만 stack 함수는 새로운 축을 생성하고 연결하기 때문에 3차원 배열을 얻을 수 있는 것입니다!! 실제로 reshape를 적용해서 concatenate 함수를 적용하면 그냥 stack 함수를 적용한 것과 동일한 결과를 얻을 수 있습니다.
a_re = a.reshape((1, 2, 2))
b_re = b.reshape((1, 2, 2))
np.concatenate((a_re, b_re), axis=0) # shape=(2, 2, 2)
# array([[[1, 2],
# [3, 4]],
# [[5, 6],
# [7, 8]]])
np.stack((a, b), axis=0) # shape=(2, 2, 2)
# array([[[1, 2],
# [3, 4]],
# [[5, 6],
# [7, 8]]])
이번에는 다른 축으로 stack 함수를 적용해보도록 하겠습니다.
np.stack((a, b), axis=1) # shape=(2, 2, 2)
# array([[[1, 2],
# [5, 6]],
# [[3, 4],
# [7, 8]]])
a_re = a.reshape((2, 1, 2))
b_re = b.reshape((2, 1, 2))
np.concatenate((a_re, b_re), axis=1) # shape=(2, 2, 2)
# array([[[1, 2],
# [5, 6]],
# [[3, 4],
# [7, 8]]])
np.stack((a, b), axis=2) # shape=(2, 2, 2)
# array([[[1, 5],
# [2, 6]],
# [[3, 7],
# [4, 8]]])
a_re = a.reshape((2, 2, 1))
b_re = b.reshape((2, 2, 1))
np.concatenate((a_re, b_re), axis=2) # shape=(2, 2, 2)
# array([[[1, 5],
# [2, 6]],
# [[3, 7],
# [4, 8]]])
위 코드를 각각 그림으로 표현하면 아래와 같습니다.
전부 동일하게 합치려는 axis는 1로 바꾸는 reshape을 적용한 뒤 axis 방향으로 합치는 모습입니다.
concatenate 함수와 stack 함수의 가장 큰 차이점은 stack 함수는 ndim을 1개 넘어서도 배열을 합칠 수 있다는 점입니다. 이것이 가능한 이유는 위에서 계속 언급한 것처럼 새로운 축을 하나 생성하기 때문에 ndim이 1 증가하게 됩니다. 따라서 차원의 개수를 넘어서는 방향에서 합치더라도 오류가 발생하지 않습니다. 예를 들어 reshape을 하지 않고 concatenate 함수를 적용해본 것과 그냥 stack 함수를 적용한 것의 차이를 보도록 하겠습니다.
np.stack((a, b), axis=2) # shape=(2, 2, 2)
# array([[[1, 5],
# [2, 6]],
# [[3, 7],
# [4, 8]]])
np.concatenate((a, b), axis=2)
---------------------------------------------------------------------------
AxisError Traceback (most recent call last)
<ipython-input-78-e6394ba58c95> in <module>
----> 1 np.concatenate((a, b), axis=2)
<__array_function__ internals> in concatenate(*args, **kwargs)
AxisError: axis 2 is out of bounds for array of dimension 2
axis = 2 방향으로 합치려고 했지만 concatenate는 차원의 개수를 넘어서서 합치지 못하고 오류를 발생하는 것을 볼 수 있습니다.
2. np.hstack(tup), np.r_[a, b]
다음 함수는 두 배열을 가로로 이어붙이는 함수입니다. 그래서 horizontal stack이라고 부릅니다. 아래의 코드를 보도록 하겠습니다.
a = np.array((1,2,3))
b = np.array((4,5,6))
np.hstack((a,b)) # shape=(6,)
# array([1, 2, 3, 4, 5, 6])
코드를 보면 두 벡터 a, b를 그냥 가로로 이어주는 것을 볼 수 있습니다. 이번에는 열벡터를 hstack 함수를 적용해보겠습니다.
a = np.array([[1],[2],[3]])
b = np.array([[2],[3],[4]])
np.hstack((a,b))# shape=(3, 2)
# array([[1, 2],
# [2, 3],
# [3, 4]])
이번에도 동일하게 가로로 연결하는 것을 볼 수 있습니다. 이를 간단하게 그림으로 표현할 수 있습니다.
다들 눈치 채셨겠지만 이 함수 역시 concatenate 함수를 이용해서 표현할 수 있습니다.
a = np.array([[1],[2],[3]])
b = np.array([[2],[3],[4]])
np.concatenate((a,b), axis=1)# shape=(3, 2)
# array([[1, 2],
# [2, 3],
# [3, 4]])
concatenate 함수를 axis=1 방향에서 합치는 것과 동일한 결과를 얻을 수 있습니다. 또한 행렬을 이용하더라도 똑같은 것을 볼 수 있습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6], [7, 8]]) # b.shape=(2, 2)
np.hstack((a,b)).shape# shape=(2, 4)
# array([[1, 2, 5, 6],
# [3, 4, 7, 8]])
np.concatenate((a,b), axis=1)# shape=(2, 4)
# array([[1, 2, 5, 6],
# [3, 4, 7, 8]])
3. np.vstack(tup)
이 함수는 hstack 함수와 마찬가지로 두 배열을 이어주는 함수입니다. 다만!! 그 합치는 방향이 이번에는 axis=0로 고정된 것입니다.
a = np.array([1, 2, 3])
b = np.array([2, 3, 4])
np.vstack((a,b))
# array([[1, 2, 3],
# [2, 3, 4]])
이 역시 concatenate 함수를 이용해서 axis=0으로 설정하면 쉽게 얻을 수 있습니다. 그림은 생략하도록 하겠습니다. 이번에는 행렬 예제를 보도록 하죠.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6], [7, 8]]) # b.shape=(2, 2)
np.vstack((a,b))# shape=(4, 2)
# array([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
np.concatenate((a,b), axis=0)# shape=(4, 2)
# array([[1, 2],
# [3, 4],
# [5, 6],
# [7, 8]])
4. np.dstack(tup)
이 함수 역시 어떤 방향이 고정되어 합치는 함수입니다. 그 방향은 axis=ndim입니다. 이때, 여러분들이 아셔야할 것은 배열의 axis는 항상 ndim-1만 쓸 수 있다는 점입니다. 하지만 저희는 axis를 ndim까지 쓸 수 있는 한가지 함수를 배웠습니다. 바로 stack 함수입니다. 즉, dstack 함수는 stack 함수의 방향이 고정된 버전이라고 보면 될 거 같습니다.
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.dstack((a, b)) # shape=(1, 3, 2)
# array([[[1, 4],
# [2, 5],
# [3, 6]]])
np.stack((a, b), axis=-1) # shape=(1, 3, 2)
# array([[1, 4],
# [2, 5],
# [3, 6]])
여기 보시는 것처럼 dstack을 쓰나, stack을 쓰나 동일할 결과를 얻을 수 있습니다. 이때 axis=-1은 마지막 axis를 의미합니다. 이 경우에는 axis=-1은 axis=1과 동일한 의미로 쓰인 것입니다.
a = np.array([[1], [2], [3]])
b = np.array([[4], [5], [6]])
np.dstack((a,b)) # shape=(3, 1, 2)
# array([[[1, 4]],
# [[2, 5]],
# [[3, 6]]])
np.stack((a,b), axis=-1) # shape=(3, 1, 2)
# array([[[1, 4]],
# [[2, 5]],
# [[3, 6]]])
열벡터에서도 동일한 결과를 얻습니다. 마지막으로 행렬에 적용해보도록 하겠습니다.
a = np.array([[1, 2], [3, 4]]) # a.shape=(2, 2)
b = np.array([[5, 6], [7, 8]]) # b.shape=(2, 2)
np.dstack((a,b)).shape# shape=(2, 2, 2)
# array([[[1, 5],
# [2, 6]],
# [[3, 7],
# [4, 8]]])
np.stack((a,b), axis=-1)# shape=(2, 2, 2)
# array([[[1, 5],
# [2, 6]],
# [[3, 7],
# [4, 8]]])
4. np.column_stack(tup)
마지막으로 소개할 합치는 함수는 np.column_stack입니다. 이 함수는 행 벡터가 주어지면 열(axis=1) 방향으로 합치는 함수입니다. 만약 합치는 배열의 2차원 배열이라면 np.concatenate 함수를 이용해서 표현할 수 있습니다.
먼저, 1차원 배열을 보도록 하겠습니다.
a = np.array([1,2,3])
b = np.array([4,5,6])
np.column_stack((a,b)) # shape=(3, 2)
# array([[1, 4],
# [2, 5],
# [3, 6]])
그 다음으로 2차원 배열을 보도록 하겠습니다. 이때, np.concatenate 함수와 비교까지 해보도록하겠습니다.
a = np.array([[1,2,3],[4, 5, 6]])
b = np.array([[7,8,9], [10, 11, 12]])
np.column_stack((a,b)) # shape=(2, 6)
# array([[ 1, 2, 3, 7, 8, 9],
# [ 4, 5, 6, 10, 11, 12]])
np.concatenate((a,b), axis=1) # shape=(2, 6)
# array([[ 1, 2, 3, 7, 8, 9],
# [ 4, 5, 6, 10, 11, 12]])
이와 같이 axis=1 방향으로 concatenate를 하게 되면 column_stack과 동일한 결과를 얻을 수 있습니다.
오늘은 이렇게 합치는 함수들에 대해서 알아보았습니다. 다음 포스팅부터는 나누는(split) 함수들에 대해서 알아보도록 하겠습니다.
'Programming > Python' 카테고리의 다른 글
넘파이 알고 쓰자 - 배열 복붙하기(Tiling) (0) | 2020.08.30 |
---|---|
넘파이 알고 쓰자 - split (0) | 2020.08.28 |
넘파이 알고 쓰자 - concatenate (1) | 2020.08.24 |
넘파이 알고 쓰자 - Random Module (0) | 2020.08.22 |
넘파이 알고 쓰자 - 브로드캐스팅 (0) | 2020.08.19 |