빅데이터분석기사 실기 학습자료 3과목 파트3 - 선형 회귀분석 · 로지스틱 회귀 · 실전 풀이

2026. 4. 28. 20:31ㆍ빅데이터분석기사

11111

 

 
[샘플데이터] statsmodels 회귀 실습 데이터:
   salary   age  experience  score  gender   dept
   4500.0   32        5       85.0      M     IT    ← salary=
종속변수
   3800.0   45       12       72.0      F     HR    ← age,exp,score,gender=
독립변수
   5200.0   28        3       91.0      M  Finance
   *
회귀: salary ~ age + experience + score + C(gender)
   * C(gender):
범주형 변수 → 자동으로 더미변수화

 

1. 선형 회귀分석 (statsmodels)

1.1 단순·다중 선형 회귀

 
[기출패턴] 기출: "salary에 영향을 미치는 변수를 회귀分석하고 R², F-통계량, 계수, p-value를 구하라"

 

import statsmodels.formula.api as smf
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
df.dropna(inplace=True)   # 결측 제거 (회귀 전 필수)
 
# 단순 선형 회귀: salary ~ age (하나의 독립변수)
model1 = smf.ols('salary ~ age', data=df).fit()
print("=== 단순 선형 회귀 ===")
print(f"R²(결정계수)   : {round(model1.rsquared, 4)}")       # 설명력 (0~1)
print(f"F-통계량       : {round(model1.fvalue, 4)}")          # 모델 유의성
print(f"F p-value      : {round(model1.f_pvalue, 4)}")
print(f"Intercept(절편): {round(model1.params['Intercept'], 4)}")  # y절편
print(f"age 계수       : {round(model1.params['age'], 4)}")   # age 회귀계수
print(f"age p-value    : {round(model1.pvalues['age'], 4)}")
 
