[python] 파이썬 Numpy Array
파이썬 Numpy Array에 대해서
Numpy Array
NumPy - 행렬 연산을 위한 핵심 라이브러리
Numpy Array - 배열 처리
numpy array의 형태변환
- reshape(), View를 생성하는 개념, 데이터 공유(원본과 뷰)
- ravel(), View를 생성 다차원 -> 1차원 numpy array 변환
- resize(), 새로운 배열이 생성, 원소의 개수가 달라도 변환이 가능
- 원본을 변환 또는 새로운 배열 생성 두가지 방법이 있다.
numpy array의 .shape을 조절해 보자.
- shape을 쓰면 -> x차원과 배열의 형태, 원소의 개수를 알 수 있다.
import numpy as np # numpay array 생성 arr = np.arange(0,12,1) # 0~12(0~11까지 표시됨)까지 1씩 증가하는 print(arr) # 결과값 : [ 0 1 2 3 4 5 6 7 8 9 10 11] print(arr.shape) # 결과값 : (12,) #원소가 하나이기 때문에 1차원 배열이다. # 튜플로 되어 있음 arr1 = arr.reshape(4,3) # 원래 arr에서 형태를 4행 3열로 변환시키자. print(arr1) # 결과값 : [[ 0 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]] # ※ reshape할 때 조심해야 할 점은, 새로 생성된 numpy배열이 view로 생성된다. # 다시 말해, 진짜 새로운 배열이 만들어 지는게 아니라 보이는 형태만 나타낸다. # 즉, 하나의 데이터(arr)를 공유한다. 예시는 아래와 같다. arr[0] = 100 # arr원본 데이터 0을 100으로 바꾸면 arr과 arr1의 데이터 둘다 바뀐다. 왜? 데이터를 공유하니깐 print(arr) # 결과값 : [100 1 2 3 4 5 6 7 8 9 10 11] print(arr1) # 결과값 : [[100 1 2] [ 3 4 5] [ 6 7 8] [ 9 10 11]] # 위와 같은 형태를 View라고 한다. # View는 원본 배열과 데이터를 공유하기 때문에 # 둘중 하나가 바뀌면 원본이 변하든, View가 변하든 둘다 데이터에 영향을 미친다.
if arr1.base is arr: # .base는 만약 view일 경우 원본데이터를 어디서 땡겼니? 라는 함수
print("데이터가 같아요!")
else:
print("데이터가 달라요!")
# 결과값 : 데이터가 같아요!
.reshape을 하고 원본 데이터를 공유하고 싶지 않을 때
import numpy as np
arr = np.arange(0,12,1)
arr2 = arr.reshape(2,6).copy() # 해당 원본데이터(arr)에 copy()를 써서 새로운 데이터를 만든다.
# 새로운 배열(데이터 공유하지 않는다.) # view가 아니다.
print(arr)
# 결과값 : [ 0 1 2 3 4 5 6 7 8 9 10 11]
print(arr2)
# 결과값 :
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
arr[0] = 100
print(arr)
# 결과값 : [100 1 2 3 4 5 6 7 8 9 10 11]
print(arr2)
# 결과값 :
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
reshape에 대해서 +@
- 자동 행과 열 계산
- 차원의 원소개수를 -1로 지정하면 배열의 전체 원소 개수와 확장된 차원 크기를 기반으로 계산을 통해서 배열을 생성.
import numpy as np arr = np.arange(0,12,1) #요소가 12개인 numpay array #0,1,2,3,4,5,6,7,8,9,10,11 1씩증가. arr1 = arr.reshape(-1,4) #행은 아직 정해지지 않았는데 열은 '4열로 구성해'라는 의미. #그럼 결과적으로 자동 계산되어 3행이 자동(auto)으로 결정된다. arr1 = arr.reshape(4,-1) #열은 아직 정해지지 않았는데 행은 '4행으로 구성해'라는 의미. #그럼 결과적으로 자동 계산되어 3열이 자동(auto)으로 결정된다.
다차원배열을 1차원 배열로 변환 .ravle()
import numpy as np
arr2 = np.random.randint(0,12,(3,4)) #균등분포로 되어있는 난수 추출, 2차원 형태의 3행 4열짜리
print(arr2)
# 결과값 :
[[ 4 0 3 3]
[11 7 5 8]
[ 8 0 1 5]]
arr3 = arr2.ravel() #ravle() 다차원함수를 1차원 함수로 데이터를 추출
print(arr3) # 데이터를 공유하는 view 형태로 출력
# 결과값 : [ 4 0 3 3 11 7 5 8 8 0 1 5]
arr3 = arr2.ravel().copy() #ravle() 다차원함수를 1차원 함수로 데이터를 추출
print(arr3) # copy()를 써서 데이터를 공유하지 않는 view 형태로 출력
# 결과값 : [ 4 0 3 3 11 7 5 8 8 0 1 5]
reshape에 대해서 +@
- .resize()는 .reshape()과 유사
- 2가지 방법으로 변환할 수 있다. 원본변경 방법, view로 리턴 방법
import numpy as np arr = np.arange(0,12,1) print(arr) # 결과값 : [ 0 1 2 3 4 5 6 7 8 9 10 11] #arr1 = arr.reshape(3,5) # arr의 12개요소에서 3행 5열은 못 만든다. 요소 개수가 틀려 에러가 난다. arr2 = arr.resize(2,6) # resize로 shape을 변경 print(arr2) # 결과값 : None # 형태를 바꾼 후 원본 데이터로 바꾼다. print(arr) # 그래서 원본인 arr의 데이터가 1차원에서 2차원으로 변경 됐다. 결과값 : [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11]] # 배열 resize()하면 원본을 변경, return은 None arr = np.arange(0,12,1) arr2 = np.resize(arr,(2,6)) #resize로 shape을 변경 print(arr2) #이런 경우 view로 리턴되고 원본이 바꾸지 않는다. # 결과값 : [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11]] print(arr) # 결과값 : [ 0 1 2 3 4 5 6 7 8 9 10 11] # 원본을 변경할 것인지 혹은 # 원본 변함없이 view를 return 받을 것이지 선택 해야한다. arr = np.arange(0,12,1) arr.resize(3,5) # arr의 12개 요소를 가지고 3행 5열의 15개 요소로 만들 수 있다. print(arr) # 모자른 원소는 0을 채워 넣는다. # 결과값 : [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 0 0 0]] arr.resize(2,2) # 크기가 작을때는 앞에것을 남기고 뒤에것 나머지를 버린다. print(arr) # 결과값 : [[0 1] [2 3]]
numpy array의 결합
- vstack(), hstack() : 두개의 array를 결합
- numpy array 연결(2개의 array를 행, 열 방향으로 연결)(두가지 방법으로 연결시킬 수 있다.)
- 행 방향으로 붙이고 싶다면 열의 개수가 같아야한다. .vstack()
- 열 방향으로 붙이고 깊다면 행의 개수가 같아야한다. .hstack() ``` import numpy as np
arr1 = np.array([[1,2,3], [4,5,6]]) # 2행 3열짜리 2차원 배열 arr2 = np.array([[1,1,1], [2,2,2], [3,3,3], [4,4,4]]) # 4행 3열짜리 2차원 배열 print(arr1) # 결과값 : [[1 2 3] [4 5 6]]
print(arr2) # 결과값 : [[1 1 1] [2 2 2] [3 3 3] [4 4 4]]
# 두개의 배열을 연결하려고 하는데 # 행 방향(수직방향)으로 연결하려면 두 배열의 열의 개수가 같아야한다. 대신 행의 개수는 상관 없다. result = np.vstack((arr1,arr2)) # 2개의 인자를 받아서 튜플형태로 print(result) # 결과로 새로운 배열이 return 된다.(view가 아니다.) # 결과값 : [[1 2 3] [4 5 6] [1 1 1] [2 2 2] [3 3 3] [4 4 4]]
arr1 = np.array([[1,2,3], [4,5,6]]) # 2행 3열짜리 2차원 배열 arr2 = np.array([[9,9,9,9], [9,9,9,9]]) # 2행 4열짜리 2차원 배열
# 두개의 배열을 연결하려고 하는데 # 열 방향(수평방향)으로 연결하려면 두 배열의 행의 개수가 같아야한다. 대신 열의 개수는 상관 없다. result = np.hstack((arr1,arr2)) # 2개의 인자를 받아서 튜플형태로 print(result) # 결과값 : [[1 2 3 9 9 9 9] [4 5 6 9 9 9 9]] ```
numpy array의 참조¶
- indexing : 원하는 요소만 가져오기
- slicing : slicing해서 가져온 게 view를 생성(새로운 배열이 생성되는게 아니다 X)
- boolean indexing(True/False 값중 True 값만 추출)
- index 배열을 이용해서 참조 : fancy indexing
1차원 numpy array의 indexing, slicing
import numpy as np
arr = np.arange(10,20,1)
for item in (arr): # 요소 값이 나온다.
# ex) 10, 11, 12, 13 ~~ (값만 나옴)
print("값 : {}".format(item))
# 결과값 :
값 : 10
값 : 11
값 : 12
값 : 13
값 : 14
값 : 15
값 : 16
값 : 17
값 : 18
값 : 19
for idx,item in enumerate(arr): #enmerate를 쓰면 인덱스값과 요소값이 같이 나온다.
#ex) 0 10, 1 11, 2 12, 3 13 ~~ (튜플형태로 나온다.)
print("인덱스 : {}, 값 : {}".format(idx,item))
# 결과값 :
인덱스 : 0, 값 : 10
인덱스 : 1, 값 : 11
인덱스 : 2, 값 : 12
인덱스 : 3, 값 : 13
인덱스 : 4, 값 : 14
인덱스 : 5, 값 : 15
인덱스 : 6, 값 : 16
인덱스 : 7, 값 : 17
인덱스 : 8, 값 : 18
인덱스 : 9, 값 : 19
import numpy as np
arr = np.arange(10,20,1)
print(arr)
# 결과값 : [10 11 12 13 14 15 16 17 18 19]
print(arr[0])
# 인덱싱 결과값 : 10
print(arr[0:3])
# 슬라이싱 결과값 : [10 11 12] # view 형태로 만든다.
import numpy as np
arr = np.arange(10,20,1)
print(arr)
# 결과값 : [10 11 12 13 14 15 16 17 18 19]
tmp = arr[0:3]
print(tmp)
# 결과값 : [10 11 12]
tmp[0] = 1000 # 0번째 인덱스값의 요소를 1000으로 변경
print(tmp)
# 결과값 : [1000 11 12]
print(arr) # numpy array의 slicing은 view를 생성
# 결과값 : [1000 11 12 13 14 15 16 17 18 19]
import numpy as np
arr = np.arange(10,20,1)
print(arr[1:-1]) # 결과값: [11 12 13 14 15 16 17 18]
print(arr[0:-1]) # 결과값: [10 11 12 13 14 15 16 17 18]
print(arr[0:-1:2]) # 결과값: [10 12 14 16 18] # 0부터 -1까지 2칸씩 가라
print(arr[::3]) # 결과값: [10 13 16 19] # 처음부터 끝까지 3칸식
print(arr[::-1]) # 결과값: [19 18 17 16 15 14 13 12 11 10] #배열 역순처리 #reverse 효과를지닌다.
2차원 numpy array의 대한 slicing
import numpy as np
arr = np.array([[1,2,3],
[4,5,6],
[7,8,9]])
print(arr[0,0]) #numpy array에서는 콤마를 기준으로 행열을 나눔
# 결과값 : 1
print(arr[:,0])
# 결과값 : [1 4 7]
print(arr[:,0:2])
# 결과값 :
[[1 2]
[4 5]
[7 8]]
print(arr[1:,:2])
# 결과값 :
[[4 5]
[7 8]]
# 참고로 print(arr[0][0]) #python list에서는 []기준으로 행열을 나눔
mask와 boolean indexing
- numpy는 기본적으로 boolean indexing을 지원
import numpy as np np.random.seed(1) # random값을 seed함수를 써서 값이 고정된다. arr = np.random.randint(0,10,(10,)) print(arr) # 결과값 : [5 8 9 5 0 0 1 7 6 9] print(arr+1) # 이항연산 가능 # 결과값 : [ 6 9 10 6 1 1 2 8 7 10] print(arr % 2 == 0) #논리연산 가능 그래서 결과값은 True 또는 False가 나온다. # 결과값 : [False True False False True True False False True False] tmp = (arr % 2 == 0) # boolean mask print(arr[tmp]) # True 해당 요소만 뽑는다. # boolean indexing # 결과값 : [8 0 0 6]
Fancy indexing
- 배열의 index배열을 전달해서 배열요소를 참조하는 방식
import numpy as np np.random.seed(0) arr = np.random.randint(0,10,(4,5)) # 0~9까지 숫자를 이용해 4행 5열 형태로 print(arr) # 결과값 : [[5 0 3 3 7] [9 3 5 2 4] [7 6 8 8 1] [6 7 7 8 1]] print(arr[0,[1,3]]) # 열과 행 부분에 배열이 들어가고 그 안에 index가 들어간다. # 결과값 : [0,3] print(arr[3,[0,1]]) # 결과값 : [6 7]
(정리) 배열 사용 시 총 4가지 방식
- indexing
- slicing
- boolean index
- fancy indexing(True/False 중 True값만 추출)
numpy array 정리
- numpy array의 형태변환
- reshape(), View를 생성하는 개념
- ravel(), View를 생성 다차원 -> 1차원 numpy array 변환
- resize(), 새로운 배열이 생성, 원소의 개수가 달라도 변환이 가능
- 원본을 변환 또는 새로운 배열 생성 두가지 방법이 있다.
- numpy array의 결합
- vstack(), hstack() : 두개의 array를 결합
- numpy array의 참조
- indexing : 원하는 요소만 가져오기
- slicing : slicing해서 가져온 게 view를 생성(새로운 배열이 생성되는게 아니다 X)
- boolean indexing(True/False 값중 True 값만 추출)
- index 배열을 이용해서 참조 : fancy indexing
numpy array의 이항연산( 사칙연산 + - * / )
- 배열의 shape가 같아야 이항연산이 가능하다.
import numpy as np np.random.seed(0) # 랜덤값 고정 arr1 = np.random.randint(0,10,(2,3)) # 0~9까지의 임의의수의 2행 3열 arr2 = np.random.randint(0,10,(2,3)) # 0~9까지의 임의의수의 2행 3열 print(arr1) # 결과값 : [[5 0 3] [3 7 9]] print(arr2) # 결과값 : [[3 5 2] [4 7 6]] print(arr1 + arr2) # 해당 인덱스 값끼리 덧셈한다. # 5 0 3 + 3 5 2 = 8 5 5 # 3 7 9 + 4 7 6 = 7 14 15 # 결과값 : [[ 8 5 5] [ 7 14 15]] # 즉 shape이 같아야 사칙연산이 가능하다.
numpy array의 행열곱(dot product) .dot()
- ex) (1)2행3열과 (2)3행2열의 곱 = (1)의 열 숫자와 (2)의 행 숫자가 같아야함 = 결과는 2행 2열로 나온다.
import numpy as np np.random.seed(0) # 랜덤값 고정 arr1 = np.random.randint(0,10,(2,3)) #0~9까지의 임의의수의 2행 3열 arr2 = np.random.randint(0,10,(2,3)) #0~9까지의 임의의수의 2행 3열 arr3 = np.random.randint(0,10,(3,2)) #0~9까지의 임의의수의 3행 3열 print(arr1) # 결과값 : [[5 0 3] [3 7 9]] print(arr2) # 결과값 : [[3 5 2] [4 7 6]] print(arr3) # 결과값 : [[8 8] [1 6] [7 7]] print(np.dot(arr1,arr3)) # 5 0 3 8 8 # 3 7 9 행렬곱 1 6 (1)2행3열과 (2)3행2열의 곱 # 7 7 = (1)의 열 숫자와 (2)의 행 숫자가 같아야함 = 결과는 2행 2열로 나온다. # 결과값 : [[ 61 61] [ 94 129]]
numpy array의 브로드캐스팅 작업이 일어나면 이항연산(사칙연산 + - * / 가능)이 가능해진다.
- (1)4행2열의 2차원 배열과 (2)1행2열의 1차원 배열을 행열곱하면
- 자동적으로 (2) 1차원 배열이 2차원 배열로 된다.
#[[1 2], [1 1] 1 2 1 1 # [3 4], 작업 -> 3 4 1 1 브로드캐스팅 작업이 이루어 진다. # [5 6], 5 6 1 1 # [7 8]] 7 8 1 1 import numpy as np arr1 = np.array([[1,2,3], [4,5,6]]) arr2 = np.array([7,8,9]) print(arr1) # 결과값 : [[1 2 3] [4 5 6]] print(arr2) # 결과값 : [7 8 9] print(arr1+arr2) #broadcasting이 일어나고 연산이 가능해진다. #(2)자동으로 형태를 만들 수 있으면 브로드캐스팅이 일어나고 안되면 안된다. # 2행3열과 1행2열의 경우는 자동으로 형태를 만들 수 없기 때문에 브로드캐스팅 작업 안일어남 # 결과값 : [[ 7 16 27] [28 40 54]]
두개의 배열이 내용이 같은지 전체비교하는 함수
- np.array_equal(N,M)
import numpy as np arr1 = np.arange(10) arr2 = np.arange(10) print(arr1) # 결과값 : [0 1 2 3 4 5 6 7 8 9] print(arr2) # 결과값 : [0 1 2 3 4 5 6 7 8 9] if arr1 is arr2: print("두개가 같다.") else: print("두개가 다르다.") # 결과값 : 두개가 다르다. # 객체 arr1, arr2로 다르게 만들었기 때문에 배열값은 똑같지만 객체는 다르다. print(np.array_equal(arr1,arr2)) # 배열안의 내용 비교 # 결과값 : True
numpy array의 1차원 집계함수(계산하는 함수)
- .sum() # 합
- .mean() # 평균
- .max() # 최대값
- .min() # 최소값
import numpy as np np.random.seed(0) arr = np.random.randint(0,10,(5,)) # 0~9까지 난수 정수형 5개 뽑기 print(arr) # 결과값 : [5 0 3 3 7] # 두가지로 출력 가능 print(arr.sum()) # 결과값: 18 # 배열에 대해 sum해도 되고 print(np.sum(arr)) # 결과값: 18 # np에 sum이 있는데 배열(arr)로 해도 된다. print(arr.mean()) # 결과값: 3.6 # 평균 print(np.mean(arr)) # 결과값: 3.6 print(arr.max()) # 결과값 : 7 # 최대값 print(np.max(arr)) # 결과값 : 7 print(arr.min()) # 결과값 : 0 # 최소값 print(np.min(arr)) # 결과값 : 0
numpy array의 2차원 집계함수와 축(axis)에 대해
- 1차원 배열로 결과값이 나온다.
import numpy as np np.random.seed(0) arr = np.random.randint(0,10,(5,)) # 0~9까지 난수 정수형 5개 뽑기 print(arr) # 결과값 : [5 0 3 3 7] arr = np.random.randint(0,10,(3,2)) print(arr) # 결과값 : [[9 3] [5 2] [4 7]] print(arr.sum()) # 만약 sum의 괄호안에 axis= 가 없으면 None으로 간주된다. # 결과값: 30 # 연산의 대상이 배열 전체가 된다. # 2차원 배열에서 axis=0 -> 행방향 # 2차원 배열에서 axis=1 -> 열방향 print(arr.sum(axis=0)) #결과적으로 행방향으로 값은 두개가 나오며 1차원 배열로 나온다. # 결과값 : [18 12] print(arr.sum(axis=1)) #결과적으로 열방향으로 값은 세개가 나오며 1차원 배열로 나온다. # 결과값 : [12 7 11] # <참고> # 1차원 배열에서는 axis=0 만 존재하며 -> 열 방향을 뜻한다. # 2차원 배열에서 axis가 x,y 2개의 축이 존재 # axis=0 -> 행 방향 # axis=1 -> 열 방향 # 3차원 배열에서 axis가 x,y,z 3개의 축이 존재 # axis=0 -> 깊이 # axis=1 -> 행 방향 # axis=2 -> 열 방향
Mask활용
import numpy as np
arr = np.array([True,False,True,False,False])
print(arr) # 결과값 : [ True False True False False]
print(arr.sum()) # 결과값 : 2 -> True=1, False=0 이렇게 계산해서 결과값이 2는 True가 2개다라는 의미
# 배열안에 True가 몇개있는지 개수를 계산할때 사용한다.
arr = np.random.normal(3,1,(4,5)) # 평균=3, 표준편차=1, 4행5열의 20개 추출을 의미
(arr > 3.0).sum() # 만들어진 배열안에 3.0초과하는 수를 True, False로 싹다 변경 후 .sum()
# 결과값 : 10 # 조건을 만족하는 개수를 count할 때 사용
정렬관련 함수 2가지
- numpy.sort(배열,axis=n) : 원본은 변화 X, 정렬된 결과가 return된다.
- 배열.sort() : 원본은 정렬 됨, return = None
import numpy as np np.random.seed(0) arr = np.random.randint(0,10,(3,3)) print("원본출력") print(arr) # 결과값 : [[5 0 3] [3 7 9] [3 5 2]] print(arr.sort()) # axis를 명시하지 않으면 axis=-1로 지정된다. -1 = 마지막 축을 의미 # 결과값 : None # axis=1을 의미 # 원본이 변한다. 열 방향으로 오름차순 정렬 print("배열 열방향 정렬 출력") print(arr) # 결과값 : [[0 3 5] [3 7 9] [2 3 5]] print(arr.sort(axis=0)) # 원본이 변한다. 행 방향으로 오름차순 정렬 # 결과값 : None print("배열 행방향 정렬 출력") print(arr) # 결과값 : [[0 3 5] [2 3 5] [3 7 9]]
중복된거 배제하고 유니크한 것만 추출
- .unique() 함수
- 중복된거 배제하고 유니크한것만 추출
import numpy as np np.random.seed(0) arr = np.random.randint(0,9,(7,)) print(arr) # 결과값 : [5 0 3 3 7 3 5] np.unique(arr) # 결과값 : [0 3 5 7] # 유일한 값만 추출
References
개발자님들 덕분에 많이 배울 수 있었습니다. 감사의 말씀 드립니다.