Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Archives
Today
Total
관리 메뉴

TechBlog

[kaggle/python] House Price exploration 본문

Study/Data Analysis

[kaggle/python] House Price exploration

jiazzang 2023. 8. 23. 09:53

2022년 7월 31일에 velog 블로그에 작성한 글을 옮겼습니다.

 

📌 주제: House Price exploration

📖 참고 솔루션

Comprehensive data exploration with Python(by Pedro Marcelino)

 


✔️ Understand the problem

⚡ 변수, 데이터셋 살펴보기

✏️ 필요한 라이브러리 불러오기

# 라이브러리 불러오기
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

import seaborn as sns
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from scipy import stats

 

# warnings 라이브러리로 경고메세지 무시하기
import warnings
warnings.filterwarnings(action='ignore')

 

✏️ 데이터셋 가져오기

# 데이터셋 가져오기
train_data = './train2.csv'
test_data = './test2.csv'
df_train = pd.read_csv(train_data)
df_test = pd.read_csv(test_data)

 

✏️ train set의 컬럼(변수) 확인하기

print(df_train.columns.values)

 

✏️ 데이터 확인하기

df_train.head()

 

 

✏️ train, test set의 요약정보 확인하기

df_train.info()
print('\n')
df_test.info()

 

🔹 Question

  • 이 변수가 집을 구매할 때 필요한가?
  • 그렇다면, 이 변수가 얼마나 중요한가?
  • 이 변수가 다른 변수에 의해 이미 설명되어 있는가?

→ 'building'과 관련된 두 개의 변수: OverallQual, YearBuilt
→ 'space'와 관련된 두 개의 변수: TotalBsmtSF, GrLivArea

 

이 문제에서 'OverallQual', 'YearBuilt', 'TotalBsmtSF', 'GrLivArea' 변수가 중요한 역할을 할 수 있다는 결론이 도출됨

 

 


✔️ analysing 'SalePrice'

 

✏️ 히스토그램 그려보기

sns.distplot(df_train['SalePrice'])

 

target 변수('SalePrice')의 분포

  • Deviate from the normal distribution: 정규분포를 벗어남.
  • Have appreciable positive skewness: 양의 왜도를 가짐.
  • Show peakedness: 뾰족한 모양을 가짐.

 


⚡ numerical 변수들과의 관계 살펴보기

✏️ scatter plot (GrLibArea, SalePrice)

var = 'GrLivArea'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1) # axis=1: 열 방향으로 결합
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000))

 

'GrLivArea', 'SalePrice' 간 산점도

  • SalePrice와 GrLivArea 간 양의 선형관계가 존재하는 것으로 보임

 

✏️ scatter plot (TotalBsmtSF, SalePrice)

var = 'TotalBsmtSF'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
data.plot.scatter(x=var, y='SalePrice', ylim=(0,800000))

 

'TotalBsmtSF', 'SalePrice' 간 산점도

  • SalePrice와 TotalBsmtSF 간 양의 선형관계가 존재하는 것으로 보임
  • TotalBsmtSF 값이 0인 데이터가 다수 존재함

※ 참고) pd.concat: 데이터프레임 결합

# pd.concat 문법
pd.concat(df, axis=0,    # axis: 축 방향 지정
          keys=None,
          levels=None,
          names=None)

 


⚡ categorical 변수들과의 관계 살펴보기

✏️ box plot (OverallQual, SalePrice)

var = 'OverallQual'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(8,6))
fig = sns.boxplot(x=var, y='SalePrice', data=data)
fig.axis(ymin=0, ymax=800000)

 

 

  • OverallQual 값이 커질수록 SalePrice의 값도 증가하는 경향을 보임

 

✏️ box plot (YearBuilt, SalePrice)

var = 'YearBuilt'
data = pd.concat([df_train['SalePrice'], df_train[var]], axis=1)
f, ax = plt.subplots(figsize=(16,8))
fig = sns.boxplot(x=var, y='SalePrice', data=data)
fig.axis(ymin=0, ymax=800000)     
plt.xticks(rotation=90)         # x축 눈금 라벨 회전하기(90도)

 

 

  • YearBuilt 값이 커질수록(시간이 지날수록) SalePrice가 증가하는 경향을 보임

 


✔️ Correlation, Scatter plot

⚡ correlation

✏️ correlation matrix

corrmat = df_train.corr()
f, ax = plt.subplots(figsize=(12,9))
sns.heatmap(corrmat, vmax=.8, square=True)

 

