IT

[파이썬 라이브러리를 활용한 데이터 분석] 1. 넘파이 NumPy

bsj54 2021. 3. 12. 17:22

1. NumPy(Numerical Python)

NumPy는 대용량 데이터 배열을 효율적으로 연산할 수 있는 라이브러리

 

1-1. 다차원 배열 객체 ndarray

ndarray: N차원의 배열 객체로, 대규모 데이터 집합을 담을 수 있는 빠르고 유연한 자료 구조

# ndarray 생성
import numpy as np

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

print(arr1.ndim) # ndim: 차원의 크기를 알려줌
print(arr1.shape) # shape: 각 차원에 대해 크기를 알려줌
print(arr1.dtype) # dtype: 배열에 저장된 자료형을 알려줌

 

NumPy에는 배열을 생성하는 다양한 함수가 구현되어 있음

# 배열 생성 함수
np.zeros((3, 6))

 

위 zeros 함수 외에도 아래와 같은 함수들이 존재

array

입력 데이터를 ndarray로 변환, 입력 데이터는 복사됨

asarray

array와 같은 기능이나 입력 데이터가 이미 ndarray이면 복사 x

arange

내장 range 함수와 유사하며 ndarray 반환

ones, ones_like

ones: 배열 내용을 1로 초기화해 생성

ones_like: 주어진 배열과 동일한 모양 dtype을 가지며 내용이 모두 1인 배열 생성

zeros, zeros_like

ones, ones_like와 동일하나 내용을 0으로 채움

empty, empty_like

메모리를 할당해 새로운 배열을 생성하나 ones나 zeros처럼 값을 초기화하지 않음

full, full_like

인자로 받은 값으로 배열을 채움

eye, identity

N*N 크기 단위행렬(or 항등행렬) 생성 

 

 

ndarray의 dtype은 메타데이터(메모리의 특정 데이터를 해석하기 위해 필요한 정보)를 담는 특수한 객체

dtype은 astype 메소드로 명시적 변환도 가능

# 배열의 dtype을 명시적 변환
float_arr1 = arr1.astype(np.float64)

print(float_arr1.dtype)

 

NumPy 배열의 중요한 특징은 반복문을 작성하지 않아도 데이터를 일괄 처리할 수 있다는 것 -> 벡터화

# 벡터화를 통한 데이터 일괄 연산
arr = np.array([[1.,2.,3.], [4.,5.,6.]])

print(arr*arr)
print(arr-arr)
print(1/arr)
print(arr**0.5)
print(arr>=3)

 

Boolean 배열은 색인으로도 활용 가능

# Boolean 값을 색인에 활용하기
names = np.array(['Bob', 'Joe', 'Will', 'Bob'])
data = np.random.randn(4, 3)

print(data[names=='Bob'])
print(data[~(names=='Bob')])

 

팬시 색인(fancy indexing)도 가능

arr = np.empty((8, 4))
for i in range(8):
    arr[i] = i;
    
print(arr)
print(arr[[4,3,0,6]])
print(arr[[-3,-5,-7]])


arr2 = np.arange(32).reshape((8, 4))
print(arr2[[1,5,7,2], [0,3,1,2]]) # [4, 23, 29, 10]
print(arr2[[1,5,7,2]][:, [0,3,1,2]])

 

배열을 전치하거나 축을 바꿀 수 있음

# 배열 전치와 축 바꾸기
arr = np.arange(15).reshape((3, 5))

print(arr)
print(arr.T)
print(np.dot(arr.T, arr))


arr2 = np.arange(16).reshape((2, 2, 4))
print(arr2)
print(arr2.transpose((1, 0, 2)))
print(arr2.swapaxes(1, 2))

 

 

1-2. 유니버설 함수 Universal Function

유니버설 함수는 ndarray 안의 데이터 원소별로 연산을 수행하는 함수

# 단항 유니버설 함수
arr = np.arange(10)

print(np.sqrt(arr))
print(np.exp(arr))

 

위 함수 외의 단항 유니버설 함수의 예시는 아래와 같음

abs, fabs

각 원소의 절댓값을 구함, 복소수가 아니라면 더 빠른 연산을 위해 fabs 사용 가능

sqrt

각 원소의 제곱근 계산(=arr**0.5)

square

각 원소의 제곱 계산(=arr**2)

exp

각 원소에서 지수 $e^{x}$ 계산

log, log10, log2, log1p

자연로그, 십진로그, 이진로그, 

sign

$ln, log_{2}, log_{10}, ln(x+1)$

ceil

각 원소의 값보다 같거나 큰 정수 중 가장 작은 정수 반환(ex. 2.5->3)

floor

각 원소의 값보다 작거나 같은 정수 중 가장 작은 수 반환(ex. 2.5->2)

