포스트

[Python 100일 챌린지] Day 62 - NumPy 활용

[Python 100일 챌린지] Day 62 - NumPy 활용

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}등급")

📝 오늘 배운 내용 정리

  1. 팬시 인덱싱: 여러 인덱스를 한번에 arr[[0, 2, 4]]
  2. 불리언 인덱싱: 조건으로 필터링 arr[arr > 5]
  3. 2차원 슬라이싱: arr2d[행범위, 열범위]
  4. 브로드캐스팅: 다른 형태의 배열 자동 연산
  5. 조건부 처리: np.where(), np.select()

🔗 관련 자료


📚 이전 학습

Day 61: NumPy 기초 ⭐⭐

어제는 NumPy 배열 생성과 기본 연산을 배웠어요!

📚 다음 학습

Day 63: Pandas 기초 ⭐⭐

내일은 데이터 분석의 핵심 라이브러리 Pandas를 시작해요!


“NumPy를 마스터하면 Pandas가 쉬워져요!” 💪

Day 62/100 Phase 7: 데이터 분석 기초 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.