꺼내먹는지식 준

numpy 본문

Python/간단한 이해 글

numpy

알 수 없는 사용자 2022. 1. 18. 14:09

numpy는 코드로 방정식을 표현하고, 쉽게 계산할 수 있도록 도와준다. 

 

$2x_{1} + 2x_{2} +x_{3} = 9\\ 2x_{1} - 1x_{2} + 2x_{3} = 6\\1x_{1} - 1 x_{2} + 2x_{3} = 5$

 

coefficient = [[2,2,1], [2,-1,2], [1, -1, 2]]

constant = [9, 6, 5]

 

numpy를 왜 쓸까? 

 

1 누군가 구현해 놓은 matrix 관련 각종 수식 사용가능 

2 python은 intepreter 언어라 느리기에, 이러한 고성능 과학 팩키지를 사용해야 한다. 

 

--> 정확히 numpy가 list보다 더 빠른 이유는 다음과 같다. (https://checkwhoiam.tistory.com/94 참고) 

 

1) list 는 다른 타입의 데이터들을 가질 수 있고, computation에 몇가지 과정을 더 거쳐야 하는 반면, numpy.ndarray는 메모리에 densely packed된 비슷한 타입의 데이터들이다.  

부연 설명: 블로그에 사전 언급된 것과 같이 파이썬에서 -5 ~ 256 값은 특정 메모리 장소에 지정되어 있다. 즉, 해당 사이 값을 넣는다는 것은 해당 주소를 저장한다는 것과 같다. 접근 메모리 구조가 더 복잡하다보니 연산이 더 느리다. 물론 numpy는 하나의 타입만 저장하여 메모리 크기도 일정하다는 장점도 있다. 

 

2) numpy는 한 task를 subtask로 알아서 나눠서 parall 하게 작동하기도 한다.

(예를 들면, np.dot()을 수행할 때, argument의 size가 크면 cpu core 전부를 쓰는 것을 확인할 수 있다.)

 

3) numpy는 C언어로 구현되어 있어서 더 빠르게 작동한다.

 

3 반복문이 필요없다. 

 

Matrix, Vector와 같은 Array 연산의 사실상 표준 

 

 

Numpy 사용법

1. Array 생성

np.array([1,2,3,4] , float)

--> array([1. , 4., 5., 8.])

numpy 는 np.array 함수로 ndarray(n-dimension) 를 생성한다. 

 

* numpy에는 하나의 데이터 type만 배열에 넣을 수 있다. (numpy가 빠른이유 1))

 

shape: numpy array의 dimension 형태을 반환 

dtype: 타입 반환 

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

a.dtype 

--> dtype('float64')

a.shape

(4, )

 

메모리 효율을 위해 

dtype 을 선언할 때 

dtype = np.float32 와 같이 메모리를 더 적게 소요하도록 선언해줄 수 있다. 

 

reshpae: Array의 shape 크기 변경, 단 element 개수는 고정되어 있다. 

 

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

a.reshape(1,8).shape

(1,8)

 

element 개수가 고정되어 있다보니, reshape 시 매개변수 한개는 정해주지 않아도 된다. 

a.reshape(2,-1).shape

(2,4)

 

flatten: 다차원 array를 1차원으로 펴주는 역할 

 

np.arange(30)

array([0,1,2,3,4, ... 29])

 

np.arange(0,5,0.5)

start point 0, end point 5, step size 0.5 (floating point 가 가능하다는 점 유념)

np.arange(30).reshape(5,6)

이런식으로도 사용 가능 

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

 

np.empty(shape=(10,), datatype = np.int8)

array([0 , 0 , 0 , ... ,0, 65, 1, 22])

empty는 shape 만 주어지면 비어있는 ndarray를 생성하는데, 문제는 memory를 initialize 하지 않아서 그 안에 이전에 저장되어 있던 값이 남아있을 수 있다. 

즉 empty는 잘 안쓴다. 그냥 빈공간 잡자 할때 쓰는 정도.. 

 

a = np.aragne(30).reshape(5,6)

np.ones_like(a)

 

모든 element를 1로 채워준다. 

 

np.zero(shape=(10,), datatype = np.int8)

0으로 초기화 생성 

 

np.identity(n=3)

 

3 by 3 크기의 단위행렬 생성 

 

np.eye(3)

3 by 3 크기의 단위행렬 생성  

그러나, 

eye는 column, row 각 크기 설정이 가능하고, 대각이 1로 설정되는 즉 시작하는 행의 위치를 정할 수 있다. 

ex) 

np.eye(3, 5, k=2) #k 가 2이므로 3번째 열부터 1 시작 

