NumPy는 Python에서 수치 계산과 데이터 분석을 효율적으로 수행하기 위해 사용하는 대표적인 라이브러리이다.
NumPy의 핵심은 ndarray라는 다차원 배열 객체이다.
NumPy가 자주 사용되는 이유는 다음과 같다.
pip install numpy
또는 다음처럼 설치할 수 있다.
python -m pip install numpy
NumPy는 관례적으로 np라는 별칭으로 import한다.
import numpy as np
ndarray는 N-dimensional array의 줄임말로, NumPy의 핵심 자료구조이다. 같은 자료형의 데이터를 다차원 형태로 저장하고 빠르게 연산할 수 있다.
import numpy as np
ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)
print(type(ndarr1))
print(type(ndarr1[0]))
ndarr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(ndarr2)
print(type(ndarr2))
print(type(ndarr2[0]))
출력 결과:
[1 2 3 4]
<class 'numpy.ndarray'>
<class 'numpy.int64'>
[[1 2 3]
[4 5 6]]
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
import numpy as np
list1 = [1, 2, 3, 4]
ndarr1 = np.array(list1)
print(ndarr1)
print(type(ndarr1))
list2 = ndarr1.tolist()
print(list2)
print(type(list2))
출력 결과:
[1 2 3 4]
<class 'numpy.ndarray'>
[1, 2, 3, 4]
<class 'list'>
Python 리스트는 서로 다른 타입을 함께 담을 수 있다.
list1 = [1, 3.14, "Python", True]
print(list1)
print(type(list1[0]))
print(type(list1[1]))
print(type(list1[2]))
print(type(list1[3]))
출력 결과:
[1, 3.14, 'Python', True]
<class 'int'>
<class 'float'>
<class 'str'>
<class 'bool'>
반면 NumPy 배열은 하나의 dtype으로 통일된다.
import numpy as np
ndarr1 = np.array([1, 2, 3.14, True])
print(ndarr1)
print(ndarr1.dtype)
print(type(ndarr1[0]))
출력 결과:
[1. 2. 3.14 1. ]
float64
<class 'numpy.float64'>
정수, 실수, 불리언이 섞여 있지만 NumPy는 더 넓은 표현이 가능한 float64로 통일한다.
문자열이 섞이면 전체가 문자열 타입으로 변환될 수 있다.
import numpy as np
ndarr2 = np.array(["1", 2, 3.14, True])
print(ndarr2)
print(ndarr2.dtype)
print(type(ndarr2[0]))
출력 결과:
['1' '2' '3.14' 'True']
<U32
<class 'numpy.str_'>
import numpy as np
ndarr = np.array([1, 2, 3.14, True], dtype=int)
print(ndarr)
print(ndarr.dtype)
출력 결과:
[1 2 3 1]
int64
dtype=int를 지정하면 가능한 값들이 정수로 변환된다. 3.14는 3, True는 1로 변환된다.
NumPy 배열을 다룰 때는 배열의 구조를 확인하는 속성이 중요하다.
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("shape:", arr.shape)
print("ndim:", arr.ndim)
print("dtype:", arr.dtype)
print("size:", arr.size)
출력 결과:
shape: (2, 3)
ndim: 2
dtype: int64
size: 6
| 속성 | 의미 |
|---|---|
shape | 배열의 형태 |
ndim | 배열의 차원 수 |
dtype | 배열 원소의 자료형 |
size | 전체 원소 개수 |
NumPy 배열은 리스트처럼 인덱싱과 슬라이싱을 지원한다. 다차원 배열에서는 arr[행, 열] 형태로 접근할 수 있다.
import numpy as np
fruits = np.array(["딸기", "수박", "바나나", "체리", "복숭아"])
print(fruits)
print(fruits.shape)
print(fruits[0])
print(fruits[4])
print(fruits[-1])
print(fruits[-2])
print(fruits[0:3])
print(fruits[2:])
print(fruits[:3])
출력 결과:
['딸기' '수박' '바나나' '체리' '복숭아']
(5,)
딸기
복숭아
복숭아
체리
['딸기' '수박' '바나나']
['바나나' '체리' '복숭아']
['딸기' '수박' '바나나']
import numpy as np
arr = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
])
print(arr)
print(arr.shape)
print(arr[0, :])
print(arr[0])
print(arr[:, 0])
print(arr[1, 2])
출력 결과:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
(3, 4)
[1 2 3 4]
[1 2 3 4]
[1 5 9]
7
Fancy indexing은 인덱스 목록을 이용해 원하는 위치의 값들을 한 번에 가져오는 방식이다.
import numpy as np
arr = np.array([10, 15, 2, 8, 20, 90, 85, 44, 23, 32])
idx = [2, 5, 9]
print(arr[idx])
arr2d = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
])
print(arr2d[[0, 1], :])
출력 결과:
[ 2 90 32]
[[1 2 3 4]
[5 6 7 8]]
Boolean indexing은 조건에 맞는 값만 선택하는 방식이다.
import numpy as np
arr = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
])
print(arr > 7)
print(arr[arr > 7])
출력 결과:
[[False False False False]
[False False False True]
[ True True True True]]
[ 8 9 10 11 12]
Boolean indexing은 조건 필터링에 매우 자주 사용된다.
NumPy는 다차원 배열을 이용해 행렬 연산을 수행할 수 있다. 행렬 연산은 데이터 과학, 머신러닝, 통계, 컴퓨터 그래픽스 등에서 중요하게 사용된다.
| 개념 | 의미 | 예시 |
|---|---|---|
| 스칼라 | 숫자 하나 | 3, 3.14 |
| 벡터 | 숫자가 1차원으로 나열된 구조 | [1, 2, 3] |
| 행렬 | 숫자가 행과 열을 가진 2차원 표 형태로 저장된 구조 | [[1, 2], [3, 4]] |
크기가 같은 배열끼리는 같은 위치의 원소끼리 연산된다.
import numpy as np
a = np.array([[1, 2, 3],
[2, 3, 4]])
b = np.array([[3, 4, 5],
[1, 2, 3]])
print(a.shape, b.shape)
print(a + b)
print(a - b)
print(a * b)
print(a / b)
출력 결과:
(2, 3) (2, 3)
[[4 6 8]
[3 5 7]]
[[-2 -2 -2]
[ 1 1 1]]
[[ 3 8 15]
[ 2 6 12]]
[[0.33333333 0.5 0.6 ]
[2. 1.5 1.33333333]]
a * b는 행렬 곱셈이 아니라 같은 위치의 원소끼리 곱하는 연산이다.
행렬 곱셈은 앞 행렬의 열 개수와 뒤 행렬의 행 개수가 같아야 가능하다.
import numpy as np
a = np.array([
[1, 2, 3],
[1, 2, 3],
[2, 3, 4],
])
b = np.array([
[1, 2],
[3, 4],
[5, 6],
])
print(a.shape)
print(b.shape)
print(a @ b)
print(np.dot(a, b))
출력 결과:
(3, 3)
(3, 2)
[[22 28]
[22 28]
[31 40]]
[[22 28]
[22 28]
[31 40]]
@와 np.dot()은 위 예제에서 행렬 곱셈을 수행한다.
내적은 두 벡터를 곱해 하나의 숫자인 스칼라를 만드는 연산이다.
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b))
출력 결과:
32
계산 과정은 1*4 + 2*5 + 3*6 = 32이다.
브로드캐스팅(broadcasting)은 모양이 다른 배열끼리도 규칙에 맞으면 자동으로 형태를 맞춰 연산하는 기능이다.
import numpy as np
scores = np.array([70, 80, 90])
print(scores + 10)
출력 결과:
[ 80 90 100]
스칼라 10이 배열의 모든 원소에 더해진다. 반복문 없이 배열 전체에 연산을 적용할 수 있는 것이 NumPy의 큰 장점이다.
NumPy의 np.sort()는 배열을 정렬한 복사본을 반환한다. 원본 배열은 기본적으로 변경되지 않는다.
import numpy as np
arr = np.array([1, 10, 5, 7, 2, 4, 3, 6, 8, 9])
print(arr)
print(np.sort(arr))
print(arr)
print(np.sort(arr)[::-1])
출력 결과:
[ 1 10 5 7 2 4 3 6 8 9]
[ 1 2 3 4 5 6 7 8 9 10]
[ 1 10 5 7 2 4 3 6 8 9]
[10 9 8 7 6 5 4 3 2 1]
import numpy as np
arr = np.array([
[11, 10, 12, 9],
[3, 1, 4, 2],
[5, 6, 7, 8],
])
print(np.sort(arr, axis=0))
print(np.sort(arr, axis=1))
print(np.sort(arr, axis=1)[:, ::-1])
출력 결과:
[[ 3 1 4 2]
[ 5 6 7 8]
[11 10 12 9]]
[[ 9 10 11 12]
[ 1 2 3 4]
[ 5 6 7 8]]
[[12 11 10 9]
[ 4 3 2 1]
[ 8 7 6 5]]
axis=0은 열 방향, axis=1은 행 방향으로 정렬한다.
import numpy as np
arr = np.array([
[
[9, 3, 5],
[8, 1, 7],
],
[
[4, 6, 2],
[10, 0, 11],
],
])
print(arr.shape)
result = np.sort(arr, axis=-1)
print(result)
출력 결과:
(2, 2, 3)
[[[ 3 5 9]
[ 1 7 8]]
[[ 2 4 6]
[ 0 10 11]]]
axis=-1은 마지막 축을 기준으로 정렬한다.
원문 내용에 추가로, NumPy를 사용할 때 자주 만나는 기능 몇 가지를 정리한다.
import numpy as np
print(np.zeros((2, 3)))
print(np.ones((2, 3)))
print(np.arange(1, 10, 2))
print(np.linspace(0, 1, 5))
출력 결과:
[[0. 0. 0.]
[0. 0. 0.]]
[[1. 1. 1.]
[1. 1. 1.]]
[1 3 5 7 9]
[0. 0.25 0.5 0.75 1. ]
| 함수 | 설명 |
|---|---|
np.zeros(shape) | 0으로 채운 배열 생성 |
np.ones(shape) | 1로 채운 배열 생성 |
np.arange(start, stop, step) | 범위 기반 배열 생성 |
np.linspace(start, stop, count) | 구간을 균등하게 나눈 배열 생성 |
reshape()는 배열의 전체 원소 개수를 유지하면서 모양을 바꾼다.
import numpy as np
arr = np.arange(1, 7)
reshaped = arr.reshape(2, 3)
print(arr)
print(reshaped)
출력 결과:
[1 2 3 4 5 6]
[[1 2 3]
[4 5 6]]
import numpy as np
scores = np.array([70, 80, 90, 100])
print("합계:", np.sum(scores))
print("평균:", np.mean(scores))
print("최댓값:", np.max(scores))
print("최솟값:", np.min(scores))
출력 결과:
합계: 340
평균: 85.0
최댓값: 100
최솟값: 70
이번 글에서는 파이썬의 코드 재사용과 데이터 분석 기초를 함께 정리했다.
| 주제 | 핵심 |
|---|---|
| 모듈 | .py 파일 단위로 코드 재사용 |
| 패키지 | 여러 모듈을 폴더 단위로 묶어 관리 |
__name__ | 직접 실행과 import 실행 구분 |
| 가상환경 | 프로젝트별 패키지 환경 분리 |
requirements.txt | 패키지 설치 환경 재현 |
| NumPy | 빠른 수치 계산을 위한 배열 라이브러리 |
| ndarray | NumPy의 핵심 다차원 배열 객체 |
| 인덱싱/슬라이싱 | 배열에서 원하는 위치나 조건의 데이터 선택 |
| 행렬 연산 | 벡터, 행렬 기반 계산 수행 |
| 정렬 | 축(axis)을 기준으로 배열 정렬 |
모듈과 패키지는 프로젝트 구조를 정리하는 도구이고, 가상환경과 requirements.txt는 실행 환경을 안정적으로 관리하는 도구이다. NumPy는 대량의 수치 데이터를 빠르게 처리하기 위한 기본 라이브러리이므로 데이터 분석을 공부한다면 반드시 익숙해져야 한다.
본 정리는 강의 내용과 아래 자료를 참고하여 학습 목적으로 작성하였습니다.