1. 기술 통계
1.1 모수 검정 vs 비모수 검정
- 모수 검정: 모집단의 분포에 대한 가정을 하고 검정
- 예: t-검정, ANOVA, 회귀 분석
- 비모수 검정: 모집단의 분포를 가정하지 않고 검정
- 예: Mann-Whitney U Test, 카이제곱 검정
- 비모수 검정이 필요한 경우
- 데이터가 정규성을 따르지 않음
- 표본 크기가 작음
- 데이터가 서열척도 또는 명목척도
1.2 독립변수(설명변수) 유형에 따른 분석
독립변수 | 종속변수 | 사용 기법 |
---|---|---|
범주형 | 범주형 | 카이제곱 검정 |
범주형 (2개 그룹) | 연속형 | t-검정 (독립표본 또는 대응표본) |
범주형 (3개 이상 그룹) | 연속형 | ANOVA (분산분석) |
연속형 | 연속형 | 회귀분석 (OLS, 다중회귀, 다항회귀) |
연속형 + 범주형 | 연속형 | 공분산분석 (ANCOVA) |
연속형 | 범주형 (이진) | 로지스틱 회귀 분석 |
연속형 | 범주형 (다중) | 다항 로지스틱 회귀 |
1.3 범주형 변수 처리 (Dummy Variable)
범주형 변수를 회귀 분석에 사용하려면 가변수(Dummy Variable) 변환이 필요
ex) Gender 변수를 0(Female), 1(Male)로 변환
범주가 여러 개인 경우
- 기준 범주를 제외하고 가변수 생성
1.4 IQR(이상치 확인)
1.1.1 사분위수(Q1, Q3)
Q1 (제1사분위수): 데이터의 하위 25% 위치의 값
Q3 (제3사분위수): 데이터의 상위 75% 위치의 값
1.1.2 IQR (Interquartile Range)
IQR = Q3 - Q1
데이터의 중앙 50%가 흩어진 정도를 의미
1.1.3 이상치 판단 기준
Minimum = Q1 - (IQR * 1.5)
Maximum = Q3 + (IQR * 1.5)
Minimum보다 크거나 Maximum보다 작으면 정상 데이터로 판단할 수 있으며 이를 벗어난 데이터는 이상치로 간주
1.1.4 전체 데이터에서 이상치 제거
이상치를 제거하려면 quantile()
을 활용하여 Q1, Q3 값을 구하고 이를 기준으로 필터링한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
q1 = data["변수"].quantile(0.25)
q3 = data["변수"].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
print(f"이상치 범위: {lower_bound} ~ {upper_bound}")
x = len(data)
# 이상치 제거: 기준 범위 내의 데이터만 선택
filtered_data = data[(data["변수"] >= lower_bound) & (data["변수"] <= upper_bound)]
print(f"제거된 이상치 개수: {x - len(filtered_data)}")
# 데이터 갱신
data = filtered_data
1.1.5 범주별 이상치 제거
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 독립변수(x)에 따른 종속변수(y)의 이상치 제거
for category in data["X"].unique():
subset = data[data["X"] == category]
q1 = subset["Y"].quantile(0.25)
q3 = subset["Y"].quantile(0.75)
iqr = q3 - q1
lower_bound = q1 - 1.5 * iqr
upper_bound = q3 + 1.5 * iqr
# 기준을 벗어난 값(이상치) 제거
filtered_data = data[~((data["X"] == category) &
((data["Y"] < lower_bound) |
(data["Y"] > upper_bound)))]
outlier_count = len(data) - len(filtered_data)
print(f"제거된 이상치 개수: {outlier_count}")
# 데이터 갱신
data = filtered_data
1.2 데이터 시각화
1.2.1 기본 그래프
- 막대 그래프 (Bar Chart)
- x축 : 범주형 변수
- y축 : 수치형 변수
→ 각 범주의 평균, 합계 등을 비교 분석
- 산점도 (Scatter Plot)
- x축 : 수치형 변수
- y축 : 수치형 변수
→ 두 변수 간의 상관관계 및 분포를 확인
1.2.2 boxplot
Boxplot은 데이터의 중앙값, 사분위 범위, 이상치를 한눈에 확인할 수 있는 시각화 도구
1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(8, 6))
sns.boxplot(x="X", y="Y", hue="색상 구분을 위한 추가범주",
data=data, palette="coolwarm", legend=False)
plt.xlabel("X")
plt.ylabel("Y")
plt.title("Distribution of Y by X")
plt.show()
상자 밖에 표시된 점(o)은 이상치로 판단한다.
1.2.3 Implot
lmplot은 두 변수 간의 선형 관계를 시각적으로 확인할 수 있는 도구
*컬럼명을 문자열로 직접 전달
*변수 적용시 sns.regplot()
사용
1
2
3
4
5
import matplotlib.pyplot as plt
import seaborn as sns
sns.lmplot(x='X', y='Y', data=data)
plt.show()
ex)
1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
import seaborn as sns
# 1)
sns.lmplot(x='Study time', y='Performance', data=data)
plt.show()
# 2)
X = data['Study time']
y = data['Performance']
sns.regplot(x=X, y=y)
plt.show()
1.2.4 plotly
Plotly Express를 사용하면 대화형 그래프를 쉽게 생성할 수 있다.
1
import plotly.express as px
histogram
데이터 분포가 정규분포 형태를 따르는지 확인할 수 있다.
1
px.histogram(data, x='독립변수', y='종속변수')
종 모양의 대칭적 분포인지 확인 → 정규분포의 형태
scatter
두 변수간의 관계를 파악하기 좋은 도구
1
2
3
import plotly.express as px
fig = px.scatter(x='독립변수', y='종속변수', data_frame=data)
fig.show()
1
2
# 성별에 따른 산점도
px.scatter(data, x='독립변수', y='종속변수', facet_col='Gender')
ex)
1
2
3
4
5
6
7
8
9
10
11
import plotly.express as px
# 1)
X = data['Study time (Hours)']
y = data['Performance in online']
fig = px.scatter(x=X, y=y, trendline='ols')
fig.show()
# 2)
fig = px.scatter(x='Study time (Hours)', y='Performance in online', data_frame=data, trendline='ols')
fig.show()
Matrix Heatmap
변수 간의 상관관계를 한눈에 확인할 수 있다.
1
2
z = data[['X', 'Y']].corr().round(2)
px.imshow(z, text_auto=True, width=600, height=600)
text_auto=True
: Heatmap위에 텍스트 넣기
2. 적합도 검정
H0 : 데이터가 정규분포를 따른다.
H1 : 데이터가 정규분포를 따르지 않는다.
2.1 카이제곱 검정
두 범주형 변수 간의 관계를 분석하며 관측 빈도와 기대 빈도의 차이가 통계적으로 유의한지 평가
2.1.1 범주형 데이터 변환
연속형 변수를 범주형으로 변환할 때는 pd.cut()
또는 pd.qcut()
을 사용
- pandas.cut : 데이터의 값 범위를 기준으로 일정한 간격으로 분할(각 구간에 속하는 데이터 개수는 다를 수 있음)
pd.cut(컬럼, bins=구간 수, labels=["라벨1", "라벨2", "라벨3"])
- pandas.qcut : 데이터를 개수가 비슷한 그룹으로 나눔(각 구간에 속하는 데이터 개수가 비슷함)
pd.qcut(컬럼, q=구간 수, labels=["라벨1", "라벨2", "라벨3"])
1
2
3
4
5
6
7
# cut을 사용한 범주화 예시
data['Y_cut'] = pd.cut(data['Y'], bins=[60, 80, 90, 100], labels=['낮음', '중간', '높음'])
data['X_cut'] = pd.cut(data['X'], bins=[50, 65, 75, 100], labels=['낮음', '중간', '높음'])
# qcut을 사용한 범주화 예시
data["Y_cut"] = pd.qcut(data["Y"], q=3, labels=['낮음', '중간', '높음'])
data["X_cut"] = pd.qcut(data["X"], q=3, labels=['낮음', '중간', '높음'])
2.1.2 카이제곱
귀무가설: 두 변수는 연관성이 없다. (독립이다)
대립가설: 두 변수는 연관성이 있다. (독립이 아니다)
scipy를 이용한 카이제곱 검정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from scipy.stats import chi2_contingency
import pandas as pd
# scipy를 이용한 카이제곱 검정(카이제곱, p, 자유도, 기대치)
observed = pd.crosstab(data['변수1'], data['변수2'])
chi2, p, dof, expected = chi2_contingency(observed)
print(f"카이제곱 통계량: {chi2}, p-value: {p}")
print(f"자유도: {dof}")
print(f"기대 빈도표: \n{expected}")
# 기대 빈도 중 5 미만 셀 개수 확인
cells = (expected < 5).sum()
percentage = (cells / expected.size) * 100
print(f"5 미만 셀 개수: {cells}")
print(f"전체 셀 대비 비율: {percentage:.1f}%")
crosstab()
을 사용하여 분팔표를 만듦기대빈도가 5 미만인 셀이 전체 셀의 20% 이상이면 카이제곱 검정을 사용할 수 없다.
p-value < 0.05이면 귀무가설 기각 → 두 변수는 독립이 아님 (즉, 관련 있음)
임계값 확인
1
2
import scipy.stats as stats
print(stats.chi2.ppf(0.95,4).round(2)) # 유의수준 95%, 자유도 4
ex) 카이제곱 검정 통계량(X²=___)이 임계값(9.49)보다 크고
p-value가 0.0 (< 0.05)이므로 귀무가설을 기각하고 대립가설을 채택
기여도(잔차) 분석
1
2
3
4
5
residuals = (observed - expected) / np.sqrt(expected)
# 잔차 = (교차표(관측된 빈도수)) - 기대빈도수 / 제곱근(기대빈도 수)
print(residuals)
#print(pow(residuals,2))
표준화잔차 = 원시 잔차를 기대 카운트의 제곱근으로 나눈 값
각 셀의 카이제곱에 대한 기여도 = 표준화 잔차 제곱
각 셀의 표준화 잔차를 계산하여 어떤 셀이 기여도가 큰지 평가
1
2
3
4
# 그룹별 데이터 추출 (ex. Low, Medium, High 그룹)
low = data[data['독립변수'] == 'Low']['종속변수']
medium = data[data['독립변수'] == 'Medium']['종속변수']
high = data[data['독립변수'] == 'High']['종속변수']
2.2 정규성 검정
히스토그램
1
2
import seaborn as sns
sns.histplot(x, kde=True) # histogram과 kde를 한 그래프에 나타내기
Q-Q plot
1
2
3
from scipy import stats
import matplotlib.pyplot as plt
stats.probplot(x, dist="norm", plot=plt);
Shapiro-Wilk 검정 (n < 5000)
표본 수가 적을 때 (n < 5000) *공식문서
1
2
3
4
5
6
import scipy.stats as stats
statis_x, p_x = stats.shapiro(data["X"])
statis_y, p_y = stats.shapiro(data["Y"])
print(f"x 정규성 검정 p-value: {p_x}")
print(f"y 정규성 검정 p-value: {p_y}")
Kolmogorov-Smirnov 검정 (n > 5000)
표본 수가 많을 때 (n > 5000)
1
2
3
4
5
6
# kstest(독립변수, 'norm', (평균, 표준편차))
statis_x, p_x = stats.kstest(data["X"], 'norm')
statis_y, p_y = stats.kstest(data["Y"], 'norm')
print(f"성적 정규성 검정 p-value: {p_x}")
print(f"출석률 정규성 검정 p-value: {p_y}")
*norm : 정규분포(𝑁(𝜇,𝜎²)
)
*데이터(변수)가 정규분포를 따르는지 검정하는 역할
*기본적으로 표준 정규분포(𝑁(0,1)
)와 비교
*모집단에서 표본을 추출한 경우 (np.mean(변수), np.std(변수))
지정
유의수준 5%에서 귀무가설이 기각된다면 정규분포가 아니므로 비모수통계를 이용해서 분석해야한다.
ks_1samp
: 적합도를 확인하기 위해 단일 표본 Kolmogorov-Smirnov 검정을 수행
ks_2samp
: 적합도를 확인하기 위해 두 표본 Kolmogorov-Smirnov 검정을 수행
kstest
: 적합도를 확인하기 위해 (단일 표본 또는 두 표본) Kolmogorov-Smirnov 검정을 수행
3. 상관 분석
두 변수 간의 관계를 파악하는 데 사용된다.
정규성 여부에 따라 Pearson (정규 분포) / Spearman (비모수) 선택
- 척도에 따른 방법 선택
- 등간척도/비율척도 → Pearson
- 서열척도 → Spearman
- 명목척도 → 상관 분석 불가
Pandas 내장 메서드 활용
1
2
3
data.corr() # data.corr(method='pearson')
data.corr(method='kendall')
data.corr(method='spearman')
scipy 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from scipy import stats
# 데이터 생성
X = [1, 2, 3, 4, 5]
y = [50, 55, 60, 65, 70]
# Pearson Correlation
correlation, _ = stats.pearsonr(X, y) # _는 p-value를 무시하겠다는 의미
print(f"Pearson 상관계수: {correlation}")
# Spearman Correlation
correlation, p_value = stats.spearmanr(X, y)
print(f'스피어만 상관계수: {correlation}, p-value: {p_value}')
# Kendall Tau
correlation, p_value = stats.kendalltau(X, y)
print(f'켄달 상관계수: {correlation}, p-value: {p_value}')
3.2 Pearson 상관분석
두 수치형 변수 간 선형 관계를 측정
상관계수: -1(완벽한 음의 관계) ~ +1(완벽한 양의 관계)
1
2
correlation = data['독립변수'].corr(data['종속변수'])
print(f"독립변수와 종속변수 간의 Pearson 상관계수: {correlation.round(2)}")
pearsonr()
: 상관계수, p-value 반환corr()
: 상관계수만 반환
3.3 spearman 상관분석
순위(서열) 기반 상관관계를 측정
정규성 불만족, 이상치 포함 데이터에 적합
1
2
3
4
5
6
7
8
from scipy import stats
spearman_corr, spearman_p = stats.spearmanr(data['X'], data['Y'])
print(f"Spearman 상관계수: {spearman_corr}, p-value: {spearman_p}")
# 3개 이상
vars = data[['X1', 'X2', 'Y']]
spearman_corr_matrix, spearman_p_matrix = stats.spearmanr(vars)
성적과 출석률 → Pearson (연속형 데이터)
성적 순위와 출석률 순위 → Spearman (서열 기반)
4. 집단 간 평균 차이 분석
t-검정, F-검정
- t-검정
- 두 그룹 간 평균 차이 검정
- 독립변수가 종속변수에 유의한 영향을 미치는지 확인
- F-검정
- ANOVA(분산 분석)에서 그룹 간 평균 차이 검정
- 회귀분석에서 전체 모델이 유의한지 검정
- t-검정은 개별 변수, F-검정은 전체 모형 검정
주요 검정 방법
검정 방법 | 설명 | 사용 조건 | 활용 예 |
---|---|---|---|
일표본 t-검정 (One-sample t-test) | 모집단 평균이 특정 값과 같은지 검정 | 단일 모집단 표본 | 특정 약물 복용 후 체온 변화 확인 |
대응표본 t-검정 (Paired t-test) | 같은 집단에서 두 번 측정한 값 비교 | 같은 개체에서 측정된 두 데이터 비교 | 학습 전후 성적 변화 분석 |
독립표본 t-검정 (Independent t-test) | 서로 다른 두 그룹의 평균 비교 | 두 그룹이 독립적이고 정규성 만족 | A 그룹과 B 그룹 간 점수 비교 |
ANOVA (분산분석) | 세 개 이상의 그룹 간 평균 비교 | 정규성 만족, 독립변수: 범주형, 종속변수: 연속형 | A, B, C 그룹 간 평균 비교 |
Mann-Whitney U Test | 독립표본 T-검정의 비모수 검정 | 정규성을 만족하지 않을 때 | 두 그룹 간 중앙값 차이 검정 |
Kruskal-Wallis Test | ANOVA의 비모수 검정 | 정규성을 만족하지 않을 때 | 세 개 이상의 그룹 중앙값 차이 검정 |
T-test는 두 그룹 간 평균 차이를 검정할 때 사용
ANOVA는 세 개 이상의 그룹 간 평균 차이를 검정할 때 사용
일표본 T-검정 (One-sample t-test)
귀무가설(H₀): 모집단 평균이 기준값(μ₀)과 같다.
대립가설(H₁): 모집단 평균이 기준값(μ₀)과 다르다.
1
2
3
4
5
from scipy import stats
t_stat, p_value = stats.ttest_1samp(data, popmean=75) # 모집단 평균 75와 비교
print("T-statistic:", t_stat)
print("P-value:", p_value)
p-value < 0.05 → 모집단 평균이 75와 같지 않다. (유의미한 차이 존재)
p-value ≥ 0.05 → 모집단 평균이 75와 같다.
독립표본 T-검정 (Independent t-test)
두 그룹(A, B)의 평균 차이 검정
등분산성 만족 → equal_var=True
등분산성 불만족 → equal_var=False
1
2
3
4
5
6
7
8
9
10
11
12
13
from scipy import stats
# 독립표본 T-검정 수행
t_statistic, p_value = stats.ttest_ind(group1, group2, equal_var=True)
print("t-statistic:", t_statistic)
print("p-value:", p_value)
# 유의수준 0.05에서의 검정
if p_value < 0.05:
print("두 그룹 간에는 통계적으로 유의미한 차이가 있다.")
else:
print("두 그룹 간에는 통계적으로 유의미한 차이가 없다.")
비모수 검정 (Mann-Whitney U Test)
정규성을 만족하지 않을 때 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
from scipy import stats
# 만-위트니 U 검정 수행
u_statistic, p_value = stats.mannwhitneyu(group1, group2)
print("U-statistic:", u_statistic)
print("p-value:", p_value)
# 유의수준 0.05에서의 검정
if p_value < 0.05:
print("두 그룹 간에는 통계적으로 유의미한 차이가 있다.")
else:
print("두 그룹 간에는 통계적으로 유의미한 차이가 없다.")
p-value < 0.05 → 두 그룹 간 중앙값 차이 존재
p-value ≥ 0.05 → 중앙값 차이 없음
ANOVA 분석
세 개 이상의 그룹 간 평균 비교
정규성 만족 → ANOVA 진행
정규성 불만족 → Kruskal-Wallis 검정
H₀(귀무가설): 모든 독립변수는 Y에 영향을 주지 않음
H₁(대립가설): 적어도 하나의 독립변수는 Y에 영향을 줌
1
2
3
low = data[data['독립변수'] == 'Low']['종속변수']
medium = data[data['독립변수'] == 'Medium']['종속변수']
high = data[data['독립변수'] == 'High']['종속변수']
1. 정규성 검정 (Shapiro-Wilk or Kolmogorov-Smirnov)
1
2
3
4
from scipy import stats
stat, p = stats.shapiro(data['종속변수'])
print(f"정규성 검정 결과: p-value = {p}")
p-value > 0.05 → 정규성 만족 → ANOVA 진행
p-value < 0.05 → 정규성 불만족 → 비모수 검정(ex. Kruskal-Wallis) 적용
2. 등분산성 검정 (Levene, Bartlett)
귀무가설(H₀): 그룹 간 분산이 같다.(등분산이다.)
대립가설(H₁): 그룹 간 분산이 다르다.(이분산이다.)
1
2
3
4
5
6
7
8
## 방법 1
stat, p = stats.levene(low, medium, high) # Levene Test
# stat, p = stats.bartlett(low, medium, high)
print(f"등분산성 검정 결과: p-value = {p}")
## 방법 2
import pingouin as pg
pg.homoscedasticity(dv='종속변수', group='독립변수', data='데이터프레임')
등분산 불만족(p-value ≤ 0.05) → Welch’s ANOVA 적용
“분산이 같다”는 것
그룹 간 분산이 같다는 것은 그룹 내 데이터의 변동성이 유사하다는 의미다.
분산이 다르면 ANOVA 결과가 왜곡될 가능성이 있음
p > 0.05 → 등분산성 만족 → ANOVA 진행
p ≤ 0.05 → Welch’s ANOVA 진행
3. ANOVA
데이터가 특정 분포(정규분포 등)를 따른다고 가정하지 않는 검정 방법.
Kruskal-Wallis 검정은 정규분포 가정을 하지 않기 때문에 비모수 검정에 해당(정규성을 만족하지 않는 데이터에서도 사용할 수 있음)
3-1. ANOVA
정규성 & 등분산 만족 → ANOVA
정규성 만족 & 등분산 불만족 → Welch’s ANOVA
정규성 불만족 → Kruskal-Wallis
일원분산분석 (One-way ANOVA, 독립변수 1개)
방법 | 설명 |
---|---|
anova_lm(ols("종속변수 ~ C(독립변수)", data=df).fit(), typ=2) | 통계 모델 기반 분석 |
f_oneway(그룹1, 그룹2, 그룹3) | 리스트 형태 입력 방식 |
pg.anova(dv='종속변수', between='독립변수', data) | DataFrame 활용 분석 |
이원분산분석 (Two-way ANOVA, 독립변수 2개)
방법 | 설명 |
---|---|
anova_lm(ols("종속변수 ~ C(독립변수1) + C(독립변수2)", data=df).fit(), typ=2) | 이원분산분석 가능 |
pg.anova(dv, between=['독립변수1', '독립변수2'], data=데이터) | DataFrame 기반 이원분산분석 |
ANOVA 실행
1
2
3
4
5
6
7
8
9
10
11
12
13
# 방법 1)
from scipy.stats import f_oneway
anova_result = f_oneway(low, medium, high)
print(f"ANOVA 결과: F-statistic={anova_result.statistic}, p-value={anova_result.pvalue}")
if anova_result.pvalue < 0.05 :
print("그룹 간 평균 차이가 유의미하다.")
# 방법 2)
import pingouin as pg
pg.anova(dv='종속변수', between='독립변수', data='데이터프레임', detailed=True)
p-value < 0.05 → 그룹 간 평균 차이가 유의미
statsmodels를 활용한 ANOVA 분석 (anova_lm)
1
2
3
4
5
6
7
8
9
10
from statsmodels.formula.api import ols
import statsmodels.api as sm
df = data[['독립변수', '종속변수']]
## ols("종속변수 ~ C(독립변수)", data=df).fit()
model = ols("Attendance ~ C(Motivation_Level)", data=df).fit() # y ~ x는 y=ax+b
anova_results = sm.stats.anova_lm(model, typ=2)
print(anova_results)
*C(독립변수)
: 범주형 변수 처리
*독립변수가 연속형 → typ=2
, 범주형 → typ=3
3-2. Welch’s ANOVA (등분산성 불만족 시 사용)
1
2
import pingouin as pg
pg.welch_anova(dv='종속변수', between='독립변수', data='데이터프레임')
3-3. Kruskal-Wallis (비모수 검정)
분산분석에서 정규성을 만족하지 않을 때 사용하는 비모수 검정법
셋 이상의 그룹 간 차이를 비교할 때 사용
모수 검정(ANOVA)은 평균을 비교하지만 Kruskal-Wallis 검정은 중앙값을 비교
귀무가설 : 모든 그룹의 중앙값은 같다
대립가설 : 모든 그룹의 중앙값이 모두 같은 것은 아니다.(적어도 하나의 그룹의 중앙값이 다르다)
1
2
3
# Kruskal-Wallis 검정
H, p = stats.kruskal(low, medium, high)
print(f"Kruskal-Wallis 결과: H-statistic={H}, p-value={p}")
p < 0.05 → 그룹 간 중앙값 차이 존재
Kruskal-Wallis 검정은 그룹 간 차이가 존재하는지만 확인 가능하다.
→ 어떤 그룹이 다른지는 알 수 없으므로 사후검정을 통해 어떤 그룹이 차이가 있는지 분석한다.
4. 사후검정 (Post-hoc test)
ANOVA나 Kruskal-Wallis 검정에서 유의미한 차이가 있으면 어떤 그룹 간 차이가 존재하는지 확인하는 과정
그룹 간 차이를 확인하기 위해 모든 그룹 조합에 대해 유의한 차이 여부를 검정
ex) 5개 그룹이 있을 경우 nC2 : 5C2 → 10번 비교
문제점
검정을 여러 번 수행하면 1종 오류(실제값은 False인데 True로 나올 확률)가 커지는 문제가 발생한다.
이를 위해 여러 사후 검정 방법들이 존재한다.
ex) Bonferroni
유의수준(α = 0.05)을 비교 횟수(nC2)로 나눠서 조정
ex) 5개 그룹 → 10번 비교 → 0.05 / 10 = 0.005
p값이 0.005보다 작아야 유의미한 차이라고 판단
귀무 가설(H0) : 그룹 간에 차이가 없다. (그룹간의 평균값은 같다.)
대립 가설(H1) : 그룹 간에 차이가 있다. (적어도 하나의 평균값은 다르다.)
사후검정 방법 | 설명 | |
---|---|---|
Tukey HSD | 모든 그룹 쌍 간 차이 비교 | 집단별 표본의 수와 분산이 동일해야함 |
Bonferroni | 유의수준을 1/N(검사한 개수) 낮춰 검정 | 집단별로 표본의 수는 다르지만 분산이 동일한 경우 |
Dunn’s Test | 비모수 검정용 사후검정 | 데이터의 순위를 기반으로 세 그룹 이상의 집단에서 효과적 |
Tukey HSD (Honestly Significant Difference) 검정
1
2
3
4
5
6
# Tukey 라이브러리 가져오기
from statsmodels.stats.multicomp import pairwise_tukeyhsd
# endog = 종속변수, groups = 독립변수
tukey_result = pairwise_tukeyhsd(endog=data['종속변수'], groups=data['독립변수'], alpha=0.05) # Significance level(유의수준)
print(tukey_result)
reject 값이 False이면 두 그룹 간 차이가 없다는 의미(True 일 경우 차이가 있다는 의미)
Dunn’s test : 비모수 검정용 사후검정
1
2
3
4
5
import scikit_posthocs as sp # scikit-posthocs로 설치
# Dunn's test (Bonferroni 보정 적용)
dunn_result = sp.posthoc_dunn([low, medium, high], p_adjust='bonferroni')
print(dunn_result)
p-value < 0.05 → 그룹 간 유의미한 차이
보정 방법(p_adjust)으로 bonferroni
, holm
, fdr_bh
등을 사용할 수 있다.
이상치 boxplot
1
2
3
plot_data = [low, medium, high]
ax = plt.boxplot(plot_data)
plt.show()
5. 회귀 분석
독립 변수(X)가 종속 변수(Y)에 미치는 영향 분석
OLS(Ordinary Least Squares)는 최소제곱법(Least Squares Method)을 기반으로 한 회귀 분석 방법
기본 가정(선형성, 정규성, 등분산성, 독립성 등) 검토 필요
분석 종류 | 사용 함수 | 목적 |
---|---|---|
회귀 분석 (OLS) | smf.ols().fit() | 독립 변수(X)가 종속 변수(Y)에 미치는 영향 평가 |
분산 분석 (ANOVA) | anova_lm(smf.ols().fit()) | 그룹 간 차이 분석 (범주형 변수의 영향 검정) |
1
2
3
4
5
6
7
8
9
10
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
# 회귀 분석
model = ols("종속변수 ~ C(독립변수)", data=data).fit()
print(model.summary()) # 회귀 분석 결과
# ANOVA 분석
anova_results = anova_lm(model)
print(anova_results) # ANOVA 결과
5.1 회귀 분석 기본 개념
잔차의 평균은 0
잔차와 독립 변수는 상관관계가 없음(0이다.)
구분 | 의미 | |
---|---|---|
단순 회귀 (Simple Linear Regression) | 독립변수(X) 1개, 종속변수(Y) 1개 | 독립변수 1개와 종속변수 1개 사이의 관계 |
다중 회귀 (Multiple Linear Regression) | 독립변수(X) 2개 이상, 종속변수(Y) 1개 | 여러 독립변수가 종속변수에 미치는 영향 평가 |
선형 회귀 (Linear Regression) | X가 증가할 때 Y가 일정한 비율로 증가하는 관계 | 독립변수 변화에 따라 종속변수가 일정 비율로 변화 |
비선형 회귀 (Nonlinear Regression) | X와 Y의 관계가 곡선 형태로 변화 | 독립변수와 종속변수 간의 관계가 곡선 형태일 때 적용 (예: 다항 회귀) |
단순 또는 다중 선형 회귀는 독립변수와 종속변수 간의 선형 관계를 가정한다.
만약 선형 가정이 위배된다면 다항 회귀(Polynomial Regression) 등을 통해 비선형 관계를 모델링할 수 있다.
5.2 회귀 모형의 가정 확인
- 선형성: 독립 변수와 종속 변수의 관계가 선형인지 확인
잔차 plot : 잔차가 무작위로 분포하면 선형성 가정 충족
특정한 패턴(예, 음의 선형 패턴 또는 곡선 형태)이 보이면 비선형 요소 존재
- 정규성: 잔차가 정규 분포를 따르는지 확인
Q-Q Plot: 대부분의 점이 직선에 가까워야 정규성을 만족
큰 이상치가 존재하면 정규성 위배
등분산성: 잔차의 분산이 일정한지 확인
- 독립성: 잔차 간의 상관관계가 없어야 함
5.3 회귀 분석 (formula vs OLS)
Ho(귀무가설) : 회귀식이 유용하지 않다. ( β1=0 )
H1(대립가설) : 회귀식이 유용하다. ( β1≠0 )
산점도
자료가 회귀분석에 적합한지를 보기 위해 x와 y의 산점도를 그려본다.
*다중회귀는 잔차분석을 통해 확인
1
2
3
import plotly.express as px
fig = px.scatter(x='Exam_Score', y='Attendance', data_frame=data, trendline="ols") # 추세선 추가
fig.show()
ex) 대부분 관계가 직선인 것을 보아 x와 y는 양의 선형관계를 갖고 있음을 알 수 있다.
Y = β0 + β1X + e
5.3.1 OLS()
방식을 이용한 회귀 분석
*pd.get_dummies()
를 사용하여 범주형 변수를 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import statsmodels.api as sm
X = data[['독립변수1', '독립변수2', '독립변수3', '독립변수4']] # 수치형
y = data['종속변수']
X = pd.get_dummies(X, columns=['Gender'], drop_first=True)
'''
X = pd.get_dummies(X, columns=['Gender'], drop_first = True)
첫번째 열을 자동으로 삭제해서 다중공선성을 피함
Male이 1이면 Female은 자동으로 0이되는 더미변수 생성
'''
X = sm.add_constant(X) # 절편 추가
model = sm.OLS(y, X).fit() # 다중 회귀 분석 실행
print(model.summary()) # 결과 출력
coef
(계수) : 기울기와 절편(상수항)R-squared
: 결정계수(설명력)P>|t|
: p-값 (0.05 미만이면 유의미)F-statistic
: 전체 모델의 유의성을 검정하는 F-통계량.Prob(F-statistic)
: 모델 전체의 p-값
회귀선 그리기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import matplotlib
import matplotlib.pyplot as plt
import statsmodels.api as sm
matplotlib.rcParams['font.family'] = 'Malgun Gothic' # Windows
data_X = data['X']
y = data['Y']
X = sm.add_constant(data_X) # 절편 추가
model = sm.OLS(y, X).fit()
# 원본 데이터 플로팅
plt.scatter(data_X, y, label='Data')
# 회귀선 플로팅
plt.plot(data_X, model.predict(X), color='red', label='Fitted line')
plt.legend()
plt.xlabel('x')
plt.ylabel('y')
plt.title('Linear Regression with statsmodels')
plt.show()
5.3.2 formula
방식을 이용한 회귀 분석
*범주형 변수는 C()
를 사용하여 자동으로 더미 변수로 변환
1
2
3
4
5
6
7
import statsmodels.formula.api as smf
formula = "Y ~ X1 + X2 + C(범주형 변수)"
model = smf.ols(formula, data=data).fit()
# 결과 출력
print(model.summary())
*수정된 결정계수 확인
특수문자나 공백이 포함된 변수명 허용하기
변수 명 중에 ‘?’이나 공백이 들어간 변수명이 있어서 SyntaxError: invalid syntax 오류가 생겼다.
Q()
를 사용해 동작하면 된다.
1
2
3
4
5
from statsmodels.formula.api import ols
formula = "Q('Clearing doubts with faculties in online mode') ~ Q('Your interaction in online mode')"
model = ols(formula, data=data).fit()
print(model.summary())
predict
1
2
3
4
5
6
model.predict(pd.DataFrame({"독립변수명": [예측하고 싶은 x값]}))
model.predict(pd.DataFrame({
"독립변수1": [예측할 X 값],
"독립변수2": [예측할 X 값]
}))
예시)
1
model.predict(pd.DataFrame({"x": [1]}))
X가 1일 때, Y의 예측 값은 1.363917
결과 분석하기
검정 | 사용 목적 | 해석 |
---|---|---|
결정계수(R²) | 모델의 설명력 확인 | 값이 클수록 좋은 모델 |
F-검정 | 전체 회귀 모형 유의성 검정 | p-value < 0.05 → 모델 유의 |
t-검정 | 개별 회귀 계수 유의성 검정 | p-value < 0.05 → 해당 변수 유의 |
잔차 분석 | 모델 가정 충족 여부 검토 | 정규성, 등분산성, 독립성 확인 |
(1) 결정계수(R²) 및 수정된 결정계수(Adj. R²)
결정계수(R²): 회귀 모델이 종속변수의 변동을 얼마나 설명하는지 나타냄. (0~1 사이 값)
- 독립변수 개수가 많아지면 R²값이 증가하는 경향이 있다.
- 독립변수의 개수와 표본 크기 고려 → 수정된 결정계수(Adj. R²)
- Adj. R²이 높을수록 좋은 모델 → __% 설명력을 가지고 있다.
(2) F-검정
F-statistic
: 회귀 모델 전체의 유의성을 검정하는 통계량Prob (F-statistic)
: F-검정의 p-value (0.05 미만이면 유의미한 모델)p-value < 0.05
→ 회귀식이 유용함p-value ≥ 0.05
→ 회귀식이 유용하지 않음
구분 | 사용 목적 | 해석 |
---|---|---|
t-검정 | 개별 회귀 계수가 유의한지 검정 | p-value < 0.05 → 해당 변수가 유의함 |
F-검정 | 전체 회귀 모형이 유의한지 검정 | p-value < 0.05 → 회귀 모델이 유의함 |
(3) 개별 회귀 계수 검정 (t-검정)
P>|t|
값 확인 : 회귀계수가 유의한지 검정p-value < 0.05
→ 해당 독립변수가 종속변수에 유의한 영향을 미침p-value ≥ 0.05
→ 해당 독립변수는 유의하지 않음
(4) 회귀 계수 (coef) 해석
- Y = β0 + β1X1 + β2X2 + …
coef 값이 클수록 종속변수(Y)에 미치는 영향력이 큼
- 표준 오차(std err): 해당 회귀 계수의 신뢰성을 나타내며 값이 작을수록 신뢰도가 높다.
→ 표준오차가 작으면 참값(모집단의 평균)에 더 가깝다
model.params
으로도 회귀 계수(절편과 기울기 출력 가능)
(5) 잔차 분석 및 정규성 검정
- 잔차의 정규성 검정
- Omnibus 검정(
Prob(Omnibus)
), Jarque-Bera((Prob(JB))
) 검정 확인 - p-value < 0.05 → 잔차가 정규성을 따르지 않음
- p-value ≥ 0.05 → 잔차가 정규성을 따름
- Omnibus 검정(
- Durbin-Watson(DW) 검정 (잔차의 독립성 검정)
- 1.5 ~ 2.5 사이이면 독립성이 있다고 판단 → 회귀모형 적합
- 0에 가까울 수록 양의 상관관계, 4에 가까울수록 음의 상관관계
(6) 다중공선성 확인
- VIF : 독립변수들 간에 강한 상관관계가 존재할 때 발생하는 문제를 평가하는 지표
- VIF < 10 → 다중공선성 문제 없음
- VIF ≥ 10 → 다중공선성 문제 있음 (변수 제거 고려)
- 조건수(Condition Number) : 행렬의 수치적 안정성을 나타내는 값
- Cond. No > 30 → 다중공선성 문제 가능성 높음(단순회귀에서는 조건수를 신경쓰지 않아도 된다.)
1
2
3
4
5
6
7
8
9
from numpy.linalg import cond
import statsmodels.api as sm
X = data[['독립변수1', '독립변수2']]
X_with_const = sm.add_constant(X)
# 조건수 계산
condition_number = cond(X_with_const)
print(f"Condition Number: {condition_number}")
1
2
3
4
5
6
7
8
9
10
# 다항회귀 조건수 계산
from sklearn.preprocessing import PolynomialFeatures
from numpy.linalg import cond
import statsmodels.api as sm
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(data[['독립변수1', '독립변수2']])
X_poly_with_const = sm.add_constant(X_poly)
condition_number = cond(X_poly_with_const)
print(f"Condition Number: {condition_number}")
(7) 잔차 독립성 및 등분산성 검정
- Breusch-Godfrey 검정 (잔차 독립성 검토)
- p-value < 0.05 → 잔차 간 자기상관 존재 (독립성 위배)
- p-value ≥ 0.05 → 자기상관 없음 (독립성 충족)
- Breusch-Pagan 검정 (잔차 등분산성 검정)
- p-value < 0.05 → 등분산성이 없음 (이분산성 존재)
- p-value ≥ 0.05 → 등분산성이 있음 (회귀 모델 적합)
(8) 이상치 탐색 (Cook’s Distance, Leverage)
- Cook’s Distance 값이 크면 영향력이 큰 이상치일 가능성이 높음
- Leverage 값이 크면 특정 데이터가 모델에 과도한 영향을 미칠 가능성이 있음
5.4 다항 회귀 (Polynomial Regression)
독립변수와 종속변수 관계가 비선형일 때 사용
*독립변수가 1개일 수도 있음
1
2
3
4
5
6
7
8
9
from sklearn.preprocessing import PolynomialFeatures
import statsmodels.api as sm
poly = PolynomialFeatures(degree=2, include_bias=False) # 2차항 생성
X_poly = poly.fit_transform(data[['X1', 'X2']])
y = data['Y']
model = sm.OLS(y, sm.add_constant(X_poly)).fit()
print(model.summary())
*수정된 결정계수 확인
5.5 다중공선성 (Variance Inflation Factor : VIF)
다중공선성과 조건수는 독립변수가 하나일 때는 의미가 없으므로 2개 이상일 때만 확인하면 된다.
다중공선성이란 독립변수들 간의 높은 상관관계로 인해 너무 비슷한 정보가 포함되어
회귀계수가 불안정해지는 문제를 말한다.
독립변수 간의 상관관계로 인해 회귀 분석이 왜곡되는지 확인
*종속변수(Y)와의 관계가 아니라, X끼리의 관계를 분석해야 함
1
2
3
4
5
6
7
8
9
10
11
12
from statsmodels.stats.outliers_influence import variance_inflation_factor
X = data[['X1', 'X2']]
# 상수항 추가
X = sm.add_constant(X)
vif_data = pd.DataFrame()
vif_data["Variable"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
print(vif_data)
한 변수를 다른 변수로 예측할 수 있을수록 VIF 값이 커진다.
- VIF < 10 → 다중공선성 문제 없음
- VIF ≥ 10 → 다중공선성 문제 있음 (변수 제거 고려)
5.6 잔차
잔차 독립성 및 등분산성 검정
잔차 독립성 (Breusch-Godfrey)
DW에 이어 추가적으로 자기상관 검토 가능
*자기상관이 있다 = 잔차(오차)가 특정 패턴을 보이며 독립적이지 않다
1
2
3
from statsmodels.stats.diagnostic import acorr_breusch_godfrey
lm_test = acorr_breusch_godfrey(model, nlags=1)
print(lm_test)
p-value < 0.05 → 잔차 간 자기상관이 존재 (독립성 위배)
p-value ≥ 0.05 → 잔차 간 자기상관 없음 (독립성 만족)
statsmodels.stats.diagnostic.acorr_breusch_godfrey
잔차의 등분산성 검정 (Breusch-Pagan Test)
*샘플수가 많아야 함, 오차항은 독립이고 정규분포를 따라야 한다., 오차의 분산은 설명변수(x)와 연관이 있어야 한다.
1
2
3
from statsmodels.stats.diagnostic import het_breuschpagan
bp_test = het_breuschpagan(residuals, model.model.exog) # 잔차, exog_het
print(bp_test)
- p-value < 0.05 → 등분산성이 없음 (이분산성 존재)
- p-value ≥ 0.05 → 등분산성이 있음 (등분산성 가정 충족)
이상치 탐색
- 표준화 잔차 및 영향 점수 (Leverage, Cook’s Distance) 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
# 영향력 분석
influence = model.get_influence()
# Cook’s Distance (값이 크면 영향력이 큰 이상치 가능성 높음)
cooks_d = influence.cooks_distance[0] # (거리 값, p-value)
# Leverage (값이 크면 특정 데이터가 모델에 과도한 영향 가능성)
leverage = influence.hat_matrix_diag
print("Cook’s Distance:", np.max(cooks_d))
print("Leverage:", np.max(leverage))
그래프
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
import statsmodels.api as sm
import matplotlib.pyplot as plt
import scipy.stats as stats
# 1. 다중 회귀
X = sm.add_constant(filtered_data[['독립변수1', '독립변수2']])
y = filtered_data['종속변수']
model = sm.OLS(y, X).fit()
residuals = model.resid # 잔차
# 2. 다항 회귀
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(data[['독립변수1', '독립변수2']])
# OLS 모델 적용
model = sm.OLS(data['종속변수'], sm.add_constant(X_poly)).fit()
residuals = model.resid # 잔차
################
# 1. 잔차 히스토그램 (정규성 확인)
plt.hist(residuals, bins=20, edgecolor='black')
plt.xlabel("Residuals")
plt.ylabel("Frequency")
plt.title("Histogram of Residuals")
plt.show()
# 2. Q-Q Plot (정규성 확인)
## 잔차가 정규 분포를 따르는지를 보는 것으로 직선으로 나와야 한다.
stats.probplot(residuals, dist="norm", plot=plt)
plt.title("Q-Q Plot")
plt.show()
# 3. 잔차 산점도 (등분산성 확인)
## 특별한 패턴이 보이지 않으면 오차항의 분산이 같다고 볼 수 있다.
plt.scatter(model.fittedvalues, residuals) # x축 : 예측된 Y값, Y축 : 잔차
plt.axhline(y=0, color='red', linestyle='--')
plt.xlabel("Fitted Values")
plt.ylabel("Residuals")
plt.title("Residuals vs Fitted Values")
plt.show()
모델 평가 지표
log(L)
(로그우도) : 모델이 데이터에 얼마나 잘 맞는지(적합도)를 측정- p : 선택된 독립변수의 개수
- n : 데이터 샘플 개수
지표 | 의미 | 해석 |
---|---|---|
AIC (Akaike Information Criterion) | 모델 적합도 평가 | 값이 작을수록 좋은 모델 |
BIC (Bayesian Information Criterion) | AIC와 비슷하나 모델 단순성을 더 강조 | 값이 작을수록 좋은 모델 |
우도(likelihood)를 가장 크게 하는 동시에 변수 갯수는 가장 적은 최적의 모델(parsimonious & explainable)을 의미
AIC = -2 * log(L) + 2p
BIC = -2*log(L) + log(n)p
BIC: AIC와 유사하지만 마지막 패널티를 수정함으로써 AIC를 보완
BIC의 경우 변수가 많을 수록 AIC보다 더 페널티를 가하는 성격을 가진다.
AIC 보다 변수 증가에 더 민감해 변수 갯수가 작은 것이 우선 순위라면 AIC보다 BIC를 참고하는게 좋다.
AIC와 BIC를 활용한 변수 선택 방법
방법 | 설명 |
---|---|
전진 선택 (Forward Selection) | 처음에는 변수가 없고 단계적으로 가장 설명력이 높은 변수를 추가 |
후진 제거 (Backward Elimination) | 모든 변수를 포함한 상태에서 시작하여 설명력이 낮은 변수를 제거 |
단계적 선택 (Stepwise Selection) | 전진 선택을 수행하면서도 통계적으로 유의하지 않은 변수를 제거 |
*AIC/BIC는 변수를 추가하거나 제거할 때 모델 선택 기준으로 사용할 수 있다.
- 전진 선택: AIC/BIC가 가장 감소하는 변수를 추가
- 후진 제거: AIC/BIC가 가장 적게 증가하는 변수를 제거
- 단계적 선택: AIC/BIC를 고려하며 추가 또는 제거를 반복
REFERENCE
- 기술통계
- 적합도 검정
- 상관 분석
- 분산분석
- 회귀분석