rint

각 원소를 반올림, dtype은 유지

modf

각 원소의 몫과 나머지를 각각의 배열로 반환

isnan

각 원소가 숫자인지에 대해 boolean 배열 반환

isfinite, isinf

각 원소가 유한한지, 무한한지에 대해 boolean 배열 반환

cos, cosh, sin, sinh, tan, tanh

삼각함수와 쌍곡삼각함수

arccos, arccosh, arcsin, arcsinh, arctan, arctanh

역삼각함수

logical_not

각 원소의 논리 부정 값 계산(=~arr)

 

 

# 이항 유니버설 함수
x = np.random.randn(8)
y = np.random.randn(8)

print(np.maximum(x, y))

 

위 함수 외의 이항 유니버설 함수의 예시는 아래와 같음

add

두 배열의 같은 위치의 원소끼리 더함

subtract

첫 번째 배열의 원소에서 두 번째 배열의 원소를 뺌

multiply

배열의 원소끼리 곱함

divide, floor_divide

첫 번째 배열의 원소를 두 번째 배열의 원소로 나눔, floor_divide는 몫만 취함

power

첫 번째 배열의 원소를 두 번째 배열의 원소만큼 제곱

maximum, fmax

각 배열의 두 원소 중 큰 값 반환, fmax는 NaN 무시

minimum, fmin

각 배열의 두 원소 중 작은 값 반환, fmin은 NaN 무시

mod

첫 번째 배열의 원소를 두 번째 배열의 원소로 나눈 나머지를 구함

copysign

첫 번째 배열의 원소의 기호를 두 번째 배열의 원소의 기호로 바꿈

greater, greater_equal, less, less_equal, equal, not_equal

각각 두 배열의 원소 간의 >, >=, <, <=, ==, != 비교 연산 결과를 boolean 배열 반환

logical_and, logical_or, logical_xor

각각 두 원소 간의 &, |, ^ 논리 연산 결과 반환

 

 

# 여러 배열을 반환하는 유니버설 함수
arr = np.random.randn(7)*5

remainder, whole_part = np.modf(arr)
print(arr)
print(remainder)
print(whole_part)

 

 

1-3. 배열지향 프로그래밍 

값이 놓여 있는 그리드에서 연산을 할 때 배열을 활용할 수 있음

# 그리드
points = np.arange(-5, 5, 0.01)
xs, ys = np.meshgrid(points, points)

z = np.sqrt(xs**2+ys**2) # 그리드 상 두 포인트로 간단히 계산 가능

 

배열 연산으로 조건절을 표현할 수 있음

# 배열 연산으로 조건절 표현
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])

result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)]
print(result)

result = np.where(cond, xarr, yarr)
print(result)

 

배열 전체 혹은 배열에서 한 축을 따르는 자료에 대해 수학, 통계 메소드 사용 가능

# 수학과 통계 메소드
# - 합, 평균
arr = np.random.randn(5, 4)

print(arr.mean())
print(arr.sum())

print(arr.mean(axis=1))
print(arr.sum(axis=0))


# - 중간 계산값 반환(cumsum, cumprod 등)
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
print(arr.cumsum())

arr = np.array([[0,1,2], [3,4,5], [6,7,8]])
arr.cumsum(axis=0)
arr.cumprod(axis=1)

 

많이 사용되는 배열 통계 메소드는 아래와 같음

sum

배열 전체 혹은 특정 축에 대한 모든 원소의 합 계산

mean

산술 평균 계산(크기가 0인 배열은 NaN 반환)

std, var

표준편차와 분산 계산(분모의 기본값은 n, 변형 가능)

min, max

최솟값과 최댓값 반환

argmin, argmax

최소 원소의 색인값(index)과 최대 원소의 색인값 반환

cumsum

각 원소의 누적합

cumprod

각 원소의 누적곱

 

 

boolean 배열의 값을 1(True) 또는 0(False)로 강제하여 메소드를 사용할 수 있음

# Boolean 배열 메소드
arr = np.random.randn(100)
print((arr>0).sum())

bools = np.array([False, False, True, False])
print(bools.any())
print(bools.all())

 

NumPy 배열 역시 파이썬 내장 리스트처럼 정렬 메소드가 존재

# 정렬
arr = np.random.randn(5, 3)

print(arr)
print(arr.sort(1))


# - 특정 분위 값 계산
large_arr = np.random.randn(1000)
large_arr.sort()
print(large_arr[int(0.05*len(large_arr))]) # 5% 분위

 

1차원 ndarray를 위한 기본적인 집합 연산 제공

# 집합 관련 함수
np.array(['Bob', 'Joe', 'Will', 'Bob'])
print(np.unique(names))

values = np.array([6, 0, 0, 3, 2, 5, 6])
print(np.in1d(values, [2, 3, 6]))

 

