NumPy 마스터로 가는 길! 🎯
어제 배열 만들기와 기본 연산을 배웠죠? 오늘은 인덱싱, 슬라이싱, 브로드캐스팅으로 데이터를 자유자재로 다뤄봐요! 실전에서 가장 많이 쓰는 기법들이에요! 💪
(30분 완독 ⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
🎯 학습 목표 1: 배열 인덱싱 마스터하기
1.1 기본 인덱싱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import numpy as np
# 1차원 배열
arr = np.array([10, 20, 30, 40, 50])
print(arr[0]) # 10 (첫 번째)
print(arr[-1]) # 50 (마지막)
print(arr[2]) # 30 (세 번째)
# 2차원 배열
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(arr2d[0, 0]) # 1 (0행 0열)
print(arr2d[1, 2]) # 6 (1행 2열)
print(arr2d[2, 1]) # 8 (2행 1열)
# Python 리스트와의 차이
print(arr2d[1][2]) # 6 - 이것도 가능하지만
print(arr2d[1, 2]) # 6 - 이게 더 빠르고 권장!
|
1.2 팬시 인덱싱 (Fancy Indexing)
여러 인덱스를 한 번에 지정할 수 있어요!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import numpy as np
arr = np.array([10, 20, 30, 40, 50])
# 여러 인덱스 한번에
indices = [0, 2, 4]
print(arr[indices]) # [10 30 50]
# 리스트로 직접 지정
print(arr[[1, 3]]) # [20 40]
# 2차원에서
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 특정 행들 선택
print(arr2d[[0, 2]])
# [[1 2 3]
# [7 8 9]]
# 특정 위치들 선택
rows = [0, 1, 2]
cols = [0, 1, 2]
print(arr2d[rows, cols]) # [1 5 9] (대각선!)
|
1.3 불리언 인덱싱
조건으로 데이터를 필터링하는 강력한 기능!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 조건으로 필터링
print(arr[arr > 5]) # [6 7 8 9 10]
print(arr[arr % 2 == 0]) # [2 4 6 8 10] (짝수만)
print(arr[arr % 3 == 0]) # [3 6 9] (3의 배수만)
# 여러 조건 조합
print(arr[(arr > 3) & (arr < 8)]) # [4 5 6 7]
print(arr[(arr < 3) | (arr > 8)]) # [1 2 9 10]
# 2차원에서
scores = np.array([[85, 90, 78],
[92, 88, 95],
[65, 70, 72]])
# 80점 이상인 점수만
print(scores[scores >= 80]) # [85 90 92 88 95]
|
🎯 학습 목표 2: 배열 슬라이싱 활용하기
2.1 1차원 슬라이싱
1
2
3
4
5
6
7
8
9
| import numpy as np
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr[2:7]) # [2 3 4 5 6]
print(arr[:5]) # [0 1 2 3 4]
print(arr[5:]) # [5 6 7 8 9]
print(arr[::2]) # [0 2 4 6 8] (2칸씩)
print(arr[::-1]) # [9 8 7 6 5 4 3 2 1 0] (역순)
|
2.2 2차원 슬라이싱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| import numpy as np
arr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 행 슬라이싱
print(arr2d[0:2]) # 0~1행
# [[1 2 3 4]
# [5 6 7 8]]
# 열 슬라이싱
print(arr2d[:, 1:3]) # 모든 행, 1~2열
# [[ 2 3]
# [ 6 7]
# [10 11]]
# 부분 배열 추출
print(arr2d[0:2, 1:3]) # 0~1행, 1~2열
# [[2 3]
# [6 7]]
# 특정 열만
print(arr2d[:, 0]) # [1 5 9] (0번째 열)
print(arr2d[:, -1]) # [4 8 12] (마지막 열)
|
2.3 슬라이싱으로 값 변경하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import numpy as np
arr = np.array([1, 2, 3, 4, 5])
# 여러 값 한번에 변경
arr[1:4] = 0
print(arr) # [1 0 0 0 5]
# 2차원에서
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 특정 영역 변경
arr2d[0:2, 0:2] = 0
print(arr2d)
# [[0 0 3]
# [0 0 6]
# [7 8 9]]
|
⚠️ 주의: 슬라이싱은 원본 배열의 뷰(view)를 반환해요. 변경하면 원본도 바뀝니다!
🎯 학습 목표 3: 브로드캐스팅 이해하기
3.1 브로드캐스팅이란?
서로 다른 형태의 배열끼리 연산할 때, NumPy가 자동으로 형태를 맞춰주는 기능!
1
2
3
4
5
6
7
8
| import numpy as np
# 배열 + 스칼라 (기본 브로드캐스팅)
arr = np.array([1, 2, 3])
print(arr + 10) # [11 12 13]
# 내부적으로는 이렇게 동작:
# [1, 2, 3] + [10, 10, 10] = [11, 12, 13]
|
3.2 브로드캐스팅 규칙
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| import numpy as np
# 예제 1: (3,) + (3,) - 같은 형태
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print(a + b) # [11 22 33]
# 예제 2: (3, 3) + (3,) - 1차원이 확장됨
arr2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
row = np.array([10, 20, 30])
print(arr2d + row)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
# 예제 3: (3, 1) + (3,) - 열 벡터와 행 벡터
col = np.array([[1], [2], [3]]) # (3, 1)
row = np.array([10, 20, 30]) # (3,)
print(col + row)
# [[11 21 31]
# [12 22 32]
# [13 23 33]]
|
3.3 실전 브로드캐스팅 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import numpy as np
# 성적 정규화 (각 과목별로 평균 빼기)
scores = np.array([[85, 90, 78],
[92, 88, 95],
[65, 70, 72]])
# 각 열(과목)의 평균
means = np.mean(scores, axis=0)
print(f"과목별 평균: {means}")
# 평균 빼기 (브로드캐스팅!)
normalized = scores - means
print(normalized)
# 온도 변환: 섭씨 → 화씨
celsius = np.array([0, 10, 20, 30, 40])
fahrenheit = celsius * 9/5 + 32
print(fahrenheit) # [ 32. 50. 68. 86. 104.]
|
🎯 학습 목표 4: 실전 데이터 조작하기
4.1 조건에 따른 값 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import numpy as np
# np.where: 조건에 따라 다른 값 반환
arr = np.array([1, 2, 3, 4, 5])
# 3보다 크면 '크다', 아니면 '작다'
result = np.where(arr > 3, '크다', '작다')
print(result) # ['작다' '작다' '작다' '크다' '크다']
# 숫자로도 가능
result2 = np.where(arr > 3, 1, 0)
print(result2) # [0 0 0 1 1]
# 점수를 합격/불합격으로
scores = np.array([85, 42, 73, 91, 55])
pass_fail = np.where(scores >= 60, '합격', '불합격')
print(pass_fail) # ['합격' '불합격' '합격' '합격' '불합격']
|
4.2 정렬하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| import numpy as np
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])
# 정렬된 복사본 반환
print(np.sort(arr)) # [1 1 2 3 4 5 6 9]
# 내림차순
print(np.sort(arr)[::-1]) # [9 6 5 4 3 2 1 1]
# 정렬된 인덱스 반환
print(np.argsort(arr)) # [1 3 6 0 2 4 7 5]
# 2차원 정렬
arr2d = np.array([[3, 1, 2],
[6, 4, 5]])
print(np.sort(arr2d, axis=1)) # 각 행 내에서 정렬
# [[1 2 3]
# [4 5 6]]
|
4.3 유일한 값 찾기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import numpy as np
arr = np.array([1, 2, 2, 3, 3, 3, 4])
# 중복 제거
print(np.unique(arr)) # [1 2 3 4]
# 개수와 함께
values, counts = np.unique(arr, return_counts=True)
print(values) # [1 2 3 4]
print(counts) # [1 2 3 1]
# 딕셔너리로 만들기
for v, c in zip(values, counts):
print(f"{v}: {c}개")
|
4.4 결측값 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import numpy as np
# nan (Not a Number) 다루기
arr = np.array([1.0, 2.0, np.nan, 4.0, 5.0])
print(np.isnan(arr)) # [False False True False False]
print(np.sum(np.isnan(arr))) # 1 (nan 개수)
# nan 제외하고 계산
print(np.nanmean(arr)) # 3.0 (nan 제외 평균)
print(np.nansum(arr)) # 12.0 (nan 제외 합계)
# nan을 다른 값으로 대체
arr_filled = np.where(np.isnan(arr), 0, arr)
print(arr_filled) # [1. 2. 0. 4. 5.]
|
💡 실전 팁 & 주의사항
✅ 성능 팁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| import numpy as np
# 1. 벡터 연산 사용하기 (for문 피하기)
arr = np.arange(1000000)
# ❌ 느림
result = []
for x in arr:
result.append(x * 2)
# ✅ 빠름
result = arr * 2
# 2. 미리 배열 크기 지정하기
# ❌ 계속 크기 변경
result = np.array([])
for i in range(1000):
result = np.append(result, i)
# ✅ 미리 할당
result = np.empty(1000)
for i in range(1000):
result[i] = i
|
⚠️ 흔한 실수
1
2
3
4
5
6
7
8
9
10
11
| import numpy as np
# 1. 뷰 vs 복사 혼동
arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4] # 뷰 (원본 공유)
copy = arr[1:4].copy() # 복사본 (독립적)
# 2. 브로드캐스팅 형태 오류
a = np.array([[1, 2], [3, 4]]) # (2, 2)
b = np.array([1, 2, 3]) # (3,)
# a + b # Error! 형태가 안 맞음
|
🧪 연습 문제
문제 1: 이미지 밝기 조절
3x3 그레이스케일 이미지(0~255)가 있습니다. 모든 픽셀 값을 50 증가시키되, 255를 넘지 않도록 하세요.
1
2
3
| image = np.array([[100, 150, 200],
[50, 100, 150],
[200, 220, 240]])
|
💡 힌트
np.where(조건, 참일때값, 거짓일때값) 사용 - 또는
np.clip(arr, min, max) 사용
✅ 정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import numpy as np
image = np.array([[100, 150, 200],
[50, 100, 150],
[200, 220, 240]])
# 방법 1: np.where 사용
brightened = image + 50
result1 = np.where(brightened > 255, 255, brightened)
print(result1)
# 방법 2: np.clip 사용 (더 간단!)
result2 = np.clip(image + 50, 0, 255)
print(result2)
|
문제 2: 성적 등급 매기기
90점 이상 A, 80점 이상 B, 70점 이상 C, 나머지 D 등급을 매기세요.
1
| scores = np.array([95, 82, 67, 73, 88, 91, 55, 78])
|
💡 힌트
np.select([조건리스트], [값리스트], default=기본값) 사용 - 또는 중첩
np.where() 사용
✅ 정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import numpy as np
scores = np.array([95, 82, 67, 73, 88, 91, 55, 78])
# np.select 사용
conditions = [
scores >= 90,
scores >= 80,
scores >= 70
]
choices = ['A', 'B', 'C']
grades = np.select(conditions, choices, default='D')
for score, grade in zip(scores, grades):
print(f"{score}점: {grade}등급")
|
📝 오늘 배운 내용 정리
- 팬시 인덱싱: 여러 인덱스를 한번에
arr[[0, 2, 4]] - 불리언 인덱싱: 조건으로 필터링
arr[arr > 5] - 2차원 슬라이싱:
arr2d[행범위, 열범위] - 브로드캐스팅: 다른 형태의 배열 자동 연산
- 조건부 처리:
np.where(), np.select()
🔗 관련 자료
📚 이전 학습
Day 61: NumPy 기초 ⭐⭐
어제는 NumPy 배열 생성과 기본 연산을 배웠어요!
📚 다음 학습
Day 63: Pandas 기초 ⭐⭐
내일은 데이터 분석의 핵심 라이브러리 Pandas를 시작해요!
“NumPy를 마스터하면 Pandas가 쉬워져요!” 💪
| Day 62/100 | Phase 7: 데이터 분석 기초 | #100DaysOfPython |