컴퓨터/프로그래밍

[Python] 파이썬 기초 9: Numpy(1, 2)

옆동네애옹이 2024. 9. 5. 14:18
728x90

개요

  • 수학, 과학, 공학, 데이터과학 응용 분야에서 python에서 사용 가능한 open source library
  • C/C++ Fortran과 통합이 쉬움
  • 데이터 구조
    • ndarray class: array class라고 부르기도 함
    • 동일한 데이터형 원소들(주로 숫자형)의 다차원 배열
    • indexing: 정수들의 tuple로 index 표현
    • broadcasting
  • 다양한 수치 연산 도구
    • 다차원 배열에 적용할 수 있는 벡터화 함수
    • 선형 대수, 푸리에 변환, 콘볼루션, 통계 라이브러리 등

Numpy 장점

  • 다차원 배열 또는 행렬 연산의 편리함
  • 우수한 메모리 효율(list, tuple에 비해) // list객체가 약 130배 정도 늦음
  • 빠른 처리속도

<aside> 💡 np.array() # 다차원 배열 생성

</aside>

Numpy 다차원 배열과 list 객체의 구조 비교

  • List
    • 이질(heterogeneous) 데이터형 원소 가능(다른 유형의 객체 가능)
    • 동적 배열
  • Numpy 다차원 배열
    • 동질(homogeneous) 데이터형 원소만 가능
    • 고정 크기 배열: 배열 추가/제거 불가능. 추가제거 위해서는 객체 새로 만들어야 함

Numpy 다차원 배열을 이용한 1차원 벡터의 표현과 연산

import numpy as np
a = np.array([1, 3, 5, 3, 1]) 
b = np.array([3, 5, 7, 1, 2])
a + b # array([4, 8, 12, 4 ,3])
# 벡터 행렬, 산술 연산 매우 간단!

2 * a # array([2, 6, 10, 6, 2])
# 스칼라 * 벡터 연산 가능

Numpy 다차원 배열 class ndarray의 주 attributes

  • ndarray.ndims: axes-축(dimensions)의 수
  • ndarray.shape: axes별 원소의 개수를 tuple로 표현
  • ndarray.size: 원소의 개수
  • ndarray.dtype: numpy.int32, numpy.int16, numpy.floar64 등
  • ndarray.itemsize: 각 원소의 크기를 byte단위로 표현
  • ndarray.nbytes: 전체 데이터의 크기를 byte단위로 표현
na = np.random.randint(1, 10, (3, 4))
# 1 ~ 10 정수 중 난수로 3*4 배열 return 

# attributes
na.ndim # 2: 2차원배열이므로
na.shape # (3, 4): 3*4 행렬이므로
na.dtype # dtype('int32'): 32bit 정수형
na.size # 12: 원소가 12개
na.itemsize # 4: 32bits = 4bytes
na.nbytes # 48: 12개 * 4bytes = 48bytes

Numpy ndarray 객체의 데이터형

 

Numpy 정수형의 overflow와 underflow

  • 정수를 원소로 갖는 numpy의 ndarray object는 표현 가능한 bit수가 사전에 정해짐 > overflow, underflow 발생 가능.
import numpy as np
a = np.array([100, 200], np.int8)
a # array([100, -56], dtype=int8)
# signed int8의 최댓값은 128, overflow 발생

np.iinfo(np.int8)
# iinof(min=-128, max=127, dtype=int8)
# 8bit signed integer max/min value

a + a # array([-56, -112], dtype=int8)
3 * a # array([44, 88], dtype=int8) 
# underflow 발생

b = np.array([100, 200], np.uint8)
b # array([100, 200], dtype=uint8)
2 * b # array([100, 1440, dtype=uint8) 
# overflow 발생

Ndarray 생성

list, tuple 이용 ndarray 생성

data1 = (1, 2, 3, 4, 5) # tuple의 모든 원소 데이터 유형이 같아야 함

