NumPy란?
NumPy는 Numeric Python이라는 뜻입니다. 이름처럼 고성능 수치 연산에 효율적으로 사용 가능한 모듈로 배열과 데이터 집계, 난수 생성 등 여러 유용한 함수를 제공합니다.
NumPy 배열만들기
numpy의 배열은 기본 파이썬 list보다 더 간편하고 빠르게 사용할 수 있습니다. 단, numpy 배열에서 주의할 점은 자료형이 같은 item만 넣을 수 있다는 것입니다.
1차원 배열을 만들기 위해서 numpy 패키지를 import하고 배열을 만들어 보겠습니다.
import numpy as np
numbers = np.array(range(1, 11), copy=True)
numbers2 = np.array([2, 4, 6, 8, 10])
print(numbers)
print(numbers2)
결과를 보면 list와 같은 것으로 보이지만, 실제로 type(numbers)를 실행해보면 다르다는 것을 알 수 있습니다.
numpy로 생성한 배열은 <class ‘numpy.ndarray’>라고 출력됩니다.
참고로 numpy 배열 안에 있는 데이터 타입이나 배열 차원의 개수, 모양을 알고 싶다면 아래와 같이 dtype, ndim, shape를 사용할 수 있습니다.
만약에 배열을 만들 때, 빈 배열이나, 0, 1 로 된 배열을 생성하고 싶다면 아래의 코드와 같이 empty(), ones(), zeros() 함수를 사용하여 생성할 수 있습니다.
ones_arr = np.ones([2, 4])
zeros_arr = np.zeros([2, 4])
empty_arr = np.empty([2, 4])
항등행렬을 만들기 위해서는 eye()라는 함수를 사용합니다.
numpy에는 일정한 간격으로 배열을 생성해주는 arange() 함수를 제공합니다. 사용할 때는 numpy.arange(start, stop, step, dtype=None)으로 작성하시면 됩니다.
zero_to_five_arr = np.arange(6)
print(zero_to_five_arr)
# 결과: [0 1 2 3 4 5]
배열의 복사
대부분의 numpy 연산은 배열의 복제가 아니라 뷰(View)를 반환하는 것입니다. SQL에서도 View에 대해서 다룬 적이 있었는데, 즉 View를 반환하는 것이라는 말은 대상 객체를 변경하면 배열 데이터 역시 변경된다는 것을 의미합니다. 따라서 값만 복사하고 배열을 복제하고자 할 때는 copy를 사용하는 것이 좋습니다. 물론, 데이터가 엄청 크다면 다시 생각해볼 문제이긴 합니다.
이 부분을 예제와 함께 확인해 보겠습니다.
먼저 예시 배열을 생성하고 arr_cp에 복사해 주겠습니다. 그 후 arr_cp의 [0]번째 인덱스 값을 7로 변경하겠습니다. 그러면 아래의 결과처럼 arr과 arr_cp가 둘 다 변경되었음을 확인할 수 있습니다.
arr = np.array([1, 2, 3, 4, 5])
arr_cp = arr
arr_cp[0] = 7
그렇다면 이제 값은 같지만 주소는 다른 배열로 복사하고 싶다면 copy()를 이용해서 복사해줍니다.
arr = np.array([1, 2, 3, 4, 5])
arr_cp = arr.copy()
arr_cp[0] = 7
이렇게 하면 arr_cp의 값을 변경해도 arr 과 분리되었기 때문에 arr의 값은 그대로 남아있게 됩니다. 따라서 앞으로 배열을 복사하게 된다면 어떤 방식으로 복사하는 것이 필요한지 판단하셔서 사용하시면 됩니다.
배열 간의 연산, 배열과 스칼라의 연산
numpy 배열에서는 일반적인 list와는 다르게 벡터화 연산을 지원합니다. 이 때 벡터화 연산은 비교 연산, 논리 연산을 포함한 모든 종류의 수학 연산에 대해 적용됩니다.
- +, - 연산자를 이용하여 array간의 덧셈과 뺄셈 연산 가능
arr1 = np.array([1, 2, 1, 2, 1])
arr2 = np.array([3, 1, 3, 1, 3])
add_arr = arr1 + arr2
print(add_arr)
# 결과: [4 3 4 3 4]
- *, / 연산자를 이용하여 array간의 곱셈과 나눗셈 연산 가능
arr1 = np.array([1, 2, 3, 4, 5])
arr2 = np.array([6, 7, 8, 9, 10])
mul_arr = arr1 * arr2
print(mul_arr)
div_arr = arr1 / arr2
print(div_arr)
# 결과
# [ 6 14 24 36 50]
# [0.16666667 0.28571429 0.375 0.44444444 0.5 ]
이 때의 결과는 요소간의 곱하기와 나누기가 진행됩니다.
- 배열과 스칼라의 곱, 합, 거듭제곱 연산 등이 가능
arr = np.array([1, 2, 3, 4, 5])
add_arr = arr + 1
print(add_arr)
mul_arr = arr * 2
print(mul_arr)
times_arr = arr ** 2
print(times_arr)
다차원 배열 생성과 형태 변경하기
이번에는 2차원 배열을 생성해보고 배열의 현태를 변형해 보도록 하겠습니다.
먼저 아래와 2차원 배열을 생성하는 것은 list의 list 형태를 이용하면 2차원 배열을 생성할 수 있습니다.
dim2_arr = np.array([[0, 1, 2], [3, 4, 5]])
꼭 2차원 배열로 생성하지 않더라도, reshape() 함수를 사용해서 배열의 모양을 바꿔줄 수도 있습니다.
company = np.array(["samsung", "apple", "dell", "lenovo", "LG", "HP"])
company2 = company.reshape(2, 3)
company2
# 결과
# array([['samsung', 'apple', 'dell'],
# ['lenovo', 'LG', 'HP']], dtype='<U7')
reshape() 함수를 마침 사용했으니 3차원 행렬도 생성해볼 수 있을 것입니다.
company = np.array(["samsung", "apple", "dell", "lenovo", "LG", "HP", "intel", "asus"])
company3 = company.reshape(2, 4, 1)
company3
# 결과
# array([[['samsung', 'apple'],
# ['dell', 'lenovo']],
#
# [['LG', 'HP'],
# ['intel', 'asus']]], dtype='<U7')
행렬을 쉽게 전치행렬로 만드는 방법도 있습니다. 아까 만들었던 company2 를 전치행렬로 만들기 위해서 .T 만작성해주시면 됩니다.
데이터 형태를 확인하면서 차원 이해하기
넘파이 배열을 생성해보면서 다차원 배열이 어떤 형태인지 확인해보겠습니다.
아래와 같이 코드를 적어서 1차원 배열을 생성하고 shape 함수를 사용해서 데이터 형태를 확인해봅니다.
# 차원 확인
d1 = np.array([1, 2, 3, 4, 5])
print(d1.shape)
결과값으로 (5, ) 가 출력됩니다.
그 다음으로 1차원 배열 d1이 4개가 쌓여있는 2차원 형태면 어떤 모습이 될까요?
d2 = np.array([d1, d1, d1, d1])
print(d2.shape)
배열이 (4 x 5)의 2차원 형태가 되면서 shape 함수의 결과값으로 (4, 5)가 출력되었습니다. 실제로 d2를 출력해보면 위와 같은 형태가 나옵니다.
이제 2차원 배열 d2를 3개를 쌓아서 3차원 형태로 만들어보겠습니다. shape 함수를 실행했을때 결과값이 어떻게 변할지 생각해보시기 바랍니다.
d3 = np.array([d2, d2, d2])
print(d3.shape)
결과로 (3, 4, 5)가 출력되었습니다. 데이터 형태와 차원에 대한 이해는 중요하므로 직접 확인해보시면 좋을것 같습니다.
배열의 인덱싱과 슬라이싱
사실 일차원 배열의 인덱싱은 list의 인덱싱과 같습니다. 예를 들어 아래와 같은 배열을 만들었을 경우를 보겠습니다.
arr = np.arange(10)
# 인덱싱
print(arr[3]) # 결과 3
# 슬라이싱
print(arr[1:4]) # 결과 [1 2 3]
다차원 배열에 대해서 접근할 때도 어렵지 않습니다.
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 2행 1열 원소
print(arr_2d[2, 1]) # 결과 8
# 2행의 모든 열
print(arr_2d[2, :]) # 결과 [7 8 9]
추가적으로 주어진 조건에 대한 bool반환값을 나열한 배열을 mask라고 합니다. 아래의 예로 확인해보겠습니다.
# 학생 이름
name_array = np.array(["Lee", "Kim", "Park", "Cho", "Kwon", "Choi"])
# 각각 학생의 국어, 영어, 수학 성적
score_array = np.array([[100, 50, 60],
[90, 95, 88],
[75, 30, 20],
[60, 70, 80],
[10, 88, 39],
[60, 67, 80]])
# 국어 성적이 80점 이상인 학생의 이름을 출력
# 국어 성적이 80점 이상인 mask 생성
mask = (score_array[:, 0]>80)
# mask 출력
print(mask)
# masking 결과 출력
print(name_array[mask])
# 결과
# [ True True False False False False]
# ['Lee' 'Kim']
유니버설 함수 정리
- 사칙 연산: add(), multiply(), negative(), exp(), log(), sqrt()
- 삼각 함수: sin(), cos(), hypot()
- 비트 단위: bitwise_and(), left_shift()
- 관계형, 논리: less(), logical_not(), equal()
- maximum()과 minimum()
- 부동소수점에 적용할 수 있는 함수: isinf(), infinite(), floor(), isnan()
벡터의 내적
벡터의 내적은 가중합(weighted sum)을 계산할 때 쓰일 수 있습니다. 예시를 보면서 이해해보도록 하겠습니다.
삼성전자, 셀트리온, 카카오로 포트폴리오를 구성하려고 할 때, 각 종목의 가격은 80,000원, 250,000원, 140,000원입니다. 이 때 삼성전자는 100주, 셀트리온은 20주, 카카오는 50주로 구성하기 위한 총 매수금액을 구하려면 각 종목의 가격 벡터와 수량 벡터를 내적하여 계산하면 됩니다.
price = np.array([80000, 250000, 140000])
num = np.array([100, 20, 50])
price @ num
'[Python] 연습' 카테고리의 다른 글
텐서플로로 간단한 머신러닝 실습하기 (0) | 2022.07.11 |
---|---|
[Python] NumPy 배열 인덱싱, 슬라이싱 연습 (0) | 2022.07.07 |
[Python] enumerate () 함수 (0) | 2022.06.30 |
Mac에서 Python venv 가상환경 생성 (0) | 2022.06.09 |
[Python] 지역변수, 전역변수, 클로저(Closure) (0) | 2022.05.28 |