Numpy 라이브러리 사용법

BoostCamp AI Tech

Python

Numpy

01/25/2021


본 정리 내용은 Naver BoostCamp AI Tech의 edwith에서 수강한 내용을 정리한 것입니다.
사실과 다른 부분이 있거나, 수정이 필요한 사항은 댓글로 남겨주세요.


Numpy

Python에서 행렬식 계산을 어떻게 구현할 수 있을까? Python의 기본 이차원 리스트를 사용할 수 있지 않을까?

  • 문제점
    • 방정식 계산 등을 위해서는 계산 기능을 직접 구현해야 한다.
    • List는 메모리 주소를 연결하는 형태기 때문에 굉장히 큰 행렬을 표현하는것이 다소 어렵다.
    • 인터프리터 언어라 처리 속도가 느리다.

이러한 점들을 해결하기 위해 만들어진 Python용 고성능 과학계산용 패키지가 Numpy 이다.

Matrix와 Vector와 같은 Array 연산의 사실상 표준역할을 하고 있다.

Numpy명확한 장점은 다음과 같다.

  • 일반 List에 비해 빠르고, 효율적이다.
  • 반복문 없이 데이터 배열 처리를 지원한다.
  • 선형대수와 관련된 다양한 기능을 제공한다.
  • C, C++ , Fortran 등의 언어와 통합이 가능하다.

ndarray 기본

array 생성

np.array 함수를 활용하여 ndarray 배열을 생성한다. 일반적인 Python List와 달리, 이런 차이점을 가진다.

  • 하나의 데이터 type만 배열에 넣을 수 있다.
  • dynamic typing이 지원되지 않는다. 따라서, array 생성 시점에서 타입을 지정해주어야 한다.
  • C의 Array를 사용하여 배열을 생성한다.
PYTHON
import numpy as np
test_array = np.array([1,4,5,8], float)
test_array # array([1., 4., 5., 8.])
type(test_array) # ndarray
type(test_array[3]) # numpy.float64
PYTHON
test_array = np.array([1,4,5,"8"], float) # String 타입의 데이터를 입력해도 자동 형변환
test_array # array([1., 4., 5., 8.])
type(test_array[3]) # numpy.float64

Numpy 와 List의 array 생성방식은 어떻게 다를까?

  • Numpy ndarray
    • 일반적인 array의 형태와 같이, 데이터들이 하나의 주소 영역에 몰려서 붙어있는 형태이다.
    • List에 비해 연산 효율성이 높다.
    • 메모리 크기가 일정하기 때문에 데이터 공간을 잡기에도 효율적이다.
  • Python List
    • -5~256까지의 자주 사용하는 값들은 특정 주소(static한 위치)에 저장되어있다.
    • 따라서 해당 숫자들을 할당하면, value 부분에 해당 값들의 static한 주소가 할당된다.
    • 변수 → 주소 값 → 실제 값으로 연결되는 형태로, 주소 값을 바꾸어주면 내부 원소들의 변경이 아주 쉬워진다.
    • 이것이 Python List가 다른 언어에 비해 변형이 용이한 이유.
    • 그러나 여러번의 주소를 타고가야 하는 이런 형태 때문에, array에 비해 연산 효율성이 낮다.
    • 또, dynamic typing을 지원하므로 메모리 크기를 지정하기 어려워 공간 측면에서도 비효율적이다.
PYTHON
a = [1,2,3,4,5]
b = [5,4,3,2,1]
a[0] is b[-1] # True
# is는 주소값을 비교하는 연산, 1의 주소는 static해서 같음
a = np.array(a)
b = np.array(b)
a[0] is b[-1] # False
# List와 달리 numpy는 array 형태이므로, 두 ndarray의 원소 주소는 다르다

array 정보 관련 코드

  • shape : ndarray의 dimension 구성을 반환한다.
    • ex) 3rd order tensor : (Channel, Height, Width)
  • dtype : ndarray의 데이터 type을 반환한다.
    • type은 Python뿐만 아니라 C 의 data type도 사용할 수 있다.
  • ndim : dimension 수준(rank)를 반환한다 - 쉽게 말해 괄호 level, 차원과 같다.
  • size : data의 개수를 반환한다.
  • nbyte : ndarray object의 메모리 크기를 반환한다.

< Array Rank에 따라 불리는 이름 >