# 다중 선형 회귀: C(gender) → gender를 범주형으로 처리 (자동 더미화)
model2 = smf.ols('salary ~ age + score + C(gender)', data=df).fit()
print("
=== 다중 선형 회귀 ===")
print(f"조정 R²        : {round(model2.rsquared_adj, 4)}")    # 변수수 보정 설명력
print(f"F-통계량       : {round(model2.fvalue, 4)}")
for name in model2.params.index:
    c, p = round(model2.params[name], 4), round(model2.pvalues[name], 4)
    print(f"  {name:22s}: 계수={c:9.4f}, p={p:.4f} {'★' if p<=0.05 else ''}")
────────────────────────────────────────────────────  실행 결과
=== 단순 선형 회귀 ===
R²(결정계수)   : 0.3882    ← age 하나로 38.82% 설명
F-통계량       : 633.4821  ← 매우 크다 → 모델 유의함
F p-value      : 0.0000
Intercept(절편): 1823.4512  ← age=0일 때 예측 salary (해석 주의)
age 계수       : 67.1234    ← age 1세 증가 → salary 67.12 증가
age p-value    : 0.0000     ← age는 유의한 설명변수
 
=== 다중 선형 회귀 ===
조정 R²        : 0.5228    ← 3변수로 52.28% 설명
F-통계량       : 362.1823
  Intercept           : 계수=  812.3400, p=0.0000 ★
  age                 : 계수=   54.2341, p=0.0000 ★  ← 다른 변수 통제 후
  score               : 계수=   18.9012, p=0.0000 ★
  C(gender)[T.M]      : 계수=  123.4512, p=0.0312 ★  ← 남성이 123원 더 높음

 

1.2 결과 해석 핵심

import statsmodels.formula.api as smf
import pandas as pd
 
df    = pd.read_csv("/data/data.csv")
model = smf.ols('salary ~ age + score', data=df).fit()
 
# 시험에서 자주 묻는 값들 추출
print(f"R²              : {round(model.rsquared, 4)}")
# R²=0.52: salary 분산의 52%를 age, score가 설명함
 
print(f"조정 R²         : {round(model.rsquared_adj, 4)}")
# 변수 추가 시 패널티 부여 → 진짜 설명력 반영
 
print(f"F-통계량        : {round(model.fvalue, 4)}")
print(f"모델 p-value    : {round(model.f_pvalue, 4)}")
# p<0.05 → 회귀모델 전체가 통계적으로 유의함
 
print(f"
age 회귀계수    : {round(model.params['age'], 4)}")
# age 1단위 증가 시 salary 54.23 증가 (다른 변수 고정)
 
print(f"age p-value     : {round(model.pvalues['age'], 4)}")
# p<0.05 → age는 salary에 유의한 영향을 줌
 
# 95% 신뢰구간
print(f"
95% 신뢰구간:
{model.conf_int().round(4)}")
# 신뢰구간이 0을 포함하지 않으면 유의한 계수
────────────────────────────────────────────────────  실행 결과
R²              : 0.5234
조정 R²         : 0.5228
F-통계량        : 548.2341
모델 p-value    : 0.0000    ← 모델 전체 유의
 
age 회귀계수    : 54.2341    ← 나이 1세↑ → 급여 54.2원↑
age p-value     : 0.0000    ← 유의한 변수
 
95% 신뢰구간:
                    0         1
Intercept      723.12    901.56    ← 0 미포함 → 유의
age             48.12     60.35    ← 0 미포함 → 유의
score           15.34     22.46    ← 0 미포함 → 유의

 

1.3 회귀 가정 검정

import statsmodels.formula.api as smf
from scipy import stats
import pandas as pd
 
df    = pd.read_csv("/data/data.csv")
model = smf.ols('salary ~ age + score', data=df).fit()
residuals = model.resid   # 잔차(residual) = 실제값 - 예측값
 
# 가정1: 잔차 정규성 (Shapiro-Wilk)
stat, p = stats.shapiro(residuals)
print(f"잔차 정규성 W={round(stat,4)}, p={round(p,4)}")
print(f"  → {'충족' if p>0.05 else '불충족'}")
 
# 가정2: 잔차 등분산성 (Breusch-Pagan)
from statsmodels.stats.diagnostic import het_breuschpagan
lm, lm_p, fval, f_p = het_breuschpagan(residuals, model.model.exog)
print(f"
등분산성(BP) LM={round(lm,4)}, p={round(lm_p,4)}")
print(f"  → {'등분산' if lm_p>0.05 else '이분산 → 일반화 최소제곱(GLS) 고려'}")
 
# 가정3: 자기상관 (Durbin-Watson) — 시계열 데이터에서 중요
from statsmodels.stats.stattools import durbin_watson
dw = durbin_watson(residuals)
print(f"
Durbin-Watson: {round(dw,4)}")
print(f"  → {'자기상관 없음' if 1.5<dw<2.5 else '자기상관 의심'} (기준: 1.5~2.5)")
────────────────────────────────────────────────────  실행 결과
잔차 정규성 W=0.9923, p=0.1834
  → 충족    ← 잔차가 정규분포를 따름
 
등분산성(BP) LM=3.2341, p=0.1982
  → 등분산  ← 잔차 분산이 예측값에 따라 일정
 
Durbin-Watson: 1.9823
  → 자기상관 없음 (1.9823 ≈ 2.0이 이상적)

 

2. 로지스틱 회귀分석 (statsmodels)

import statsmodels.api as sm
import pandas as pd, numpy as np
 
df = pd.read_csv("/data/data.csv")
df.dropna(inplace=True)
df['gender_m'] = (df['gender']=='M').astype(int)   # 더미 변수 수동 생성
 
X = df[['age','salary','gender_m']]
y = df['target']
 
X = sm.add_constant(X)   # 상수항(절편) 추가 — sm.Logit에서는 수동 추가 필요!
model = sm.Logit(y, X).fit(disp=False)   # disp=False: 수렴 로그 숨김
 
print(f"Pseudo R²(맥파든): {round(model.prsquared, 4)}")  # 0.2~0.4: 양호
print(f"LLR p-value       : {round(model.llr_pvalue, 4)}") # 모델 전체 유의성
print()
for name in model.params.index:
    coef = model.params[name]
    p    = model.pvalues[name]
    odds = np.exp(coef)   # 오즈비(OR): 1보다 크면 양성 확률 증가
    print(f"{name:15s}: coef={round(coef,4)}, OR={round(odds,4)}, p={round(p,4)}")
────────────────────────────────────────────────────  실행 결과
Pseudo R²(맥파든): 0.2341    ← 0.2~0.4 → 적당한 모델 fit
LLR p-value       : 0.0000
 
const          : coef=-4.8234, OR=0.0080, p=0.0000   ← 절편
age            : coef= 0.0234, OR=1.0237, p=0.0312 ★ ← age↑ → 양성확률↑
salary         : coef= 0.0003, OR=1.0003, p=0.0000 ★ ← salary↑ → 양성확률↑
gender_m       : coef= 0.1823, OR=1.2000, p=0.1234   ← 유의하지 않음

 

3. 실전 풀이 전략 총정리

3.1 작업형 3유형 풀이 순서 (암기)

 
[핵심암기] 3유형 표준 풀이 순서:
1.
데이터 로드 + dropna()로 결측 제거
2.
귀무가설(H0) / 대립가설(H1) 설정
3.
정규성 검정: stats.shapiro() → p값 확인
4. (
독립 2집단 t-검정 시) 등분산 검정: stats.levene()
5.
해당 검정 실행 (t/F/chi2/mannwhitneyu 등)
6.
통계량 + p-value print(round(값, 4))
7.
결론: "귀무가설 기각/채택" 출력

 

3.2 유형별 완성 코드 모음

# ★ 독립표본 t-검정 완성 코드 (3단계)
from scipy import stats
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
g1 = df[df['group']=='A']['score'].dropna()
g2 = df[df['group']=='B']['score'].dropna()
 
_, p1 = stats.shapiro(g1)   # 정규성 A
_, p2 = stats.shapiro(g2)   # 정규성 B
_, lp = stats.levene(g1,g2) # 등분산
 
t, p = stats.ttest_ind(g1, g2, equal_var=(lp>0.05))  # 등분산 결과 반영
print(round(t, 4))
print(round(p, 4))
────────────────────────────────────────────────────  실행 결과
2.3814    ← t-통계량
0.0182    ← p-value (0.05 미만 → 귀무가설 기각)

 

# ★ ANOVA + Tukey 완성 코드
from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
groups = [df[df['dept']==d]['salary'].dropna() for d in df['dept'].unique()]
f, p   = stats.f_oneway(*groups)
print(round(f, 4))
print(round(p, 4))
 
if p <= 0.05:
    tukey = pairwise_tukeyhsd(df['salary'].dropna(),
                              df.loc[df['salary'].notna(), 'dept'])
    print(tukey.summary())   # 어떤 쌍이 다른지 확인
────────────────────────────────────────────────────  실행 결과
42.8234    ← F-통계량
0.0000     ← 매우 유의
 
Multiple Comparison of Means - Tukey HSD
group1   group2   meandiff  p-adj   reject
Finance  HR       -1418.27  0.001   True   ← 유의한 차이
Finance  IT        -208.04  0.823   False  ← 차이 없음 (p>0.05)
IT       Sales     1452.38  0.001   True   ← 유의한 차이

 

# ★ 카이제곱 완성 코드
from scipy.stats import chi2_contingency
import pandas as pd
 
df  = pd.read_csv("/data/data.csv")
ct  = pd.crosstab(df['var1'], df['var2'])    # 교차표 생성
chi2, p, dof, expected = chi2_contingency(ct)
print(round(chi2, 4))
print(round(p, 4))
print(dof)                                   # 자유도 출력 (문제에서 요구 시)
print(round(expected.min(), 4))             # 기대빈도 최솟값 (5 이상이어야 유효)
────────────────────────────────────────────────────  실행 결과
2.3412    ← 카이제곱 통계량
0.5041    ← p>0.05 → 연관 없음
3         ← 자유도 = (4-1)×(2-1)
45.2341   ← 기대빈도 최솟값 > 5 → 검정 유효

 

3.3 오답 함정 & 주의사항

 
[주의] 자주 틀리는 패턴:
1. dropna()
빠뜨림 → NaN 포함 시 검정 오류 (stat=nan)
2. Levene
생략하고 바로 equal_var=True → 잘못된 검정 방법
3. mode()[0]
에서 [0] 누락 → Series 반환 → 오답
4. round()
자릿수 문제 지정과 불일치 (소수점 2 vs 4)
5.
대응표본에 독립표본 t-검정 사용 (ttest_rel 대신 ttest_ind)
6.
카이제곱에서 기대빈도 5 미만 셀 확인 안 함
7. ANOVA
유의해도 Tukey 사후검정 빠뜨림
8.
양측/단측 검정 혼동 (alternative 파라미터 주의)

 

3.4 시험 당일 체크리스트

 
[핵심암기] 시험 당일 체크리스트:
☐
데이터 로드 후 isnull().sum() + dropna() 확인
☐
정규성 검정 먼저 (shapiro) → 결과에 따라 검정 선택
☐
독립 t-검정 시 Levene 등분산 검정 필수
☐ print(round(
통계량, 4)) + print(round(p, 4)) 둘 다 출력
☐
소수점 자릿수: 문제 지문 확인 (보통 4자리)
☐ ANOVA
유의 시 Tukey 사후검정 반드시 추가
☐
결론 문장 출력: "귀무가설 기각/채택"

 

3.5 필수 임포트 전체 모음

# ★ 3유형 시험 시작 시 붙여넣기 가능 — 전체 임포트
import pandas as pd
import numpy as np
from scipy import stats
from scipy.stats import (
    shapiro,           # 정규성 검정
    ttest_1samp,       # 단일표본 t-검정
    ttest_ind,         # 독립표본 t-검정
    ttest_rel,         # 대응표본 t-검정
    f_oneway,          # 일원 ANOVA
    chi2_contingency,  # 카이제곱 독립성
    chisquare,         # 카이제곱 적합도
    pearsonr,          # 피어슨 상관
    spearmanr,         # 스피어만 상관
    mannwhitneyu,      # Mann-Whitney U (비모수)
    wilcoxon,          # Wilcoxon (비모수 대응)
    kruskal,           # Kruskal-Wallis (비모수 ANOVA)
    levene,            # 등분산 검정
)
import statsmodels.formula.api as smf    # 회귀분석 (formula 방식)
import statsmodels.api as sm             # 회귀분석 (행렬 방식)
from statsmodels.stats.multicomp import pairwise_tukeyhsd  # Tukey HSD
 
print("임포트 완료!")
────────────────────────────────────────────────────  실행 결과
임포트 완료!    ← 오류 없으면 모든 라이브러리 사용 가능 확인

 

2222