포스트

[Python 100일 챌린지] Day 70 - 미니 프로젝트: 데이터 분석 리포트

[Python 100일 챌린지] Day 70 - 미니 프로젝트: 데이터 분석 리포트

Phase 7 총정리! 실전 데이터 분석 프로젝트 🎯

NumPy, Pandas, Matplotlib 모두 활용해서 실제 데이터 분석 리포트를 만들어봅시다! 이것만 완성하면 데이터 분석 기초 완료! 🎉

(60분 완독 ⭐⭐⭐)

🎯 오늘의 학습 목표

📚 사전 지식

Phase 7에서 배운 모든 내용:


🎯 학습 목표 1: 프로젝트 소개 및 데이터 준비

1.1 프로젝트 개요

📊 “온라인 쇼핑몰 매출 분석 리포트”

가상의 온라인 쇼핑몰 데이터를 분석하여:

  1. 매출 현황 파악
  2. 고객 행동 분석
  3. 제품별 성과 분석
  4. 비즈니스 인사이트 도출

1.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
27
28
29
30
31
32
33
34
35
36
37
38
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

# 재현 가능한 랜덤 시드
np.random.seed(42)

# 데이터 생성
n_orders = 500

# 날짜 생성 (2024년 1~6월)
start_date = datetime(2024, 1, 1)
dates = [start_date + timedelta(days=np.random.randint(0, 180)) for _ in range(n_orders)]

# 데이터프레임 생성
orders = pd.DataFrame({
    '주문번호': [f'ORD{str(i).zfill(5)}' for i in range(1, n_orders + 1)],
    '주문일': dates,
    '고객ID': [f'C{np.random.randint(1, 101):03d}' for _ in range(n_orders)],
    '제품': np.random.choice(['노트북', '스마트폰', '태블릿', '이어폰', '키보드'], n_orders),
    '카테고리': np.random.choice(['전자기기', '악세서리'], n_orders, p=[0.6, 0.4]),
    '수량': np.random.randint(1, 5, n_orders),
    '단가': np.random.choice([50000, 100000, 200000, 500000, 1000000], n_orders),
    '지역': np.random.choice(['서울', '경기', '부산', '대구', '인천'], n_orders),
    '결제방법': np.random.choice(['카드', '계좌이체', '간편결제'], n_orders),
    '고객등급': np.random.choice(['브론즈', '실버', '골드', 'VIP'], n_orders, p=[0.4, 0.3, 0.2, 0.1])
})

# 매출액 계산
orders['매출'] = orders['수량'] * orders['단가']

# 일부 결측값 추가 (현실적인 데이터)
orders.loc[np.random.choice(orders.index, 20), '지역'] = np.nan
orders.loc[np.random.choice(orders.index, 10), '고객등급'] = np.nan

print("=== 데이터 샘플 ===")
print(orders.head(10))
print(f"\n총 주문 건수: {len(orders)}")

1.3 데이터 저장 및 불러오기

1
2
3
4
5
6
7
# CSV로 저장
orders.to_csv('shopping_orders.csv', index=False, encoding='utf-8-sig')
print("데이터 저장 완료: shopping_orders.csv")

# 불러오기
df = pd.read_csv('shopping_orders.csv', parse_dates=['주문일'])
print(f"\n불러온 데이터: {df.shape[0]}행 x {df.shape[1]}")

🎯 학습 목표 2: 데이터 정제 및 전처리

2.1 데이터 품질 확인

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
27
import pandas as pd
import numpy as np

# 데이터 불러오기 (위에서 생성한 orders 사용)
df = orders.copy()

print("=" * 50)
print("         데이터 품질 점검 보고서")
print("=" * 50)

# 1. 기본 정보
print(f"\n📊 데이터 크기: {df.shape[0]}행 x {df.shape[1]}")
print(f"📅 기간: {df['주문일'].min().date()} ~ {df['주문일'].max().date()}")

# 2. 결측값 확인
print("\n🔍 결측값 현황:")
missing = df.isnull().sum()
for col, count in missing[missing > 0].items():
    print(f"  - {col}: {count}개 ({count/len(df)*100:.1f}%)")

# 3. 중복 확인
duplicates = df.duplicated().sum()
print(f"\n🔄 중복 행: {duplicates}")

# 4. 데이터 타입
print("\n📋 데이터 타입:")
print(df.dtypes)

2.2 결측값 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd

