강의자료 · 머신러닝의 이해

[5주차] 강력한 데이터 계산: NumPy 첫걸음 🔢

안녕하세요! 5주차 수업에 오신 것을 환영합니다.

지난 시간까지 우리는 파이썬의 기본 문법으로 데이터를 다루는 법을 배웠습니다.

하지만 만약 우리가 다룰 데이터가 수백만 개라면 어떨까요?

for 반복문만으로는 너무 느리고 비효율적일 겁니다.

오늘은 이 문제를 해결해 줄 Machine Learning의 필수 라이브러리, NumPy를 소개합니다.

NumPy를 이용해 대규모 숫자 데이터를 얼마나 빠르고 효율적으로 처리할 수 있는지,

그리고 이것이 왜 Machine Learning에서 중요한지 직접 확인해 보겠습니다.

🐢 모듈 1: 왜 파이썬 리스트로는 부족할까?

# 파이썬 리스트로 점수 관리
scores_list = [80, 95, 72, 100, 88]

# 모든 점수를 5점씩 올리기 위한 과정
new_scores_list = [] # 새로운 점수를 담을 빈 리스트
for score in scores_list:
    # 하나씩 꺼내서 5를 더하고 새 리스트에 추가
    new_scores_list.append(score + 5)

print(f"원본 점수 리스트: {scores_list}")
print(f"5점 올린 후 리스트: {new_scores_list}")
print("\n데이터가 5개만 있어도 이렇게 번거로운데, 100만 개라면...?")
원본 점수 리스트: [80, 95, 72, 100, 88]
5점 올린 후 리스트: [85, 100, 77, 105, 93]

데이터가 5개만 있어도 이렇게 번거로운데, 100만 개라면...?

✨ 모듈 2: NumPy 배열(Array)의 등장

import numpy as np # 관습적으로 np라는 별명으로 불러옵니다.

# 파이썬 리스트를 NumPy 배열로 변환
scores_array = np.array([80, 95, 72, 100, 88])

print(f"NumPy 배열: {scores_array}")
print(f"자료형: {type(scores_array)}")
NumPy 배열: [ 80  95  72 100  88]
자료형: <class 'numpy.ndarray'>
# NumPy 배열을 이용해 모든 점수 5점 올리기
# 반복문이 전혀 필요 없습니다!

new_scores_array = scores_array + 5

print(f"원본 점수 배열: {scores_array}")
print(f"5점 올린 후 배열: {new_scores_array}")

# 덧셈뿐만 아니라 모든 산술 연산이 가능합니다.
print(f"\n모든 점수 10% 인상: {scores_array * 1.1}")
print(f"모든 점수 2로 나누기: {scores_array / 2}")
원본 점수 배열: [ 80  95  72 100  88]
5점 올린 후 배열: [ 85 100  77 105  93]

모든 점수 10% 인상: [ 88.  104.5  79.2 110.   96.8]
모든 점수 2로 나누기: [40.  47.5 36.  50.  44. ]

🚀 모듈 3: Machine Learning을 위한 NumPy 핵심 기능

1. 배열의 형태 (Shape)

# 1차원 배열 (벡터)
# [상자1, 상자2, 상자3, 상자4, 상자5, 상자6] -> 한 줄로 6개
array_1d = np.array([1, 2, 3, 4, 5, 6])
print(f"1D 배열: {array_1d}")
print(f"1D 배열의 Shape: {array_1d.shape}") # (6,) -> 아이템이 6개인 한 줄

print("-" * 20)

# 2차원 배열 (행렬)
# [[국어1, 영어1],  -> 첫 번째 줄 (행)
#  [국어2, 영어2],  -> 두 번째 줄 (행)
#  [국어3, 영어3]] -> 세 번째 줄 (행)
array_2d = np.array([
    [90, 85], # 학생 1
    [75, 92], # 학생 2
    [88, 80]  # 학생 3
])
print(f"2D 배열:\n{array_2d}")
print(f"2D 배열의 Shape: {array_2d.shape}")
# (3, 2) -> 3개의 행, 2개의 열
1D 배열: [1 2 3 4 5 6]
1D 배열의 Shape: (6,)
--------------------
2D 배열:
[[90 85]
 [75 92]
 [88 80]]
2D 배열의 Shape: (3, 2)

