1. NumPy 개요
NumPy(Numerical Python)는 대규모 다차원 배열과 행렬 연산을 효율적으로 수행하는 라이브러리 이다. 과학 계산, 데이터 분석, 머신러닝 등의 분야에서 널리 사용된다.
NumPy란 무엇인가?
NumPy는 대량의 숫자 데이터를 빠르고 효율적으로 처리하기 위해 개발된 라이브러리 이다. Python의 기본 리스트보다 메모리 사용량이 적고, 연산 속도가 빠르며, 다양한 수학 연산을 지원 한다.
NumPy는 다음과 같은 기능을 제공한다.
다차원 배열 객체(ndarray
)
리스트와 유사하지만, 행렬처럼 연산이 가능하고 빠르다.
벡터화 연산 (Vectorized Operations)
반복문 없이 배열 단위의 연산을 수행 하여 속도를 향상한다.
수학, 선형대수, 통계 함수 제공
sum()
, mean()
, std()
, dot()
등
다양한 데이터 파일 입출력 지원
.csv
, .txt
, .npy
등 파일을 쉽게 저장 및 로드 가능
NumPy의 특징과 장점
NumPy는 Python의 기본 리스트보다 빠르고 효율적 이며, 수학 및 과학 계산을 위한 다양한 기능을 제공한다.
빠른 연산 속도
내부적으로 C언어로 구현되어 있어 리스트보다 연산 속도가 훨씬 빠름
메모리 효율성
같은 데이터를 저장할 때 리스트보다 적은 메모리를 사용
벡터 연산 지원
반복문 없이 배열 단위로 연산이 가능
다차원 배열 처리
1차원, 2차원, N차원 배열을 쉽게 다룰 수 있음
다양한 수학/통계 함수 제공
행렬 연산, 난수 생성, 변환 함수 등 수학 계산 기능 내장
외부 라이브러리와 연동
Pandas, SciPy, Scikit-learn, TensorFlow 등과 호환 가능
왜 NumPy를 사용하는가? (리스트와의 차이점)
Python의 리스트와 NumPy 배열을 비교하면 NumPy의 장점이 더욱 명확해진다.
1) 메모리 사용량 비교
NumPy 배열은 같은 데이터를 저장할 때 더 적은 메모리를 사용 한다.
import numpy as np
import sys
# 크기 5인 리스트와 NumPy 배열 생성
list_data = [1 , 2 , 3 , 4 , 5 ]
numpy_array = np.array(list_data)
# 메모리 크기 비교
print ("리스트 메모리 크기:" , sys.getsizeof(list_data) * len (list_data), "bytes" )
print ("NumPy 배열 메모리 크기:" , numpy_array.nbytes, "bytes" )
리스트 메모리 크기: 520 bytes
NumPy 배열 메모리 크기: 40 bytes
NumPy 배열은 데이터 타입을 통일하여 메모리 사용량을 줄인다.
2) 연산 속도 비교
NumPy는 벡터 연산 을 지원하여 리스트보다 연산 속도가 빠르다.
import time
# 크기 100만의 리스트와 NumPy 배열 생성
size = 1_000_000
list1 = list (range (size))
list2 = list (range (size))
array1 = np.arange(size)
array2 = np.arange(size)
# 리스트 연산 속도 측정
start = time.time()
result_list = [x + y for x, y in zip (list1, list2)]
end = time.time()
print ("리스트 연산 시간:" , end - start)
# NumPy 배열 연산 속도 측정
start = time.time()
result_array = array1 + array2
end = time.time()
print ("NumPy 연산 시간:" , end - start)
리스트 연산 시간: 0.05739021301269531
NumPy 연산 시간: 0.0020427703857421875
결과: NumPy 배열 연산이 리스트보다 수십 배 빠르게 실행 된다.
3) 편리한 수학 연산 지원
리스트는 기본적으로 수학 연산을 직접 지원하지 않음
list_a = [1 , 2 , 3 , 4 ]
list_b = [5 , 6 , 7 , 8 ]
# 리스트 연산 (에러 발생)
print (list_a + 10 ) # TypeError
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[3], line 5
2 list_b = [5 , 6 , 7 , 8 ]
4 # 리스트 연산 (에러 발생)
----> 5 print (list_a + 10 ) # TypeError
TypeError : can only concatenate list (not "int") to list
반면 NumPy 배열은 벡터 연산이 가능
array_a = np.array(list_a)
print (array_a + 10 ) # 배열의 모든 요소에 10을 더함
NumPy 설치 및 기본 사용법
1) NumPy 설치
NumPy는 pip
을 사용하여 설치할 수 있다.
설치 확인
import numpy as np
print (np.__version__) # NumPy 버전 출력
2) NumPy 기본 사용법
NumPy를 사용하려면 numpy
를 np
로 줄여서 불러오는 것이 일반적이다.
import numpy as np
# 1차원 배열 생성
arr = np.array([1 , 2 , 3 , 4 , 5 ])
print (arr)
print (type (arr)) # <class 'numpy.ndarray'>
# 배열의 속성 확인
print ("배열 크기:" , arr.shape)
print ("배열 차원:" , arr.ndim)
print ("배열 데이터 타입:" , arr.dtype)
[1 2 3 4 5]
<class 'numpy.ndarray'>
배열 크기: (5,)
배열 차원: 1
배열 데이터 타입: int64
NumPy 배열은 리스트와 달리 배열 크기(shape
), 차원(ndim
), 데이터 타입(dtype
) 등의 속성을 가진다.
2. NumPy 배열 생성
NumPy에서 가장 중요한 데이터 구조는 ndarray
(N-dimensional array, 다차원 배열)이다. 배열을 생성하는 방법에는 numpy.array()
를 이용한 직접 생성, 특정 값으로 채운 배열, 난수 배열 등이 있다.
numpy.array()를 이용한 배열 생성
Python 리스트 또는 튜플을 변환하여 NumPy 배열을 만들 수 있다.
import numpy as np
# 리스트를 NumPy 배열로 변환
arr1 = np.array([1 , 2 , 3 , 4 , 5 ])
print (arr1) # [1 2 3 4 5]
print (type (arr1)) # <class 'numpy.ndarray'>
# 튜플을 NumPy 배열로 변환
arr2 = np.array((10 , 20 , 30 ))
print (arr2) # [10 20 30]
[1 2 3 4 5]
<class 'numpy.ndarray'>
[10 20 30]
2차원 이상의 배열도 생성 가능
# 2차원 배열 (행렬)
arr3 = np.array([[1 , 2 , 3 ], [4 , 5 , 6 ]])
print (arr3)
다양한 배열 생성 방법
NumPy는 특정한 값으로 채워진 배열을 쉽게 생성할 수 있는 다양한 함수를 제공한다.
1) np.zeros()
, np.ones()
, np.full()
, np.eye()
np.zeros(shape)
0으로 채워진 배열 생성
np.zeros((2, 3))
np.ones(shape)
1로 채워진 배열 생성
np.ones((3, 3))
np.full(shape, value)
지정된 값으로 채워진 배열 생성
np.full((2, 2), 7)
np.eye(n)
단위 행렬(Identity Matrix) 생성
np.eye(3)
# 0으로 채워진 배열
print (np.zeros((2 , 3 )))
# 1로 채워진 배열
print (np.ones((3 , 3 )))
# 특정 값(7)으로 채워진 배열
print (np.full((2 , 2 ), 7 ))
# 단위 행렬 (3x3)
print (np.eye(3 ))
[[0. 0. 0.]
[0. 0. 0.]]
[[1. 1. 1.]
[1. 1. 1.]
[1. 1. 1.]]
[[7 7]
[7 7]]
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
2) np.arange()
, np.linspace()
np.arange(start, stop, step)
start
부터 stop
전까지 step
간격으로 배열 생성
np.arange(0, 10, 2)
np.linspace(start, stop, num)
start
부터 stop
까지 num
개의 균일한 간격의 값 생성
np.linspace(0, 1, 5)
# 0부터 9까지 1씩 증가하는 배열
print (np.arange(10 ))
# 0부터 9까지 2씩 증가하는 배열
print (np.arange(0 , 10 , 2 ))
# 0과 1 사이를 5개의 값으로 나눈 배열
print (np.linspace(0 , 1 , 5 ))
[0 1 2 3 4 5 6 7 8 9]
[0 2 4 6 8]
[0. 0.25 0.5 0.75 1. ]
3) 난수 배열 생성 (np.random
)
NumPy는 난수를 생성하는 다양한 함수를 제공한다.
np.random.rand(d0, d1, …)
0~1 사이의 균일 분포 난수 생성
np.random.rand(2, 3)
np.random.randn(d0, d1, …)
평균 0, 표준편차 1의 정규 분포 난수 생성
np.random.randn(3, 3)
np.random.randint(low, high, size)
low
이상 high
미만의 정수 난수 생성
np.random.randint(1, 10, (2, 2))
np.random.seed(seed)
난수 고정(재현 가능)
np.random.seed(42)
# 0~1 사이의 균일 분포 난수 (2x3)
print (np.random.rand(2 , 3 ))
# 평균 0, 표준편차 1의 정규 분포 난수 (3x3)
print (np.random.randn(3 , 3 ))
# 1~9 사이의 정수 난수 (2x2)
print (np.random.randint(1 , 10 , (2 , 2 )))
# 난수 고정 (재현 가능)
np.random.seed(42 )
print (np.random.rand(3 ))
[[0.56624101 0.87519731 0.95303816]
[0.88335615 0.33414231 0.23235504]]
[[-1.67777144 -0.37638005 0.43896975]
[ 1.70491327 -0.0135657 0.44518269]
[-1.14087118 0.41654261 0.07910898]]
[[1 7]
[8 4]]
[0.37454012 0.95071431 0.73199394]
배열의 속성
NumPy 배열은 다양한 속성을 제공하여 배열의 정보를 쉽게 확인할 수 있다.
shape
배열의 형태(차원별 크기)
arr.shape
dtype
배열의 데이터 타입
arr.dtype
size
배열의 전체 요소 개수
arr.size
ndim
배열의 차원(축 개수)
arr.ndim
arr = np.array([[1 , 2 , 3 ], [4 , 5 , 6 ]])
print ("배열: \n " , arr)
print ("배열의 형태(shape):" , arr.shape)
print ("배열의 데이터 타입(dtype):" , arr.dtype)
print ("배열의 전체 크기(size):" , arr.size)
print ("배열의 차원 수(ndim):" , arr.ndim)
배열:
[[1 2 3]
[4 5 6]]
배열의 형태(shape): (2, 3)
배열의 데이터 타입(dtype): int64
배열의 전체 크기(size): 6
배열의 차원 수(ndim): 2
3. 배열의 인덱싱과 슬라이싱
NumPy 배열에서 개별 요소에 접근하는 방법을 인덱싱(indexing) , 특정 범위를 추출하는 방법을 슬라이싱(slicing) 이라고 한다. 또한, 조건을 이용한 검색(불리언 인덱싱) 과 특정 인덱스를 지정하는 팬시 인덱싱 도 활용할 수 있다.
기본적인 인덱싱 (1D
, 2D
, 3D
배열)
NumPy 배열은 Python 리스트와 유사한 방식으로 인덱싱이 가능하다. Python에서는 0부터 시작하는 인덱스 를 사용한다.
1차원 배열(1D) 인덱싱
import numpy as np
arr1 = np.array([10 , 20 , 30 , 40 , 50 ])
print (arr1[0 ]) # 10 (첫 번째 요소)
print (arr1[2 ]) # 30 (세 번째 요소)
print (arr1[- 1 ]) # 50 (마지막 요소)
2차원 배열(2D) 인덱싱
arr2 = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]])
print (arr2[0 , 0 ]) # 1 (첫 번째 행, 첫 번째 열)
print (arr2[1 , 2 ]) # 6 (두 번째 행, 세 번째 열)
print (arr2[- 1 , - 1 ]) # 9 (마지막 행, 마지막 열)
3차원 배열(3D) 인덱싱
arr3 = np.array([[[1 , 2 ], [3 , 4 ]],
[[5 , 6 ], [7 , 8 ]]])
print (arr3[0 , 1 , 1 ]) # 4 (첫 번째 2D 배열, 두 번째 행, 두 번째 열)
print (arr3[1 , 0 , 0 ]) # 5 (두 번째 2D 배열, 첫 번째 행, 첫 번째 열)
슬라이싱을 이용한 부분 배열 추출
배열의 특정 부분을 추출할 때 슬라이싱(Slicing) 을 사용한다.
형식: arr[start:end:step]
(end는 포함되지 않음)
1차원 배열 슬라이싱
arr = np.array([10 , 20 , 30 , 40 , 50 ])
print (arr[1 :4 ]) # [20 30 40] (1~3번째 요소)
print (arr[:3 ]) # [10 20 30] (처음부터 3번째 요소까지)
print (arr[2 :]) # [30 40 50] (2번째 요소부터 끝까지)
print (arr[::2 ]) # [10 30 50] (2칸씩 건너뛰기)
[20 30 40]
[10 20 30]
[30 40 50]
[10 30 50]
2차원 배열 슬라이싱
arr2 = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]])
print (arr2[:2 , :2 ]) # 상위 2x2 부분 배열
print (arr2[1 :, 1 :]) # 하위 2x2 부분 배열
print (arr2[:, 1 ]) # 두 번째 열만 선택
print (arr2[::2 , ::2 ]) # 행과 열을 2칸씩 건너뛰며 선택
[[1 2]
[4 5]]
[[5 6]
[8 9]]
[2 5 8]
[[1 3]
[7 9]]
불리언 인덱싱과 조건 검색
불리언 인덱싱(Boolean Indexing) 을 이용하면 배열에서 특정 조건을 만족하는 값만 선택할 수 있다.
arr = np.array([10 , 20 , 30 , 40 , 50 ])
# 30보다 큰 값만 선택
print (arr[arr > 30 ]) # [40 50]
# 짝수만 선택
print (arr[arr % 2 == 0 ]) # [10 20 30 40 50]
# 10보다 크고 40보다 작은 값 선택
print (arr[(arr > 10 ) & (arr < 40 )]) # [20 30]
[40 50]
[10 20 30 40 50]
[20 30]
2차원 배열에서도 적용 가능
arr2 = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]])
print (arr2[arr2 % 2 == 0 ]) # [2 4 6 8] (짝수만 선택)
팬시 인덱싱 (arr[[0, 2, 4]]
)
팬시(Fancy) 인덱싱을 사용하면 특정한 인덱스의 여러 개 요소를 한 번에 선택 할 수 있다.
arr = np.array([10 , 20 , 30 , 40 , 50 ])
print (arr[[0 , 2 , 4 ]]) # [10 30 50] (0, 2, 4번째 요소 선택)
2차원 배열에서 행 또는 열 선택
arr2 = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]])
# 특정 행 선택 (0번째, 2번째 행)
print (arr2[[0 , 2 ]])
# 특정 열 선택 (0번째, 2번째 열)
print (arr2[:, [0 , 2 ]])
[[1 2 3]
[7 8 9]]
[[1 3]
[4 6]
[7 9]]
4. NumPy의 주요 연산
NumPy는 배열을 활용한 다양한 연산 을 지원한다. 기본적인 산술 연산부터 통계 연산, 행렬 연산까지 여러 기능을 제공한다.
기본적인 산술 연산 (+, -, *, /, **, %
)
NumPy 배열에서는 요소별(element-wise) 연산 이 가능하다.
import numpy as np
a = np.array([1 , 2 , 3 ])
b = np.array([4 , 5 , 6 ])
print (a + b) # [5 7 9]
print (a - b) # [-3 -3 -3]
print (a * b) # [4 10 18]
print (a / b) # [0.25 0.4 0.5]
print (a ** 2 ) # [1 4 9] (제곱)
print (a // 2 ) # [0 1 1] (몫)
print (a % 2 ) # [1 0 1] (나머지)
[5 7 9]
[-3 -3 -3]
[ 4 10 18]
[0.25 0.4 0.5 ]
[1 4 9]
[0 1 1]
[1 0 1]
NumPy는 스칼라 값과 배열 간 연산 도 자동 적용된다.
print (a + 10 ) # [11 12 13]
print (a * 3 ) # [3 6 9]
브로드캐스팅 개념
브로드캐스팅(Broadcasting) 은 크기가 다른 배열 간의 연산을 가능하게 하는 기능이다.
a = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ]])
b = np.array([10 , 20 , 30 ]) # (1,3) 크기의 배열
print (a + b) # (2,3) 배열에 (1,3) 배열이 확장되어 연산 수행
NumPy는 차원이 맞지 않는 배열도 자동 확장 하여 연산할 수 있다.
a = np.array([[1 ],
[2 ],
[3 ]]) # (3,1) 배열
b = np.array([10 , 20 , 30 ]) # (1,3) 배열
print (a + b) # (3,3) 배열로 확장하여 연산 수행
[[11 21 31]
[12 22 32]
[13 23 33]]
배열 간 연산 (np.add
, np.subtract
, np.multiply
, np.divide
)
NumPy에서는 연산자를 사용하는 대신 함수형 연산 도 가능하다.
a = np.array([1 , 2 , 3 ])
b = np.array([4 , 5 , 6 ])
print (np.add(a, b)) # [5 7 9]
print (np.subtract(a, b)) # [-3 -3 -3]
print (np.multiply(a, b)) # [4 10 18]
print (np.divide(a, b)) # [0.25 0.4 0.5]
[5 7 9]
[-3 -3 -3]
[ 4 10 18]
[0.25 0.4 0.5 ]
통계 연산 (np.mean
, np.sum
, np.min
, np.max
, np.std
, np.var
)
NumPy는 배열의 통계 연산을 쉽게 수행할 수 있도록 다양한 함수를 제공한다.
arr = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]])
print (np.sum (arr)) # 전체 합: 45
print (np.mean(arr)) # 평균: 5.0
print (np.min (arr)) # 최솟값: 1
print (np.max (arr)) # 최댓값: 9
print (np.std(arr)) # 표준편차: 2.581
print (np.var(arr)) # 분산: 6.666
45
5.0
1
9
2.581988897471611
6.666666666666667
축을 기준으로 연산할 수도 있다.
print (np.sum (arr, axis= 0 )) # 열 기준 합계: [12 15 18]
print (np.sum (arr, axis= 1 )) # 행 기준 합계: [ 6 15 24]
행렬 연산 (np.dot
, np.matmul
, np.linalg.inv
, np.linalg.det
)
NumPy는 행렬 연산을 위한 다양한 기능을 제공한다.
1. 행렬 곱 (np.dot
, np.matmul
)
A = np.array([[1 , 2 ],
[3 , 4 ]])
B = np.array([[5 , 6 ],
[7 , 8 ]])
print (np.dot(A, B)) # 행렬 곱 (일반적인 내적)
print (np.matmul(A, B)) # 행렬 곱 (권장 방식)
[[19 22]
[43 50]]
[[19 22]
[43 50]]
2. 역행렬 (np.linalg.inv
)
A = np.array([[1 , 2 ],
[3 , 4 ]])
print (np.linalg.inv(A)) # 역행렬
3. 행렬식 (np.linalg.det
)
print (np.linalg.det(A)) # 행렬식 계산
5. 배열 변형 및 조작
NumPy는 배열의 구조를 쉽게 변경하고 조작할 수 있도록 다양한 기능을 제공한다.
배열의 형태를 변경하는 기능부터, 배열을 연결하거나 분할하는 기능, 정렬 기능까지 포함된다.
배열의 형태 변경 (reshape
, ravel
, flatten
)
배열의 모양을 바꾸는 방법에는 reshape()
을 사용하고, 다차원 배열을 1차원으로 변환할 때는 ravel()
, flatten()
을 사용한다.
1. 배열의 형태 변경 (reshape
)
import numpy as np
arr = np.array([1 , 2 , 3 , 4 , 5 , 6 ])
reshaped = arr.reshape(2 , 3 ) # (2행, 3열) 배열로 변경
print (reshaped)
reshape()
사용 시, 기존 요소 개수와 변경 후 개수가 일치해야 한다.
-1
을 사용하면 자동 계산이 가능하다.
arr.reshape(- 1 , 2 ) # 자동으로 (3, 2) 형태로 변환
array([[1, 2],
[3, 4],
[5, 6]])
2. 1차원 배열로 변환 (ravel
, flatten
)
arr2D = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ]])
print (arr2D.ravel()) # [1 2 3 4 5 6] (참조 반환)
print (arr2D.flatten()) # [1 2 3 4 5 6] (새 배열 반환)
[1 2 3 4 5 6]
[1 2 3 4 5 6]
ravel()
은 원본 배열의 참조(view) 를 반환하고, flatten()
은 새로운 배열(copy) 을 반환한다.
차원 변경 (np.newaxis
, squeeze
)
차원을 추가하거나 줄일 수 있다.
1. 차원 추가 (np.newaxis
)
arr = np.array([1 , 2 , 3 ])
print (arr[:, np.newaxis]) # (3,1) 형태로 변환
print (arr[np.newaxis, :]) # (1,3) 형태로 변환
2. 차원 축소 (squeeze
)
arr3D = np.array([[[1 , 2 , 3 ]]]) # (1,1,3) 배열
print (arr3D.shape) # (1, 1, 3)
squeezed = arr3D.squeeze() # 불필요한 차원 제거
print (squeezed.shape) # (3,)
배열 연결 (np.concatenate
, np.hstack
, np.vstack
)
1. np.concatenate()
(축 기준 연결)
a = np.array([[1 , 2 ], [3 , 4 ]])
b = np.array([[5 , 6 ], [7 , 8 ]])
print (np.concatenate((a, b), axis= 0 )) # 행 방향(아래) 연결
print (np.concatenate((a, b), axis= 1 )) # 열 방향(옆) 연결
[[1 2]
[3 4]
[5 6]
[7 8]]
[[1 2 5 6]
[3 4 7 8]]
axis=0
: 행 방향(수직)으로 연결
axis=1
: 열 방향(수평)으로 연결
2. 수평 연결 (hstack
), 수직 연결 (vstack
)
a = np.array([1 , 2 , 3 ])
b = np.array([4 , 5 , 6 ])
print (np.hstack((a, b))) # [1 2 3 4 5 6]
print (np.vstack((a, b))) # [[1 2 3] [4 5 6]]
[1 2 3 4 5 6]
[[1 2 3]
[4 5 6]]
배열 분할 (np.split
, np.hsplit
, np.vsplit
)
1. np.split()
(축 기준 분할)
arr = np.array([1 , 2 , 3 , 4 , 5 , 6 ])
split_arr = np.split(arr, 3 ) # 3개의 배열로 분할
print (split_arr) # [array([1, 2]), array([3, 4]), array([5, 6])]
[array([1, 2]), array([3, 4]), array([5, 6])]
2. 수평 분할 (hsplit
), 수직 분할 (vsplit
)
arr2D = np.array([[1 , 2 , 3 ],
[4 , 5 , 6 ]])
print (np.hsplit(arr2D, 3 )) # 열 기준 분할
print (np.vsplit(arr2D, 2 )) # 행 기준 분할
[array([[1],
[4]]), array([[2],
[5]]), array([[3],
[6]])]
[array([[1, 2, 3]]), array([[4, 5, 6]])]
배열 정렬 (np.sort
, np.argsort
)
1. 요소 정렬 (np.sort
)
arr = np.array([3 , 1 , 2 ])
print (np.sort(arr)) # [1 2 3]
2차원 배열에서 축을 기준으로 정렬 가능하다.
arr2D = np.array([[3 , 1 , 2 ],
[6 , 4 , 5 ]])
print (np.sort(arr2D, axis= 0 )) # 열 기준 정렬
print (np.sort(arr2D, axis= 1 )) # 행 기준 정렬
[[3 1 2]
[6 4 5]]
[[1 2 3]
[4 5 6]]
2. 정렬된 인덱스 반환 (np.argsort
)
arr = np.array([3 , 1 , 2 ])
print (np.argsort(arr)) # [1 2 0] (정렬된 인덱스)
6. NumPy 고급 기능
NumPy는 배열 연산을 최적화하기 위해 다양한 고급 기능을 제공한다. 고급 인덱싱, 유니버설 함수, 마스크 연산, 메모리 절약 기법 등을 활용하면 보다 효율적인 데이터 처리가 가능하다.
고급 인덱싱 (np.where
, np.take
, np.put
)
기본적인 인덱싱 외에도 특정 조건에 맞는 값을 찾거나, 특정 위치의 요소를 가져오거나 변경하는 기능이 있다.
1. 조건에 맞는 값 찾기 (np.where
)
import numpy as np
arr = np.array([10 , 20 , 30 , 40 , 50 ])
indices = np.where(arr > 25 ) # 25보다 큰 값의 인덱스 반환
print (indices) # (array([2, 3, 4]),)
print (arr[indices]) # [30 40 50]
(array([2, 3, 4]),)
[30 40 50]
2. 특정 위치의 요소 가져오기 (np.take
)
arr = np.array([10 , 20 , 30 , 40 , 50 ])
print (np.take(arr, [0 , 2 , 4 ])) # [10 30 50]
3. 특정 위치의 요소 변경 (np.put
)
arr = np.array([10 , 20 , 30 , 40 , 50 ])
np.put(arr, [0 , 2 ], [100 , 300 ]) # 0번과 2번 인덱스를 변경
print (arr) # [100 20 300 40 50]
유니버설 함수 (np.exp
, np.log
, np.sin
, np.cos
)
NumPy는 유니버설 함수(Universal Function, UFunc) 를 제공하여 배열의 요소별(element-wise) 수학 연산을 빠르게 수행할 수 있다.
1. 지수/로그 함수 (np.exp
, np.log
)
arr = np.array([1 , 2 , 3 ])
print (np.exp(arr)) # [2.718 7.389 20.085] (e^x)
print (np.log(arr)) # [0. 0.693147 1.098612] (자연로그)
[ 2.71828183 7.3890561 20.08553692]
[0. 0.69314718 1.09861229]
2. 삼각 함수 (np.sin
, np.cos
)
angles = np.array([0 , np.pi / 2 , np.pi])
print (np.sin(angles)) # [0. 1. 0.]
print (np.cos(angles)) # [ 1. 0. -1.]
[0.0000000e+00 1.0000000e+00 1.2246468e-16]
[ 1.000000e+00 6.123234e-17 -1.000000e+00]
마스크 연산 (np.ma.masked_array
)
마스크 연산을 사용하면 특정 값을 숨기거나 연산에서 제외할 수 있다.
arr = np.array([1 , 2 , 3 , - 1 , 5 ])
masked_arr = np.ma.masked_array(arr, arr < 0 ) # 음수 값 마스킹
print (masked_arr) # [1 2 3 -- 5] (마스킹된 값은 --로 표시)
print (masked_arr.mean()) # 2.75 (마스킹된 값 제외 후 평균 계산)
메모리 절약 기법 (dtype
변경, astype
)
NumPy 배열은 dtype
을 변경하여 메모리를 절약할 수 있다.
1. 데이터 타입 변경 (astype
)
arr = np.array([1.1 , 2.2 , 3.3 ])
int_arr = arr.astype(np.int32) # float -> int 변환
print (int_arr) # [1 2 3]
2. 작은 데이터 타입 사용 (dtype
)
arr = np.array([1 , 2 , 3 ], dtype= np.int8) # int8은 1바이트 크기
print (arr.dtype) # int8
int8
, int16
, int32
, float32
등을 사용하면 메모리 사용량을 줄일 수 있다.
7. 파일 입출력
CSV 파일 읽기 (np.loadtxt
, np.genfromtxt
)
CSV 파일에서 데이터를 불러오는 방법은 여러 가지가 있다.
1. np.loadtxt()
를 이용한 파일 읽기
data = np.loadtxt("data.csv" , delimiter= "," , skiprows= 1 )
print (data)
skiprows=1
: 첫 번째 행(헤더)을 건너뛰고 데이터만 읽음
2. np.genfromtxt()
를 이용한 파일 읽기 (결측값 처리 가능)
data = np.genfromtxt("data.csv" , delimiter= "," , skip_header= 1 , filling_values= 0 )
print (data)
filling_values=0
: 누락된 값을 0으로 대체
파일 입출력 (np.save
, np.load
, np.savetxt
)
NumPy는 .npy
또는 .csv
형식으로 데이터를 저장할 수 있다.
1. NumPy 배열 저장 (.npy
파일)
arr = np.array([[1 , 2 , 3 ], [4 , 5 , 6 ]])
np.save("array.npy" , arr) # 저장
loaded_arr = np.load("array.npy" ) # 불러오기
print (loaded_arr)
2. CSV 파일 저장 (np.savetxt
)
np.savetxt("output.csv" , arr, delimiter= "," , fmt= " %.2f " )
fmt="%.2f"
: 소수점 두 자리까지 저장