# arr 생성법> numpy의 array class의 instance 만들기.
# list, tuple과 같은 객체를 전달(sequence type only?)
arr1 = np.array(data1) # 1d array
arr1 # array([1, 2, 3, 4, 5])
arr1.dtpye # dtype('int32') 
# 별도의 data type 지정않는 경우 32가 default
arr1.ndim # 1: 1차원이니까.
arr1.size # 5: 원소 5개니까.

data2 = [[1, 2, 3., 4], [5, 6, 7, 8.]] 
# nested list: list는 type 다른 원소를 가질 수 있지
arr2 = np.array(data2) # 2d array
data2 # [[1, 2, 3.0, 4], [5, 6, 7, 8.0]]
arr2 # array([[1., 2., 3., 4.], [5., 6., 7., 8.]])
# list는 실수&정수 혼용 가능, np.array는 모두 실수로 변환
arr2.dtype # dtype('float64')
# 실수형 data에 별도로 지정않는 경우 64기본.
arr2.ndim # 2: 2차원이니까
arr2.size # 8: 원소 8개니까

data3 = [[1, 2, 3., 4], [5+1j, 6, 7, 8.]]
# nested list: 정수 + 실수 + 복소수
arr3 = np.array(data3) # 2d array
data3 # [[1, 2, 3.0, 4], [5+1j, 6, 7, 8.0]]
arr3 # array([[1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j], [5.+1.j, 6.+0.j, 7.+0.j, 8.+0.j]])
# list는 numeric 혼용 가능, np.array는 모두 복소수로 변환
arr3.dtype # dtype('complex128')
arr3.ndim # 2: 2dim
arr3.size # 8

data4 = ['a', 'b']
arr4 = np.array(data4)
arr4.dtype # dtype('<U1'): 4byte 길이의 unicode 문자열
arr4.itemsize # 4

data5 = ['ab', 'b']
arr5 = np.array(data5)
arr5.dtype # dtype('<U2'): 8byte 길이의 unicode 문자열

data6 = ['abc', 'b']
arr6 = np.array(data6)
arr6.dtype # dtype('<U3'): 12byte 길이의 unicode 문자열

getsizeof(np.array([])), getsizeof(arr6) 
# (96, 120), arr6에 각 12byte의 메모리 공간 할당됨
# 기본 array size=96. 
  • 결론, array는 각 data type이 모두 달라도, 가장 긴 문자열의 길이를 한 원소에 배정함.
  • 복소수 + 정수 혼합 array : 복소수 * 원소수
  • 긴 문자열 + 짧은 문자열 혼합 array: 긴 문자열원소수 * 4 * 원소수
  • 문자열을 원소로 갖는 ndarray는 각 문자열의 원소 길이가 달라도, 모든 원소 중 가장 메모리가 긴 길이를 일률적으로 할당한다

Ndarray의 Axis

  • Ndarray에 연산을 적용하면(벡터화 된 연산) 내부에서 iteration 과정 발생
  • Axis: numpy 연산에서 iteration이 진행되는 방향
    • 다차원 배열에서 반복과정이 적용되는 방향: axis
# 1차원 배열
import numpy as np
x = np.array([0, 1, 2, 3])
x # array([0, 1, 2, 3])
x.ndim # 1
# 0 - 1 - 2 - 3이 한 방향으로 있는거라고 생각하면 됨

# 2차원 배열
x = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
print(x.ndim) # 2: 0123 / 4567 0 - 4(axis2) & 0 - 1 - 2 - 3(axis1)

# 다차원 배열 원소들의 합 return
print(x.sum()) # 28
print(x.sum(axis=0)) # [4, 6, 8, 10], row
print(x.sum(axis=1)) # [6 22], col

# 3차원 배열
x = np.arange(24).reshape(3, 2, 4)
# reshape: 다차원 배열 shape 재구성.
# (3, 2, 4): Nr of elements (axis[0], axis[1], axis[2])

# sum 함수 사용시, axis라는 용어 없이 sum만 때려도 ok
x.sum(0) # array[[24, 27, 30, 33], [36, 39, 42, 45]]
x.sum(1) 
x.sum(2)

 

numpy axis 3차원 구조

 

  • Question. x의 shape이 (3, 2, 4)인 경우
    • shape of sum(0): (2, 4)
    • shape of sum(1): (3, 4)
    • shape of sum(2): (3, 2)
