Skip to content

11주차 · NumPy 기초 입문

이 장을 마치면 다음을 할 수 있습니다.

  • import numpy as npnp.array(...)의 의미를 설명한다.
  • NumPy의 기본 자료구조 ndarray를 “shape가 있는 숫자 배열”로 이해한다.
  • ndim, shape, size, dtype로 배열을 점검한다.
  • np.array, np.zeros, np.ones, np.arange, np.linspace로 배열을 만든다.
  • 1D/2D 배열에서 인덱싱, 슬라이싱, 불리언 마스크를 사용한다.
  • reshapenp.newaxis(3,), (1, 3), (3, 1)의 차이를 확인한다.
  • 원소별 연산 *와 행렬곱 @를 구분한다.
  • axis=0, axis=1 방향으로 합계와 평균을 계산한다.
  • 브로드캐스팅(broadcasting)이 되는 경우와 실패하는 경우를 설명한다.
  • 원본을 함께 보는 배열(view)과 별도 복사본(copy)의 차이를 알고 원본 변경 위험을 피한다.

NumPy 예제를 찾아보다 보면 다음처럼 >>>가 붙은 코드를 볼 수 있습니다.

>>> import numpy as np
>>> np.array([1, 2, 3])
array([1, 2, 3])

여기서 >>>는 Python 대화형 프롬프트입니다. 실제 코드에 붙이지 않습니다.

# 실제 코드에는 이렇게 씁니다.
import numpy as np
arr = 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)

처음에는 네 가지를 고정 습관으로 두세요.

  1. import numpy as np로 NumPy를 불러온다.
  2. np.array([...])로 리스트를 배열로 바꾼다.
  3. 계산 전 shape, dtype를 확인한다.
  4. arr * 2, arr + arr는 원소별 연산임을 기억한다.
예제 1 · NumPy 첫 배열 코드를 실행하고 출력 결과를 확인하세요.
Ready

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 listNumPy 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]
예제 2 · list와 ndarray 연산 비교 코드를 실행하고 출력 결과를 확인하세요.
Ready

처음 실습에서 자주 쓰는 배열 생성 함수를 정리하면 다음과 같습니다.

함수역할
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)
예제 3 · 배열 생성 함수 코드를 실행하고 출력 결과를 확인하세요.
Ready

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입니다.
예제 4 · ndim, shape, size, dtype 코드를 실행하고 출력 결과를 확인하세요.
Ready

6. 인덱싱과 슬라이싱: 원하는 칸, 행, 열 꺼내기

Section titled “6. 인덱싱과 슬라이싱: 원하는 칸, 행, 열 꺼내기”
a = np.array([10, 20, 30, 40])
print(a[0]) # 첫 번째 값 10
print(a[1:3]) # index 1부터 3 직전까지 → [20 30]
print(a[-1]) # 마지막 값 40
A = np.array([
[1, 2, 3],
[4, 5, 6],
])
print(A[0, 0]) # 1행 1열, NumPy index로는 [0, 0]
print(A[1, 2]) # 2행 3열 → 6
print(A[0, :]) # 첫 번째 행 전체
print(A[:, 1]) # 두 번째 열 전체
코드의미
A[row, col]특정 칸 하나
A[row, :]특정 행 전체
A[:, col]특정 열 전체
A[start:stop]start부터 stop 직전까지
예제 5 · 2D indexing과 slicing 코드를 실행하고 출력 결과를 확인하세요.
Ready

7. reshapenp.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)
예제 6 · reshape와 newaxis 코드를 실행하고 출력 결과를 확인하세요.
Ready

8. 원소별 연산과 행렬곱 구분하기

Section titled “8. 원소별 연산과 행렬곱 구분하기”

NumPy에서 +, -, *, /는 기본적으로 같은 위치 원소끼리 계산합니다.

A + B # 원소별 덧셈
A * B # 원소별 곱셈, 행렬곱 아님

10주차에서 행렬곱은 행과 열이 만나 만드는 계산이라고 배웠습니다. NumPy에서는 이 계산을 @로 씁니다.

A @ x
A @ B
인터랙티브 · 10주차의 Ax 계산 다시 보기 값을 바꾸며 계산 규칙을 눈으로 확인하세요.

행렬-벡터 곱은 행렬의 각 행이 벡터와 만나 결과 숫자 하나씩을 만드는 계산입니다.

예제 7 · *와 @ 비교 코드를 실행하고 출력 결과를 확인하세요.
Ready
인터랙티브 · NumPy shape와 연산 가능 여부 값을 바꾸며 계산 규칙을 눈으로 확인하세요.

인터랙티브 · A @ 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개열 인덱스 방향으로 옆으로 합쳐서 행별 결과가 남음
인터랙티브 · axis=0과 axis=1 방향 확인 값을 바꾸며 계산 규칙을 눈으로 확인하세요.

예제 8 · axis별 sum과 mean 코드를 실행하고 출력 결과를 확인하세요.
Ready

10. 브로드캐스팅(broadcasting): NumPy가 모양을 맞춰 주는 경우

