Pandas를 실무 흐름으로 이해하기
Pandas로 CSV, Excel, DB 데이터를 읽고 정제하며 집계합니다. 인덱싱, 병합, groupby 내부 동작, 성능 최적화까지 — AI/ML에 들어가기 전 데이터 품질을 만들기 위한 핵심 도구를 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
Pandas로 CSV, Excel, DB 데이터를 읽고 정제하며 집계합니다. 인덱싱, 병합, groupby 내부 동작, 성능 최적화까지 — AI/ML에 들어가기 전 데이터 품질을 만들기 위한 핵심 도구를 다룹니다.
Pandas로 CSV, Excel, DB 데이터를 읽고 정제하며 집계합니다. 인덱싱, 병합, groupby 내부 동작, 성능 최적화까지 — AI/ML에 들어가기 전 데이터 품질을 만들기 위한 핵심 도구를 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.
문법보다 요청이 들어와 검증, 처리, 저장, 응답으로 이어지는 경계를 먼저 잡습니다.
글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Pandas를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.
Pandas를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.
| 객체 | 설명 |
|---|---|
| Series | 1차원 라벨 배열입니다. |
| DataFrame | 행과 열을 가진 2차원 표 데이터입니다. |
| Index | 행/열 라벨과 정렬 기준입니다. |
여기서는 데이터 로드을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
import pandas as pd
df = pd.read_csv("orders.csv")
print(df.head())
print(df.info())여기서는 인덱싱 & 필터링을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
# loc — 라벨 기준 (컬럼 이름, 인덱스 값)
df.loc[0, "amount"]
df.loc[df["amount"] > 10000, ["user_id", "amount"]]
# iloc — 정수 위치 기준 (엑셀의 셀 좌표처럼)
df.iloc[0, 2] # 0번째 행, 2번째 열
df.iloc[:5, :3] # 처음 5행, 처음 3열
# 불리언 인덱싱 — 조건을 만족하는 행만 선택
high_value = df[(df["amount"] > 10000) & (df["month"] == "2026-01")]| 방식 | 기준 | 예시 |
|---|---|---|
| loc | 라벨(컬럼명, 인덱스 값) | df.loc[3, "amount"] |
| iloc | 정수 위치 | df.iloc[3, 1] |
| 불리언 인덱싱 | 조건식 결과(True/False) | df[df["amount"] > 100] |
여기서는 정제을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
df = df.dropna(subset=["user_id", "amount"])
df["amount"] = df["amount"].astype(float)
df["ordered_at"] = pd.to_datetime(df["ordered_at"])
df["month"] = df["ordered_at"].dt.to_period("M")여기서는 병합 (merge/concat)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
orders = pd.DataFrame({"order_id": [1, 2, 3], "user_id": [10, 20, 10]})
users = pd.DataFrame({"user_id": [10, 20, 30], "name": ["Ada", "Linus", "Grace"]})
# INNER JOIN — 양쪽에 모두 있는 user_id만 남음
inner = orders.merge(users, on="user_id", how="inner")
# LEFT JOIN — orders는 모두 유지, 매칭 안 되면 NaN
left = orders.merge(users, on="user_id", how="left")
# 단순 이어붙이기 (같은 컬럼 구조의 데이터를 위아래로)
combined = pd.concat([orders, orders], ignore_index=True)| how | 결과 |
|---|---|
| inner (기본값) | 양쪽 키가 모두 일치하는 행만 남김 |
| left | 왼쪽 DataFrame은 모두 유지, 매칭 없으면 NaN |
| outer | 양쪽 모든 행을 유지, 매칭 없으면 NaN |
여기서는 집계 (Split-Apply-Combine)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
monthly = (
df.groupby("month")
.agg(order_count=("id", "count"), revenue=("amount", "sum"))
.reset_index()
)
print(monthly)여기서는 성능 최적화을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
# 느림 — 한 행씩 Python 레벨에서 반복
df["discounted"] = df.apply(lambda row: row["amount"] * 0.9 if row["vip"] else row["amount"], axis=1)
# 빠름 — 벡터화 연산 (내부적으로 C로 구현된 배열 연산)
df["discounted"] = df["amount"].where(~df["vip"], df["amount"] * 0.9)
# 대용량 CSV는 처음부터 필요한 컬럼/타입만 지정해 메모리 절약
df = pd.read_csv("orders.csv", usecols=["user_id", "amount"], dtype={"user_id": "int32"})여기서는 저장을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.
monthly.to_csv("monthly_report.csv", index=False)
monthly.to_parquet("monthly_report.parquet", index=False)Pandas 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 결정 지점 | 확인 질문 | 실무 기준 |
|---|---|---|
| 경계 | Pandas 코드에서 바뀌기 쉬운 부분은 어디인가? | 입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다. |
| 상태 | 상태가 어디서 생성되고 어디서 사라지는가? | 상태 소유자와 수명 주기를 코드로 드러냅니다. |
| 장애 | 실패했을 때 호출자는 무엇을 받는가? | timeout, fallback, error contract를 먼저 정합니다. |
이 섹션은 Pandas 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.
Pandas 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.
| 품질 축 | 검증 방법 | 완료 기준 |
|---|---|---|
| 정확성 | 정상/실패 케이스를 자동화합니다. | 핵심 시나리오가 재현 가능하게 통과합니다. |
| 회귀 방지 | 버그 수정 시 동일 케이스를 테스트로 남깁니다. | 같은 장애가 다시 배포되지 않습니다. |
| 운영성 | 로그, 메트릭, 알림을 확인합니다. | 문제가 생겼을 때 원인 추적 경로가 있습니다. |