NumPy 사용해보기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


NumPy

NumPy는 Python에서 과학 연산을 위해 사용하는 가장 기본적인 패키지 중 하나라고 한다. 다차원 배열과 행렬 연산에 필요한 다양한 함수와 메소드를 제공한다.

NumPy 설지

pip install NumPy

# 다 설치되고나면 
import numpy as np  # import 해주고
np.__version__  # 버전 확인

NumPy 배열 생성

NumPy에서 가장 기본적인 데이터 구조는 배열이다. NumPy 배열은 동일한 타입의 데이터를 담는 다차원 배열로 ndarray 클래스를 사용해 생성할 수 있다.

import numpy as np

# 1차원 배열 생성
a = np.array([1,2,3])
# 2차원 배열 생성
b = np.array([[1,2,3], [4,5,6]])
# 3차원 배열 생성
c = np.array([[[1,2], [3,4]], [[1,2], [3,4]]])

ndim

배열의 차원(Dimension)을 알기 위한 함수

a.ndim  # > 결과값: 1
b.ndim  # > 결과값: 2
c.ndim  # > 결과값: 3

shape

배열의 크기(Shape)를 확인

a.shape  # 결과값: (3,)
b.shape  # 결과값: (2,3)
c.shape  # 결과값: (2,2,2)

size

배열에 있는 총 원소의 개수

a = np.array([1,2,3])
a.size  # > 3

배열 연산

NumPy 배열은 다른 배열 또는 스칼라와의 연산을 지원한다.

스칼라: 스칼라의 어원은 라틴어 scala에서 왔다고 하며 이는 “저울” 이란 뜻이라고 한다. 즉, 어떤 양(크기)을 나타내는 개념인 것이다. 선형대수학에서의 스칼라의 정의 또한 “벡터 공간에서 벡터를 곱할 수 있는 양”이다. 쉽게 생각해서 실수가 선형대수학에선 스칼라인 것이다.

스칼라에 대해 찾아봤는데, 이 말이 그나마 제일 이해하기 쉬웠던 것 같다.(문과생 웁니다..)

결국 스칼라는 실수라는건데.. 실수는 유리수와 무리수 모든것을 말하는거니.. 이쯤..이해하고..일단 패스..

a = np.array([1,2,3])
b = np.array([4,5,6])

# 덧셋
c = a + b  # > [5,7,9]
# 곱셈
d = a * b  # > [4,10,18]
# 스칼라와 연산 
e = a + 1  # > [2,3,4]

추가로 NumPy 배열 연산 안에는 sum, mean, min, max 등로 지원해준다.

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

# 합
b = np.sum(a)  # > 6
# 평균
c = np.mean(a)  # > 2.0
# 최소값
d = np.min(a)  # > 1
# 최대값
e = np.max(a)  # > 3

### 배열 인덱싱과 슬라이싱

 a = np.array([1,2,3,4,5])

# 인뎅싱
 b = a[0]  # > 1
 c = a[3]  # > 4

# 슬라이싱 
 d = a[1:4]  # > [2,3,4]
 e = a[:3]  # > [1,2,3]
 f = a[1:]  # > [2,3,4,5]

NumPy 에서는 다차원 배열에서의 인덱싱, 슬라이싱 또한 가능하다.

a = np.array([[1,2,3], [4,5,6]])
# 위 배열의 출력값은 아래처럼 나온다
# array([[1,2,3]
#       [4,5,6]])

# 인덱싱
b = a[0, 0]  # 1
c = a[1, 2]  # 6

# 슬라이싱
d = a[0, 1:3]  # [2, 3]
e = a[:, 1]    # [2, 5]
f = a[:, :2]   # [[1, 2], [4, 5]]

(다차원 배열에서의 슬라이싱이 좀 어렵다.. 이해는 간다만..ㅎ 버퍼링이 좀 생김ㅎㅋ)

arange (start, stop, step)

정해진 step만큼 일정하게 떨어진 숫자를 배열로 반환

start의 기본값은 0, step의 기본값은 1로 직접 지정해주지 않으면 기본값을 행한다. stop 값은 반드시 전달되어야 한다!

np.arange[3]  # > [0,1,2]
np.arange[1,10,2]  # > [1,3,5,7,9]

dot

배열간의 행렬 곱셈을 배열로 반환

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

np.dot(a,b)
# array[[19 22]
#       [43 50]]

이때 궁금증은 *연산자와의 차이는 무엇일까?

  • dot은 배열간의 행렬 곱셈을 한다면 *은 요소별 곱셈을 한다.

위에서 다룬 dot의 경우 행해진 곱셈 연산은 아래와 같다.

# [[1*5 + 2*7, 1*6 + 2*8]
#  [3*5 + 4*7, 3*6+ 4*8]]

