주택 가격 예측¶
IOWA주 AMES 주택 데이터 기반 내재 특성 중심 회귀 분석 프로젝트
개요¶
2006~2010년 IOWA주 AMES 카운티의 실제 주택 거래 데이터를 기반으로, 주택 가격 형성 요인을 구조화하고 내재 특성 중심의 예측 모델을 설계한 프로젝트. 외부 요인을 배제하고 건물 자체의 특성이 가격에 미치는 영향을 분석했다.
- 수행 기간: 2024.09 ~ 2024.10 (프로그래머스 데브코스 4기)
- 데이터 출처: Kaggle House Prices
- 분석 도구: Python (pandas, scikit-learn, XGBoost)
- GitHub: Gyuhyeon-Eom/project3
- Notion: 프로젝트 상세
문제정의¶
주택 가격은 다양한 특성(위치, 면적, 연식 등)에 의해 결정됨.
핵심 질문:
- 어떤 내부 특성이 주택 가격에 가장 큰 영향을 미치는가?
- 외부 요인을 배제하고 내재 특성만으로 가격 예측이 가능한가?
- 어떤 모델이 가장 안정적인 예측 성능을 보이는가?
데이터¶
데이터셋 구조¶
| 항목 | 내용 |
|---|---|
| 총 거래 건수 | 약 2,900건 |
| 변수 수 | 81개 |
| 기간 | 2006~2010년 |
| 지역 | IOWA주 AMES 카운티 |
특성 분류¶
내부 특성 (건물 구조, 품질) - OverallQual: 전반적 품질 (1-10) - GrLivArea: 지상 생활 면적 - GarageArea: 차고 면적 - TotalBsmtSF: 지하실 면적 - 1stFlrSF, 2ndFlrSF: 층별 면적
외부 특성 (지역, 연도) - Neighborhood: 동네 - YearBuilt: 건축 연도 - YearRemodAdd: 리모델링 연도
분석/모델링¶
1. 전처리¶
총 2,900여 건의 거래 데이터와 81개 변수를 기반으로 내부(건물 구조, 품질 등)와 외부(지역, 연도 등) 특성을 구분했다.
결측치 처리 (15개 이상 변수)
# 평균값 대체
df['LotFrontage'].fillna(df['LotFrontage'].mean(), inplace=True)
# 최빈값 대체
df['MasVnrType'].fillna(df['MasVnrType'].mode()[0], inplace=True)
# None 대체 (해당 시설 없음)
for col in ['GarageType', 'GarageFinish', 'BsmtQual']:
df[col].fillna('None', inplace=True)
이상치 처리
# 범위 클리핑
df['GrLivArea'] = df['GrLivArea'].clip(upper=df['GrLivArea'].quantile(0.99))
# Yeo-Johnson 정규화 (왜도 변수)
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer(method='yeo-johnson')
df[skewed_cols] = pt.fit_transform(df[skewed_cols])
서수형/범주형 변수 인코딩
# 품질 등급 → 1~5 매핑
quality_mapping = {'Po': 1, 'Fa': 2, 'TA': 3, 'Gd': 4, 'Ex': 5}
for col in ['ExterQual', 'BsmtQual', 'KitchenQual']:
df[col] = df[col].map(quality_mapping)
파생 변수 생성
# 건물 연식
df['BuildingAge'] = df['YrSold'] - df['YearBuilt']
# 총 면적
df['TotalSF'] = df['TotalBsmtSF'] + df['1stFlrSF'] + df['2ndFlrSF']
2. 모델 학습 및 비교¶
Ridge, Lasso, XGBoost, ElasticNetCV 등 회귀 모델을 학습하고 교차검증 기반 성능을 비교했다.
from sklearn.linear_model import Ridge, Lasso, ElasticNetCV
from sklearn.model_selection import cross_val_score
from xgboost import XGBRegressor
models = {
'Ridge': Ridge(alpha=10),
'Lasso': Lasso(alpha=0.001),
'ElasticNet': ElasticNetCV(cv=5),
'XGBoost': XGBRegressor(n_estimators=100, max_depth=5)
}
for name, model in models.items():
scores = cross_val_score(model, X_train, y_train,
scoring='neg_mean_squared_error', cv=5)
mse = -scores.mean()
print(f'{name}: MSE = {mse:.4f}')
3. 최종 모델: Ridge 회귀¶
from sklearn.linear_model import Ridge
ridge = Ridge(alpha=10)
ridge.fit(X_train, y_train)
# 평가
y_pred = ridge.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
print(f'Test MSE: {mse:.4f}') # 약 0.0901
결과¶
모델별 성능 비교¶
| 모델 | MSE | 특징 |
|---|---|---|
| Linear Regression | 0.112 | 기본 모델 |
| Lasso | 0.095 | 변수 선택 효과 |
| Ridge | 0.0901 | 가장 안정적 |
| ElasticNet | 0.093 | L1+L2 혼합 |
| XGBoost | 0.088 | 성능 좋으나 해석력 낮음 |
Ridge 회귀 선택 이유: - MSE 약 0.0901로 가장 안정적 성능 확보 - 다중공선성 처리 효과 - 모든 변수를 유지하며 계수 축소 → 해석 가능
주요 영향 변수 (특성 중요도)¶
| 변수 | 영향력 | 설명 |
|---|---|---|
| GrLivArea | +++ | 지상 생활 면적 (가장 큰 영향) |
| OverallQual | ++ | 전반적 품질 등급 |
| GarageArea | ++ | 차고 면적 |
| TotalBsmtSF | + | 지하실 면적 |
| BuildingAge | - | 건물 연식 (오래될수록 감소) |
주요 성과¶
- 외부 요인 배제, 내부 특성 중심 영향력 (GrLivArea, OverallQual, GarageArea) 도출 → 특성별 가격 민감도 시각화
- 지역·건축 특성별 상대 가격 영향도를 정량화하여 실거래 가격의 과대/과소 평가 구간 식별
- 통계 기반 변수 처리 경험 및 해석 가능한 회귀 모델 설계를 통해 정책 활용성과 해석력을 겸비한 모델 구축
배운 점¶
- 전처리의 중요성: 결측치, 이상치, 왜도 처리가 모델 성능에 큰 영향
- 해석 가능성: XGBoost가 성능은 좋지만, Ridge가 해석력 측면에서 우수
- 파생 변수: BuildingAge 같은 도메인 기반 변수가 예측력 향상에 기여
- 앙상블 vs 단일 모델: 목적(성능 vs 해석력)에 따른 모델 선택 필요
기술 스택¶
| 구분 | 기술 |
|---|---|
| 데이터 처리 및 모델링 | Python (pandas, numpy, scikit-learn, XGBoost) |
| EDA 및 시각화 | matplotlib, seaborn |
| 기타 | Excel |