데이터 분석/자료구조(Data structure)

파이썬 - Numpy 기초 정리(1)

Jerry Jun 2020. 12. 16. 17:29
728x90

데이터 사이언스 분야에서 쉽게 접하는 것이 바로 Numpy 이다. 

대체로 다차원 배열(ndarray)을 다룰 때 사용하는 모듈이기도 하다.

Numpy 를 깊게 배울 필요는 없지만 이런 기능이 있다고 알아둔다면 나중에 필요한 기능이 생각날 때 떠오를 수 있을 것이다. 

 

Numpy 는 list 보다 빠르고 적은 메모리를 사용하여 좀 더 유리하다. 그리고 수학적인 계산(선형대수학, 통계 등)을 위한 함수가 많이 있어 상황에 맞게 유용하게 사용할 수 있다.

 

1. 다차원 배열 계산

그림과 같은 행렬이 있다고 가정하자. 각 배열의 원소마다 +1 씩 더하고 싶다면 어떤 코드를 작성해야 할까?

num = [[1,2,3], [4,5,6], [7,8,9]]

for i in range(len(num)):
    for j in range(len(num[i])):
        num[i][j] += 1
print(num)
-----------------------------------
[[2, 3, 4], [5, 6, 7], [8, 9, 10]]

많은 방식으로 구할 수 있겠지만 지금의 경우는 이중 반복문을 돌려 실행하는 방법이 있을 것이다. 만약 지금은 2차원 배열이지만 3차원... 4차원... 10차원 등등 차원이 커질 경우에도 계속 반복문을 중첩시키는 것이 바람직할까? 이런 경우에도 사용할 수 있는 것이 Numpy 이다.

 

import numpy as np

num = np.array([[1,2,3], [4,5,6], [7,8,9]])
num += 1
print(num)
---------------------------------------------
[[ 2  3  4]
 [ 5  6  7]
 [ 8  9 10]]

이차원 배열을 np.array 로 선언하고 +1 만으로 계산을 완성시켰다. 이와 같은 편리한 기능을 브로드캐스팅이라고 한다.

 

이외에도 많은 메소드 기능들이 들어있다.

  • np.sum(다차원배열) : 배열 원소들의 총합
  • np.mean(다차원배열) : 배열 원소들의 전체 평균
  • np.mean(다차원배열, axis=0) : 각 열마다 원소들의 평균
  • np.mean(다차원배열, axis=1) : 각 행마다 원소들의 평균
  • 이외에도 많음.

# 배열

import numpy as np

arr = np.arange(10)
arr
------------------
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

numpy 의 배열은 range 가 아닌 arange 로 만들 수 있다. 

 

arr[1:4] = 10
arr
-------------------
array([ 0, 10, 10, 10,  4,  5,  6,  7,  8,  9])

그리고 슬라이스를 통해 상수 값을 대입하면 브로드캐스팅이 일어난다.

여기서 중요한 점은 원본 배열의 뷰(View) 라는 것이고 뷰의 변경은 원본 배열과 연동된다.

 

copy_arr = arr[1:4]
copy_arr
--------------------
array([10, 10, 10])



copy_arr[1] = 20
arr
---------------------
array([ 0, 10, 20, 10,  4,  5,  6,  7,  8,  9])

다음과 같이 원본 배열 arr 의 일부 인덱스를 불러 copy_arr 에 넣었다.

그리고 copy_arr 의 1 위치에 대해 20으로 변경하고 원본 배열 arr을 보니 20으로 변경되어 있는 것을 확인할 수 있다.

그래서 만약 원본 배열의 복사본(연동 안되는 배열)을 선언하고 싶다면 copy( ) 를 이용해야 한다.

 

 

# 색인

색인 중에 특이한 팬시 색인(fancy index)이 있다.

