< reshape() 함수와 resize() 함수 >
# reshape()와 resize()
import numpy as np
# ndarray의 shape 조절 함수
# reshape() : 원하는 형태(shape)로 ndarray의 shape를 조절
# ravel() : 1차원 vector(1차원 ndarray)로 shape를 변형
# 정수형태의 난수를 이용해서 (3,4) shape을 가지는 ndarray 생성
np.random.seed(10)
arr = np.random.randint(0, 10, (3,4))
# result = arr.resize(2,6) # error : view가 생성되는 것이 아니라 원본이 변함
# reshape() 함수는 요소의 개수가 맞지 않으면 reshape가 되지 않음
# resize() 함수는 요소의 개수가 맞지 않아도 shape 변경 가능
# 요소수가 줄어들면 기존 데이터를 버리고, 늘어나면 0으로 설정
arr.resize(3,5) # 요소수가 늘어남
print(arr)
arr.resize(2,2) # 요소수가 줄어듬
print(arr)
arr.resize(2,2)
arr.resize(3,5)
< indexing과 slicing >
# indexing & slicing
import numpy as np
arr = np.arange(10, 20, 1)
# ndarray의 각 요소를 출력하려면?
# for 문을 사용하는 방법
for tmp in arr:
print(tmp)
# enumerate를 사용하면 (index, value)의 tuple 형태로 가져옴
for (idx, tmp) in enumberate(arr):
print('인덱스 : {}, 데이터 : {}'.format(idx,tmp))
# indexing & slicing
print(arr[3]) # 13
print(arr[1:4]) # [11 12 13]
print(arr[:-1]) # [10 11 12 ... 18]
print(arr[1:-1:2) # [11 13 15 17]
# 2차원 배열 indexing & slicing
arr = np.arange(1,17,1).reshape(4,4).copy()
print(arr[1,2]) # 7
print(arr[1]) # [5 6 7 8]
print(arr[1][2]) # 7
print(arr[2,:]) # [9 10 11 12]
print(arr[1:3,:])
# [[ 5 6 7 8]
# [ 9 10 11 12]]
print(arr[1:3,:2])
# [[ 5 6]
# [ 9 10]]
< Boolean indexing & Fancy indexing >
# Boolean indexing
# boolean indexing은 ndarray 각 요소의 선택여부를 나타냄
# True, False로 구성된 boolean mask를 이용하여 지정
# boolean mask의 True에 해당하는 index 만 조회하는 방식
import num as np
np.random.seed(1)
arr = np.random.randint(0, 10, (5,))
print(arr) # [5 8 9 5 0]
print(arr % 2) # [1 0 1 1 0]
print(arr % 2 == 0) # [False True False False True] => boolean mask
# boolean indexing : boolean mask의 값이 True인 항목들만 저장
print(arr[arr % 2 == 0]) # [8 0] => boolean indexing
# Fancy indexing
# ndarray에 index 배열을 전달하여 배열요소를 참조하는 방식
arr = np.arange(0,12,1).reshape(3,4).copy()
#[[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(arr[2,2]) # indexing : 10
print(arr[1:2,2]) # slicing : [6] => slicing 이므로 배열로 나옴에 유의
print(arr[1:2,1:2]) # slicing : [[5]]
# index 배열을 이용한 indexing
# 배열의 값에 해당하는 항목한 추출
print(arr[[0,2],2] # [2 10]
print(arr[[0,2],2:3] => slicing이 있으므로 2차원 배열이 return 됨
# [[ 2]
# [10]]
# 예제 : 다음의 2차원 ndarray를 추출해보자
# [[1 3]
# [9 11]]
# Fancy indexing을 배운대로 사용하면...
# print(arr[[0,2],[1,3]]) 으로 작성할텐데 결과는 [1 11]로 나옴
# 행과열에 동시에 Fancy indexing이 적용되지 않기 때문!!!
# 해결방법 1 - fancy indexing 두 번 적용
print(arr[[0,2]][:,[1,3]])
# 해결방법 2 - NumPy가 제공하는 'ix_' 라는 이름의 함수 이용
print(arr[np.ix_([0,2],[1,3])])
< ndarray의 사칙연산과 행렬곱 >
# ndarray의 사칙연산과 행렬곱
# +, -, *, /
# 행렬곱 => dot product
import numpy as np
arr1 = np.array([[1,2,3], [4,5,6]]) # 2 x 3 ndarray
arr2 = np.array([[7,8,9], [10,11,12]]) # 2 x 3 ndarray
# python의 list에서 + 연산자는 concatenation 수행
# ndarray에서 + 연산자는 vector, matrix 연산
# ndarray의 사칙연산 기본 전제는 shape이 같아야 연산 성립
# print(arr1 + arr2) # 각각을 서로 더함
# [[ 8 10 12]
# [14 16 18]]
# 만약 연산하는 항목이 shape가 안맞는 경우, ndarray가 broadcasting을 수행
arr2 = 3
# arr1이 2 x 3 이므로 arr2도 이에 맞게 broadcasting
# [[3 3 3]
# [3 3 3]]
print(arr1 + arr2)
# [[4 5 6]
# [7 8 9]]
arr2 = np.array([[1,2,3])
# arr2를 broadcasting
# [[1 2 3]
# [1 2 3]]
print(arr1 + arr2)
# [[2 4 6]
# [5 7 9]]
arr2 = np.array([1,2])
# broadcasting은 해당 값을 복사하는 형태로 수행됨
# ex) [1 2 1 2 1 2 ...]
# 그러므로 위의 경우에는 broadcasting이 불가능
# print(arr1 + arr2) # error : 2 x 3 형태로 broadcasting 불가
## 행렬곱연산
# 두 행렬간의 행렬곱은 np.dot(), np.matmul()로 수행 가능
# np.dot(A,B)에서 A행렬의 열 vector와 B행렬의 행 vector의 size가 같아야 한다.
# broadcasting은 사칙연산에만 적용되고 dot product에서는 일어나지 않는다!!!
# 만약 크기가 다르다면, reshape(), resize() 등을 이용해서 크기를 맞추어야 한다.
arr1 = np.array([[1,2,3], [4,5,6]]) # 2 x 3 ndarray
arr2 = np.array([[7,8], [9,10], [11,12]]) # 3 x 2 ndarray
print(arr1.shape) # (2, 3)
print(arr2.shape) # (3, 2)
print(np.dot(arr1, arr2))
# [[ 58 64]
# [139 154]]
# 행렬곱 연산을 알아야 하는 이유는?
# 만약 행렬곱 연산이 없다면, matrix 연산은 같은 크기로만 연산이 수행된다.
# 행렬곱 연산을 이용해서 행렬곱 조건만 만족시키면 다양한 크기의 행렬을
# 연속적으로 이용해서 특정 작업을 수행할 수 있다.
# ML, 이미지처리 분야에서 사용
# ex) 입력 : 32 x 32 matrix (이미지파일)
# 출력 : 32 x 10 matrix (다양한 처리가 적용된 이미지)
# 행렬곱 : (32 x 32) dot (32 x 128) dot (128 x 64) dot (64 x 10) = (32 x 10)
# 이와 같이 필터 처리를 여러번 수행해서 원하는 이미지로 만들 수 있다.
< Transpose(전치행렬) >
# Transpose
# 일반적으로 전치행렬이라고 불린다.
# 원본행렬의 행은 열로, 열은 행으로 바꾼 행렬
# 전치행렬의 표현은 윗첨자로 T를 이용
import numpy as np
arr = np.array([[1,2,3], [4,5,6]]) # 2 x 3 ndarray
# 전치행렬을 속성을 이용해서 간단하게 구할 수 있다.
t_array = arr.T # 원본 데이터를 공유하는 구조
print(t_array)
# [[1 4]
# [2 5]
# [3 6]]
arr[0][0] = 100
print(t_array) # view : 원본 데이터를 공유하고 있기 때문에 값이 변경됨
arr = np.array([1,2,3,4]) # vector (1차원 ndarray)
t_array = arr.T # 1차원 vector를 transpose 해도 그대로이므로 의미가 없음
arr = np.array([[1,2,3,4]]) # 2차원 ndarray
t_array = arr.T
print(t_array)
# [[1]
# [2]
# [3]
# [4]]
< NumPy iterator >
# NumPy iterator
# 목표 : iterator를 왜 써야 하는지 알고 동작방식을 익힌다.
import numpy as np
# 1차원 ndarray를 만들어서 각 요소를 출력해보자
arr = np.array([1,2,3,4,5])
# for 문을 이용하여 각 요소에 접근하는 방법
for tmp in arr:
print(tmp, end=' ')
# iterator로 각 요소에 접근하는 방법
# 1차원 배열의 경우 flags는 c_index로 설정
it = np.nditer(arr, flags=['c_index'])
while not it.finished: # iterator가 지정하는 위치가 끝이 아닐때 까지 반복
idx = it.index # iterator가 현재 가리키는 곳의 index 숫자를 가져옴
print(arr[idx], end=' ')
it.iternext() # 다음 요소로 iterator를 이동
## 2차원 ndarray를 출력해보자
# for문을 사용하여 각 요소에 접근하는 방법
for row in range(arr.shape[0]): # shape가 tuple이므로 indexing
for col in range(arr.shape[1]):
print(arr[row, col], end=' ')
# 만약 3차원 이면? => 3중 for문이 구성되어야 함
# 차원이 높아질수록 코드처리가 힘들어진다!!
# 다차원의 경우 flags 값은 multi_index
it = np.nditer(arr.flags=['multi_index']
while not it.finished:
idx = it.multi_index # index를 tuple 형태로 가져옴( ex. (0, 2) )
print(arr[idx], end=' ')
it.iternext()
< ndarray의 비교연산 >
# ndarray의 비교연산
# 사칙연산과 마찬가지로 비교연산도 같은 index 끼리 수행
np.random.seed(0)
arr1 = np.random.randint(0,10,(2,3))
arr2 = np.random.randint(0,10,(2,3))
print(arr1)
# [[5 0 3]
# [3 7 9]]
print(arr2)
# [[3 5 2]
# [4 7 6]]
print(arr1 == arr2) # boolean mask
# [[False False False]
# [False True False]]
# 만약 2개의 ndarray가 같은 데이터를 가지고 있는지 비교할 때는 어떻게?
arr1 = np.arange(10)
arr2 = np.arange(10)
# array_equal() 이라는 함수를 이용한다.
print(np.array_equal(arr1, arr2)) # True
< NumPy 집계함수 및 axis(축) >
# NumPy 집계함수 및 axis(축)
import numpy as np
arr = np.arange(1,7,1).reshape(2,3).copy()
# [[1 2 3]
# [4 5 6]]
# for문을 이용한 누적 집계
result = 0
for row in range(arr.shape[0]):
for col in range(ar.shape[1]):
result += arr[row, col]
# numpy array에 대해서 sum() 함수를 호출하면 모든 항목 누적 가능
print(arr.sum())
# 또는 아래의 방법도 사용 가능
print(np.sum(arr))
# 누적합을 구해서 1차원 벡터로 출력 - cumsum()
print(np.cumsum(arr)) # [1 3 6 10 15 21]
# 평균 - mean()
print(np.mean(arr)) # 3.5
# 최대값 - max()
print(np.max(arr)) # 6
# 최소값 - min()
print(np.min(arr)) # 1
# 최대값의 index()
print(np.argmax(arr)) # 5 (tuple 형태로 (1, 2)로 출력되는 것이 아님에 유의!!
# 최소값의 index()
print(np.argmin(arr)) # 0
# 표준편차 - std()
print(np.std(arr)) # 1.707
# 자연상수 - exp()
print(np.exp(arr)) # arr[index] 거듭제곱 한 것들을 배열에 저장
## Numpy의 모든 집계함수는 axs를 기준으로 계산됨
# 만약 axis를 지정하지 않으면, axis는 None으로 설정됨
# None인 경우 함수의 대상범위를 전체 dnarray로 지정함
# axis의 설정방법과 효과는?
arr = np.array([1,2,3,4,5])
# 1차원은 축이 1개, 2차원은 축이 2개, 3차원은 축이 3개...
# 축(axis)은 숫자로 표현
# 1차원인 경우, axis=0 열방향(가로방향) 밖에 사용 못함
print(arr.sum(axis=0)) # 15
print(arr.sum(axis=1)) # error : axis가 1이라는 것은 적어도 배열이 2차원 이상임을 의미
# 2차원 ndarray로 axis를 조절해보자
arr = np.array([[1,2,3], [4,5,6]] # 2 x 3 ndarray
print(arr.sum()) # 21
# 2차원에서 axis=0은 행방향(세로방향)을 의미
print(arr, sum(axis=0)) # [5 7 9]
# 2차원에서 axis=1은 열방향(가로방향)을 의미
print(arr, sum(axis=1)) # [6 15]
print(arr.argmax(axis=1)) # [2 2]
# 3차원 ndarray 라면?
# axis = 0 => depth 방향
# axis = 1 => 행방향(세로방향)
# axis = 2 => 열방향(가로방향)
< NumPy 집계함수 vs for 구문 >
1. for 구문
%%time
# 맨위에 %%time을 적어주면, 현재 셀을 수행하는데 걸리는 시간을 보여준다.
# 단, 다른 내용(주석포함)을 아무것도 그 줄에 적으면 안된다.
arr = np.arange(100000, dtype=np.float64)
result = 0
for tmp in arr:
result += tmp
print(result) # 4999950000.0 , Wall time: 43 ms
2. 집계함수
%%time
arr = np.arange(100000, dtype=np.float64)
print(arr.sum()) # 4999950000.0, Wall time: 1e+03 µs
- 결론 : 엄청난 시간차이가 발생하는 것을 확인할 수 있다.
- 나중에 ML 학습할 때, 이런 연산시간을 줄여두지 않으면 학습이 매우 오래 걸릴 수 있다.
## 연습문제
# ndarray 안에 10보다 큰 수가 몇개 있는지 알려면?
import numpy as np
arr = np.arange(1,17).reshape(4,4).copy()
# Boolean mask를 이용하며, True = 1, False = 0 임을 이용한다.
print((arr > 10).sum())
< ndarray의 정렬 >
# ndarray의 정렬
# NumPy array는 axis를 기준으로 정렬하는 sort() 함수를 제공
# 만약 axis를 지정하지 않으면, -1값으로 지정 => -1은 마지막 axis(열방향)을 의미
# np.sort(arr) : 원본은 변하지 않고, 정렬된 결과 ndarray를 return
# arr.sort() : 원본을 정렬, return 값은 None
import numpy as np
arr = np.arange(10)
np.random.shuffle(arr)
print(arr) # [7 6 4 3 2 9 8 1 5 0]
# sort() 함수의 default는 오름차순 정렬
print(np.sort(arr)) # [0 1 2 3 4 5 6 7 8 9]
# slicing의 3번째 인자로 -1을 주면 역순으로 slicing을 수행
print(np.sort(arr)[::-1]) # [9 8 7 6 5 4 3 2 1 0]
# 2차원 ndarray의 정렬
arr = np.arange(1,17)
np.random.shuffle(arr)
arr = arr.reshape(4,4).copy()
print(arr)
# [[16 2 15 12]
# [10 5 1 7]
# [ 6 3 8 4]
# [11 13 9 14]]
print(np.sort(arr, axis=1)) # 2차원에서 axis=1이면 열방향(가로방향)
# [[ 2 12 15 16]
# [ 1 5 7 10]
# [ 3 4 6 8]
# [ 9 11 13 14]]
## 연습문제
# 표준정규분포에서 200개의 샘플을 추출한 후
# 내림차순으로 상위 5% 까지의 결과만 출력
arr = np.random.randn(200)
arr = np.sort(arr)[::-1]
end = int(200 * 0.05)
print(arr[0:end])
'Python > Data Analysis' 카테고리의 다른 글
Data Analysis / pandas / DataFrame(3) (0) | 2020.09.11 |
---|---|
Data Analysis / pandas / DataFrame(2) (0) | 2020.09.11 |
Data Analysis / Pandas / DataFrame(1) (0) | 2020.09.09 |
Data Analysis / Pandas / Series (0) | 2020.09.09 |
Data Analysis / NumPy / ndarray(1) (1) | 2020.09.07 |