포스트

[Python 100일 챌린지] Day 69 - 데이터 분석 워크플로우

[Python 100일 챌린지] Day 69 - 데이터 분석 워크플로우

실무 데이터 분석의 전체 흐름을 배워봅시다! 🔄

데이터 수집 → 정제 → 분석 → 시각화 → 인사이트 실제 분석가들이 일하는 방식을 그대로! 이 흐름만 알면 어떤 데이터든 분석할 수 있어요! 💪

(40분 완독 ⭐⭐⭐)

💡 Day 68 복습: 어제 배운 시각화

차트 용도 Pandas 코드
원 그래프 비율 표현 df.plot(kind='pie')
히스토그램 분포 확인 df.plot(kind='hist')
산점도 두 변수 관계 df.plot(kind='scatter')

오늘은 이 시각화들을 실제 분석 워크플로우 안에서 활용해요!

🎯 오늘의 학습 목표

📚 사전 지식


🎯 학습 목표 1: 데이터 분석 프로세스 이해하기

1.1 분석 프로세스 개요

graph LR
    A[1. 문제 정의] --> B[2. 데이터 수집]
    B --> C[3. 데이터 정제]
    C --> D[4. 탐색적 분석]
    D --> E[5. 심층 분석]
    E --> F[6. 시각화/보고]

1.2 각 단계 설명

단계 목적 주요 작업
문제 정의 분석 목표 설정 질문 정의, KPI 설정
데이터 수집 원천 데이터 확보 CSV, DB, API
데이터 정제 품질 향상 결측값, 이상치 처리
탐색적 분석 데이터 이해 기술 통계, 분포 확인
심층 분석 인사이트 도출 그룹 분석, 상관 분석
시각화/보고 결과 전달 차트, 리포트

1.3 분석 질문 예시

1
2
3
4
5
6
7
8
# 좋은 분석 질문의 예시
questions = [
    "어떤 제품이 가장 많이 팔리나?",
    "매출이 증가하는 추세인가?",
    "어떤 고객층이 주요 타겟인가?",
    "어떤 요인이 매출에 영향을 미치나?",
    "이탈 위험이 있는 고객은 누구인가?"
]

🎯 학습 목표 2: 탐색적 데이터 분석(EDA) 배우기

💡 EDA(Exploratory Data Analysis)란?

비유: 새 집에 이사 가면 먼저 집 구석구석을 둘러보죠? 🏠

  • 방이 몇 개인지 (데이터 크기)
  • 어디가 넓고 좁은지 (분포)
  • 문제가 있는 곳은 없는지 (결측값, 이상치)

EDA도 마찬가지예요! 데이터를 분석하기 전에 먼저 탐색하는 거예요.

EDA 단계 질문 확인 방법
크기 파악 데이터가 얼마나 있지? df.shape
타입 확인 숫자? 문자? 날짜? df.dtypes
분포 확인 값들이 어떻게 퍼져있지? df.describe(), 히스토그램
품질 확인 빈 값이나 이상한 값은? df.isnull().sum()

EDA를 건너뛰면 나중에 분석 결과가 이상해도 원인을 찾기 어려워요!

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