array([[0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [4., 4., 4., 4.],
       [5., 5., 5., 5.],
       [6., 6., 6., 6.],
       [7., 7., 7., 7.]])

위와 같이 배열이 있다고 가정하자. 이 중에 원하는 줄만 가져오고 싶을 때 사용하는 것이 팬시 색인이다.

 

arr[[6,4,2,7]]
------------------
array([[6., 6., 6., 6.],
       [4., 4., 4., 4.],
       [2., 2., 2., 2.],
       [7., 7., 7., 7.]])

다음과 같이 불러오게 되면 원하는 줄의 값들만 따로 불러와 array 를 만든다. 

 

 

# 전치

arr = np.arange(15).reshape(3,5)
arr
----------------------------------
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
       
       
arr.T
--------------------
array([[ 0,  5, 10],
       [ 1,  6, 11],
       [ 2,  7, 12],
       [ 3,  8, 13],
       [ 4,  9, 14]])

계산을 할 때 전치행렬을 쓰는 경우가 있다. 이럴 때 쓰는 것이 T(Transpose) 이다.

행렬의 내적np.dot(arr.T, arr) 로 구할 수 있게 된다.

 

 

 

# 단위행렬

np.eye(4)
-----------------------
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])

단위행렬이 필요할 때 사용하느느 것은 eye( ) 이다. 행과 열이 항상 같아야 하므로 필요한 파라미터는 한 개이다.

 

 

 

# random

(1) rand : 0 과 1 사이의 랜덤한 ndarray 가 생성된다.

np.random.rand(3,3)
-----------------------
array([[0.88499933, 0.56020496, 0.7523721 ],
       [0.38443715, 0.28231815, 0.42155364],
       [0.48028346, 0.06235042, 0.50708997]])

 

 

(2) randn : rand 함수에서 더 나아간 함수이다. n 은 정규분포라는 뜻으로 정규분포의 랜덤 ndarray 가 생성된다.

np.random.randn(3,3)
--------------------
array([[ 0.49133662, -0.40500377,  0.52077561],
       [ 0.18060573, -1.20984827, -2.4968032 ],
       [ 1.70030765, -0.19023195, -0.35498264]])

 

 

(3) randint : 출력되는 숫자가 정수값으로 나오게 된다.

np.random.randint(1,10, size = (3,3))
-------------------------------------
array([[7, 3, 6],
       [6, 5, 5],
       [8, 1, 8]])

 

 

(4) choice : 1차원 ndarray 로부터 랜덤으로 샘플링한다.

np.random.choice(100, size = (3,3))
--------------------------------------
array([[33, 77, 31],
       [44, 12, 11],
       [22, 80, 80]])

 

 

이러한 랜덤함수들을 실행할 때마다 값이 바뀌는데 바뀌고 싶지 않을 때 사용하는 것이 seed( ) 이다.

np.random.seed(33)

seed 의 숫자는 어떠한 크기도 상관없다. seed 를 실행하고 랜덤 함수를 실행한 것은 항상 같은 값이 나오는 것을 알 수 있다.

 


2. 배열 형태 바꾸기

배열 형태를 바꾸는 대표적인 함수는 reshape( ) 이다.

np.arange(20).reshape(4,5)
----------------------------
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

0 부터 19까지의 숫자를 4행 5열로 바꾼 형태이다.

그렇다면 다차원 배열을 다시 1차원 배열로 돌리려면 어떻게 해야할까.

 

num = np.arange(20).reshape(4,5)
num.ravel()
-----------------------------
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])

정답은 ravel( ) 을 사용하는 것이다. 하지만 이것만이 아니라 flatten( ) 도 사용 가능하다.

 

num.flatten()
--------------------------
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19])

그렇다면 ravel 과 flattern 의 차이점이 있어야 한다.

ravel 은 원본 데이터에 영향을 미친다. 만약 num 의 배열이 변경되면 원본인 x 배열 또한 변경된다는 것이다.

이외에도 order 속성이 존재한다.

  • C : 행 우선 정렬
  • F : 열 우선 정렬
num.ravel(order='F')
--------------------------------
array([ 0,  5, 10, 15,  1,  6, 11, 16,  2,  7, 12, 17,  3,  8, 13, 18,  4,
        9, 14, 19])
300x250