2. 배열의 형태 바꾸기 (Reshape)

# 1차원 배열 (아이템 6개)
array_1d = np.array([1, 2, 3, 4, 5, 6])
print(f"원본 1D 배열: {array_1d}")
print(f"원본 1D 배열 Shape: {array_1d.shape}")

# 2행 3열의 2차원 배열로 형태 변경
# [1,2,3,4,5,6] -> [[1,2,3],
#                   [4,5,6]]
array_reshaped = array_1d.reshape(2, 3)
print(f"\n(2, 3)으로 Reshape:\n{array_reshaped}")
print(f"Shape: {array_reshaped.shape}")

# -1을 사용하여 열의 개수를 자동으로 계산
# "6개 원소를 3개의 행으로 만들어줘. 열은 알아서!" -> 2개의 열 필요
array_reshaped_auto = array_1d.reshape(3, -1)
print(f"\n(3, -1)으로 Reshape:\n{array_reshaped_auto}")
print(f"Shape: {array_reshaped_auto.shape}")
원본 1D 배열: [1 2 3 4 5 6]
원본 1D 배열 Shape: (6,)

(2, 3)으로 Reshape:
[[1 2 3]
 [4 5 6]]
Shape: (2, 3)

(3, -1)으로 Reshape:
[[1 2]
 [3 4]
 [5 6]]
Shape: (3, 2)

3. 축(Axis)을 따른 연산

# 3명 학생의 국어, 영어 점수
scores_2d = np.array([
    [90, 85], # 학생 1
    [75, 92], # 학생 2
    [88, 80]  # 학생 3
])
print(f"원본 점수 배열 (Shape: {scores_2d.shape}):\n{scores_2d}")

# 1. 전체 합계 / 평균
print(f"\n전체 총점: {scores_2d.sum()}")
print(f"전체 평균: {scores_2d.mean():.2f}")

# 2. axis=0 (세로 방향 ↓) 연산
# 각 '열'의 합계/평균 -> 과목별 통계
print(f"\n--- axis=0 (과목별 통계) ---")

print(f"과목별 총점: {scores_2d.sum(axis=0)}")
# [90+75+88, 85+92+80]

print(f"과목별 평균: {scores_2d.mean(axis=0)}")
# [avg(90,75,88), avg(85,92,80)]

print(f"과목별 최고점: {scores_2d.max(axis=0)}")
# [max(90,75,88), max(85,92,80)]

# 3. axis=1 (가로 방향 →) 연산
# 각 '행'의 합계/평균 -> 학생별 통계
print(f"\n--- axis=1 (학생별 통계) ---")

print(f"학생별 총점: {scores_2d.sum(axis=1)}")
# [90+85, 75+92, 88+80]

print(f"학생별 평균: {scores_2d.mean(axis=1)}")
# [avg(90,85), avg(75,92), avg(88,80)]

print(f"학생별 최고점: {scores_2d.max(axis=1)}")
# [max(90,85), max(75,92), max(88,80)]
원본 점수 배열 (Shape: (3, 2)):
[[90 85]
 [75 92]
 [88 80]]

전체 총점: 510
전체 평균: 85.00

--- axis=0 (과목별 통계) ---
과목별 총점: [253 257]
과목별 평균: [84.33333333 85.66666667]
과목별 최고점: [90 92]

--- axis=1 (학생별 통계) ---
학생별 총점: [175 167 168]
학생별 평균: [87.5 83.5 84. ]
학생별 최고점: [90 92 88]

4. 유용한 배열 생성 함수

# 0부터 9까지의 숫자를 가진 배열
range_array = np.arange(10)
print(f"np.arange(10):\n{range_array}")

# (2, 3) 형태로 모든 값이 0인 배열
zeros_array = np.zeros((2, 3))
print(f"\nnp.zeros((2, 3)):\n{zeros_array}")

# (3, 2) 형태로 모든 값이 1인 배열
ones_array = np.ones((3, 2))
print(f"\nnp.ones((3, 2)):\n{ones_array}")
np.arange(10):
[0 1 2 3 4 5 6 7 8 9]

np.zeros((2, 3)):
[[0. 0. 0.]
 [0. 0. 0.]]

np.ones((3, 2)):
[[1. 1.]
 [1. 1.]
 [1. 1.]]