x[2, 0, 1] #17
# tuple 내 숫자의 index가 axis의 index 의미.
(axis0[2], axis1[0], axis2[1])

 

특별한 ndarray를 생성하는 내장 함수들

  • numpy.arange()
    • numpy.arange(start, stop[, step])
    • range 객체와 return하는 element similar.
n = np.arange(5) 
n # array([0, 1, 2, 3, 4])
n.dtype # dtype('int32')

n = np.arange(5, 0, -1)
n # array([5, 4, 3, 2, 1]_

# arange는 argument로 float 사용가능.
x = np.arange(0, 1, 0.1)
x # array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
x.dtype # 
  • numpy.linspace()
    • np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
    • [start, stop] 구간에서 균등하게 나누어진 num개의 숫자들을 ndarray 객체로 return, start&stop(양끝점) 포함
    • 양끝점을 포함하기 때문에 구간의 개수는 n-1개?
    • endpoint=False: 우측 끝 점 미포함
x = np.linspace(1, 2, 5)
x # array([1. , 1.25, 1.5, 1.85, 2. ])

x = np.linpace(1, 2, 5, endpoint=False)
x # array([1. , 1.2, 1.4, 1.6, 1.8])
  • numpy.logspace()
    • np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0)
    • [basestart, basestop] 구간에서 log 스케일로 균등하게 나누어진 num개의 숫자를 ndarray object로 return
    • 양끝점을 포함하기 때문에 구간의 개수는 n-1개?
    • logspace가 반환하는 값은 linspace가 반환하는 값들을 base의 지수로 사용한 값들을 반환하는 것.
np.logspace(1, 5, 5)
# array([1.e+01, 1.e+02, i.e+03, 1.e+04, 1.e+05])
np.logspace(0, 2, 3)
# array([1., 10., 100.])
  • numpy.zeros()
    • np.zeros(shape, dtype=Non, order='C')
    • 모든 원소가 0.인 ndarray 객체를 반환
    • default type=np.float64
x = np.zeros(5)
x # array([0., 0., 0., 0., 0.])

np.zeros((3, 4))
# 0으로 이루어진 3*4 matrix return.

x = np.zeros(5, dtype=np.int32) # dtype 지정 가능

np.zeros((3, 4, 2)) # 3차원 array
  • numpy.zeros_like()
    • np.zeros_like(a, dtype=None, order='K', subok=True, shpae=one)
    • a와 shape, dtype이 같은 모든 원소가 0.인 ndarray 객체 반환
x = np.arange(5)
x, x.dtype, x.shape
# (array([0, 1, 2, 3, 4]), dtype('int32'), (5,))
  • np.ones()
    • np.ones(shape, dtype=None, order='C')
    • 모든 원소가 1.인 ndarray 객체를 반환
    • default type: np.float64
  • np.ones_like()
    • np.ones_like(a, dtype=None, order='K')
  • np.full()
    • np.full(shape, fill_value, dtype=None, order='C')
    • 모든 원소가 fill_value인 ndarray object return.
    • first argument = dimension of the array int: 1dim array, tuple: ndim array(shape)
    • second argument = fulfill value, also determine data type
x = np.full(5, 3)
x # array([3, 3, 3, 3, 3])
x.dtype # dtype('int32')
  • np.full_like()
    • same shape and data type with a, return ndarray object with all elements are fill_value
a = np.full_like(x, 3.)
# x와 shape, data type이 같고, 3.0으로 채워진 array object return.
  • np.eye()
    • np.eye(N, M=None, k=0, dtype=<class 'float'>, order='C')
    • 대각선이 모두 1이고, shape이 (N,M)인 2차원 ndarray return
    • M이 생략되면 shape(N,N)
    • k는 대각선의 위치를 결정하는 index
    • int 넣어도 float이 기본 data type?
