11주차 · NumPy 기초 입문
이 장을 마치면 다음을 할 수 있습니다.
import numpy as np와np.array(...)의 의미를 설명한다.- NumPy의 기본 자료구조
ndarray를 “shape가 있는 숫자 배열”로 이해한다. ndim,shape,size,dtype로 배열을 점검한다.np.array,np.zeros,np.ones,np.arange,np.linspace로 배열을 만든다.- 1D/2D 배열에서 인덱싱, 슬라이싱, 불리언 마스크를 사용한다.
reshape와np.newaxis로(3,),(1, 3),(3, 1)의 차이를 확인한다.- 원소별 연산
*와 행렬곱@를 구분한다. axis=0,axis=1방향으로 합계와 평균을 계산한다.- 브로드캐스팅(broadcasting)이 되는 경우와 실패하는 경우를 설명한다.
- 원본을 함께 보는 배열(view)과 별도 복사본(copy)의 차이를 알고 원본 변경 위험을 피한다.
0. 예제 코드에서 >>>가 보이면
Section titled “0. 예제 코드에서 >>>가 보이면”NumPy 예제를 찾아보다 보면 다음처럼 >>>가 붙은 코드를 볼 수 있습니다.
>>> import numpy as np>>> np.array([1, 2, 3])array([1, 2, 3])여기서 >>>는 Python 대화형 프롬프트입니다. 실제 코드에 붙이지 않습니다.
# 실제 코드에는 이렇게 씁니다.import numpy as nparr = np.array([1, 2, 3])print(arr)1. NumPy 빠르게 시작하기: 기본 습관 네 가지
Section titled “1. NumPy 빠르게 시작하기: 기본 습관 네 가지”NumPy 코드는 보통 아래처럼 시작합니다.
import numpy as np
arr = np.array([1, 2, 3], dtype=float)print(arr.shape)print(arr * 2)처음에는 네 가지를 고정 습관으로 두세요.
import numpy as np로 NumPy를 불러온다.np.array([...])로 리스트를 배열로 바꾼다.- 계산 전
shape,dtype를 확인한다. arr * 2,arr + arr는 원소별 연산임을 기억한다.
2. ndarray란 무엇인가요?
Section titled “2. ndarray란 무엇인가요?”NumPy의 핵심 자료구조는 ndarray입니다. 이름 그대로 N-dimensional array, 즉 여러 차원을 담는 배열입니다.
| 차원 | 수학적 느낌 | NumPy 예 | shape |
|---|---|---|---|
| 0D | 스칼라처럼 보이는 값 | np.array(3.3) | () |
| 1D | 숫자 줄, 벡터처럼 사용 | np.array([1, 2, 3]) | (3,) |
| 2D | 행렬, 표 | np.array([[1, 2], [3, 4]]) | (2, 2) |
3. list와 ndarray: 같은 기호도 의미가 다릅니다
Section titled “3. list와 ndarray: 같은 기호도 의미가 다릅니다”Python 리스트와 NumPy 배열은 모두 값을 담을 수 있지만, 연산 의미가 다릅니다.
| 코드 | Python list | NumPy ndarray |
|---|---|---|
a + b | 이어붙이기 | 원소별 덧셈 |
a * 2 | 리스트 반복 | 원소별 곱셈 |
shape | 없음 | 있음 |
dtype | 공통 dtype 없음 | 배열 전체의 자료형을 명시적으로 관리 |
a = [1, 2, 3]b = [10, 20, 30]print(a + b) # [1, 2, 3, 10, 20, 30]print(a * 2) # [1, 2, 3, 1, 2, 3]import numpy as np
a = np.array([1, 2, 3])b = np.array([10, 20, 30])print(a + b) # [11 22 33]print(a * 2) # [2 4 6] 4. 배열을 만드는 기본 도구
Section titled “4. 배열을 만드는 기본 도구”처음 실습에서 자주 쓰는 배열 생성 함수를 정리하면 다음과 같습니다.
| 함수 | 역할 | 예 |
|---|---|---|
np.array(data) | 직접 값을 넣어 배열 생성 | np.array([1, 2, 3]) |
np.zeros(shape) | 0으로 채운 배열 | np.zeros((2, 3)) |
np.ones(shape) | 1로 채운 배열 | np.ones((2, 3)) |
np.arange(start, stop, step) | 일정 간격 숫자 | np.arange(0, 10, 2) |
np.linspace(start, stop, num) | 구간을 num개로 균등 분할 | np.linspace(0, 1, 5) |
5. 배열 건강검진 4종: ndim, shape, size, dtype
Section titled “5. 배열 건강검진 4종: ndim, shape, size, dtype”계산 전에 아래 네 가지를 확인하면 많은 오류를 미리 막을 수 있습니다.
| 속성 | 뜻 | 예 |
|---|---|---|
ndim | 축의 개수, 차원 수 | 2D 배열이면 2 |
shape | 각 축의 길이 | (3, 4) |
size | 전체 원소 개수 | 12 |
dtype | 원소 자료형 | float64, int64 |
shape가 (3, 4)이면 size는 3 × 4 = 12입니다. 6. 인덱싱과 슬라이싱: 원하는 칸, 행, 열 꺼내기
Section titled “6. 인덱싱과 슬라이싱: 원하는 칸, 행, 열 꺼내기”a = np.array([10, 20, 30, 40])
print(a[0]) # 첫 번째 값 10print(a[1:3]) # index 1부터 3 직전까지 → [20 30]print(a[-1]) # 마지막 값 40A = np.array([ [1, 2, 3], [4, 5, 6],])
print(A[0, 0]) # 1행 1열, NumPy index로는 [0, 0]print(A[1, 2]) # 2행 3열 → 6print(A[0, :]) # 첫 번째 행 전체print(A[:, 1]) # 두 번째 열 전체| 코드 | 의미 |
|---|---|
A[row, col] | 특정 칸 하나 |
A[row, :] | 특정 행 전체 |
A[:, col] | 특정 열 전체 |
A[start:stop] | start부터 stop 직전까지 |
7. reshape와 np.newaxis: 1D를 행/열 모양으로 바꾸기
Section titled “7. reshape와 np.newaxis: 1D를 행/열 모양으로 바꾸기”10주차에서 보았듯이 (3,), (1, 3), (3, 1)은 다릅니다. 지금부터는 같은 숫자라도 shape가 달라지면 NumPy가 다르게 해석할 수 있다는 점을 확인합니다.
(3,) → 축이 하나인 길이 3 배열(1, 3) → 1행 3열(3, 1) → 3행 1열reshape는 전체 원소 개수를 유지하면서 모양을 바꿉니다.
a = np.arange(6)print(a.shape) # (6,)print(a.reshape(2, 3))print(a.reshape(3, 2))np.newaxis는 새 축을 하나 추가합니다.
v = np.array([10, 20, 30])print(v[:, np.newaxis].shape) # (3, 1)print(v[np.newaxis, :].shape) # (1, 3) 8. 원소별 연산과 행렬곱 구분하기
Section titled “8. 원소별 연산과 행렬곱 구분하기”NumPy에서 +, -, *, /는 기본적으로 같은 위치 원소끼리 계산합니다.
A + B # 원소별 덧셈A * B # 원소별 곱셈, 행렬곱 아님10주차에서 행렬곱은 행과 열이 만나 만드는 계산이라고 배웠습니다. NumPy에서는 이 계산을 @로 씁니다.
A @ xA @ B행렬-벡터 곱은 행렬의 각 행이 벡터와 만나 결과 숫자 하나씩을 만드는 계산입니다.
결과의 한 칸은 앞 행렬의 한 행과 뒤 행렬의 한 열을 같은 위치끼리 곱해 더한 값, 즉 내적입니다.
9. axis별 집계: 어느 축을 접어 모으나요?
Section titled “9. axis별 집계: 어느 축을 접어 모으나요?”2D 배열에서 합계나 평균을 구할 때 axis를 지정할 수 있습니다.
arr.sum(axis=0) # 0번 축(행 인덱스 방향)을 내려가며 모음 → 열별 결과가 남음arr.sum(axis=1) # 1번 축(열 인덱스 방향)을 옆으로 모음 → 행별 결과가 남음말이 헷갈리면 “어느 축을 없애서 결과가 무엇으로 남는가”를 생각하세요.
| 원래 shape | 코드 | 결과 개수 | 의미 |
|---|---|---|---|
(3, 4) | sum(axis=0) | 4개 | 행 인덱스 방향으로 내려가며 합쳐서 열별 결과가 남음 |
(3, 4) | sum(axis=1) | 3개 | 열 인덱스 방향으로 옆으로 합쳐서 행별 결과가 남음 |
10. 브로드캐스팅(broadcasting): NumPy가 모양을 맞춰 주는 경우
Section titled “10. 브로드캐스팅(broadcasting): NumPy가 모양을 맞춰 주는 경우”수학 교과서의 행렬 덧셈은 보통 같은 크기끼리만 가능합니다. NumPy는 일부 경우에 작은 배열을 필요한 방향으로 반복해 큰 배열과 계산하게 해 줍니다. 이것을 브로드캐스팅(broadcasting)이라고 합니다.
아무 모양이나 자동으로 맞춰 주는 것은 아닙니다. NumPy는 뒤쪽 축부터 shape를 비교합니다. 각 축이 다음 중 하나면 브로드캐스팅이 가능합니다.
- 두 길이가 같다.
- 둘 중 하나가 1이다.
- 앞쪽에 없는 축은 길이 1처럼 본다.
- 스칼라 shape
()는 모든 shape와 호환된다.
예:
A.shape # (2, 3)row.shape # (3,)A + row # 가능: row가 각 행에 반복됨
col.shape # (2, 1)A + col # 가능: 각 행의 보정값이 옆으로 반복됨실패 예:
A.shape # (2, 3)bad.shape # (2,)A + bad # 실패: 마지막 축 3과 2가 맞지 않음 11. 불리언 마스크(boolean mask): 조건에 맞는 원소만 고르기
Section titled “11. 불리언 마스크(boolean mask): 조건에 맞는 원소만 고르기”불리언 마스크는 각 원소가 조건을 만족하는지를 True/False로 나타내는 배열입니다.
data = np.array([3.0, 7.5, 1.2, 9.8])mask = data > 5.0print(mask) # [False True False True]print(data[mask]) # [7.5 9.8]여러 조건을 묶을 때는 괄호와 &, |를 사용합니다.
normal = (data >= 0.0) & (data <= 10.0)2D 배열에 2D 마스크를 적용하면 조건을 만족한 값만 뽑혀 1D 배열로 나옵니다. 예를 들어 corrected[mask]는 정상 범위에 들어온 값만 한 줄로 모은 배열입니다.
12. 원본을 함께 보는 배열과 별도 복사본
Section titled “12. 원본을 함께 보는 배열과 별도 복사본”NumPy에서 기본 slicing(arr[1:3])은 새 배열처럼 보이지만, 실제로는 원본 데이터를 함께 바라보는 view일 수 있습니다. view를 수정하면 원본도 바뀝니다.
반대로 불리언 마스크(arr[mask])나 정수 배열 인덱싱은 보통 별도 복사본을 만듭니다. 그래서 “잘라 낸 배열이 원본과 연결되어 있는가?”는 선택 방식에 따라 달라질 수 있습니다.
13. 종합 예제: 2D 센서 배열 처리
Section titled “13. 종합 예제: 2D 센서 배열 처리”아래 예제는 이번 주 내용을 한 번에 묶습니다.
- 3일 × 4회 측정값 배열 생성
- shape 건강검진
- 센서 보정값 브로드캐스팅
- axis별 평균
- 불리언 마스크로 정상 범위 비율 계산
종합 예제 해설
Section titled “종합 예제 해설”voltage.shape == (3, 4)이므로 3일, 4회 측정 표로 읽을 수 있습니다.offset.shape == (4,)인 보정값은 각 행에 반복 적용됩니다. 이것이 브로드캐스팅입니다.mean(axis=1)은 1번 축, 즉 열 인덱스 방향으로 옆으로 모아 일별 평균을 만듭니다.mean(axis=0)은 0번 축, 즉 행 인덱스 방향으로 내려가며 같은 측정 회차끼리 모아 회차별 평균을 만듭니다.- 불리언 마스크는 정상 범위에 들어오는 칸을
True로 표시합니다.
실습 문제 · 직접 코딩
Section titled “실습 문제 · 직접 코딩”이번 주 실습은 “계산 결과”보다 shape와 해석 문장을 함께 출력하는 것이 중요합니다.
목표: 배열을 만들고 ndim, shape, size, dtype를 확인합니다.
완료 조건: size == shape[0] * shape[1] 검증을 통과하고, 배열이 몇 행 몇 열인지 한 문장으로 출력하세요.
목표: 2D 배열에서 특정 행, 열, 칸을 꺼냅니다.
완료 조건: 첫 번째 행, 두 번째 열, 3행 4열 값을 출력하세요.
목표: *와 @를 구분합니다.
완료 조건: 원소별 곱, 행렬-행렬 곱, 행렬-벡터 곱의 결과를 각각 출력하고 차이를 한 문장으로 설명하세요.
목표: axis별 평균을 계산하고 결과 shape를 해석합니다.
완료 조건: axis=0, axis=1 평균을 모두 출력하고 각각 무엇을 모은 결과인지 설명하세요.
목표: 브로드캐스팅과 불리언 마스크를 함께 사용합니다.
완료 조건: 보정 후 데이터, 정상 범위 mask, 정상 데이터 개수와 평균을 출력하세요.
제출 전 자기점검
Section titled “제출 전 자기점검”import numpy as np를 사용했는가?- 계산 전
ndim,shape,size,dtype중 최소 2개 이상 확인했는가? *와@의 차이를 설명할 수 있는가?(3,),(1, 3),(3, 1)의 차이를 말할 수 있는가?axis=0,axis=1결과가 몇 개 나오는지 예측했는가?- 브로드캐스팅이 성공한 이유 또는 실패한 이유를 shape로 설명했는가?
- mask 조건을 괄호와
&/|로 작성했는가? - 원본을 보존해야 하는 slice에는
.copy()가 필요한지 생각했는가?