# 샘플 데이터 생성
np.random.seed(42)
df = pd.DataFrame({
    '고객ID': range(1, 101),
    '나이': np.random.randint(20, 60, 100),
    '성별': np.random.choice(['', ''], 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '구매횟수': np.random.randint(1, 20, 100),
    '등급': np.random.choice(['브론즈', '실버', '골드'], 100)
})

# 1. 데이터 크기
print(f"행: {df.shape[0]}, 열: {df.shape[1]}")

# 2. 데이터 타입
print("\n=== 데이터 타입 ===")
print(df.dtypes)

# 3. 기본 정보
print("\n=== 기본 정보 ===")
print(df.info())

# 4. 처음/끝 확인
print("\n=== 처음 5행 ===")
print(df.head())

2.2 기술 통계량 확인

💡 기술 통계량(Descriptive Statistics)이란?

데이터를 숫자 하나로 요약해서 설명하는 것이에요!

통계량 의미 비유 예시 (10, 20, 30)
평균 (mean) 전체를 더해서 나눔 전체 피자를 똑같이 나눔 20
중앙값 (median) 줄 세웠을 때 가운데 키 순서로 줄 서면 가운데 사람 20
표준편차 (std) 평균에서 얼마나 퍼졌는지 성적이 고르면 낮고, 들쭉날쭉하면 높음 10
최솟값 (min) 가장 작은 값 반에서 제일 작은 키 10
최댓값 (max) 가장 큰 값 반에서 제일 큰 키 30

평균 vs 중앙값, 언제 뭘 볼까?

  • 데이터가 고르면: 평균 ≈ 중앙값 (둘 다 OK)
  • 이상치(극단값)가 있으면: 중앙값이 더 대표적!
    • 예: 연봉 (몇 명의 억대 연봉자가 평균을 왜곡)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
import numpy as np

np.random.seed(42)
df = pd.DataFrame({
    '나이': np.random.randint(20, 60, 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '구매횟수': np.random.randint(1, 20, 100)
})

# 수치형 변수 요약
print("=== 기술 통계량 ===")
print(df.describe())

# 개별 통계
print(f"\n평균 구매금액: {df['구매금액'].mean():,.0f}")
print(f"중앙값: {df['구매금액'].median():,.0f}")
print(f"표준편차: {df['구매금액'].std():,.0f}")

2.3 범주형 변수 분석

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

np.random.seed(42)
df = pd.DataFrame({
    '성별': np.random.choice(['', ''], 100),
    '등급': np.random.choice(['브론즈', '실버', '골드'], 100)
})

# 빈도수 확인
print("=== 성별 분포 ===")
print(df['성별'].value_counts())

print("\n=== 등급 분포 ===")
print(df['등급'].value_counts())

# 비율 확인
print("\n=== 등급 비율 ===")
print(df['등급'].value_counts(normalize=True).round(3) * 100)

2.4 EDA 시각화

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
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 = pd.DataFrame({
    '나이': np.random.randint(20, 60, 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '등급': np.random.choice(['브론즈', '실버', '골드'], 100)
})

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

# 1. 나이 분포
axes[0, 0].hist(df['나이'], bins=15, edgecolor='black')
axes[0, 0].set_title('나이 분포')
axes[0, 0].set_xlabel('나이')

# 2. 구매금액 분포
axes[0, 1].hist(df['구매금액'], bins=15, edgecolor='black')
axes[0, 1].set_title('구매금액 분포')
axes[0, 1].set_xlabel('금액')

# 3. 등급별 비율
grade_counts = df['등급'].value_counts()
axes[1, 0].pie(grade_counts, labels=grade_counts.index, autopct='%1.1f%%')
axes[1, 0].set_title('등급 비율')

# 4. 등급별 평균 구매금액
grade_avg = df.groupby('등급')['구매금액'].mean()
axes[1, 1].bar(grade_avg.index, grade_avg.values)
axes[1, 1].set_title('등급별 평균 구매금액')
axes[1, 1].set_ylabel('금액')

plt.tight_layout()
plt.show()

🎯 학습 목표 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
30
import pandas as pd
import numpy as np

# 결측값이 있는 데이터
df = pd.DataFrame({
    '이름': ['철수', '영희', '민수', None, '지영'],
    '나이': [25, None, 30, 28, None],
    '구매금액': [50000, 30000, None, 45000, 35000]
})

print("=== 원본 데이터 ===")
print(df)

# 1. 결측값 확인
print("\n=== 결측값 개수 ===")
print(df.isnull().sum())

# 2. 결측값 처리 전략
# 방법 1: 삭제
df_dropped = df.dropna()
print("\n=== 결측값 삭제 후 ===")
print(df_dropped)

# 방법 2: 대체
df_filled = df.copy()
df_filled['나이'] = df_filled['나이'].fillna(df_filled['나이'].mean())
df_filled['구매금액'] = df_filled['구매금액'].fillna(df_filled['구매금액'].median())
df_filled['이름'] = df_filled['이름'].fillna('미상')
print("\n=== 결측값 대체 후 ===")
print(df_filled)

3.2 이상치 탐지

💡 이상치(Outlier)란?

비유: 반 평균 키가 165cm인데 혼자 200cm인 학생이 있다면? 🏀 그 학생이 바로 이상치예요! 다른 데이터와 동떨어진 값이죠.

이상치가 있으면 평균, 표준편차 등이 왜곡될 수 있어서 처리가 필요해요.

💡 IQR 방식으로 이상치 찾기

IQR (Inter-Quartile Range) = Q3 - Q1 (데이터 중간 50%의 범위)

1
2
3
4
5
6
데이터를 작은 것부터 정렬했을 때:

|---25%---|---25%---|---25%---|---25%---|
         Q1       Q2       Q3
          ↑   (중앙값)   ↑
          └──── IQR ────┘
용어 의미 위치
Q1 (1사분위수) 하위 25% 지점 quantile(0.25)
Q2 (2사분위수) 중앙값 (50%) quantile(0.50)
Q3 (3사분위수) 상위 25% 지점 quantile(0.75)
IQR Q3 - Q1 중간 50% 범위

이상치 판단 기준:

  • 정상 범위: Q1 - 1.5×IQR ~ Q3 + 1.5×IQR
  • 이 범위를 벗어나면 이상치!

왜 1.5인가요? 통계학에서 정규분포 데이터의 약 99.3%가 이 범위 안에 들어가요. 그래서 관례적으로 1.5를 사용해요!

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

np.random.seed(42)
# 이상치가 포함된 데이터
df = pd.DataFrame({
    '구매금액': [50000, 45000, 55000, 48000, 52000,
                500000,  # 이상치!
                49000, 51000, 47000, 46000]
})

print("=== 기술 통계 ===")
print(df.describe())

# IQR 방식으로 이상치 탐지
Q1 = df['구매금액'].quantile(0.25)
Q3 = df['구매금액'].quantile(0.75)
IQR = Q3 - Q1

lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR

print(f"\n정상 범위: {lower:,.0f} ~ {upper:,.0f}")

# 이상치 필터링
outliers = df[(df['구매금액'] < lower) | (df['구매금액'] > upper)]
print(f"\n이상치 개수: {len(outliers)}")
print(outliers)

# 이상치 제거
df_clean = df[(df['구매금액'] >= lower) & (df['구매금액'] <= upper)]
print(f"\n정제 후 데이터 수: {len(df_clean)}")

3.3 데이터 타입 변환

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

df = pd.DataFrame({
    '날짜': ['2024-01-01', '2024-01-02', '2024-01-03'],
    '금액': ['10,000', '20,000', '15,000'],
    '수량': ['5', '3', '8']
})

print("=== 변환 전 ===")
print(df.dtypes)

# 날짜 변환
df['날짜'] = pd.to_datetime(df['날짜'])

# 숫자 변환 (쉼표 제거 후)
df['금액'] = df['금액'].str.replace(',', '').astype(int)
df['수량'] = df['수량'].astype(int)

print("\n=== 변환 후 ===")
print(df.dtypes)
print(df)

🎯 학습 목표 4: 분석 결과 해석하기

4.1 상관관계 분석

💡 상관관계(Correlation)란?

두 변수가 함께 변하는 정도를 수치로 나타낸 것이에요!

비유: 🌡️ 기온이 올라가면 아이스크림 판매량도 올라가죠? 이런 관계를 양의 상관관계라고 해요!

상관계수 해석 가이드:

상관계수 해석 예시
+1 완벽한 양의 상관 A가 오르면 B도 항상 오름
+0.7 ~ +1 강한 양의 상관 키 ↔ 몸무게
+0.3 ~ +0.7 중간 양의 상관 공부시간 ↔ 성적
-0.3 ~ +0.3 약한/무상관 혈액형 ↔ 성격 (거의 없음)
-0.7 ~ -0.3 중간 음의 상관 운동 ↔ 체지방률
-1 ~ -0.7 강한 음의 상관 속도 ↔ 소요시간
-1 완벽한 음의 상관 A가 오르면 B는 항상 내림

⚠️ 주의: 상관관계 ≠ 인과관계!

  • 아이스크림 판매 ↔ 익사 사고 (둘 다 여름에 증가하지만 인과관계는 없음!)
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
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'

np.random.seed(42)
df = pd.DataFrame({
    '나이': np.random.randint(20, 60, 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '구매횟수': np.random.randint(1, 20, 100),
    '방문횟수': np.random.randint(1, 30, 100)
})

# 상관계수 계산
correlation = df.corr()
print("=== 상관계수 ===")
print(correlation.round(2))

# 히트맵으로 시각화
fig, ax = plt.subplots(figsize=(8, 6))
im = ax.imshow(correlation, cmap='coolwarm', aspect='auto')

ax.set_xticks(range(len(correlation.columns)))
ax.set_yticks(range(len(correlation.columns)))
ax.set_xticklabels(correlation.columns)
ax.set_yticklabels(correlation.columns)

# 값 표시
for i in range(len(correlation)):
    for j in range(len(correlation)):
        ax.text(j, i, f'{correlation.iloc[i, j]:.2f}',
                ha='center', va='center')

plt.colorbar(im)
plt.title('상관관계 히트맵')
plt.tight_layout()
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
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'AppleGothic'

np.random.seed(42)
df = pd.DataFrame({
    '등급': np.random.choice(['브론즈', '실버', '골드'], 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '구매횟수': np.random.randint(1, 20, 100)
})

# 등급별 분석
grade_analysis = df.groupby('등급').agg({
    '구매금액': ['mean', 'sum', 'count'],
    '구매횟수': 'mean'
}).round(0)

print("=== 등급별 분석 ===")
print(grade_analysis)

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

grade_mean = df.groupby('등급')['구매금액'].mean()
grade_mean.plot(kind='bar', ax=axes[0], color=['#CD7F32', '#C0C0C0', '#FFD700'])
axes[0].set_title('등급별 평균 구매금액')
axes[0].set_ylabel('금액')
axes[0].tick_params(axis='x', rotation=0)

grade_count = df.groupby('등급')['구매금액'].count()
grade_count.plot(kind='bar', ax=axes[1], color=['#CD7F32', '#C0C0C0', '#FFD700'])
axes[1].set_title('등급별 고객 수')
axes[1].set_ylabel('')
axes[1].tick_params(axis='x', rotation=0)

plt.tight_layout()
plt.show()

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

np.random.seed(42)
df = pd.DataFrame({
    '등급': np.random.choice(['브론즈', '실버', '골드'], 100),
    '구매금액': np.random.randint(10000, 100000, 100),
    '구매횟수': np.random.randint(1, 20, 100)
})

# 분석 결과 요약
print("=" * 50)
print("         고객 데이터 분석 보고서")
print("=" * 50)

print(f"\n📊 전체 고객 수: {len(df):,}")
print(f"💰 총 매출: {df['구매금액'].sum():,}")
print(f"📈 평균 구매금액: {df['구매금액'].mean():,.0f}")

print("\n🏆 등급별 현황:")
grade_stats = df.groupby('등급').agg({
    '구매금액': ['count', 'mean', 'sum']
})
for grade in ['골드', '실버', '브론즈']:
    if grade in grade_stats.index:
        count = grade_stats.loc[grade, ('구매금액', 'count')]
        avg = grade_stats.loc[grade, ('구매금액', 'mean')]
        total = grade_stats.loc[grade, ('구매금액', 'sum')]
        print(f"  - {grade}: {count}명, 평균 {avg:,.0f}원, 합계 {total:,}")

print("\n💡 인사이트:")
print("  1. 골드 등급 고객의 구매력이 가장 높음")
print("  2. 브론즈 고객 업그레이드 프로모션 권장")
print("  3. 충성 고객 리텐션 프로그램 필요")

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

💡 실전 팁

✅ EDA 체크리스트

1
2
3
4
5
6
7
8
# 데이터 분석 시작할 때 항상 확인!
def eda_checklist(df):
    print("1. 데이터 크기:", df.shape)
    print("2. 컬럼:", df.columns.tolist())
    print("3. 데이터 타입:\n", df.dtypes)
    print("4. 결측값:\n", df.isnull().sum())
    print("5. 기술 통계:\n", df.describe())
    print("6. 중복 행:", df.duplicated().sum())

🧪 연습 문제

문제: 판매 데이터 종합 분석

다음 데이터로 완전한 EDA를 수행하세요.

1
2
3
4
5
6
7
sales = pd.DataFrame({
    '날짜': pd.date_range('2024-01-01', periods=100),
    '제품': np.random.choice(['A', 'B', 'C'], 100),
    '지역': np.random.choice(['서울', '부산', '대구'], 100),
    '판매량': np.random.randint(10, 100, 100),
    '매출': np.random.randint(100000, 1000000, 100)
})

💡 힌트: EDA 5단계로 접근하세요!

단계 할 일 사용할 코드
1️⃣ 기본 정보 크기, 기간 확인 shape, min(), max()
2️⃣ 기술 통계 수치형 요약 describe()
3️⃣ 그룹 분석 제품별, 지역별 groupby().agg()
4️⃣ 시각화 4개 차트 plt.subplots(2, 2)
5️⃣ 인사이트 결론 도출 idxmax()

차트 아이디어:

  • 시계열 → 선 그래프 (plot)
  • 제품별 비교 → 막대 그래프 (bar)
  • 지역별 비율 → 원 그래프 (pie)
  • 분포 확인 → 히스토그램 (hist)
정답 코드
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
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)
sales = pd.DataFrame({
    '날짜': pd.date_range('2024-01-01', periods=100),
    '제품': np.random.choice(['A', 'B', 'C'], 100),
    '지역': np.random.choice(['서울', '부산', '대구'], 100),
    '판매량': np.random.randint(10, 100, 100),
    '매출': np.random.randint(100000, 1000000, 100)
})

# 1. 기본 정보
print("=== 데이터 기본 정보 ===")
print(f"행: {sales.shape[0]}, 열: {sales.shape[1]}")
print(f"기간: {sales['날짜'].min()} ~ {sales['날짜'].max()}")

# 2. 기술 통계
print("\n=== 기술 통계 ===")
print(sales.describe())

# 3. 제품별 분석
print("\n=== 제품별 분석 ===")
print(sales.groupby('제품').agg({
    '판매량': ['sum', 'mean'],
    '매출': ['sum', 'mean']
}))

# 4. 시각화
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 일별 매출 추이
daily = sales.groupby('날짜')['매출'].sum()
axes[0, 0].plot(daily.index, daily.values)
axes[0, 0].set_title('일별 매출 추이')

# 제품별 매출
prod_sales = sales.groupby('제품')['매출'].sum()
axes[0, 1].bar(prod_sales.index, prod_sales.values)
axes[0, 1].set_title('제품별 총 매출')

# 지역별 비율
region_sales = sales.groupby('지역')['매출'].sum()
axes[1, 0].pie(region_sales, labels=region_sales.index, autopct='%1.1f%%')
axes[1, 0].set_title('지역별 매출 비율')

# 판매량 분포
axes[1, 1].hist(sales['판매량'], bins=15, edgecolor='black')
axes[1, 1].set_title('판매량 분포')

plt.tight_layout()
plt.show()

# 5. 인사이트
print("\n=== 인사이트 ===")
best_product = prod_sales.idxmax()
best_region = region_sales.idxmax()
print(f"1. 최고 매출 제품: {best_product}")
print(f"2. 최고 매출 지역: {best_region}")

📝 오늘 배운 내용 정리

  1. 분석 프로세스: 문제정의 → 수집 → 정제 → 분석 → 보고
  2. EDA: 기술통계, 분포, 상관관계 확인
  3. 데이터 정제: 결측값, 이상치, 타입 변환
  4. 결과 해석: 상관분석, 그룹비교, 인사이트 도출

🔗 관련 자료


📚 이전 학습

Day 68: 데이터 시각화 실전 ⭐⭐

어제는 원 그래프, 히스토그램, 산점도와 대시보드를 배웠어요!

📚 다음 학습

Day 70: 미니 프로젝트: 데이터 분석 리포트 ⭐⭐⭐


“늦었다고 생각할 때가 가장 빠른 때입니다. 오늘도 한 걸음 성장했어요!” 🚀

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