RankNameExample
0scalar5
1vector[5,10]
2matrix[[1,2], [3,4]]
33-tensor[[[1,3,5], [2,4,6]], [[-1,-3,-5], [-2,-4,-6]]]
4n-tensor
PYTHON
tensor = [[[1,2,5,8],[1,2,5,8],[1,2,5,8]],
[[1,2,5,8],[1,2,5,8],[1,2,5,8]],
[[1,2,5,8],[1,2,5,8],[1,2,5,8]],
[[1,2,5,8],[1,2,5,8],[1,2,5,8]]]
tensor = np.array(tensor, int)
PYTHON
tensor.shape # (4, 3, 4)
tensor.dtype # dtype('int64')
tensor.ndim # 3
tensor.size # 48
tensor.nbytes # 384

Handling Shape

reshape

Array의 shape 크기를 변경한다. element 개수는 동일하게 유지한다.

reshape는 원본을 변경하지 않고, 변경된 matrix를 반환한다.

PYTHON
test_matrix = [[1,2,3,4], [1,2,5,8]]
np.array(test_matrix).shape # (2,4)
np.array(test_matrix).reshape(8,).shape # (8,)
# -1을 지정해주면, size를 기반으로 다른 값들을 보고 해당 값을 선정한다.
np.array(test_matrix).reshape(-1,2).shape # (4,2)

flatten

다차원 array를 1차원 array로 변환한다.

원본을 변경하지 않고, 변경된 matrix를 반환한다.

PYTHON
test_matrix = [[[1,2,3,4],[1,2,5,8],[1,2,3,4],[1,2,5,8]]]
np.array(test_matrix).flatten().size # 16

인덱싱과 슬라이싱

indexing

리스트와 달리, 2차원 배열에서 [0,0] 표기법을 제공한다.

matrix일 경우 앞은 row, 뒤는 column을 의미한다.

