안녕하세요. 지난 포스팅 넘파이 알고 쓰자 - split 에서는 넘파이 객체를 다양한 방식으로 쪼개는 방법에 대해서 알아보았습니다. 오늘은 1개의 배열이 있을 때 그 배열을 복사하고 붙혀넣는 방법에 대해서 알아보도록 하겠습니다. 일전에 배운 concatenate, stack과 유사한 기능처럼 보이지만 자세히 보면 전혀다른 기능입니다.
1. numpy.tile(A, reps)
"A"는 임의의 shape을 가진 배열입니다. "reps"는 조금 이해하기 어렵습니다. 간단하게 설명하게 이 인자는 어떤식으로 A 배열을 복사해서 붙혀넣을 것인지에 대한 인자입니다. 튜플이나 리스트를 넘겨주면 됩니다. 직접 예제를 통해서 보도록 하겠습니다. 먼저, 제일 간단한 1차원 배열부터 보도록 하겠습니다.
a = np.array([0, 1, 2])
np.tile(a, 2) # shape=(6,)
# array([0, 1, 2, 0, 1, 2])
배열 a를 선언합니다. 그 다음 tile 함수를 이용해서 reps=2로 넘겨주었습니다. 그 결과 a 배열의 오른쪽으로 2번 연속으로 써진것을 얻을 수 있습니다. 즉, a를 복사해서 오른쪽으로 연결하여 하나의 배열로 만든것이군요! 다만 a 배열의 ndim과 tile 함수를 적용한 배열의 ndim은 바뀌지 않았습니다. 이번에는 reps=(1, 2)로 넘겨 ndim=2로 만들어보았습니다.
np.tile(a, (1, 2)) # shape=(1, 6)
# array([[0, 1, 2, 0, 1, 2]])
차이가 보이시나요? 반환된 배열의 ndim이 2로 변하였습니다. 하지만 결과 자체를 유사합니다. a 배열의 오른쪽으로 2번 연속으로 써진거 같은 모양이기 때문입니다. 이번에는 동일한 ndim=2이지만 reps=(2, 1)로 넘겨보도록 하겠습니다.
np.tile(a, (2, 1)) # shape=(2, 3)
# array([[0, 1, 2],
# [0, 1, 2]])
위의 예제를 통해서 알 수 있는 것이 있습니다. 만약, reps가 정수로 넘긴다면 반환 배열은 기존의 배열의 ndim이 유지됩니다. 하지만, 튜플의 형태로 묶어서 ndim=2로 넘겼더니 반환 배열의 ndim이 2가 되었습니다. 즉, reps의 길이 d는 결과 배열의 최종 ndim을 의미합니다. 또한 (a, b)에서 a는 axis=0 방향으로 a번 붙혀넣는 다는 것을 의미하고 b는 axis=1 방향으로 b번 붙혀넣는 다는 것과 동일한 것을 알 수 있습니다. 아래의 그림을 보면 더 쉽게 이해하실 수 있을 것입니다.
따라서 최종적으로 얻는 배열의 shape은 a 배열의 경우 (a, 3b)가 될 것입니다. 그래서 reps를 (2, 2)로 넘겨보면 shape이 (2, 6)이 되는 것을 볼 수 있습니다.
np.tile(a, (2, 2)) # shape=(2, 6)
# array([[0, 1, 2, 0, 1, 2],
# [0, 1, 2, 0, 1, 2]])
이번에는 reps을 3차원으로 넘겨보도록 하겠습니다.
np.tile(a, (2, 1, 1)) # shape=(2, 1, 3)
# array([[[0, 1, 2]],
# [[0, 1, 2]]])
np.tile(a, (1, 2, 1)) # shape=(1, 2, 3)
# array([[[0, 1, 2],
# [0, 1, 2]]])
np.tile(a, (1, 1, 2)) # shape=(1, 1, 6)
# array([[[0, 1, 2, 0, 1, 2]]])
이렇게 보니 무슨 일이 생긴건지 잘 모르겠습니다. 그림을 통해 보도록 하겠습니다.
간단하게 설명하면 먼저, reps와 a 배열 중 큰 ndim 방향의 axis로 나란히 만들어 줍니다. 이 예제에서는 reps의 ndim은 3이고 a 배열의 ndim은 1이기 때문에 axis=2로 나란히 만들어 줍니다. 그래서 위의 그림과 같이 a 배열이 axis=2 방향을 따라서 나란히 있는 것을 볼 수 있습니다. 이제부터는 똑같이 특정 방향을 따라서 붙혀주면 됩니다. 가장 왼쪽 그림에서는 reps가 (2, 1, 1)이기 때문에 axis=0 방향으로 2개를 붙히고, 중간 그림에서는 reps가 (1, 2, 1)이기 때문에 axis=1 방향으로 1개를 붙히고, 가장 오른쪽 그림에서는 reps가 (1, 1, 2)이기 때문에 axis=2 방향으로 1개 붙혀줍니다.
이번에는 reps를 (2, 2, 1), (2, 1, 2), (1, 2, 2), (2, 2, 2)로 넘기면 어떻게 될까요? 위의 그림와 100% 동일합니다. 다만 여러 방향에서 여러 번 붙혀줄 뿐이죠. 중요한 점은 마지막 axis 방향으로 나란하게 만들어준다는 거 밖에 없습니다.
np.tile(a, (2, 2, 1)).shape # shape=(2, 2, 3)
# array([[[0, 1, 2],
# [0, 1, 2]],
# [[0, 1, 2],
# [0, 1, 2]]])
np.tile(a, (2, 1, 2)) # shape=(2, 1, 6)
# array([[[0, 1, 2, 0, 1, 2]],
# [[0, 1, 2, 0, 1, 2]]])
np.tile(a, (1, 2, 2)) # shape=(1, 2, 6)
# array([[[0, 1, 2, 0, 1, 2],
# [0, 1, 2, 0, 1, 2]]])
np.tile(a, (2, 2, 2)) # shape=(2, 2, 6)
# array([[[0, 1, 2, 0, 1, 2],
# [0, 1, 2, 0, 1, 2]],
# [[0, 1, 2, 0, 1, 2],
# [0, 1, 2, 0, 1, 2]]])
아래의 그림에서 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래는 각각 reps를 (2, 2, 1), (2, 1, 2), (1, 2, 2), (2, 2, 2)로 넘겼을 때를 보여주고 있습니다.
2. numpy.repeat(a, repeats, axis=None)
repeat 함수 역시 tile 함수와 매우 유사한 기능을 하는 함수입니다. 하지만 조금 다릅니다. a는 배열, repeats은 합치는 방법, axis는 합치는 방향을 의미합니다. 이때 axis은 a 배열의 최대 axis를 넘기면 오류가 발생합니다. 예제 코드를 보고 분석해보도록 하겠습니다.
np.repeat(3, 4) # (4,)
# array([3, 3, 3, 3])
매우 간단합니다. 3이라는 스칼라를 4번 반복해서 쓴 것입니다. 무슨 그림이 필요가 없습니다!! 다음은 tile 함수의 예제에서 사용했던 배열을 넣어보도록 하겠습니다. tile 함수와 어떤 차이점이 있는 지 보면서 확인해주시길 바랍니다.
a = np.array([0, 1, 2]) # shape=(3, )
np.repeat(a, 2) # shape=(6,)
# array([0, 0, 1, 1, 2, 2])
tile 함수와 다른 결과를 줍니다. repeat 함수는 같은 자리에 연속으로 2번 쓰는 것을 볼 수 있습니다. 이번에는 a 배열을 2차원 배열로 넘겨보도록 하겠습니다.
a = np.array([[0, 1, 2], [3, 4, 5]]) # shape=(2, 3)
np.repeat(a, 2) # shape=(12,)
# array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5])
2차원 배열로 넘겼는 데 그 결과는 1차원 배열을 얻었습니다. 그리고 각 수는 2번씩 반복되어 있는 것을 볼 수 있습니다. 이번에는 axis를 1로 넘겨보겠습니다.
a = np.array([[0, 1, 2], [3, 4, 5]]) # shape=(2, 3)
np.repeat(a, 2, axis=1) # shape=(2, 6)
# array([[0, 0, 1, 1, 2, 2],
# [3, 3, 4, 4, 5, 5]])
이번에는 2차원 배열을 얻었습니다. axis=1 방향으로 각 수가 1번씩 반복되었습니다. 이제 repeats를 배열 형태로 넘겨보도록 하겠습니다. 간단하게 2차원 배열부터 시작해보죠!
np.repeat(a, (1, 2), axis=0) # shape=(3, 3)
# array([[0, 1, 2],
# [3, 4, 5],
# [3, 4, 5]])
일단 a 배열을 axis=0 방향으로 동일할 수를 2번 쓰는 건 알겠지만 repeats를 어떻게 해석해야 할까요? 이것은 "axis=0 방향으로 두번째 행만 2번 반복해서 쓴다."라고 해석하면 동일한 결과를 얻을 수 있습니다. 실제로 repeats를 (2, 2)로 쓰게 되면 단순히 2로 쓴것과 동일한 결과를 얻을 수 있습니다. 왜냐하면 (2, 2)라는 것은 "axis=0 방향으로 첫번째 행은 2번씩 쓰고, 두번째 행은 2번씩 쓴다."라고 해석할 수 있기 때문입니다.
np.repeat(a, 2, axis=0) # shape=(4, 3)
# array([[0, 1, 2],
# [0, 1, 2],
# [3, 4, 5],
# [3, 4, 5]])
np.repeat(a, (2, 2), axis=0) # shape=(4, 3)
# array([[0, 1, 2],
# [0, 1, 2],
# [3, 4, 5],
# [3, 4, 5]])
'Programming > Python' 카테고리의 다른 글
넘파이 알고 쓰자 - 넘파이 원소 재배열하기 (0) | 2020.09.03 |
---|---|
넘파이 알고 쓰자 - 넘파이의 원소 제거 및 추가(delete, add) (0) | 2020.09.01 |
넘파이 알고 쓰자 - split (0) | 2020.08.28 |
넘파이 알고 쓰자 - stack, hstack, vstack, dstack, column_stack (7) | 2020.08.26 |
넘파이 알고 쓰자 - concatenate (1) | 2020.08.24 |