무효 클릭 IP 추적 중...
머신러닝,딥러닝/넘파이,numpy

[넘파이 기초] np.repeat , np.tile (배열 반복 | array 복사)

꼬예 2021. 5. 25.

아직도 for문을 이용해서 열과 행을 복사하시나요?

오늘 이시간에는 넘파이를 통해 손쉽게 열과 행을 복사하는 api를 배워 보도록 하겠습니다. 

 

repeat

repeat api의 파라미터로는 a, repeats, axis 3개가 있습니다.

이중 a는 우리가 복사할 값, repeats는 몇번을 반복할것인지, 마지막으로 axis 는 복사를 할때 어떤 방향으로 할지를 의미합니다. 

 

코드를 통해 자세히 알아보죠!

import numpy as np

x = 3

rep = np.repeat(x, 2) 
print(f"x: {x}")
print(f"np.repeat(x, 2): \n{rep}\n")

output :

보시는것처럼 스칼라값 3 하나를 repeat을 통해 두번 반복시키니 3이 두개있는 배열이 완성된것을 확인할 수 있습니다. 

참고로 스칼라값인 x = 3은 축의 방향 자체가 없기 때문에 axis를 이용하지 않았습니다. 

 

 

1차원 array 복사

import numpy as np

x = np.array([1, 2, 3])

rep = np.repeat(x, 3) # x를 반복시킬건데 세번을 반복시킨다는 것
print(f"x: {x}")
print(f"np.repeat(x, 3): \n{rep}\n")

output :

[1,2,3] 을 3번반복시켜보았는데 위와 같은 결과가나왔네요.

output을 보니 repeat은 각 원소들을 각각 3번씩 반복하는걸 알 수 있습니다.

 

2차원 array(with axis)

2차원 array 부터는 어떤 방향(axis)으로 연산을 할지가 중요합니다.

우선 axis = None 디폴트를 사용할때 인데요.

# with axis

import numpy as np

x = np.arange(4).reshape((2,2))

rep = np.repeat(x,3)
print(f"x: {x}")
print(f"np.repeat(x, 3): \n{rep}\n")

output :

보시는 것처럼 원래값 2차원 행렬인데도 불구하고 default 인 none값을 이용하자,

기존 shape이 사라지고 1차원 array형태가 출력이 됩니다. 

 

 

다음으로 axis = 0 즉 row 방향으로 복사를 해보겠습니다.

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")


rep = np.repeat(x,repeats=3, axis=0) # axis = 0 , row 방향으로 반복
print(f"np.repeat(x, 3, 0): \n{rep.shape}\n{rep}\n")

output :

앞서 말씀드렸듯이 repeat은 각 원소를 순서대로 반복하는데요 

output을 보시면 2차원 행렬에서 각 원소는 각 row를 의미하는 것을 알 수 있습니다.

즉 [0,1]이 첫번째 요소 [2,3]이 두번째 요소이죠.

 

그리고 각 요소들을 우리가 요청한대로 3번씩 반복한 형태입니다. 

 

 

이어서 axis =1 즉 컬럼방향으로 연산된 형태를 살펴보겠습니다. 

# with axis

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")


rep = np.repeat(x,repeats=3, axis=1) # axis = 1, column 방향으로 반복
print(f"np.repeat(x, 3, 0): \n{rep.shape}\n{rep}\n")

output :

output을 보시면 감이 오시겠지만, axis=1(컬럼방향연산)에서는 각가의 원소가 [0,2], [1,3]입니다.

즉 [0,2]를 3번반복 후 [1,3]을 3번 반복한 형태가 됩니다. 

 

 

axis 방향에따라 원소가 달라지니 주의해야겠지요?

 

지금까지는 repeats= 숫자 형태를 써보았는데요 이외에도 repeats= [숫자,숫자,.. ]와 같이 리스트에 숫자값을 넣은 형태도 있습니다. 

 

예를들어 repeats= 3 을 적게되면 각각의 원소들을 모두다같이 3번씩 반복해야합니다. 

하지만 원소별로 반복 수를 다르게 하고싶다면 어떻게할까요?

 

이때 사용하는 것이 repeats=[숫자 ,숫자, .. ] 형태입니다. 

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")