Section titled “10. 브로드캐스팅(broadcasting): NumPy가 모양을 맞춰 주는 경우”

수학 교과서의 행렬 덧셈은 보통 같은 크기끼리만 가능합니다. NumPy는 일부 경우에 작은 배열을 필요한 방향으로 반복해 큰 배열과 계산하게 해 줍니다. 이것을 브로드캐스팅(broadcasting)이라고 합니다.

인터랙티브 · 브로드캐스팅 성공과 실패 값을 바꾸며 계산 규칙을 눈으로 확인하세요.

아무 모양이나 자동으로 맞춰 주는 것은 아닙니다. NumPy는 뒤쪽 축부터 shape를 비교합니다. 각 축이 다음 중 하나면 브로드캐스팅이 가능합니다.

  1. 두 길이가 같다.
  2. 둘 중 하나가 1이다.
  3. 앞쪽에 없는 축은 길이 1처럼 본다.
  4. 스칼라 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가 맞지 않음
예제 9 · broadcasting으로 센서 보정값 더하기 코드를 실행하고 출력 결과를 확인하세요.
Ready

11. 불리언 마스크(boolean mask): 조건에 맞는 원소만 고르기

Section titled “11. 불리언 마스크(boolean mask): 조건에 맞는 원소만 고르기”

불리언 마스크는 각 원소가 조건을 만족하는지를 True/False로 나타내는 배열입니다.

data = np.array([3.0, 7.5, 1.2, 9.8])
mask = data > 5.0
print(mask) # [False True False True]
print(data[mask]) # [7.5 9.8]

여러 조건을 묶을 때는 괄호와 &, |를 사용합니다.

normal = (data >= 0.0) & (data <= 10.0)

2D 배열에 2D 마스크를 적용하면 조건을 만족한 값만 뽑혀 1D 배열로 나옵니다. 예를 들어 corrected[mask]는 정상 범위에 들어온 값만 한 줄로 모은 배열입니다.

예제 10 · 불리언 마스크로 정상 범위 필터링 코드를 실행하고 출력 결과를 확인하세요.
Ready

12. 원본을 함께 보는 배열과 별도 복사본

Section titled “12. 원본을 함께 보는 배열과 별도 복사본”

NumPy에서 기본 slicing(arr[1:3])은 새 배열처럼 보이지만, 실제로는 원본 데이터를 함께 바라보는 view일 수 있습니다. view를 수정하면 원본도 바뀝니다.

반대로 불리언 마스크(arr[mask])나 정수 배열 인덱싱은 보통 별도 복사본을 만듭니다. 그래서 “잘라 낸 배열이 원본과 연결되어 있는가?”는 선택 방식에 따라 달라질 수 있습니다.

예제 11 · 원본과 복사본 차이 코드를 실행하고 출력 결과를 확인하세요.
Ready

13. 종합 예제: 2D 센서 배열 처리

Section titled “13. 종합 예제: 2D 센서 배열 처리”

아래 예제는 이번 주 내용을 한 번에 묶습니다.

  • 3일 × 4회 측정값 배열 생성
  • shape 건강검진
  • 센서 보정값 브로드캐스팅
  • axis별 평균
  • 불리언 마스크로 정상 범위 비율 계산
종합 예제 · 2D 센서 데이터 처리 코드를 실행하고 출력 결과를 확인하세요.
Ready
  1. voltage.shape == (3, 4)이므로 3일, 4회 측정 표로 읽을 수 있습니다.
  2. offset.shape == (4,)인 보정값은 각 행에 반복 적용됩니다. 이것이 브로드캐스팅입니다.
  3. mean(axis=1)은 1번 축, 즉 열 인덱스 방향으로 옆으로 모아 일별 평균을 만듭니다.
  4. mean(axis=0)은 0번 축, 즉 행 인덱스 방향으로 내려가며 같은 측정 회차끼리 모아 회차별 평균을 만듭니다.
  5. 불리언 마스크는 정상 범위에 들어오는 칸을 True로 표시합니다.

이번 주 실습은 “계산 결과”보다 shape와 해석 문장을 함께 출력하는 것이 중요합니다.

목표: 배열을 만들고 ndim, shape, size, dtype를 확인합니다.

완료 조건: size == shape[0] * shape[1] 검증을 통과하고, 배열이 몇 행 몇 열인지 한 문장으로 출력하세요.

실습 1 · 배열 건강검진 코드를 실행하고 출력 결과를 확인하세요.
Ready

  • import numpy as np를 사용했는가?
  • 계산 전 ndim, shape, size, dtype 중 최소 2개 이상 확인했는가?
  • *@의 차이를 설명할 수 있는가?
  • (3,), (1, 3), (3, 1)의 차이를 말할 수 있는가?
  • axis=0, axis=1 결과가 몇 개 나오는지 예측했는가?
  • 브로드캐스팅이 성공한 이유 또는 실패한 이유를 shape로 설명했는가?
  • mask 조건을 괄호와 &/|로 작성했는가?
  • 원본을 보존해야 하는 slice에는 .copy()가 필요한지 생각했는가?