위 코드 외의 배열 집합 연산 메소드는 아래와 같음

unique(x)

배열 x에서 중복된 원소를 제거한 뒤 정렬해 반환

intersect1d(x, y)

두 배열의 교집합을 정렬해 반환

union1d(x, y)

두 배열의 합집합을 반환

in1d(x, y)

x의 원소가 y의 원소에 포함되는지 나타내는 boolean 배열 반환

setdiff1d(x, y)

두 배열의 차집합 반환

setxor1d(x, y)

대칭차집합(한 배열에는 포함되나 두 배열 모두에는 포함되지 않는 원소들의 집합) 반환

 

 

 

1-4. 배열 데이터의 파일 입출력

압축되지 않은 원시 바이너리 형식의 .npy 파일로 저장 및 불러오기 할 수 있음

# .npy 파일 입출력
arr = np.arange(10)

np.save('some_array', arr) # .npy 파일로 저장

np.load('some_array.npy')

 

여러 개의 배열을 압축된 형식으로 저장 및 불러오기 할 수 있음

# .npz 파일 입출력
np.savez('array_archive.npz', a=arr, b=arr) # 압축이 잘 되는 형식은 savez_compressed 사용 가능

arch = np.load('array_archive.npz')
arch['b']

 

 

1-5. 선형대수

numpy.linalg에는 행렬의 분할과 역행렬 등의 계산이 구현되어 있음

# 행렬 곱셈
x = np.array([[1,2,3], [4,5,6]])
y = np.array([[6, 23], [-1, 7], [8, 9]])

print(x.dot(y))
# numpy.linalg
from numpy.linalg import inv, qr

X = np.random.randn(5, 5)

mat = X.T.dot(X)
print(inv(mat))
q, r = qr(mat)
print(r)

 

자주 사용하는 선형대수 함수(numpy.linalg)는 아래와 같음

diag

정사각 행렬의 대각/비대각 원소를 1차원 배열로 반환하거나,

1차원 배열을 대각선 원소로 하고 나머지는 0으로 채운 단위행렬 반환

dot

행렬 곱셈 계산

trace

행렬의 대각선 원소의 합 계산

det

행렬식 계산

eig

정사각 행렬의 고윳값과 고유벡터 계산

inv

정사각 행렬의 역행렬 계산

pinv

정사각 행렬의 무어-펜로즈 유사역원 역행렬 계산

qr

QR 분해 계산

svd

특잇값 분해(SVD) 계산

solve

A가 정사각 행렬일 때 Ax = b를 만족하는 x 계산

lstsq

Ax = b를 만족하는 최소제곱해 계산

 

 

 

1-6. 난수 생성

numpy.random 모듈로 다양한 종류의 확률분포로부터 표본값을 생성 가능

# (유사) 난수 생성기
samples = np.random.normal(size=(4, 4))
print(samples)

np.random.seed(1234)

(엄밀하게는 유사 난수라고 부르는데, 난수 생성기의 시드값에 따라 정해진 난수를 알고리즘으로 생성하기 때문)

# 격리된 난수 생성기
rng = np.random.RandomState(1234)
rng.randn(10)

 

아래는 numpy.random에 포함된 일부 함수들임

seed

난수 생성기의 시드 지정

permutation

순서를 임의로 바꾸거나 임의의 순열 반환

shuffle

리스트나 배열의 순서 뒤섞기

rand

균등분포에서 표본 추출

randint

주어진 최소/최대 범위 안에서 임의의 난수 추출

randn

표준편차가 1이고 평균값이 0인 정규분포에서 표본 추출

bionomial

이항분포에서 표본 추출

normal

정규분포(가우시안)에서 표본 추출

beta

베타분포에서 표본 추출

chisquare

카이제곱분포에서 표본 추출

gamma

감마분포에서 표본 추출

uniform

균등 [0, 1) 분포에서 표본 추출

 

 

 

 

 

Textbook used: book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=308325343&gclid=CjwKCAiA4rGCBhAQEiwAelVti5kx4-AYBWlOkBjZ8xz8cBfeg5ie_2syKcI4W2qlIlhjRYynicd6URoCAkMQAvD_BwE

 

싸니까 믿으니까 인터파크도서

CHAPTER 1 시작하기 전에 -1.1 이 책에서 다루는 내용 -1.2 왜 데이터 분석에 파이썬을 사용하나 -1.3 필수 파이썬 라이브러리 -1.4 설치 및 설정 -1.5 커뮤니티와 컨퍼런스 -1.6 이 책을 살펴보는 방법 CHAP

book.interpark.com

 

<Source>

towardsdatascience.com/how-to-create-numpy-arrays-from-scratch-3e0341f9ffea