그치만 *은 아래와 같다

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

a * b
# array[[ 5 12]
#       [21 32]]

reshape

NumPy 배열의 형태를 변경

reshape을 사용하기 위해서는 기존 배열과 변환 후 배열의 원소개수가 동일해야한다. 즉 (2,4)와 (4,2)의 reshape은 가능하다. reshape 메서드를 사용하는 주 목적이 차원을 유지한 상태에서 배열의 형태를 변환하는 것이라고 한다.

# a라는 np array 생성 > 2차원 배열 
a = np.array([[1, 2], [3, 4], [5, 6]])
# array([[1, 2],
#        [3, 4],
#        [5, 6]])
a.shape  # > (3,2) 
a.ndim  # > 2

b = a.reshape(2,3)
# array([[1, 2, 3],
#        [4, 5, 6]])
b.shape  # > (2,3)
b.ndim  # > 2

tranpose

NumPy 배열의 차원을 변경하여 전치된 배열을 반환

전치: 행렬을 대각선으로 뒤집는 연산자..(뭔..)로 행렬의 행과 열 인덱스를 바꾸어 다른 행렬을 생성하는 것을 의미한다.

설명이 어려우니.. 코드로 확인해보자!

original_array = np.array([[1, 2, 3], [4, 5, 6], 7, 8, 9]])
# array([[1, 2, 3],
#        [4, 5, 6],
#        [7, 8, 9]])

transposed_array = np.transpose(original_array)
# array([[1, 4, 7],
#        [2, 5, 8],
#        [3, 6, 9]])

직접 찍어보니 알 것 같다! 행과 열을 바꾼다라는 개념..!

중요한 포인트는 tranpose 또한 reshape과 같이 배열 요소 개수가 같아야하는 것은 동일하다! 즉 2x3 배열을 전치해서 3x2 배열을 반환하는 것!

zeros, ones

  1. zeros: 모든 원소가 0인 배열을 생성
  2. ones: 모든 원소가 1인 배열을 생성
a = np.zeros((2,3))
# array[[0. 0. 0.]
#       [0. 0. 0]]

b = np.ones((2,3))
# array[[1. 1. 1.]
#       [1. 1. 1.]]

where

where은 조건에 부합하는 인덱스를 반환해주는 함수

예시를 들자면

a = np.arange(5,15)
# array([5,6,7,8,9,10,11,12,13,14])

np.where(a > 10) 
# 11~14까지의 인덱스를 반환
# array([6,7,8,9]) > 인덱스를 반환해준거임!!

linspace

지정해준 범위 내에서 균등한 간격으로 원하는 개수의 배열을 생성

a = np.linspace(0,1,5)  # [0.  0.25 0.5  0.75 1.  ]

random.random

0과 1사이의 균등분포에서 난수를 생성해 배열을 만듦. 함수의 인수로 생성할 배열의 크기를 지정 가능.

a = np.random.random(3,3)
#array([[0.5254058 , 0.39879012, 0.61469921],
#       [0.72924439, 0.10349449, 0.84673281],
#       [0.61774079, 0.71552584, 0.50629848]])

random.randn

평균이 0이고 표준편차가 1인 정규분포를 따르는 난수를 생성해 배열을 만듦. 함수의 인수로 생성할 배열의 크기를 지정 가능.

a = np.random.randn(3,3)
# array([[-1.90644415,  1.58210398, -2.03024185],
#        [ 0.07563648,  0.10644284, -0.73066284],
#        [ 0.07573001,  0.19376196, -1.18768222]])

정규분포, 표준편차.. 다 까먹었다. 나중에 따로 공부해서 정리해야겠다.

Numpy 난수 함수

난수: 정의된 범위 내에서 무작위로 추출된 수

  • seed() : 난수 발생기의 seed를 지정
  • shuffle() : 리스트나 배열의 순서를 뒤섞음
  • choice(): 배열에서 무작위로 추출
  • rand() : 균등분포에서 표본을 추출
  • randint() : 주어진 최소/최대 범위 안에서 임의의 난수를 추출
  • randn() : 표준편차가 1이고 평균값이 0인 정규분포에서 표본을 추출
  • normal() : 정규분포(가우시안)에서 표본을 추출

randn 과 normal?

두 함수 모두 정규분포에서 난수를 생성한다는 공통점이 있지만 약간의 차이점도 존재한다.

  1. randn(): 표준편차가 1이고 평균이 0인 표준정규분호에서 난수 생성
  2. normal(): 평균값과 표준편차를 직접 지정할 수 있음

따라서 randm() 함수는 normal(0,1)과 동일한 역할을 한다!

주요 유니버설 함수

  • sqrt(): 제곱근
  • log(): 자연로그
  • greater_equal(): 비교
  • logical_and(): 논리