df = orders.copy()

print("=== 결측값 처리 전 ===")
print(df.isnull().sum())

# 결측값 처리 전략
# 1. 지역: '미확인'으로 대체
df['지역'] = df['지역'].fillna('미확인')

# 2. 고객등급: '브론즈'(최저등급)로 대체
df['고객등급'] = df['고객등급'].fillna('브론즈')

print("\n=== 결측값 처리 후 ===")
print(df.isnull().sum())

2.3 파생 변수 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd

df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')

# 날짜 관련 파생 변수
df['연월'] = df['주문일'].dt.to_period('M')
df['요일'] = df['주문일'].dt.day_name()
df[''] = df['주문일'].dt.month

# 매출 구간
df['매출구간'] = pd.cut(df['매출'],
                      bins=[0, 100000, 500000, 1000000, float('inf')],
                      labels=['소액', '중간', '고액', '프리미엄'])

print("=== 파생 변수 추가 완료 ===")
print(df[['주문일', '연월', '요일', '', '매출', '매출구간']].head())

🎯 학습 목표 3: 탐색적 데이터 분석

3.1 매출 현황 분석

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
27
28
29
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False

# 데이터 준비
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')
df['연월'] = df['주문일'].dt.to_period('M')
df[''] = df['주문일'].dt.month

print("=" * 50)
print("         📊 매출 현황 분석")
print("=" * 50)

# 전체 요약
print(f"\n💰 총 매출: {df['매출'].sum():,}")
print(f"📦 총 주문: {len(df):,}")
print(f"📈 평균 주문금액: {df['매출'].mean():,.0f}")
print(f"📊 중앙값: {df['매출'].median():,.0f}")

# 월별 매출
print("\n=== 월별 매출 추이 ===")
monthly = df.groupby('')['매출'].agg(['sum', 'count', 'mean'])
monthly.columns = ['총매출', '주문건수', '평균매출']
print(monthly)

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False

# 데이터 준비
np.random.seed(42)
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')
df['연월'] = df['주문일'].dt.to_period('M')
df[''] = df['주문일'].dt.month

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 1. 월별 매출 추이
monthly_sales = df.groupby('')['매출'].sum()
axes[0, 0].plot(monthly_sales.index, monthly_sales.values / 1e6, 'bo-', linewidth=2)
axes[0, 0].set_title('월별 매출 추이', fontsize=14)
axes[0, 0].set_xlabel('')
axes[0, 0].set_ylabel('매출 (백만원)')
axes[0, 0].grid(True, alpha=0.3)

# 2. 제품별 매출
product_sales = df.groupby('제품')['매출'].sum().sort_values(ascending=True)
colors = plt.cm.Blues(np.linspace(0.4, 0.8, len(product_sales)))
axes[0, 1].barh(product_sales.index, product_sales.values / 1e6, color=colors)
axes[0, 1].set_title('제품별 총 매출', fontsize=14)
axes[0, 1].set_xlabel('매출 (백만원)')

# 3. 지역별 매출 비율
region_sales = df.groupby('지역')['매출'].sum()
axes[1, 0].pie(region_sales, labels=region_sales.index, autopct='%1.1f%%',
               colors=plt.cm.Pastel1(np.linspace(0, 1, len(region_sales))))
axes[1, 0].set_title('지역별 매출 비율', fontsize=14)

# 4. 결제방법별 매출
payment_sales = df.groupby('결제방법')['매출'].sum()
axes[1, 1].bar(payment_sales.index, payment_sales.values / 1e6,
               color=['#FF6B6B', '#4ECDC4', '#45B7D1'])
axes[1, 1].set_title('결제방법별 매출', fontsize=14)
axes[1, 1].set_ylabel('매출 (백만원)')

plt.tight_layout()
plt.savefig('sales_analysis.png', dpi=150, bbox_inches='tight')
plt.show()

3.3 고객 분석

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False

np.random.seed(42)
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')

print("=" * 50)
print("         👥 고객 분석")
print("=" * 50)

# 고객 수
unique_customers = df['고객ID'].nunique()
print(f"\n👤 총 고객 수: {unique_customers}")

# 고객별 구매 현황
customer_stats = df.groupby('고객ID').agg({
    '매출': ['sum', 'mean', 'count']
}).round(0)
customer_stats.columns = ['총구매액', '평균구매액', '구매횟수']
customer_stats = customer_stats.sort_values('총구매액', ascending=False)