✏️ SalePrice correlation matrix

k = 10      # heatmap의 변수의 개수
cols = corrmat.nlargest(k, 'SalePrice')['SalePrice'].index
cm = np.corrcoef(df_train[cols].values.T)
sns.set(font_scale=1.25)
hm = sns.heatmap(cm, cbar=True, annot=True, square=True, fmt='.2f', annot_kws={'size': 10}, yticklabels=cols.values,xticklabels=cols.values)
# cbar: colorbar의 유무, annot: 각 셀에 값 표기 유무
# fmt: 값의 데이터타입 설정 -> fmt='.2f': 소수점 둘째자리까지
# yticklabels=cols.values: y축에 컬럼명 출력 
plt.show()

 

heatmap

  • OverallQual, GrLivArea, TotalBsmtSF → SalePrice와 correlation 높음
  • GarageCars, GarageArea → SalePrice와의 correlation이 각각 0.64, 0.62임
  • TotalBsmtSF, 1stFloor → SalePrice와의 correlation이 0.61로 같음
  • YearBuilt → SalePrice와 correlation이 존재함

 

※ 참고) heatmap (참고자료)

# heatmap 문법
heatmap(df,	
	vmin=100,				# 최소값
        vmax=700,				# 최대값
        cbar=True,				# colorbar의 유무
        center=400,				# 중앙값 
        linewidths=0.5,				# cell 사이에 선을 집어 넣음
        annot=True,				# 각 cell의 값 표기 유무
        fmt="d",				# cell에 표시된 값의 데이터 타입
        cmap='Blues')				# heatmap의 색깔

 


⚡ scatter plot

✏️ correlation matrix