PYTHON
test_example = np.array([[1,2,3],[4,5,6], int)
test_example[0][2] # 3
test_example[0,2] # 3

slicing

리스트와 달리, 행과 열 부분을 나누어서 slicing이 가능하다.

matrix의 부분집합을 추출할 때에 유용하다.

PYTHON
a = np.array([[1,2,3,4,5], [6,7,8,9,10]], int)
a[:,2:] # 전체 Row의 2열 이상
a[1,1:3] # 1 Row의 1열~2열
a[1:3] # 1 Row ~ 2 Row 전체

Create Function

arange

array의 범위를 지정하여 값의 리스트를 생성한다.

PYTHON
np.arange(30) # 0~29까지의 array
np.arange(0, 5, 0.5) # arange(시작, 끝, step)
np.arange(30).reshape(5,6)

ones, zeros, empty

1, 0으로 가득찬 ndarray를 생성하거나, 빈 ndarray를 생성한다.

PYTHON
np.ones((2,5)) # 2 by 5 one matrix 생성
np.zeros(shape=(10,), dtype=np.int8) # 10크기의 zero vector 생성

특히, empty의 경우 shape만 주어지고 비어있는 ndarray가 생성된다. 이때, memory initialization도 되지 않는다.

PYTHON
np.empty((2,4))
'''
array([[4.9e-324, 9.9e-324, 1.5e-323, 2.0e-323],
[4.9e-324, 9.9e-324, 2.5e-323, 4.0e-323]])
'''

something_like

기존 ndarray shape 크기만큼 1,0 또는 empty array를 반환한다.

PYTHON
test_matrix = np.arange(30).reshape(5,6)
np.ones_like(test_matrix) # 5 by 6의 one 행렬

identity

단위 행렬(i 행렬)을 생성한다.

PYTHON
np.identity(n=3, dtype=np.int8) # n == row 개수 == column 개수
'''
array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]], dtype=int8)
'''

eye

대각선이 1인 행렬을 생성한다.

identity 함수와 다른 점은, k값을 파라미터로 넘겨 시작 index의 변경이 가능하다는 것이다. 또 행과 열의 크기를 다르게 지정 가능하다.

PYTHON
np.eye(3)
np.eye(N=3, M=5, dtype=np.int8) # 3 by 5 행렬
PYTHON
np.eye(3, 5, k=2)
'''
array([[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])
'''

diag

대각행렬의 값을 추출한다. k값을 지정하여 시작 index를 바꿀 수 있다.

PYTHON
matrix = np.arrage(12).reshape(3,4)
'''
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
'''
np.diag(matrix, k=2) # array([2, 7])
# k값은 음수 인덱스도 가능하다.

random sampling

데이터 분포에 따른 sampling으로 array를 생성한다.

PYTHON
#randome.distribution_type(시작값, 끝값, size)
np.random.uniform(0,1,10).reshape(2,5) # 균등분포
np.random.normal(0,1,6).reshape(3,2) # 정규 분포
np.random.exponential(scale=2, size=100) # 지수 분포

Operation Functions

axis

모든 operation function을 실행할 때 기준이 되는 dimension 축을 의미한다.

shape를 찍었을 때, 앞의 숫자부터 axios index로 볼 수 있다.

PYTHON
test_array = np.arange(1,13).reshape(3,4)
test_array
'''
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
axis=0은 column을 기준으로
axis=1은 row를 기준으로
'''
# 3rd order에서는 axios가 한칸씩 밀려, Channel이 0, Row가 1, Column이 2가 된다.
  • 축을 추가하는 방법
    • reshape : 파라미터로 -1을 넘기고, 나머지 축을 size 크기로 맞춰주기
    • newaxis : 파라미터에 np.newaxis 를 추가하고 나머지 축에 : 사용

sum, mean, std

  • sum
    • ndarray의 element 합을 구한다. 리스트의 sum 기능과 같다.
  • mean
    • element 평균을 반환한다.
  • std
    • element 표준편차를 반환한다.
PYTHON
test_array = np.arange(1,13).reshape(3,4)
test_array.sum(axis=1) # 각 row마다 합을 계산하여 array 반환
test_array.mean(axis=0) # 각 column마다 평균을 계산하여 array 반환
test_array.std(axis=0) # 각 column마다 표준편차를 계산하여 array 반환

이외에도 mathematical function의 종류가 다양하다.

np.something 을 이용하여 종류를 확인할 수 있다.

  • exponential
    • exp, expml, exp2, log, log10, log1p, log2, power, sqrt
  • trigonometric
    • sin, cos, tan, acsin, arccos, atctan
  • hyperbolic
    • sinh, cosh, tanh, acsinh, arccosh, atctanh

concatenate

numpy array를 합쳐주는 함수의 종류는 다음과 같다.

  • vstack
    • 두 array를 수직(vertical)으로 붙인 새로운 matrix를 반환한다.
  • hstack
    • 두 array를 수평(horizon)으로 붙인 새로운 matrix를 반환한다.
  • concatenate((m1,m2), axis=n)
    • 두 array를 지정한 axis에 따라 붙인 새로운 matrix를 반환한다.
PYTHON
a = np.array([1,2,3])
b = np.array([2,3,4])
np.vstack((a,b)) # 수직 병합
'''
array([[1, 2, 3],
[2, 3, 4]])
'''
np.hstack((a,b)) # 수평 병합
'''
array([1, 2, 3, 2, 3, 4])
'''
PYTHON
a = np.array([[1,2],[3,4]])
b = np.array([[5, 6]])
np.concatenate((a,b.T), axis=1) # column 방향으로 붙임
'''
array([[1, 2, 5],
[3, 4, 6]])
'''

Operations b/t arrays

Numpy 는 array간의 기본적인 사칙연산을 지원한다.

단, Element-wise Operation,즉 Array간의 shape가 같을때만 지원한다. 그래야만 같은 위치에 있는 원소끼리 계산할 수 있기 때문이다.

Dot product

행렬간 곱셈 연산으로, dot 함수를 사용한다.

PYTHON
test_a = np.arange(1,7).reshape(2,3)
test_b = np.arange(7,13).reshape(3,2)
test_a.dot(test_b)

transpose

행렬을 전치시키는 연산이다. transpose() 또는 T 어트리뷰트를 사용한다.

PyTorch 모델을 작성하거나, 논문 구현시에 자주 사용하는 operation이다.

PYTHON
test = np.arange(1,7).reshape(2,3)
test.transpose()
test.T
'''
array([[1, 4],
[2, 5],
[3, 6]])
'''

broadcasting

shape이 서로 다른 배열간의 연산을 지원하는 기능이다.

Scalar - vector 외에 vector - matrix간 연산도 지원한다.

PYTHON
test_matrix = np.array([[1,2,3],[4,5,6]], float)
scalar = 3
test_matrix + scalar # broadcasting 연산
'''
array([[4., 5., 6.],
[7., 8., 9.]])
'''

Numpy performance

  • %timeit [함수명 or 코드] : Jupyter 환경에서 코드 퍼포먼스를 체크하는 함수
  • Numpy는 C로 구현되어 있어, 성능을 확보하는 대신에 Python의 dynamic typing이라는 큰 특징을 희생했다.
  • 일반적으로 연산속도는 for loop < list comprehension < Numpy 순으로 빠르다.
    • 1억번의 루프 기준으로 약 4배 이상의 성능차이를 보인다.
  • 대용량 계산에서는 Numpy 가 가장 흔히 사용된다.
  • Concatenate 처럼 계산이 아닌 할당에서는 연산속도의 이점이 없다.

Comparison

All & Any

Array의 데이터 전부(and) 또는 일부(or)가 조건에 맞는지 확인하고, 만족 여부를 반환한다.

PYTHON
a = np.arange(10)
np.any(a>5), np.any(a<0) # (True, False)
np.all(a>5), np.all(a<10) # (False, True)

Comparison operation

  • 배열의 크기가 동일할때 element-wise operation으로 element간 비교 결과를 Boolean type 반환한다.

  • logical_and, logical_or, logical_not 등의 연산을 이용하여 각 조건들이나 boolean 상태를 조합할 수 있다.

    PYTHON
    test_a = np.array([1,2,3], int)
    test_b = np.array([1,2,4], int)
    test_a == test_b # array([True, True, False])
    logical_or(b>3, b<2) # array([True, False, True])

np.where

여러가지 사용법을 가진다.

  • where(condition, True시의 값, False시의 값)

    • condition의 만족여부에 따라서 세팅해둔 값을 집어넣은 array를 반환한다.
  • where(condition)

    • condition을 만족하는 원소들의 index값 array를 반환한다.

      PYTHON
      a = np.array([3,5,-1])
      np.where(a >0, 3, 2) # array([3, 3, 2])
      b = np.arange(10)
      np.where(a>7) # array([8,9])

다음과 같이 특이한 값, 함수들을 이용할 수도 있다.

  • np.Nan , np.Inf
  • np.isnan() ,np.isfinite()
PYTHON
a = np.array([1, np.NaN, np.Inf], float) # NaN = Not a Number, Inf = Infinite
np.isnan(a) # array([False,True,False], dtype=bool)
np.isfinite(a) # array([True,False,False], dtype=bool)

argmax & argmin

Array 내부의 최대값 또는 최소값의 index를 반환한다.

axis를 지정해 각 axis별로 계산한 값을 반환받을수도 있다.

PYTHON
a=np.array([[1,2,4,7],[9,88,6,45],[9,76,3,4]])
np.argmax(a) # 5 --> flatten한 array 기준으로 index를 뽑는다
np.argmax(a, axis=1) # array([3,1,1])
np.argsort(a) # a를 sorting할 수 있다

boolean & fancy index

boolean index

특정 조건의 여부 값을 배열 형태로 추출하여 사용한다.

Comparison operation 함수들도 모두 사용가능하다.

PYTHON
test_array = np.array([1,4,0,2,3,8,9,7], float)
test_array > 3
'''
array([False, True, False, False, False, True, True, True])
'''
# 이를 이용하여 조건을 만족하는 element들만 추출할 수도 있다.
test_array[test_array > 3] # 조건이 True인 index의 element만 추출
'''
array([4., 8., 9., 7.])
'''
# 아래와 같은 형태로 자주 사용한다.
condition = test_array < 3
test_array[condition]
'''
array([1., 0., 2.])
'''

fancy index

Numpy 는 array를 index value로 사용해서 값을 추출하므로, 특정 array를 다른 array의 인덱스에 집어넣어 value값들을 추출해 낼수도 있다.

matrix 형태의 데이터에서도 추출 가능하다.

PYTHON
a = np.array([1,2,3,4,5], float)
b = np.array([0,0,3,1,2], int) # index 값이므로 반드시 int로 선언해야 한다
a[b]
'''
array([1., 1., 4., 2., 3.])
'''
a.take(b) # take 함수: bracket index와 같은 효과를 가진다.
'''
array([1., 1., 4., 2., 3.])
'''

Numpy Data I/O

loadtxt & savetxt

text 타입 데이터를 읽고, Numpy object를 저장할 수 있다.

  • save 만 사용하면 pickle형태로 저장된다.
PYTHON
a = np.loadtxt("./sample.txt")
a[:10] # 10개 데이터 가져오기
a_int = a.astype(int) # integer 변환
a_int_3 = a_int[:3]
np.savetxt("int_data_3.csv", a_int_3, fmt="%.2", delimeter=",")

WRITTEN BY

알파카의 Always Awake Devlog

Seoul