rep = np.repeat(x, repeats=[2,1], axis=0) # 해석 방법 : axis = 0방향 즉 row 방향으로 첫번째 원소는 2번, 두번째 원소는 1번 반복하라는 뜻.
print(f"repeats=[2,1]: {rep.shape}\n{rep}\n")

output :

 

우선 먼저 봐야될것은 axis 방향입니다. axis = 0이기때문에 각 원소들은 row들이 되겠군요.

x에서 첫번째 원소는 [0,1], 두번째 원소는 [2,3]입니다. 

 

이때 우리가 처음본 리스트 형태의 repeats 인데요, 

여기서 주의할것은 리스트안에 값과 그 리고 그 값이 위치한 인덱스 입니다. 

 

예를 들어 위와같은 repeats = [2,1]의 형태는 0번째 원소를 2번반복 1번째 원소를 3번 반복하라는 뜻입니다.

 

 

 

그럼 다음 예는 어떤가요?

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")

rep = np.repeat(x, repeats=[1,2], axis=0) # 해석 방법 : axis = 0방향 즉 row 방향으로 첫번째 원소는 1번, 두번째 원소는 2번 반복하라는 뜻.
print(f"repeats=[1,2]: {rep.shape}\n{rep}\n")

output :

이번에는 0번째 인덱스에 1, 1번째 인덱스에 2를 넣었음으로

[0 1]을 1번 복사 [2 3]을 2번 복사했다는 것을 알 수있네요.

 

 

이어서 axis =1 인경우도 확인해볼까요?

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")

rep = np.repeat(x, repeats=[1,2], axis=1) # 해석 방법 : axis = 1방향 즉 column 방향으로 첫번째 원소는 1번, 두번째 원소는 2번 반복하라는 뜻.
print(f"repeats=[1,2]: {rep.shape}\n{rep}\n")

output :

axis =1 일때는 각 원소들은 각 column들입니다. 즉 여기서는 첫번째 원소로 [0 2] , 두번째 원소로 [1 3]입니다. 

repeats 리스트안에 0번째 인덱스에는 1, 1번째 인덱스에는 2가 있음으로 output과 같은 형태가 되는 것을 알 수있습니다.

 

repeats = [숫자, 숫자, ..] 형태를 쓸때는 x의 shape과 방향을 확인한후 그에 맞게 작성을 해줘야 합니다.

import numpy as np

x = np.arange(4).reshape((2,2))
print(f"x: {x.shape}\n{x}")

rep = np.repeat(x, repeats=[1,2,3], axis=1) # 원소 갯수 이상으로 리스트안에 넣으면 오류남
print(f"repeats=[2,1]: {rep.shape}\n{rep}\n")

우선 axis = 1임으로 각 원소는 column, 여기서는 [0 2] [1 3] 이되겠네요

즉 두개 밖에 없습니다. 

그런데 리스트에 3개를 넣으면 이건 뭘 의미할까요?

남는 1개는 어떤 값을 반복하라는것이지? 라는 의문이 들게되는것이지요.

 

예상대로 output도 아래와 같이 "ValueError: operands could not be broadcast together with shape"를 발생합니다.

output :

 

 

2차원 row 벡터의 형태

우리가 앞서 배운 1차원 row벡터 형태가 아닌 2차원 row벡터입니다. 헷갈리지 않도록 주의하세요!

2차원이기 때문에 axis 를 통해 row방향 또는 column 방향으로 연산이 가능합니다. 

이번 예제에서는 row 방향으로 연산을 해보겠습니다 .

# row 벡터
import numpy as np

x = np.arange(4).reshape((1,-1)) # 2차원 row 벡터
print(f"x: {x.shape}\n{x}")

rep = np.repeat(x, repeats=3, axis=0) # 2차원에서는 axis = 0 은 row 방향
print(f"repeats=3: {rep.shape}\n{rep}\n")

output :

output을 보시면 충분히 이해가 되실겁니다.

만약 axis =1 연산을 하다면..

이와 같은 output이 나오겠죠.

 

 

지금까지는 예를 들어 [0 1 2]를 3번 반복한다면 [0 0 0 1 1 1 2 2 2]

와 같이 반복했었죠 그러면 [ 0 1 2 0 1 2 0 1 2]와 같이 반복을 할 수 없을까요?