sns.set()
cols = ['SalePrice', 'OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
sns.pairplot(df_train[cols], size=2.5)  # 변수 간 관계 파악
plt.show()

 


✔️ Data cleaning

⚡ missing data

✏️ missing data의 개수 확인하기

total = df_train.isnull().sum().sort_values(ascending=False)
# isnull의 결과 -> True(1): 누락데이터, False(0): 유효한 데이터
percent = (df_train.isnull().sum()/df_train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data.head(20)

 

  Total Percent
PoolQC 1453 0.995205
MiscFeature 1406 0.963014
Alley 1369 0.937671
Fence 1179 0.807534
FireplaceQu 690 0.472603
LotFrontage 259 0.177397
GarageYrBlt 81 0.055479
GarageCond 81 0.055479
GarageType 81 0.055479

 

  • PoolQC, MiscFeature, Alley,Fence, FireplaceQu, LotFrontage: 결측치 매우 많고, 집을 구매할 때 중요한 요소는 아닌 것으로 판단됨 → 해당 변수 제거 고려
  • GarageYrBlt, GarageCond, GarageType, GarageFinish, GarageQual: 결측치 개수가 같음. garage와 중요한 변수 중 SalePrice와 가장 correlation이 높은 것은 'GarageCars'이므로, 해당 변수들은 제거함
  • BsmtFinType2, BsmtExposure, BsmtQual, BsmtCond, BsmtFinType1: 위와 같은 논리를 적용하여, 해당 변수들은 제거함
  • MasVnrArea, MasVnrType: 이미 고려대상인 YearBuild, OverallQual과 강한 correlation을 갖고 있으므로, 해당 변수는 제거함
  • Electrical: 결측치 1개 존재하므로, 결측치만 제거함

 

 

✏️ dealing with missing data

df_train = df_train.drop((missing_data[missing_data['Total'] > 1]).index,1)     # missing data가 1개보다 많으면 drop
df_train = df_train.drop(df_train.loc[df_train['Electrical'].isnull()].index)   # Electrical에 존재하는 missing data(1개)를 drop
df_train.isnull().sum().max()

 


⚡ outlier

✏️ 데이터 표준화

  • outlier를 판단하기 위해 데이터를 표준화함
# standardizing data
saleprice_scaled = StandardScaler().fit_transform(df_train['SalePrice'][:,np.newaxis])
low_range = saleprice_scaled[saleprice_scaled[:,0].argsort()][:10]
high_range= saleprice_scaled[saleprice_scaled[:,0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of the distribution:')
print(high_range)

 

✏️ scatter plot (GrLibArea, SalePrice)

'GrLivArea', 'SalePrice' 간 산점도

  • 그래프의 오른쪽 아래에 위치한 2개의 점을 outlier로 판단하고 제거함
  • 그래프의 오른쪽 위에 위치한 2개의 점은 trend를 따르고 있으므로, 제거하지 않음

 

 

✏️ Deleting points

  • outlier로 판단되는 점 2개를 제거함
# GrLivArea를 내림차순으로 정렬하고, 그중 가장 큰 GrLivArea 값을 갖는 2개의 행만 출력
df_train.sort_values(by='GrLivArea', ascending=False)[:2] 

# Id가 1299, 524인 행(outlier) 삭제
df_train = df_train.drop(df_train[df_train['Id'] == 1299].index)
df_train = df_train.drop(df_train[df_train['Id'] == 524].index)

 


✏️ scatter plot (saleprice, TotalBsmtSF)

'TotalBsmtSF', 'SalePrice' 간 산점도

  • outlier로 판단할 만한 관측치가 발견되지 않음

 


⚡ checking assumption

1) Normality

✏️ normality (SalePrice)

# histogram and normal probability plot(Q-Q Plot)
sns.distplot(df_train['SalePrice'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

 

'SalePrice'의 분포

 

'SalePrice'의 QQ Plot

  • SalePrice는 normal distribution을 따르지 않는 것으로 판단됨

 

✏️ 로그변환

df_train['SalePrice'] = np.log(df_train['SalePrice'])
# transformed histogram and normal probability plot
sns.distplot(df_train['SalePrice'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['SalePrice'], plot=plt)

 

✏️ normality (GrLivArea)

# histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

 

✏️ 로그변환

df_train['GrLivArea'] = np.log(df_train['GrLivArea'])
# transformed histogram and normal probability plot
sns.distplot(df_train['GrLivArea'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['GrLivArea'], plot=plt)

 

✏️ normality (TotalBsmtSF)

# histogram and normal probability plot
sns.distplot(df_train['TotalBsmtSF'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train['TotalBsmtSF'], plot=plt)

 

' TotalBsmtSF'의 분포

 

' TotalBsmtSF'의 QQ Plot

  • 다수의 관측치가 0 값을 가짐(basement가 없는 집인 경우) → 로그변환을 할 수 없음
  • 따라서 basement 존재 여부에 따라 0 또는 1 값을 갖는 변수를 생성해, 0이 아닌 관측치에 대해서만 로그변환을 실시함

 

✏️새로운 변수 생성하기

▶ TotalBsmtSF의 값이 0보다 크면, HasBsmt에 1값을 줌

# 새로운 변수 생성 (basement의 존재 여부를 0, 1로 범주화)
df_train['HasBsmt'] = pd.Series(len(df_train['TotalBsmtSF']), index=df_train.index)
df_train['HasBsmt'] = 0
df_train.loc[df_train['TotalBsmtSF'] > 0, 'HasBsmt'] = 1

HasBsmt의 값이 1이면(basement가 존재하면), TotalBsmtSF에 로그변환 실시함

# transform data
df_train.loc[df_train['HasBsmt'] == 1, 'TotalBsmtSF'] = np.log(df_train['TotalBsmtSF'])

 

# histogram and normal probability plot
sns.distplot(df_train[df_train['TotalBsmtSF'] > 0]['TotalBsmtSF'], fit=norm)
fig = plt.figure()
res = stats.probplot(df_train[df_train['TotalBsmtSF'] > 0]['TotalBsmtSF'], plot=plt)

 


2) homoscedasticity

✏️ scatter plot (SalePrice, GrLivArea)

plt.scatter(df_train['GrLivArea'], df_train['SalePrice'])

 

 

  • 등분산성을 만족하는 것으로 보임

 

 

✏️ scatter plot (SalePrice, TotalBsmtSF)

plt.scatter(df_train[df_train['TotalBsmtSF'] > 0]['TotalBsmtSF'], df_train[df_train['TotalBsmtSF'] > 0]['SalePrice'])

 

 

  • 등분산성을 만족하는 것으로 보임

 


⚡ checking assumption

✏️ categorical 변수를 dummy 변수로 변환하기

df_train = pd.get_dummies(df_train)

 

 

 


참고 자료

House Prices - Advanced Regression Techniques
Comprehensive data exploration with Python(by Pedro Marcelino)
heatmap 기본 문법

 

 

※ 배경 이미지 출처: Image by Pexels from Pixabay