✅ 오늘 한 것
통계 및 머신러닝 정리, 태블로를 활용한 데이터 시각화
✏️ 오늘 배운 점
데이터 준비 과정
1) 데이터 불러오기 & 확인
# 데이터 크기(shape)
df.shape
# 결측치 여부
df.isnull().sum()
# 변수 타입 확인
df.info()
# 고유값(unique values) 확인
df['컬럼명'].unique() # 고유값 확인
df.nunique() # 고유값 개수 확인
# 기초 통계치(mean, std, min, max 등)
df.describe()
2) 결측치 탐색
# 결측치 비율
df.isnull().mean() * 100
# 결측치 패턴 분석
1. Missingno를 활용한 결측치 패턴 시각화
import missingno as msno
# 결측치 행렬 시각화
msno.matrix(df)
# 결측치 막대 그래프
msno.bar(df)
# 결측치 상관관계
msno.heatmap(df)
2. 결측치 상관 분석
# 결측 여부를 0/1로 변환
null_df = df.isnull().astype(int)
# 결측치 간 상관 분석
null_df.corr()
# 결측치 처리 전략(제거/대체/모델 기반 처리 등)
1. 결측치 제거
df = df.dropna() # 결측치 포함된 행 제거
df = df.dropna(subset=['컬럼명']) # 특정 컬럼만 기준으로 결측치 제거
2. 결측치 대체
df['컬럼명'] = df['컬럼명'].fillna(df['컬럼명'].mean())
df['컬럼명'] = df['컬럼명'].fillna(df['컬럼명'].median())
df['컬럼명'] = df['컬럼명'].fillna(df['컬럼명'].mode()[0])
3. 다중 대체(MICE), KNN 등 대체 방법
# KNN 기반 결측치 대체
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=n)
df_knn = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
# MICE
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer = IterativeImputer()
df_mice = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
- KNN Imputer
기본적으로 n 값은 3~5를 많이 사용한다.
데이터가 많을수록 n은 크게, 작을수록 n은 작게 사용한다.
데이터가 1000~5000개 사이는 3~5 사용, 5000~10000개 사이면 7~10 사용
3) 이상치 탐색
# Boxplot, Scatterplot, Z-score, IQR 기반 탐지
1. Boxplot (이상치를 시각적으로 확인)
import seaborn as sns
import matplotlib.pyplot as plt
sns.boxplot(x=df['컬럼명'])
plt.show()
2. Scatterplot (변수 간 관계에서 이상치 확인)
plt.scatter(df['컬럼명'], df['컬럼명'])
plt.xlabel('컬럼명')
plt.ylabel('컬럼명')
plt.show()
3. Z-score 기반 이상치 탐지
from scipy.stats import zscore
df['컬럼명_z'] = zscore(df['컬럼명'])
outliers_z = df[df['컬럼명_z'].abs() > 3]
outliers_z
4. IQR 기반 탐지
Q1 = df['컬럼명'].quantile(0.25)
Q3 = df['컬럼명'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = df[(df['컬럼명'] < lower_bound) | (df['컬럼명'] > upper_bound)]
outliers_iqr
# 도메인 지식 기반 판단
현실적으로 불가능한 값 제거 or 수정
# 제거, 변환, 조정 여부 결정
1. 이상치 제거
df_clean = df.drop(outlier_indices)
2. Log 변환 (왜도 감소)
df['컬럼명_log'] = np.log1p(df['컬럼명'])
3. Scaling (극단값 영향 줄이기)
표준화(StandardSclaer)
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(df['컬럼명'])
정규화(MinMaxScaler)
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
4) 단변량 분석
1. 히스토그램(Histogram)
sns.histplot(df['컬럼명'], kde=False)
2. KDE
sns.kdeplot(df['컬럼명'])
3. Boxplot
sns.boxplot(x=df['컬럼명'])
4. 왜도 & 첨도
from scipy.stats import skew, kurtosis
skew_value = skew(df['컬럼명'].dropna())
kurt_value = kurtosis(df['컬럼명'].dropna())
5) 변수 간 관계 분석
1. 수치형 ↔ 수치형 (상관 분석 & Heatmap)
df.corr()
sns.heatmap(df.corr(), annot=True, cmap='coolwarm')
2. 범주형 ↔ 수치형 (그룹 간 차이 탐색)
sns.boxplot(x=df['범주형컬럼'], y=df['수치형컬럼'])
3. 범주형 ↔ 범주형 (교차표)
pd.crosstab(df['cat1'], df['cat2'])
sns.heatmap(pd.crosstab(df['cat1'], df['cat2']), annot=True, cmap='Blues')
범주형 데이터: 수치로 측정할 수 없고 종류별로 분류되는 데이터(성별, 혈액형, 거주 지역 등)
수치형 데이터: 수치로 측정할 수 있고 수학적 계산이 가능한 데이터(키, 몸무게 등)
6) 정규성 검정
1. Shapiro-Wilk Test
from scipy.stats import shapiro
stat, p = shapiro(df['컬럼명'].dropna())
print('Shapiro Test: stat=%.3f, p=%.4f' % (stat, p))
2. Q-Q Plot
import scipy.stats as stats
stats.probplot(df['컬럼명'].dropna(), dist="norm", plot=plt)
- p < 0.05 → 정규성 X
- p ≥ 0.05 → 정규성 O
7) 등분산성 검정
1. Levene Test
from scipy.stats import levene
levene(
df[df['그룹']=='A']['컬럼명'],
df[df['그룹']=='B']['컬럼명'],
df[df['그룹']=='C']['컬럼명']
)
2. Bartlett Test
from scipy.stats import bartlett
bartlett(
df[df['그룹']=='A']['컬럼명'],
df[df['그룹']=='B']['컬럼명'],
df[df['그룹']=='C']['컬럼명']
)
- p < 0.05 → 분산이 동일하지 않음 (등분산성 X)
- p ≥ 0.05 → 등분산성 O
통계적 검정
통계적 가설 검정 선택 (모수 / 비모수)
정규성 O → 모수 검정
정규성 X → 비모수 검정
단일 집단 확인
# 단일표본 t-검정: 한 집단의 평균이 특정 값과 다른지 확인할 때 사용
from scipy.stats import ttest_1samp
t_stat, p_value = ttest_1samp(x, y)
print("t-statistic:", t_stat)
print("Two-tailed p-value:", p_value)
두 집단 비교
| 목적 | 모수 | 비모수 |
| 두 집단 평균 비교 | 독립표본 t-검정 | Mann–Whitney U test |
| 짝지어진 두 집단 | 대응표본 t-검정 | Wilcoxon signed-rank |
모수 검정
# 독립표본 t-검정: 서로 다른 두 그룹 간 평균 차이를 비교할 때 사용
양측 검정: 평균에 차이가 있는지만 검정
단측 검정: 평균이 크거나 작다는 방향성 있는 검정
from scipy.stats import ttest_ind
t_stat, p_value_two_tailed = ttest_ind(x, y)
print("t-statistic:", t_stat)
print("Two-tailed p-value:", p_value_two_tailed
if t_stat > 0:
p_value_one_tailed = p_value_two_tailed / 2
else:
p_value_one_tailed = 1 - (p_value_two_tailed / 2)
print("One-tailed p-value (A > B):", p_value_one_tailed)
# 대응표본 t-검정: 동일한 대상에서 전/후 값을 비교할 때 사용
from scipy.stats import ttest_rel
t_stat, p_value = ttest_rel(x, y)
print("t-statistic:", t_stat)
print("Two-tailed p-value:", p_value)
Cohen’s d – 두 집단 간 차이의 효과 크기
import numpy as np
def cohens_d(x, y):
nx, ny = len(x), len(y)
pooled_std = np.sqrt(((nx-1)*np.var(x, ddof=1) + (ny-1)*np.var(y, ddof=1)) / (nx+ny-2))
return (np.mean(x) - np.mean(y)) / pooled_std
d = cohens_d(x, y)
print("Cohen's d:", d)
| d 값 | 해석 |
| 0.2 | 작은 효과 |
| 0.5 | 중간 효과 |
| 0.8 이상 | 큰 효과 |
비모수 검정
# Mann-Whitney U Test: 두 독립 집단 간의 중앙값 차이 또는 분포 차이를 비교
from scipy.stats import mannwhitneyu
stat, p = mannwhitneyu(group1, group2, alternative='two-sided')
print(f'U = {stat}, p-value = {p:.4f}')
# Wilcoxon Test: 같은 집단에서의 사전-사후 변화 등 쌍으로 대응되는 데이터의 차이를 비교
from scipy.stats import wilcoxon
stat, p = wilcoxon(x, y)
print(f'W = {stat}, p-value = {p:.4f}')
t-stat: |t|(절대값)이 클수록 두 평균의 차이가 크다는 것을 의미한다.
|t|(절대값)이 클 때
- 두 집단의 평균 차이가 큼
- 오차(분산)가 작음
- 차이가 일관적으로 나타남
t > 0: 그룹1 평균 > 그룹2 평균
t < 0: 그룹 1 평균 < 그룹2 평균
p-value < 0.05 --> 통계적으로 유의하게 다름. (귀무가설(H0) 기각)
p-value ≥ 0.05 --> 차이가 없다. (귀무가설 채택)
p-value와 t값은 반비례 관계 (t가 클수록 p-value가 작음. // t가 작을수록 p-value가 큼.)
세 집단 이상 비교
| 목적 | 모수 | 비모수 |
| 세 집단 평균 비교 | One-way ANOVA | Kruskal–Wallis |
모수 검정
# One-Way ANOVA
from scipy.stats import f_oneway
print(f"p-value = {p_val_levene:.4f}")
f_stat, p_value = f_oneway(x_a, x_b, x_c)
print(f"\n--- 일원 분산분석 결과 ---")
print(f"F 통계량: {f_stat:.3f}")
print(f"p-value: {p_value:.4f}")
# Two-Way ANOVA
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
model = ols('Strength ~ C(Supplier) + C(Temp) + C(Supplier):C(Temp)', data=data).fit()
anova_table = anova_lm(model, typ=2)
print("\n--- 이원 분산분석 결과 (ANOVA Table) ---")
print(anova_table)
η² (Eta Squared) – ANOVA 효과 크기
η²: 총 변화 중 집단 차이가 설명하는 비율
import numpy as np
# ANOVA 실행
f_stat, p_value = f_oneway(x_a, x_b, x_c)
# eta squared 계산
SS_between = (
((np.mean(x_a) - np.mean(np.concatenate([x_a, x_b, x_c])))**2 * len(x_a)) +
((np.mean(x_b) - np.mean(np.concatenate([x_a, x_b, x_c])))**2 * len(x_b)) +
((np.mean(x_c) - np.mean(np.concatenate([x_a, x_b, x_c])))**2 * len(x_c))
)
SS_total = np.sum((np.concatenate([x_a, x_b, x_c]) - np.mean(np.concatenate([x_a, x_b, x_c])))**2)
eta_squared = SS_between / SS_total
print("η² (Eta Squared):", eta_squared)
비모수 검정
# Kruskal–Wallis
from scipy.stats import kruskal
h_stat, p_val_kruskal = kruskal(x_a, x_b, x_c)
print(f"--- Kruskal-Wallis Test ---")
print(f"H-statistic: {h_stat:.4f}, P-value: {p_val_kruskal:.4f}")
- p-value < 0.05 → 세 집단 중 적어도 한 곳의 평균이 다르다 (귀무가설 기각)
- p-value ≥ 0.05 → 평균 차이가 유의하지 않다 (귀무가설 유지)
사후검정(Post-hoc)
- ANOVA → Tukey HSD
- Kruskal → Dunn test
# Tukey's HSD(모수 사후검정)
from statsmodels.stats.multicomp import pairwise_tukeyhsd
tukey = pairwise_tukeyhsd(endog=df['값'], groups=df['그룹'], alpha=0.05)
print(tukey)
# Dunn's Test(비모수 사후검정)
import scikit_posthocs as sp
sp.posthoc_dunn(df, val_col='값', group_col='그룹', p_adjust='bonferroni')
상관/연관 분석
- Pearson (정규성)
from scipy.stats import pearsonr
corr, p_value = pearsonr(df['x'], df['y'])
print("Pearson Correlation:", corr)
print("p-value:", p_value)
| corr 값 | 의미 |
| +1 | 완전한 양의 선형 관계 |
| 0 | 선형 관계 없음 |
| -1 | 완전한 음의 선형 관계 |
p-value < 0.05 → 통계적으로 유의한 상관관계 존재
p-value ≥ 0.05 → 우연일 가능성, 상관 없음
- Spearman (비정규)
from scipy.stats import spearmanr
corr, p_value = spearmanr(df['x'], df['y'])
print("Spearman Correlation:", corr)
print("p-value:", p_value)
| corr 값 | 의미 |
| +1 | 완전한 양의 선형 관계 |
| 0 | 선형 관계 없음 |
| -1 | 완전한 음의 선형 관계 |
p-value < 0.05 → 통계적으로 유의한 상관관계 존재
p-value ≥ 0.05 → 우연일 가능성, 상관 없음
- Kendall tau
from scipy.stats import kendalltau
corr, p_value = kendalltau(df['x'], df['y'])
print("Kendall Tau:", corr)
print("p-value:", p_value)
- 카이제곱 검정(범주형↔범주형)
import pandas as pd
table = pd.crosstab(df['cat1'], df['cat2'])
print(table)
from scipy.stats import chi2_contingency
chi2, p_value, dof, expected = chi2_contingency(table)
print("Chi-square:", chi2)
print("p-value:", p_value)
print("Degrees of freedom:", dof)
print("Expected frequencies:\n", expected)
p-value < 0.05
→ 두 범주형 변수 간 연관성이 존재 (독립이 아니다)
p-value ≥ 0.05
→ 두 변수는 독립이며 관계 없음
단순/다중 회귀 분석(Regression)
- 선형회귀
# 단순 선형 회귀(X가 증가할수록 Y도 증가/감소하는가?)
from sklearn.linear_model import LinearRegression
import numpy as np
X = df[['x']]
y = df['y']
model = LinearRegression()
model.fit(X, y)
print("기울기(β1):", model.coef_[0])
print("절편(β0):", model.intercept_)
print("R²:", model.score(X, y))
# 다중 선형 회귀(여러 독립변수가 Y에 어떤 영향을 미치는가?)
X = df[['x1', 'x2', 'x3']]
y = df['y']
model = LinearRegression()
model.fit(X, y)
print("회귀계수:", model.coef_)
print("절편:", model.intercept_)
print("R²:", model.score(X, y))
다중공선성(VIF)
from statsmodels.stats.outliers_influence import variance_inflation_factor
import pandas as pd
X = df[['x1', 'x2', 'x3']]
vif_df = pd.DataFrame()
vif_df['feature'] = X.columns
vif_df['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
print(vif_df)
| VIF 값 | 해석 |
| 1~5 | 문제 없음 |
| 5~10 | 다소 높음 → 주의 필요 |
| 10 이상 | 강한 다중공선성 → 변수 제거 고려 |
- 다항회귀
# 다항 회귀(X가 증가하면서 곡선 형태로 Y가 변하는 경우)
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
poly = PolynomialFeatures(degree=2)
X_poly = poly.fit_transform(df[['x']])
y = df['y']
model = LinearRegression()
model.fit(X_poly, y)
print("회귀계수:", model.coef_)
print("R²:", model.score(X_poly, y))
- 로지스틱 회귀
# 로지스틱 회귀(독립 변수의 선형 결합을 이용하여 사건의 발생 가능성을 예측)
from sklearn.linear_model import LogisticRegression
X = df[['x1', 'x2', 'x3']]
y = df['label']
model = LogisticRegression()
model.fit(X, y)
print("회귀계수:", model.coef_)
print("절편:", model.intercept_)
print("정확도:", model.score(X, y))
- Poisson/Negative Binomial 회귀
# 포아송 회귀(단위 시간 또는 공간 내에서 어떤 사건이 평균 λ번 발생할 때, 실제 사건 발생 횟수(k)에 대한 이산형 확률분포)
import statsmodels.api as sm
import statsmodels.formula.api as smf
model = smf.glm("count ~ x1 + x2", data=df,
family=sm.families.Poisson()).fit()
print(model.summary())
# Negative Binomial Regression(분산 > 평균 (과산포)일 때, 포아송보다 더 적합)
import statsmodels.api as sm
import statsmodels.formula.api as smf
model = smf.glm("count ~ x1 + x2", data=df,
family=sm.families.NegativeBinomial()).fit()
print(model.summary())
- GLM
# GLM(선형 회귀의 확장판)
import statsmodels.api as sm
import statsmodels.formula.api as smf
model = smf.glm("y ~ x1 + x2", data=df,
family=sm.families.Gaussian()).fit()
print(model.summary())
| family | 용도 |
| Gaussian | 일반 선형 회귀 |
| Binomial | 로지스틱 회귀 |
| Poisson | 카운트 |
| NegativeBinomial | 과산포 카운트 |
| Gamma | 시간/비율 등 |
태블로를 활용한 데이터 시각화
Visual Analytics: 인간의 시각적 지각 능력을 활용해서 데이터를 표현하고 분석하는 과정
비즈니스 인텔리전스(Business Inteligence, BI): 조직에서 비즈니스 데이터를 수집, 분석 및 시각화하여 실행 가능한 통찰과 의미 있는 정보를 생성하기 위해 사용하는 기술, 전략 프로세스
Tableau: 데이터 시각화와 비즈니스 인텔리전스(BI) 도구
Tableau Prep: 데이터를 결합, 변형, 정리하는 과정 담당
Tableau Desktop: 데이터 탐색, 시각화, 분석 수행
Tableau Server & Tableau Cloud: 조직 내 협업, 배포, 거버넌스 관리
📌추가로 해야할 점
머신러닝 정리, 태블로를 활용한 데이터 시각화
'품질관리(QAQC) 데이터 부트캠프(본캠프)' 카테고리의 다른 글
| 본캠프_10주차(수)_TIL(심화 프로젝트 기반 피드백) (0) | 2025.11.19 |
|---|---|
| 본캠프_10주차(화)_TIL(머신러닝 정리) (0) | 2025.11.18 |
| 본캠프_9주차(금)_TIL(심화 프로젝트 기간) (0) | 2025.11.14 |
| 본캠프_9주차(목)_TIL(심화 프로젝트 기간) (0) | 2025.11.13 |
| 본캠프_9주차(수)_TIL(심화 프로젝트 기간) (0) | 2025.11.12 |