x = np.eye(3)
np.eye(4, k=1) # 4*4 matrix에서 k=0~3까지인데, k=4벗어나면 
# 모든 원소의 값은 0
  • np.diag()
    • np.diag(v, k=0)
    • 2차원 ndarray 객체에서 대각선을 추출해 1차원 ndarray 객체 return
    • 1차원 ndarray 객체v를 대각선 원소로 갖는 2차원 ndarray 객체 반환
    • k는 대각선의 위치를 결정하는 index
    • 행렬 개수 다른 경우에도 OK
# 용법 1.
x = np.arange(16).reshape((4, 4))
np.diag(x) # array([0, 5, 10, 15])
np.diag(x, k=1) # array([1, 6, 11])

# 용법 2. 첫 번째 argument에 1차원 sequence 입력해서
# 2차원 ndarray 객체를 생성 가능.
np.diag([1, 2, 3]) 
# 대각선 원소들이 1, 2, 3이고 3*3인 2차원 ndarray 객체 생성

np.diag([1, 2, 3])[:, ::-1]
# axis0은 그대로 두고, axis1을 역순으로(100 020 003 > 001 020 300)
np.diag([1, 2, 3])[::-1, :]
# axis0을 역순으로(100 020 003 > 003 020 100)
np.diag([1, 2, 3])[::-1, ::-1]
# axis0,1을 역순으로(100 020 003 > 300 020 001)

Numpy 특수 다차원 배열: Random Module

  • randint(low, high=None, size=None, dtype='l'): discrete uniform in [low, high)
    • 난수 생성 알고리즘이 정해져있긴 함.
    • 지정한 구간 내 균일한 분포 따르는 정규분포 내 정수 반환.
    • 구간은 low, high라는 integer parameter로 지정
    • low는 구간 포함, high는 구간 미포함
    • low만 지정하고 high를 지정하지 않는 경우: 지정한 low = high, low=0
np.random.randint(5) # 1
# 0~5까지 4개 정수 가운데 하나 반환(p=1/5)
np.random.randint(5, 20) 
# 5~19까지 15개의 정수 가운데 하나 반환(p=1/15)

# if size parameter > 0, array object being returned
a = np.random.randint(5, size=0)
a #  array([], dtype=int32), 빈 ndarray.

a = np.random.randint(5, size=5)
a #  array([1, 1, 0, 3, 4], dtype=int32)

np.random.randint(0, 10, (2, 3)) 
# size에 tuple 지정> tuple(2*3) matrix의 정수 난수 배열 return 
  • random(size=None): uniform in [0, 1)
    • 난수의 확률분포는 균일분포.
np.random.random() # [0, 1) 사이 random 실수 반환
np.random.random(5) # [0, 1) 사이 random 실수 5개 반환해 array로
np.random.random((2, 3, 4) # shape이 size인, [0, 1) 사이 난수배열
  • uniform(low=0.0, high=1.0, size=None)
    • 균일분포 따르는 난수 발생
    • 난수가 발생하는 범위는 low, high로 설정 가능
    • randint의 low/high parameter는 int지만
    • uniform의 low/high parameter는 실수이고 default 값이 0, 1이라는 점이 다름.
np.random.uniform() # [0, 1) 사이 random 함수 반환
np.random.uniform(1.5) 
# 균일분포 따르는 0, 1.5 사이 난수 하나 발생
np.random.uniform(-1, 1) 
# 균일분포 따르는 -1, 1 사이 난수 하나 발생
np.random.uniform(-1, 1, 4)
# 균일분포 따르는 -1, 1 사이 난수 4개 발생해 array로 return
np.random.uniform(-2, 2, (2, 3, 4))
# 균일분포 따르는 -2, 2 사이 난수 발생해 (2, 3, 4) array로 return
  • normal(loc=0.0, scale=1.0, size=None): 평균이 loc, 표준편차가 scale인 정규분포
    • 정규분포의 평균: loc, 표준편차: scale로 각 지정 가능

Numpy 특수 다차원 배열: Random Module

  • choice(a, size=None, replace=True, p=None)
  • sequence형 객체의 임의의 원소 반환.
  • a = 정수인 경우 range(a)의 임의의 원소 하나 반환
  • a = sequence, sequence 내 임이의 원소 하나 반환
728x90