array([0., 0., 1., 0., 0.],

[0., 0., 0., 1., 0.]

[0., 0., 0., 0., 1.])

 

np.random.uniform(0,1,10).reshape(2,5) 0~ 1 까지의 값을 10개 균등분포에서 뽑아서 생성 

array([0.23123 , 0. 545, 0. 12134, 0. 6543, 0. 43555]

[0.34333, 0.45434, 0.5452432, 0.11234, 0. 9184])

 

diag 

대각 행렬 생성 

 

 

2. indexing slicing 

indexing 

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

a[0,0]

--> 1 

a[0][0]

--> 1 

 

slicing 

열과 행을 나눠서 slicing 이 가능하다. 

a[:, 2:]

a[1, 1:3]

a[1:3]

--> indexing에서 [ , ]로 indexing 이 가능하다는 것을 기반으로 slicing 이 된다고 보면 된다. 

 

a[:, ::2]

전체 row 에 대해 ( x: y: z --> start point x, end point y, stepsize z)

전체 column에 대해서 2씩 뛰어서 접근해라. 

 

a[::2, ::3]

전체 row에 대해 2칸씩 뛰고, 전체 column에 대해 3칸씩 뛰어서 접근해라. 

 

 

3. operation functions 

a.sum()

모든 element 합 

a.std()
모든 elemenet 의 std 

 

a.sum(axis=1) 

(2,3,4)의 shape ndarray가 있다고 할 때 

axis 0 : 2 3D 

axis 1: 3 row 

axis 2: 4 column

 

ex) b = np.arange(30).reshape(3,5,2)

array([0,1], [2,3]...)

b[0][0][0] = 0 

b[0][0][1] = 1

 

b.sum(axis=2)

array([1,5,9,13,17],...)

sum으로 차원수는 하나 떨어지고, 해당 axis 의 값들은 다 더해진다. 

 

mathmatical function 들은 다 작동 

exponential: exp, expm1, exp2, log, log10, power, sqrt ... 

trigonometric: sin, cos, tan, acsin... 

hyperbolic: sinh, cosh, tanh ... 

b.mean() b.std() 등도 다 작동 

심지어는 error function도 존재 

 

concatenate 

numpy array vstack 

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

b = np.array([4,5,6])

np.vstack ((a,b))

vertically concetenate

 

--> array([[1,2,3], [4,5,6]])

 

numpy array hstack 

 

np.hstack((a,b)) * 매개변수는 (a,b) 이런 형태로 묶어줘야 한다. 

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

 

차원이 더 커지면 v, h stack 만으로는 불가능하다. 

이때는 concatenate( (a,b), axis = n )  으로 가능 

 

사실상 concatenate 만 사용하고 vstack hstack은 사용하지 않아도 좋다. 

 

*reshape 하지 않고, 축을 추가하는 방법 

a = a[np.newaxis, :] 값은 그대로면서 축만 추가된다. 

 

Transpose 

a.T or a.transpose()

 

4.Array Operations

numpy 간 기본적 사칙연산 

+, - , *(element-wise) , /, **, // 

 

dot product 

단순한 행렬 곱 

 

broadcasting 

scalar 값을 tensor 의 모든 element 에 적용 

+, -, *, /, **, // 등 다 작동 

예제 

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

b = 2

a + b

--> array([[3,4,5], [6,7,8]])

 

vector, matrix 간의 연산도 지원한다. 

이 경우 

알아서 부족한 column, row 의 크기를 현재 존재하는 tensor 를 복재하여 맞춰준 후 연산. 

 

ex) 

a = np.arrange(1,13).reshape(4,3)

b = np.arage(10, 40, 10)

a + b 

array([11,22,33], [14,25,36], [17,28,39], [20,31,42]) 모든 항에 a 가 복제되어 더해진 것 확인 가능 

 

*timeit 으로 코드 동작 시간 확인 가능 

for loop --> list comprehension --> numpy 순으로 빠르다. 

 

5. Comparison 

 

all , any 

np.any(a>5) 하나만 조건을 만족해도 true

np.all(a>5) 모두 조건을 만족해야 true

 

numpy 의 배열 크기가 동일 할 때 비교 가능 

a.shape 

b.shape 

--> (3,)

a > b

--> [True, False, False]

 

a == b 

--> [False, True, True]

 

(a > b).any() 

하나라도 true 면 true 

--> True 

 

np.logical_and(a>0, a<3)

두 조건 다 만족하는가? 

np.logical_not(b)

b에 not 을 건 condition 

np.logical_or(b,c)

or 을 건 컨디션 

 