print("\n=== 상위 10명 우수고객 ===")
print(customer_stats.head(10))

# 고객등급별 분석
grade_analysis = df.groupby('고객등급').agg({
    '고객ID': 'nunique',
    '매출': ['sum', 'mean']
})
grade_analysis.columns = ['고객수', '총매출', '평균매출']
grade_analysis = grade_analysis.round(0)

print("\n=== 고객등급별 현황 ===")
print(grade_analysis)

3.4 고객 시각화

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
27
28
29
30
31
32
33
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False

np.random.seed(42)
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 등급별 고객 수
grade_order = ['VIP', '골드', '실버', '브론즈']
grade_counts = df.groupby('고객등급')['고객ID'].nunique()
grade_counts = grade_counts.reindex(grade_order)
colors = ['#FFD700', '#FFA500', '#C0C0C0', '#CD7F32']
axes[0].bar(grade_counts.index, grade_counts.values, color=colors)
axes[0].set_title('등급별 고객 수', fontsize=14)
axes[0].set_ylabel('고객 수')

# 등급별 평균 구매액
grade_avg = df.groupby('고객등급')['매출'].mean()
grade_avg = grade_avg.reindex(grade_order)
axes[1].bar(grade_avg.index, grade_avg.values / 10000, color=colors)
axes[1].set_title('등급별 평균 구매금액', fontsize=14)
axes[1].set_ylabel('금액 (만원)')

plt.tight_layout()
plt.savefig('customer_analysis.png', dpi=150, bbox_inches='tight')
plt.show()

🎯 학습 목표 4: 분석 리포트 완성

4.1 종합 대시보드

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'
plt.rcParams['axes.unicode_minus'] = False

np.random.seed(42)
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')
df[''] = df['주문일'].dt.month

# 대시보드 생성
fig = plt.figure(figsize=(16, 12))

# 1. 월별 매출 추이
ax1 = fig.add_subplot(2, 3, 1)
monthly = df.groupby('')['매출'].sum() / 1e6
ax1.plot(monthly.index, monthly.values, 'b-o', linewidth=2)
ax1.fill_between(monthly.index, monthly.values, alpha=0.3)
ax1.set_title('📈 월별 매출 추이')
ax1.set_ylabel('매출 (백만원)')
ax1.grid(True, alpha=0.3)

# 2. 제품별 매출 TOP 5
ax2 = fig.add_subplot(2, 3, 2)
product = df.groupby('제품')['매출'].sum().sort_values()
ax2.barh(product.index, product.values / 1e6)
ax2.set_title('📦 제품별 매출')
ax2.set_xlabel('매출 (백만원)')

# 3. 지역별 매출
ax3 = fig.add_subplot(2, 3, 3)
region = df.groupby('지역')['매출'].sum()
ax3.pie(region, labels=region.index, autopct='%1.1f%%')
ax3.set_title('🗺️ 지역별 매출 비율')

# 4. 고객등급별 매출
ax4 = fig.add_subplot(2, 3, 4)
grade = df.groupby('고객등급')['매출'].sum()
grade_order = ['VIP', '골드', '실버', '브론즈']
grade = grade.reindex(grade_order)
colors = ['#FFD700', '#FFA500', '#C0C0C0', '#CD7F32']
ax4.bar(grade.index, grade.values / 1e6, color=colors)
ax4.set_title('👥 등급별 매출')
ax4.set_ylabel('매출 (백만원)')

# 5. 결제방법 분포
ax5 = fig.add_subplot(2, 3, 5)
payment = df['결제방법'].value_counts()
ax5.pie(payment, labels=payment.index, autopct='%1.1f%%',
        colors=['#FF6B6B', '#4ECDC4', '#45B7D1'])
ax5.set_title('💳 결제방법 분포')

# 6. 주요 KPI
ax6 = fig.add_subplot(2, 3, 6)
ax6.axis('off')
kpi_text = f"""
═══════════════════════════════════
         📊 주요 KPI 요약
═══════════════════════════════════

💰 총 매출: {df['매출'].sum():,}원

📦 총 주문: {len(df):,}건

👤 총 고객: {df['고객ID'].nunique()}명

📈 평균 주문금액: {df['매출'].mean():,.0f}원

🏆 최고 매출 제품: {df.groupby('제품')['매출'].sum().idxmax()}

🗺️ 최고 매출 지역: {df.groupby('지역')['매출'].sum().idxmax()}

═══════════════════════════════════
"""
ax6.text(0.1, 0.5, kpi_text, transform=ax6.transAxes,
         fontsize=11, verticalalignment='center',
         fontfamily='AppleGothic',
         bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.5))

