Skip to content

Latest commit

 

History

History

step10_numpy

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

step10_numpy — NumPy 배열

학습 순서 10번. 데이터 분석 도구의 첫 챕터입니다.

⚠️ 이 저장소에서 처음으로 설치가 필요합니다. step01~step09 는 파이썬만 있으면 됐지만 여기부터는 외부 라이브러리를 씁니다.

설치

python3 -m venv .venv          # 가상환경 (프로젝트마다 따로 두는 것이 표준)
source .venv/bin/activate      # 윈도우는 .venv\Scripts\activate
pip install -r requirements.txt

requirements.txt 는 저장소 루트에 있습니다. 가상환경을 쓰는 이유는 프로젝트마다 필요한 버전이 다르기 때문입니다. 시스템 파이썬에 전부 설치하면 나중에 버전이 충돌합니다.

왜 NumPy를 쓰나

step05 에서 배운 리스트로도 숫자를 담을 수 있습니다. 그런데 왜 배열이 따로 필요할까요.

[1, 2, 3, 4] * 2        # [1, 2, 3, 4, 1, 2, 3, 4]  ← 이어 붙이기
np.array([1,2,3,4]) * 2 # [2, 4, 6, 8]              ← 각 원소에 곱하기

리스트는 "값을 담는 상자"이고 배열은 "계산의 대상"입니다. 100만 개 숫자에 2를 곱할 때 리스트는 for 를 돌아야 하지만 배열은 한 줄입니다. 그리고 훨씬 빠릅니다 — vectorEx.py 를 실행하면 실측이 나옵니다 (이 환경에서 약 19배).

pandas의 DataFrame 도 내부는 NumPy 배열입니다. 여기를 이해하면 pandas가 훨씬 쉬워집니다.

학습 목표

  • ndarray 를 만들고 shape·dtype·ndim 으로 구조를 읽는다
  • 리스트와 결정적으로 다른 두 가지 — 같은 타입만 담는다, 연산이 원소별로 적용된다
  • [행, 열] 로 2차원을 한 번에 자른다
  • 불리언 인덱싱으로 조건에 맞는 값만 고른다
  • axis 를 이해한다 (넘파이에서 가장 헷갈리는 개념)
  • reshape·전치·합치기로 모양을 바꾼다

파일 지도

파일 내용
arrayEx1.py ★ 배열 만들기, shape/dtype, 리스트와의 차이
arrayEx2.py ★ 인덱싱·슬라이싱, 불리언 인덱싱, where, 뷰 vs 복사
vectorEx.py ★ 벡터 연산, 브로드캐스팅, for 문과 속도 비교
statEx.py ★★ 통계 함수와 axis
reshapeEx.py reshape·flatten·전치·vstack/hstack
test1.py 연습문제 — 성적 행렬 집계, 표준화

실행 방법

python3 step10_numpy/arrayEx1.py

전부 입력 없이 실행됩니다.

짚고 갈 지점

배열은 타입을 섞을 수 없다

['홍길동', 23, 175.5, True]        # 리스트 — 아무거나 섞어 담는다
np.array([1, 2.5, 3])              # → [1.0 2.5 3.0]  전부 float 로 통일
np.array([1, 2, '3'])              # → ['1' '2' '3']  전부 문자열로 통일!

조용히 통일해 버립니다. 숫자 배열에 문자열이 하나 섞이면 전체가 문자열이 되어 계산이 안 됩니다. step08 에서 CSV를 읽으면 전부 문자열이었던 것과 이어지는 이야기입니다 — 그래서 dtype 확인이 중요합니다.

같은 타입만 담는 덕분에 원소가 같은 크기로 메모리에 나란히 놓이고, 그게 속도의 근거입니다.

[행, 열] 로 한 번에

b[1][2]      # 리스트 방식도 되지만
b[1, 2]      # 이게 넘파이 방식
b[:, 1]      # 1열 전체 — 리스트로는 for 를 돌려야 했다
b[0:2, 1:3]  # 부분 블록

b[:, 1](열 전체 뽑기)이 리스트 대비 가장 큰 편의입니다. step05 의 중첩 리스트에서 열을 뽑으려면 [row[1] for row in table] 이 필요했습니다.

불리언 인덱싱