이때 사용하는 것이 np.tile 입니다.

 

 

np.tile

repeat과 비슷한 형태입니다. A에는 우리가 반복할값, reps 에는 우리가 반복할 수를 나타냅니다.

parameter가 repeats가 reps로 바뀐거 이외에는 같은 형태인거 눈치 채셨죠?

 

코드를 통해 자세히 알아보겠습니다. 

 

1차원 array 연산

import numpy as np

a = np.arange(4)
print(f"ndarray: {a.shape}\n{a}\n")

tile = np.tile(a, reps=3)
print(f"reps=3: {tile.shape}\n{tile}\n")

 

output :

repeat과는 다르게 한배열 전체를 반복하는 특징을 확인할 수 있습니다. 

 

 

tile도 repeat과 마찬가지로 reps = [숫자, 숫자..] 형태를 사용할 수있는데요.

사용방법이 다르기 때문에 헷갈리지 않도록 주의하세요!

 

우선 코드를 통해 감을 잡아보겠습니다. 

import numpy as np

a = np.arange(3)
print(f"ndarray: {a.shape}\n{a}\n")

tile = np.tile(a, reps=[2, 2]) # 첫번째는 axis = 0 기준으로 2번째 axis = 1 기준으로 2번
print(f"reps=[2, 2] : {tile.shape}\n{tile}\n")

output :

output을 보시니까 감이 좀 오시나요.?

reps에서 리스트 인덱스는 axis 의 방향을 의미합니다. 

즉 인덱스 0은 axis = 0 , 1은 axis = 1을 의미하죠.

 

다시말해 위에 예시에서 reps = [2, 2]는

axis = 0(row 방향)으로 2번 반복 axis = 1(column 방향)으로 2번 반복을 하라는 뜻입니다.

 

 

이해를 돕기 위해 예를 하나 더 들어볼게요.

import numpy as np

a = np.arange(4)
print(f"ndarray: {a.shape}\n{a}\n")

tile = np.tile(a, reps=[5, 1]) 
print(f"reps=[5, 1] : {tile.shape}\n{tile}\n")

output :

axis = 0 기준으로 5번이기 때문에 row 방향으로는 5번 반복되었고  

반면 axis = 1 기준으로 1번 반복 시킨 형태입니다. 

 

일반적으로 넘파이 연산에서 위와 1차원 벡터에서는 axis가 하나 밖에 없기 때문에, axis = 0 방향 즉 row 방향으로 확장이 가능하지 않은데요. np.tile에서는 특이하게도 확장이 가능합니다.

 

 

실제로 그런지 확인해볼까요?

import numpy as np

a = np.arange(4).reshape((1, -1)) # row 벡터

tile = np.tile(a, reps=[5,1])

print(f"ndarray: {a.shape}\n{a}")
print(f"tile: {tile.shape}\n{tile}\n")

output :

1

1차원 ndarray와 2차원 ndarray가 같은 값이 도출 된것을 알 수있죠.

 

 

2차원 행렬에서의 연산

import numpy as np

a = np.arange(6).reshape((2,3))
print(f"ndarray: {a.shape}\n{a}\n")

tile = np.tile(a, reps=[1, 2]) # axis = 0 즉 row방향으로는 1번 반복, axis = 1 즉 column 방향으론 2번 반복
print(f"reps=[1, 2] : {tile.shape}\n{tile}\n")

output :

연산방법은 1차원 array와 같습니다.

row 방향으로 1번 반복, column 방향으로 2번 반복한 형태입니다. 

 

 

참고로 reps에는 리스트뿐만 아니라 넘파이 array도 넣을 수 있습니다. 

import numpy as np

a = np.arange(6).reshape((2,3))
print(f"ndarray: {a.shape}\n{a}\n")

reps = np.array([2, 1]) 
tile = np.tile(a, reps=reps)
print(f"reps= np.array([3, 5]) : {tile.shape}\n{tile}\n")

output :

 

이 글과 읽으면 좋은글

  • 트위터 공유하기
  • 페이스북 공유하기
  • 카카오톡 공유하기
이 컨텐츠가 마음에 드셨다면 커피 한잔(후원) ☕

댓글