plt.suptitle('🛒 온라인 쇼핑몰 매출 분석 대시보드', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.savefig('dashboard_final.png', dpi=150, bbox_inches='tight')
plt.show()

4.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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import pandas as pd
import numpy as np

np.random.seed(42)
df = orders.copy()
df['지역'] = df['지역'].fillna('미확인')
df['고객등급'] = df['고객등급'].fillna('브론즈')
df[''] = df['주문일'].dt.month

print("=" * 60)
print("         📋 분석 결과 및 비즈니스 인사이트")
print("=" * 60)

# 1. 매출 분석 인사이트
monthly = df.groupby('')['매출'].sum()
growth = (monthly.iloc[-1] - monthly.iloc[0]) / monthly.iloc[0] * 100

print("\n📈 매출 트렌드:")
print(f"  • 분석 기간 매출 성장률: {growth:.1f}%")
print(f"  • 최고 매출 월: {monthly.idxmax()}월 ({monthly.max():,}원)")
print(f"  • 최저 매출 월: {monthly.idxmin()}월 ({monthly.min():,}원)")

# 2. 제품 분석 인사이트
product_sales = df.groupby('제품')['매출'].sum()
top_product = product_sales.idxmax()
bottom_product = product_sales.idxmin()

print("\n📦 제품 성과:")
print(f"  • 베스트셀러: {top_product} ({product_sales[top_product]:,}원)")
print(f"  • 개선 필요: {bottom_product} ({product_sales[bottom_product]:,}원)")

# 3. 고객 분석 인사이트
vip_ratio = len(df[df['고객등급'] == 'VIP']) / len(df) * 100
vip_sales_ratio = df[df['고객등급'] == 'VIP']['매출'].sum() / df['매출'].sum() * 100

print("\n👥 고객 인사이트:")
print(f"  • VIP 고객 비율: {vip_ratio:.1f}%")
print(f"  • VIP 매출 기여도: {vip_sales_ratio:.1f}%")

# 4. 제언
print("\n💡 비즈니스 제언:")
print("  1. VIP 고객 리텐션 프로그램 강화 (매출 기여도 높음)")
print(f"  2. {bottom_product} 제품 마케팅 전략 재검토")
print(f"  3. {monthly.idxmin()}월 매출 부진 원인 분석 및 프로모션 검토")
print("  4. 간편결제 사용 유도로 결제 편의성 향상")

print("\n" + "=" * 60)

💡 실전 팁

✅ 프로젝트 체크리스트

1
2
3
4
5
6
7
8
9
10
11
12
# 데이터 분석 프로젝트 완료 체크리스트
checklist = """
✅ 데이터 품질 점검 완료
✅ 결측값/이상치 처리 완료
✅ 파생 변수 생성 완료
✅ 기술 통계 분석 완료
✅ 시각화 대시보드 완성
✅ 인사이트 도출 완료
✅ 비즈니스 제언 작성 완료
✅ 분석 결과 저장 (PNG, CSV)
"""
print(checklist)

🧪 도전 과제

추가 분석 아이디어

  1. 시계열 분석: 요일별 매출 패턴 분석
  2. RFM 분석: 고객 세분화
  3. 코호트 분석: 월별 첫 구매 고객 추적
  4. 제품 연관 분석: 함께 구매되는 제품

📝 Phase 7 완료!

축하합니다! 🎉 Phase 7에서 배운 내용:

  1. NumPy: 배열 연산, 인덱싱, 슬라이싱
  2. Pandas: DataFrame, 데이터 로딩, 조작, 그룹화
  3. Matplotlib: 다양한 차트, 대시보드
  4. 데이터 분석: EDA, 워크플로우, 리포트

이제 실전 데이터 분석이 가능합니다! 🚀


📚 이전 학습

Day 69: 데이터 분석 워크플로우 ⭐⭐⭐

📚 다음 학습

Day 71: Phase 8 시작 - 데이터베이스와 SQL 소개 ⭐⭐


“Phase 7 완료! 데이터 분석의 기초를 마스터했습니다!” 🎊

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