np.where(a>5)

index 값 반환 

np.where(a>5, 3, 2)

True 면 3으로 대체, false 면 2로 대체 

 

np.isnan(a)

np.NaN 이 element로 포함되어 있는가? 

 

np.isinfinit(a)

학습 진행 중, 한없이 커지고 발산되는 경우에 memory의 크기를 넘어가면 잡아낼 수 있어야 한다. 

그때 사용이 많이 된다. 

 

argmax, argmin (np.argmax, np.argmin)

array내 최대값 또는 최소값의 index 반환 

 

np.argmax(a, axis = 1)와 같이 axis 사용도 가능 

 

6. boolean,fancy index 

특정 조건이 맞을 때 해당 index 를 뽑아오는 방법 

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

a[ a>3 ]

array([4, 5, 6])

 

condition = a < 6

a[condition] 이렇게도 사용 가능 

 

b =  np.array([0,0,1,2])

a[b]  == a.take(b)

array([1,1,2,3])

array 값을 index 처럼 사용 가능 

 

3차 이상의 텐서에서도 

a[b,c] 이런 형태로 사용 가능 

 

* numpy 에도 load text, save text 기능이 있다. 

a = np.loadtxt("./hajunaccount.txt")

b = np.savetxt("./hajunaccount.csv", a , fmt = ".%2e",delimiter = "," )

 

 

*유의점 

a = np.arange(9).reshape((3,3))

[[0,1,2],

[3,4,5],

[6,7,8]]

 

a[:,1] vs a[:][1]

 

a[:,1]

a에서 전체 row에 접근해서 그 중 2번째 element들만 모아와라 라는 뜻이 된다.

즉, 1,4,7 을 모아오는 방법.

a[:][1]

a[:][1] 도 직관적으로 똑같이 동작할 것이라 생각했는데, 이렇게 선언하면 3,4,5 가 반환된다.

[:][1]는 기본적으로 전체 row를 반환하고, 그 중 2번째 row를 반환해라로 읽히는 즉, a[1] 와 차이가 없는 듯 하다.


Numpy 실전 꿀팁

 

np.dot(ndarray1, ndarray2.T)

ndarray1.dot(ndarray2)

모두 가능 (대부분 두가지 방법으로 사용 가능)

 

axis 햇갈리지 말자. 

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

shape: (2,5)

[1, 2] $\rightarrow$ 8 

 

newaxis 사용법 

K = np.random.normal(1, size = (3,5))

array([[ 0.39484584, -0.38801044,  0.16092178,  0.37693302,  1.74812049],
       [-0.9199373 , -0.13282686, -2.05797436, -2.32262587, -0.67222107],
       [-0.76677438,  0.66006324,  0.82848315,  1.22709894, -0.42932928]])

이 때 다음의 두개의 ndarray가 있다고 하자. 

b = np.array(range(3))
c = np.array(range(5))

여기서 K 의 3이 뜻하는 것은 유저의 수이고, 5가 뜻하는 것은 각 유저별 5개 아이템에 대한 예측값이다. 

그리고 b 는 각 유저별 편향 값이고 c는 각 아이템별 편향값이다. 

b는 5, 5, 5 마다 b각각의 값을 더하고자하고  , c는 각 5개 각각에 c 각각 값을 더하고자 한다. 

이때 newaxis 를 사용하면 된다. 

 b[:,np.newaxis]
 
 array([[0],
       [1],
       [2]])
       
       
K + b[:, np.newaxis]

array([[ 0.39484584, -0.38801044,  0.16092178,  0.37693302,  1.74812049],
       [ 0.0800627 ,  0.86717314, -1.05797436, -1.32262587,  0.32777893],
       [ 1.23322562,  2.66006324,  2.82848315,  3.22709894,  1.57067072]])

 

c[np.newaxis, :]

array([[0, 1, 2, 3, 4]])

K + c[np.newaxis, :]

array([[ 0.39484584,  0.61198956,  2.16092178,  3.37693302,  5.74812049],
       [-0.9199373 ,  0.86717314, -0.05797436,  0.67737413,  3.32777893],
       [-0.76677438,  1.66006324,  2.82848315,  4.22709894,  3.57067072]])

이 경우는 사실 K + c 해도 결과는 동일하다. 

'Python > 간단한 이해 글' 카테고리의 다른 글

Python Zip  (0) 2022.02.04
Pandas  (0) 2022.01.18
String 및 변수, 함수 관련  (0) 2022.01.17
Python List  (0) 2022.01.17
컴퓨터 기본 내용 정리, 파이썬 기본 내용 정리  (0) 2022.01.17
Comments