scores[scores >= 60]                    # 조건에 맞는 값만
(scores >= 60).sum()                    # 개수 (True 는 1로 세어진다)
(scores >= 60).mean()                   # 비율
scores[(scores >= 60) & (scores < 90)]  # 여러 조건

⚠️ and/or 가 아니라 &/| 입니다. 그리고 각 조건을 괄호로 묶어야 합니다. & 가 비교 연산자보다 우선순위가 높아서 괄호가 없으면 엉뚱하게 묶입니다.

pandas의 필터링이 정확히 이 문법이라, 여기서 익혀 두면 그대로 씁니다.

⚠️ 슬라이싱이 복사가 아닙니다

d = np.array([1, 2, 3, 4, 5])
view = d[1:4]        # '뷰' — 원본과 메모리를 공유한다
view[0] = 999
print(d)             # [1 999 3 4 5]  ← 원본이 바뀐다!

리스트와 반대입니다. step05 에서 c[:] 는 복사였지만 넘파이 슬라이싱은 뷰입니다. 원본을 지키려면 .copy() 를 명시해야 합니다.

큰 배열을 자를 때마다 복사하면 메모리가 감당이 안 되기 때문에 이렇게 설계됐습니다. 편의가 아니라 성능을 위한 선택입니다.

axis — 그림으로 외우세요

가장 많이 틀리는 부분입니다.

        국어  영어  수학
홍길동   85   73   68
김길동   90   88   95
최길동   55   62   48
         ↓    ↓    ↓
      axis=0 (행을 따라 아래로 접는다) → 과목별 결과

홍길동   85   73   68  →  axis=1 (열을 따라 옆으로 접는다) → 학생별 결과
table.mean(axis=0)   # 과목별 평균 — 학생들이 뭉개진다
table.mean(axis=1)   # 학생별 평균 — 과목들이 뭉개진다

"지정한 축이 사라진다" 고 외우면 편합니다. shape 로 확인할 수 있습니다.

table.shape              # (3, 3)
table.sum(axis=0).shape  # (3,)  ← 0번 축(행)이 사라졌다

방향이 헷갈릴 때는 statEx.py 처럼 for 문 결과와 대조해 보세요. 확실합니다.

브로드캐스팅

a + 100                       # 모든 원소에 100
m + np.array([10, 20, 30])    # 행마다 같은 열에 더한다

모양이 다른 배열끼리도 규칙에 맞으면 자동으로 늘려서 계산합니다. test1.py 의 표준화가 이걸 씁니다.

z = (table - table.mean(axis=0)) / table.std(axis=0)
#    (4,3)  -      (3,)          /       (3,)

(3,) 짜리가 4개 행에 각각 적용됩니다. for 문이 하나도 없습니다.

흔한 실수

증상 원인
계산이 안 되고 문자열이 이어붙음 dtype 이 문자열. astype(float) 필요
원본이 몰래 바뀜 슬라이싱은 뷰. .copy() 필요
ValueError: The truth value of an array... and/or 를 씀 → &/`
결과가 행/열 반대로 나옴 axis 를 반대로 지정
cannot reshape array of size 12 into shape (5,3) 원소 개수가 안 맞음
astype(int) 결과가 예상과 다름 반올림이 아니라 버림 (1.7 → 1)

연습 과제

  1. arrayEx1.py 에서 np.array([1, 2, '3']) 의 dtype 을 확인하고, 이 배열에 + 1 을 해 보세요. 어떤 에러가 나나요?
  2. arrayEx2.py 의 뷰 예제에서 .copy() 를 넣고 빼며 원본이 바뀌는지 확인하세요.
  3. statEx.py 의 table 에 학생 두 명을 더 추가하고, axis=0/axis=1 결과의 shape 가 어떻게 변하는지 예측한 뒤 확인하세요.
  4. test1.py 의 표준화 결과에서 가장 잘한 과목(z값이 가장 큰 과목)을 학생별로 찾으세요. (힌트: argmax(axis=1))
  5. vectorEx.py 의 속도 비교에서 n 을 10배로 늘리면 배수가 어떻게 변하나요?
  6. step04_loop/test1.py 의 "1~100 중 3의 배수 개수"를 넘파이 한 줄로 구하세요.
  7. np.random 으로 5×3 난수 배열을 만들고, 각 행에서 최댓값을 뺀 값을 구하세요. (step03 의 random 모듈과 무엇이 다른지도 보세요)