오늘은 그동안 배웠던 내용들을 정리하면서 데이터 통계 분석을 할 때 나만의 워크플로우를 만들어봤다.
1단계 : 척도 파악 → 2단계 : 탐색적 데이터 분석(EDA) → 3단계 : 분석 방법 선택 → 4단계 : 귀무가설 및 사후 검정
import os, hds
import numpy as np
import pandas as pd
from plt_rcs import *
from scipy import stats
import pingouin as pg
import scikit_posthocs as sp
os : 데이터 파일 경로 제어hds : 데이터 시각화 및 통계분석numpy pandas : 데이터 분석plt_rcs : matplotlib seaborn 기본 설정 모듈plt_rcs.pyimport seaborn as sns
import matplotlib.pyplot as plt
plt.rc(group='font', family='Gowun Batang', size=10)
plt.rc(group='figure', figsize=(8, 4), dpi=120)
plt.rc(group='axes', unicode_minus=False)
plt.rc(group='legend', frameon=True, fc='0.9', ec='0.9')scipy : 기본적인 통계 공식pingouin : scipy 보다 사용하기 편한 통계 라이브러리scikit_posthocs : 사후 분석 라이브러리| 척도 | 설명 | 예시 | 판별법 |
|---|---|---|---|
| 명목 | 순서가 없는 범주형 데이터 | 성별, 혈액형, 지역 | 순서 의미 없음 |
| 서열 | 순서가 있는 범주형 데이터 | 학접(A/B/C), 만족도(상/중/하) | 순서 있으나 간격 불균등 |
| 등간 | 균동 간격, 절대 0 없음 | 온도, 연도 | 0이 “없음”이 아님 |
| 비율 | 균등 간격, 절대 0 있음 | 나이, 소득, 무게 | 0 = 실제로 없음 |
info() 를 통해 데이터와 Dtype확인astype() 으로 변환하나의 컬럼에 대해 변경
df['가격'] = df['가격'].astype(int)
여러개의 컬럼 한번에 변경
apt = apt.astype(dtype={
'계약년도': int,
'계약월': int,
'계약일': int
})
cols = ['계약년도', '계약월', '계약일']
apt[cols] = apt[cols].astype(str)
np.where() np.select() pd.cut() pd.qcut() 등 사용해서 구간화 진행
cond = apt['경과년수'].ge(30)
apt['재건축'] = np.where(cond, '충족', '부족')
apt['재건축']
# 0 부족
# 1 부족
# 2 충족
# 3 부족
# 4 충족
# ..
# 231899 부족
# 231900 부족
# 231901 부족
# 231902 부족
# 231903 부족
# Name: 재건축, Length: 231904, dtype: object
np.select(
condlist=[
apt['경과년수'].le(5),
apt['경과년수'].le(10),
apt['경과년수'].gt(10)
],
choicelist=['신축', '준신축', '구축'],
default='0'
)
pd.cut(
x=apt['경과년수'],
bins=[-2, 5, 10, 62],
labels=['신축', '준신축', '구축']
)
pd.qcut(
x=apt['경과년수'],
q=3,
labels=['신축', '준신축', '구축']
)
describe() 와 columns 확인describe().round(n)mean - median 확인min - max 확인25% - median - 75% 확인unique() 로 고유값 개수 확인| 데이터 타입 | 확인 함수 | 그래프 | 목적 |
|---|---|---|---|
| 범주형 | value_counts() | hds.plot.bar_freq() | 빈도 비교 |
| 연속형 | describe() agg() | histplot() | |
kdeplot() + axvline() | 분포 형태 확인 | ||
| 연속형 | mean() median() | kdeplot() + axvline() | 밀도 곡선 + 평균/중앙값 |
| 연속형 | quantile() | boxplot() | 이상치 확인 |
| 연속형 | skew() kurtosis() | - | 정규성 확인 |
hds.plot.bar_freq()
hds.plot.bar_freq(
data=df, x='admit', palette=['skyblue', 'orange']
)
histplot()
sns.histplot(
df, x='Price', binrange=[4000, 16000],
binwidth=1000, facecolor='0.8'
)
plt.show()
boxplot()
sns.boxplot(
data=apt,
y='거래금액',
color='0.8',
linewidth=0.5
)
plt.show()
kdeplot() + axvline()
sns.kdeplot(
df, x='Price',
fill=True, color='0.8'
)
plt.axvline(df['Price'].mean())
plt.axvline(df['Price'].median(), color='red', linestyle='--')
plt.show()
| 데이터 타입 | 확인 함수 | 그래프 | 목적 |
|---|---|---|---|
| 범주 vs 범주 | crosstab() | hds.plot.bar_freq() | 범주별 비교 |
| 범주 vs 범주 | crosstab() | hds.plot.bar_dodge_freq() hds.plot.bar_stack_freq() | |
hds.plot.bar_stack_prop() | 범주 그룹별 비교 | ||
| 범주 vs 연속 | groupby() pivot_table() | hds.plot.box_group() | 그룹별 분포 비교 |
| 연속 vs 연속 | corr() | regplot() hds.plot.regline() | |
scatterplot() | 관계 파악 | ||
| (산점도 + 회귀선) |
hds.plot.bar_freq()
hds.plot.bar_freq(
data=df, x='admit', palette=['skyblue', 'orange']
)
hds.plot.bar_dodge_freq()
hds.plot.bar_dodge_freq(
data=df, x='rank', g='admit',
palette=['skyblue', 'orange']
)
hds.plot.bar_stack_freq()
hds.plot.bar_stack_freq(
data=df, x='rank', g='admit',
palette=['skyblue', 'orange']
)
hds.plot.bar_stack_prop()
hds.plot.bar_stack_prop(
data=df, x='rank', g='admit',
palette=['skyblue', 'orange']
)
hds.plot.box_group()
hds.plot.box_group(
data=df, x='admit', y='gre',
palette=['skyblue', 'orange']
)
scatterplot() + regplot()
plt.figure(figsize=(4, 4))
sns.regplot(
data=df, x='Age', y='Price', ci=None,
scatter_kws={'color': '0.8', 's': 10, 'ec': '0.8'},
line_kws={'color': 'red', 'lw': 1.5}
)
sns.scatterplot(
data=df.loc[out_index, :], x='Age', y='Price',
fc='red', ec='red', s=20, label='Outlier'
)
plt.legend()
plt.show()
hds.plot.regline()
hds.plot.regline(
df, x='Age', y='Price'
)
df.corr(numeric_only=True) : 상관계수 행렬hds.plot.corr_heatmap() : 전체 상관행렬 확인hds.plot.corr_heatmap(df)corr = df.corr(numeric_only=True)
sns.heatmap(
corr, annot=True, fmt='.2f',
annot_kws={'size': 8},
cmap='RdYlBu', linewidths=1
)
plt.show()agg() 사용해서 여러 집계 함수 한번에 사용
apt['거래금액'].agg(func=['count', 'sum', 'mean', 'std'])
value_counts() 상대도수 확인
df['rank'].value_counts(normalize=True).sort_index()
describe() 범주형 조회
df.describe(include=object)
##3단계 : 분석 방법 선택
⏺ X, Y 척도 확인
│
├── 연속형-연속형
│ └── 가정 검정: 각 변수 정규성
│ ├── n>5000: pg.normality(method='jarque_bera')
│ └── n≤5000: pg.normality()
│ │
│ ├── 정규성O → Pearson 상관분석 (pg.corr())
│ └── 정규성X → Spearman 상관분석 (pg.corr(method='spearman'))
│
├── 범주형-연속형
│ └── 가정 검정: 그룹별 정규성 + 등분산
│ ├── pg.normality(data, dv='Y', group='X')
│ └── pg.homoscedasticity(data, dv='Y', group='X')
│ │
│ ├── 2 그룹
│ │ ├── 정규성O, 등분산성O → 독립표본 t-test (pg.ttest())
│ │ ├── 정규성O, 등분산성X → Welch's t-test (pg.ttest(correction=True))
│ │ └── 정규성X → Mann-Whitney U (pg.mwu())
│ │
│ └── 3+ 그룹
│ ├── 정규성O, 등분산성O → One-way ANOVA (pg.anova())
│ ├── 정규성O, 등분산성X → Welch's ANOVA (pg.welch_anova())
│ └── 정규성X → Kruskal-Wallis (pg.kruskal())
│
└── 범주형-범주형
└── 가정 검정 불필요
└── 카이제곱 검정 (pg.chi2_independence())
| 검정 목적 | 함수 | 비고 | 판단 기준 |
|---|---|---|---|
| 정규성 검정 | pg.normality() | n ≤ 5000 | p > 0.05 → 정규 |
pg.normality(method='jarque_bera') | n > 5000 | p > 0.05 → 정규 | |
| 등분산성 검정 | pg.homoscedasticity() | - | p > 0.05 → 등분산 |
개별 검정(연속형-연속형)
pg.normality(df1['before'])
그룹별 검정(범주형-연속형)
pg.normality(data=df, dv='Price', group='MetColor')
pg.homoscedasticity(data=df, dv='Price', group='MetColor')| 방법 | 함수 | 특징/조건 |
|---|---|---|
Pearson | pg.corr() | 정규성O, 선형 관계 (기본) |
Spearman | pg.corr(method='spearman') | 정규성X, 단조 관계 |
Kendall | pg.corr(method='kendall') | 작은 샘플(n<30), 동점 많을 때 |
Pearson
pg.corr(x=df['Age'], y=df['Price'])
Spearman
pg.corr(x=df['Age'], y=df['Price'], method='spearman')
Kendall
pg.corr(x=df['Age'], y=df['Price'], method='kendall')
| X (독립변수) | Y (종속변수) | 조건 | 분석 방법 | 함수 |
|---|---|---|---|---|
| 범주형 | 범주형 | - | 카이제곱 검정 | pg.chi2_independence() |
| 범주형 (2그룹) | 연속형 | 정규성O, 등분산O | 독립표본 t-test | pg.ttest() |
| 정규성O, 등분산X | Welch's t-test | pg.ttest(correction=True) | ||
| 정규성X | Mann-Whitney U | pg.mwu() | ||
| 범주형 (3+그룹) | 연속형 | 정규성O, 등분산O | One-way ANOVA | pg.anova() |
| 정규성O, 등분산X | Welch's ANOVA | pg.welch_anova() | ||
| 정규성X | Kruskal-Wallis | pg.kruskal() | ||
| 대응표본(전/후) | 연속형 | 정규성O | 대응표본 t-test | pg.ttest(paired=True) |
| 정규성X | Wilcoxon 부호순위 | pg.wilcoxon() |
카이제곱 검정
test = pg.chi2_independence(data=df2, x='Coupon', y='Purchase', correction=True)
test[2]
그룹 추출 방식
auto_y1 = df.loc[df['Automatic'].eq('0'), 'Price']
auto_y2 = df.loc[df['Automatic'].eq('1'), 'Price']
pg.ttest(x=auto_y1, y=auto_y2, correction=False)
데이터프레임 방식
pg.ttest(data=df, dv='Price', between='Automatic')
그룹 추출 방식
y1 = df.loc[df['MetColor'].eq('0'), 'Price']
y2 = df.loc[df['MetColor'].eq('1'), 'Price']
pg.ttest(x=y1, y=y2, correction=True)
데이터프레임 방식
pg.ttest(data=df, dv='Price', between='MetColor', correction=True)
그룹 추출 방식
y1 = df.loc[df['MetColor'].eq('0'), 'Price']
y2 = df.loc[df['MetColor'].eq('1'), 'Price']
pg.mwu(x=y1, y=y2)
데이터프레임 방식
pg.mwu(data=df, dv='Price', between='MetColor')
One-way ANOVA
pg.anova(data=df, dv='Price', between='FuelType')
Welch's ANOVA
pg.welch_anova(data=df, dv='Price', between='FuelType')
Kruskal-Wallis
pg.kruskal(data=df, dv='Price', between='FuelType')
대응표본 t-test
pg.ttest(x=df1['before'], y=df1['after'], paired=True)
Wilcoxon 부호순위
pg.wilcoxon(x=df1['before'], y=df1['after'])
| 검정 유형 | 검정 | 귀무가설 (H0) | p < 0.05 의미 | 사후 분석 |
|---|---|---|---|---|
| 상관분석 | Pearson / Spearman / Kendall | 두 변수 간 상관이 없다 (ρ = 0) | 유의한 상관 있음 ✅ | - |
| 평균 비교 (독립표본) | 독립표본 t-test | 두 그룹의 평균이 같다 (μ₁ = μ₂) | 평균 차이 있음 ✅ | - |
Welch's t-test | 두 그룹의 평균이 같다 (μ₁ = μ₂) | 평균 차이 있음 ✅ | - | |
Mann-Whitney U | 두 그룹의 분포가 같다 | 분포 차이 있음 ✅ | - | |
One-way ANOVA | 모든 그룹의 평균이 같다 (μ₁ = μ₂ = μ₃) | 그룹 간 차이 있음 ✅ | Tukey HSD | |
Welch's ANOVA | 모든 그룹의 평균이 같다 (μ₁ = μ₂ = μ₃) | 그룹 간 차이 있음 ✅ | Tamhane | |
Kruskal-Wallis | 모든 그룹의 분포가 같다 | 그룹 간 차이 있음 ✅ | Nemenyi | |
| 평균 비교 (대응표본) | 대응표본 t-test | 전후 평균 차이가 0이다 (μd = 0) | 전후 차이 있음 ✅ | - |
Wilcoxon 부호순위 | 전후 분포가 같다 | 전후 차이 있음 ✅ | - | |
| 독립성 검정 | 카이제곱 검정 | 두 변수는 독립적이다 | 연관성 있음 ✅ | - |
| 방법 | 전제 조건 | 특징 | 함수 |
|---|---|---|---|
Tukey HSD | 정규성O, 등분산O | 균형 잡힌 검정력 모든 쌍 비교 | sp.posthoc_tukey() |
Scheffe | 정규성O, 등분산O | 매우 보수적, 복잡한 대비 가능 | sp.posthoc_scheffe() |
Tamhane | 정규성O, 등분산X | Welch 기반 보수적 | sp.posthoc_tamhane() |
Nemenyi | 정규성X비모수 | Kruskal 기반 순위 사용 | sp.posthoc_nemenyi() |
p < 0.05 → 쌍 간 차이 ✅
Tukey HSD
sp.posthoc_tukey(a=df, val_col='Price', group_col='FuelType')
Scheffe
sp.posthoc_scheffe(a=df, val_col='Price', group_col='FuelType')
Tamhane
sp.posthoc_tamhane(a=df, val_col='Price', group_col='FuelType')
Nemenyi
sp.posthoc_nemenyi(a=df, val_col='Price', group_col='FuelType')
이번 연휴 간 통계 이론 공부와 데이터 전처리 템플릿 제작, SQLD 자격증 공부 등 부족한 부분을 최대한 보충할 수 있도록 해봐야겠다.