EDA with Housing Price Prediction - Handling Discrete Variables

Page content

강의 홍보

I. 개요

  • 이제 본격적으로 Kaggle 데이터를 활용하여 분석을 진행한다.
  • 데이터는 이미 다운 받은 상태를 전제로 하며, 만약에 데이터가 없다면 이전 포스팅에서 절차를 확인하기 바란다. (미리보기 가능)

II. 구글 드라이브 연동

  • 구글 코랩을 시작하면 언제든지 가장 먼저 해야 하는 것은 드라이브 연동이다.
from google.colab import drive # 패키지 불러오기 
from os.path import join  

ROOT = "/content/drive"     # 드라이브 기본 경로
print(ROOT)                 # print content of ROOT (Optional)
drive.mount(ROOT)           # 드라이브 기본 경로 Mount

MY_GOOGLE_DRIVE_PATH = 'My Drive/Colab Notebooks/inflearn_kaggle/' # 프로젝트 경로
PROJECT_PATH = join(ROOT, MY_GOOGLE_DRIVE_PATH) # 프로젝트 경로
print(PROJECT_PATH)
/content/drive
Go to this URL in a browser: https://accounts.google.com/o/oauth2/auth?client_id=947318989803-6bn6qk8qdgf4n4g3pfee6491hc0brc4i.apps.googleusercontent.com&redirect_uri=urn%3aietf%3awg%3aoauth%3a2.0%3aoob&response_type=code&scope=email%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdocs.test%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdrive%20https%3a%2f%2fwww.googleapis.com%2fauth%2fdrive.photos.readonly%20https%3a%2f%2fwww.googleapis.com%2fauth%2fpeopleapi.readonly

Enter your authorization code:
··········
Mounted at /content/drive
/content/drive/My Drive/Colab Notebooks/inflearn_kaggle/
%cd "{PROJECT_PATH}"
/content/drive/My Drive/Colab Notebooks/inflearn_kaggle
  • 위 코드가 에러 없이 돌아간다면 이제 데이터를 불러올 차례다.
!ls
data  docs  source
  • 필자는 inflearn_kaggle 폴더안에 data, docs, source 등의 하위 폴더를 추가로 만들었다.
  • 즉, data 안에 다운로드 받은 파일이 있을 것이다.

III. 캐글 데이터 수집 및 EDA

  • 우선 데이터를 수집하기에 앞서서 EDA에 관한 필수 패키지를 설치하자.
import pandas as pd
import pandas_profiling
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import seaborn as sns

from IPython.core.display import display, HTML
from pandas_profiling import ProfileReport
/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm
%matplotlib inline
import matplotlib.pylab as plt

plt.rcParams["figure.figsize"] = (14,4)
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['lines.color'] = 'r'
plt.rcParams['axes.grid'] = True 

(1) 데이터 수집

  • 지난 시간에 받은 데이터가 총 4개임을 확인했다.
    • data_description.txt
    • sample_submission.csv
    • test.csv
    • train.csv
  • 여기에서는 우선 test.csv & train.csv 파일을 받도록 한다.
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')
print("data import is done")
data import is done

(2) 데이터 확인

  • Kaggle 데이터를 불러오면 우선 확인해야 하는 것은 데이터셋의 크기다.
    • 변수의 갯수
    • Numeric 변수 & Categorical 변수의 개수 등을 파악해야 한다.
  • Point 1 - train데이터에서 굳이 훈련데이터와 테스트 데이터를 구분할 필요는 없다.
    • 보통 Kaggle에서는 테스트 데이터를 주기적으로 업데이트 해준다.
  • Point 2 - 보통 test 데이터의 변수의 개수가 하나 더 작다.
train.shape, test.shape
((1460, 81), (1459, 80))
  • 그 후 train데이터의 상위 5개의 데이터만 확인한다.
display(train.head())
IdMSSubClassMSZoningLotFrontageLotAreaStreetAlleyLotShapeLandContourUtilitiesLotConfigLandSlopeNeighborhoodCondition1Condition2BldgTypeHouseStyleOverallQualOverallCondYearBuiltYearRemodAddRoofStyleRoofMatlExterior1stExterior2ndMasVnrTypeMasVnrAreaExterQualExterCondFoundationBsmtQualBsmtCondBsmtExposureBsmtFinType1BsmtFinSF1BsmtFinType2BsmtFinSF2BsmtUnfSFTotalBsmtSFHeating...CentralAirElectrical1stFlrSF2ndFlrSFLowQualFinSFGrLivAreaBsmtFullBathBsmtHalfBathFullBathHalfBathBedroomAbvGrKitchenAbvGrKitchenQualTotRmsAbvGrdFunctionalFireplacesFireplaceQuGarageTypeGarageYrBltGarageFinishGarageCarsGarageAreaGarageQualGarageCondPavedDriveWoodDeckSFOpenPorchSFEnclosedPorch3SsnPorchScreenPorchPoolAreaPoolQCFenceMiscFeatureMiscValMoSoldYrSoldSaleTypeSaleConditionSalePrice
0160RL65.08450PaveNaNRegLvlAllPubInsideGtlCollgCrNormNorm1Fam2Story7520032003GableCompShgVinylSdVinylSdBrkFace196.0GdTAPConcGdTANoGLQ706Unf0150856GasA...YSBrkr85685401710102131Gd8Typ0NaNAttchd2003.0RFn2548TATAY0610000NaNNaNNaN022008WDNormal208500
1220RL80.09600PaveNaNRegLvlAllPubFR2GtlVeenkerFeedrNorm1Fam1Story6819761976GableCompShgMetalSdMetalSdNone0.0TATACBlockGdTAGdALQ978Unf02841262GasA...YSBrkr1262001262012031TA6Typ1TAAttchd1976.0RFn2460TATAY29800000NaNNaNNaN052007WDNormal181500
2360RL68.011250PaveNaNIR1LvlAllPubInsideGtlCollgCrNormNorm1Fam2Story7520012002GableCompShgVinylSdVinylSdBrkFace162.0GdTAPConcGdTAMnGLQ486Unf0434920GasA...YSBrkr92086601786102131Gd6Typ1TAAttchd2001.0RFn2608TATAY0420000NaNNaNNaN092008WDNormal223500
3470RL60.09550PaveNaNIR1LvlAllPubCornerGtlCrawforNormNorm1Fam2Story7519151970GableCompShgWd SdngWd ShngNone0.0TATABrkTilTAGdNoALQ216Unf0540756GasA...YSBrkr96175601717101031Gd7Typ1GdDetchd1998.0Unf3642TATAY035272000NaNNaNNaN022006WDAbnorml140000
4560RL84.014260PaveNaNIR1LvlAllPubFR2GtlNoRidgeNormNorm1Fam2Story8520002000GableCompShgVinylSdVinylSdBrkFace350.0GdTAPConcGdTAAvGLQ655Unf04901145GasA...YSBrkr1145105302198102141Gd9Typ1TAAttchd2000.0RFn3836TATAY192840000NaNNaNNaN0122008WDNormal250000
......................................................................................................................................................................................................................................................
1455145660RL62.07917PaveNaNRegLvlAllPubInsideGtlGilbertNormNorm1Fam2Story6519992000GableCompShgVinylSdVinylSdNone0.0TATAPConcGdTANoUnf0Unf0953953GasA...YSBrkr95369401647002131TA7Typ1TAAttchd1999.0RFn2460TATAY0400000NaNNaNNaN082007WDNormal175000
1456145720RL85.013175PaveNaNRegLvlAllPubInsideGtlNWAmesNormNorm1Fam1Story6619781988GableCompShgPlywoodPlywoodStone119.0TATACBlockGdTANoALQ790Rec1635891542GasA...YSBrkr2073002073102031TA7Min12TAAttchd1978.0Unf2500TATAY34900000NaNMnPrvNaN022010WDNormal210000
1457145870RL66.09042PaveNaNRegLvlAllPubInsideGtlCrawforNormNorm1Fam2Story7919412006GableCompShgCemntBdCmentBdNone0.0ExGdStoneTAGdNoGLQ275Unf08771152GasA...YSBrkr1188115202340002041Gd9Typ2GdAttchd1941.0RFn1252TATAY0600000NaNGdPrvShed250052010WDNormal266500
1458145920RL68.09717PaveNaNRegLvlAllPubInsideGtlNAmesNormNorm1Fam1Story5619501996HipCompShgMetalSdMetalSdNone0.0TATACBlockTATAMnGLQ49Rec102901078GasA...YFuseA1078001078101021Gd5Typ0NaNAttchd1950.0Unf1240TATAY3660112000NaNNaNNaN042010WDNormal142125
1459146020RL75.09937PaveNaNRegLvlAllPubInsideGtlEdwardsNormNorm1Fam1Story5619651965GableCompShgHdBoardHdBoardNone0.0GdTACBlockTATANoBLQ830LwQ2901361256GasA...YSBrkr1256001256101131TA6Typ0NaNAttchd1965.0Fin1276TATAY736680000NaNNaNNaN062008WDNormal147500

1460 rows × 81 columns

  • 그 다음 확인해야 하는 것은 Numerical 변수와 Categorical 변수를 구분한다.
    • 먼저 numerical_features를 구분하자.
numeric_features = train.select_dtypes(include=[np.number])
print(numeric_features.columns)
print("The total number of numeric features are: ", len(numeric_features.columns))
Index(['Id', 'MSSubClass', 'LotFrontage', 'LotArea', 'OverallQual',
       'OverallCond', 'YearBuilt', 'YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1',
       'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF', '1stFlrSF', '2ndFlrSF',
       'LowQualFinSF', 'GrLivArea', 'BsmtFullBath', 'BsmtHalfBath', 'FullBath',
       'HalfBath', 'BedroomAbvGr', 'KitchenAbvGr', 'TotRmsAbvGrd',
       'Fireplaces', 'GarageYrBlt', 'GarageCars', 'GarageArea', 'WoodDeckSF',
       'OpenPorchSF', 'EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea',
       'MiscVal', 'MoSold', 'YrSold', 'SalePrice'],
      dtype='object')
The total number of numeric features are:  38
  • numeric_features을 제외한 나머지 변수를 추출하자.
categorical_features = train.select_dtypes(exclude=[np.number])
print(categorical_features.columns)
print("The total number of numeric features are: ", len(categorical_features.columns))
Index(['MSZoning', 'Street', 'Alley', 'LotShape', 'LandContour', 'Utilities',
       'LotConfig', 'LandSlope', 'Neighborhood', 'Condition1', 'Condition2',
       'BldgType', 'HouseStyle', 'RoofStyle', 'RoofMatl', 'Exterior1st',
       'Exterior2nd', 'MasVnrType', 'ExterQual', 'ExterCond', 'Foundation',
       'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2',
       'Heating', 'HeatingQC', 'CentralAir', 'Electrical', 'KitchenQual',
       'Functional', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual',
       'GarageCond', 'PavedDrive', 'PoolQC', 'Fence', 'MiscFeature',
       'SaleType', 'SaleCondition'],
      dtype='object')
The total number of numeric features are:  43

IV. 연도 데이터 탐색 개요

  • 지난시간에 각 데이터에서 categorical_featuresnumeric_features를 각각 추출하는 방법에 대해 배웠다.

    • categorical_features: Alley, BldgType, BsmtCond, BsmtExposure, BsmtFinType1, BsmtFinType2, BsmtQual, CentralAir, Condition1, Condition2, Electrical, ExterCond, ExterQual, Exterior1st, Exterior2nd, Fence, FireplaceQu, Foundation, Functional, GarageCond, GarageFinish, GarageQual, GarageType, Heating, HeatingQC, HouseStyle, KitchenQual, LandContour, LandSlope, LotConfig, LotShape, MSZoning, MasVnrType, MiscFeature, Neighborhood, PavedDrive, PoolQC, RoofMatl, RoofStyle, SaleCondition, SaleType, Street, Utilitif
    • numeric_features:‘Id’, ‘MSSubClass’, ‘LotFrontage’, ‘LotArea’, ‘OverallQual’, ‘OverallCond’, ‘YearBuilt’, ‘YearRemodAdd’, ‘MasVnrArea’, ‘BsmtFinSF1’, ‘BsmtFinSF2’, ‘BsmtUnfSF’, ‘TotalBsmtSF’, ‘1stFlrSF’, ‘2ndFlrSF’, ‘LowQualFinSF’, ‘GrLivArea’, ‘BsmtFullBath’, ‘BsmtHalfBath’, ‘FullBath’, ‘HalfBath’, ‘BedroomAbvGr’, ‘KitchenAbvGr’, ‘TotRmsAbvGrd’, ‘Fireplaces’, ‘GarageYrBlt’, ‘GarageCars’, ‘GarageArea’, ‘WoodDeckSF’, ‘OpenPorchSF’, ‘EnclosedPorch’, ‘3SsnPorch’, ‘ScreenPorch’, ‘PoolArea’, ‘MiscVal’, ‘MoSold’, ‘YrSold’, ‘SalePrice’
  • 여기에서 우선 categorical_features는 논의에서 제외한다.

  • 이 중에서, 우선 numeric_features를 다시 살펴본다.

    • 우선 훈련데이터의 ID는 삭제한다.
    • 또한, 종속변수인 salesprice는 테스트 데이터에는 존재하지 않는다.
    • 그럼 결과적으로 36개numeric_features만 남게 된다.
  • 36개의 features만 남았다.

    • 여기에서 유심히 살펴보면 Year과 관련된 features가 보인다.
  • 특히 매출과 관련된 데이터를 다루는데 있어서, 연,월,일은 매우 중요하다. 패턴을 찾아서 특정 변수만 추출하는 코드가 필요하다.

    • YearBuilt, YearRemodAdd, GarageYrBlt, YrSold만 추출해보자.
year_fea = [fea for fea in numeric_features if 'Yr' in fea or 'Year' in fea]
print(year_fea)
['YearBuilt', 'YearRemodAdd', 'GarageYrBlt', 'YrSold']

(1) 연도의 변수 처리 방법

  • 여기에서 잊지 말아야 하는 것은 새로운 데이터셋을 만들더라도 항상 종속변수(sales price)는 늘 함께 움직여야 한다.
  • 각각의 변수는 어떻게 이해해야 할까?
    • 이 때, 필요한 것이 일종의 데이터 정의서가 필요하다.
    • data_description.txt를 참고하자.
  • 각 변수는 다음과 같다.
    • YearBuilt: Original construction date
    • YearRemodAdd: Remodel date (same as construction date if no remodeling or additions)
    • GarageYrBlt: Year garage was built
    • YrSold: Year Sold (YYYY)
  • 여기에서 우선 각 변수별로 연도가 차이가 나는지 확인해보자.
for fea in year_fea:
  data = train.copy()
  data[fea].value_counts(sort=False).plot(kind='bar')
  plt.xlabel(fea)
  plt.title(fea)
  plt.show()

png

png

png

png

  • 위 그래프를 보면서 알 수 있는 것은 무엇일까?
    • 우선, 첫 건축 시기는 1872년이고 첫 리모델링 시기는 1950년이고, 마지막으로 첫 차고 건축시기는 1930년으로 확인된다.
    • 그리고 매매 시기는 2006-2010년 사이로 집계된 것으로 확인할 수 있다.

(2) SalePrice와의 관계

  • 위 데이터는 각 연도의 특성을 이해하는데는 도움이 될지 모르지만, 결국 Kaggle대회에서 중요한 것은 종속변수와의 관계다
  • 조금 의미있는 변수를 만들어야 한다. (위 4가지 변수의 조합)
    • 보통 실무에서는 이를 도출변수라고 하는데.. 이 부분은 Intermediate level에서 조금 더 자세히 다루기로 한다.
    • 여기서 강사가 하고자 하는 것은 YrSold에서 그 외 다른 변수와의 연도 시기의 차이를 계산하면 통상적으로 연수가 짧으면 짧을수록 매매가도 올라가고 연수가 길면 길수록 매매가가 하락하는 것을 예상할 수 있다.
    • 실제 그러한 그래프를 그리도록 해보자.
for fea in year_fea:
    if fea!='YrSold': # `YrSold` 변수는 제외 한다.
        data=train.copy() # 이렇게 해주는 것이 좋다. (원본 데이터는 늘 보존할 수 있다)
        data[fea]=data['YrSold']-data[fea] #  여기가 사실 핵심 포인트다. 연수 차이 계산
        plt.scatter(data[fea], data['SalePrice']) # 산점도 그래프를 그린다.
        plt.title(fea)
        plt.xlabel(fea)
        plt.ylabel('SalePrice')
        plt.show()

png

png

png

  • 굳이 회귀선을 그리지 않더라도, 연수의 차이가 작을수록 매매가가 높게 형성된 것을 확인할 수 있다.

V. 양적 변수 시각화 - 이산형 그래프

  • 양적변수에는 크게 2가지의 변수가 존재한다.
    • 이산형(Discrete) 변수: 인원수, 개수 등
    • 연속형(Continuous) 변수: 온도, 키, 몸무게, 소득 등
  • 각각의 시각화 방법에 차이가 있을까?

(1) 가상 데이터 생성

  • 우선 데이터셋을 만드는 데, train데이터셋과 유사하게 만들었다.
    • number_of_room은 방의 개수를 의미한다.
    • sales_price는 매매가격을 의미한다.
# discrete dataframe
temp = pd.DataFrame({'id' : [1,2,3,4,5,6,7,8,9,10],
                    'number_of_room' : [2,4,3,2,4,3,3,3,4,2],
                    'SalePrice' : [1000,1300,2000,1030,2030,2050,2000,5000,3000,3500]
                    })
print(temp)
   id  number_of_room  SalesPrice
0   1               2        1000
1   2               4        1300
2   3               3        2000
3   4               2        1030
4   5               4        2030
5   6               3        2050
6   7               3        2000
7   8               3        5000
8   9               4        3000
9  10               2        3500
  • 우선, number_of_room의 데이터의 시각화를 작성하면 아래와 같다.
    • 이 때 작성해야 하는 그래프는 막대 그래프가 필요하다.
    • 우선, matplotlib 형태로 작성한다.
temp['number_of_room'].value_counts(sort=False).plot.bar()
plt.show()

png

  • 이번에는 seaborn 방식으로 그래프를 작성한다.
sns.countplot(
    data= temp,
    x= "number_of_room", 
    color='blue'
)
plt.show()

png

  • 위 두 방식 중에서 무엇이 더 편한지는 선택하면 된다.
    • 다만, 여기에서는 seaborn 위주로 그래프를 작성한다.
    • seaborn이 처음 입문자분들에게 조금 쉽다.
  • 그러나, 이산형 그래프를 단독으로 그래프를 그릴 때는 막대그래프가 필요하며 개수가 count가 되는 것만 기억한다.

(2) 이산형 변수와 SalePrice

  • 앞서서 설명했지만, SalePricenumber_of_room과의 관계 그래프가 중요하다.
  • 이럴 때 필요한 그래프는 보통 boxplot그래프다.
  • boxplot에 대한 설명은 교재를 참고한다.
sns.boxplot(x = "number_of_room", y = "SalePrice", data = temp)
plt.show()

png

(3) 실무 데이터 적용

  • 이제 실 데이터에 적용해보자.
  • 그런데, 문제가 하나 있다. numeric_features에서 연속형 데이터와 이산형 데이터를 구분해야 한다.
  • 어떻게 구분할까?
    • 가장 좋은 방법은 일일이 데이터를 확인해서 추출하는 방법이다.
    • 그러나, 실무에서는 그렇게 할 수가 없다.
    • unique()함수를 사용한다. 즉 유일한 값을 뽑는데, 그 개수가 50개 이상이면 연속형으로 보고, 그 이하면 discrete로 판단한다.
    • 이 때, year_feaid는 같이 제거한다.
discrete_vars=[fea for fea in numeric_features if len(train[fea].unique()) < 50 and fea not in year_fea + ['Id']]
print("Discrete Variables Count: {}".format(len(discrete_vars)))
Discrete Variables Count: 17
  • 이제 시각화를 작성하는데, boxplot으로 작성한다.
for fea in discrete_vars:
  data = train.copy()
  sns.boxplot(x = fea, y='SalePrice', data = data)
  plt.show()

png

png

png

png

png

png

png

png

png

png

png

png

png

png

png

png

png

  • 총 17개의 boxplot 그래프가 그려졌다.
  • 그럼 여기에서 무엇을 봐야할까?
    • 우선, 도움이 되지 않는 그래프는 제거하는게 좋다. (구분이 되지 않는 것, 그리고, 값이 나타나지 않는 것은 boxplot에서 제거한다.
rem_vars = ['PoolArea', 'LowQualFinSF', 'MiscVal', '3SsnPorch']
discrete_vars=[fea for fea in numeric_features if len(train[fea].unique()) < 50 and fea not in year_fea + rem_vars + ['Id']]
print("Discrete Variables Count: {}".format(len(discrete_vars)))

for fea in discrete_vars:
  data = train.copy()
  sns.boxplot(x = fea, y='SalePrice', data = data)
  plt.show()
Discrete Variables Count: 13

png

png

png

png

png

png

png

png

png

png

png

png

png

  • 위와 같은 방법으로 그래프를 업데이트 해주면 좋다.

VI. Review & What’s next

  • 처음 프로그래밍을 입문하시는 분들에게 for-loop 또는 if 문법에 대해 고민하지 말 것을 권한다.
    • 우선 intermediate 들어가기전에 그래프의 종류, 기초 문법, 데이터 타입의 종류 등에 대해 한번 정리를 할 것이다.
  • 오늘 배운 것의 핵심은 다변량의 그래프를 for-loop를 통해서 비교적 쉽고 빠르게 그래프를 구현했다는 점이다.
  • 오늘 배운 소스코드를 복습하고, 또한 boxplot의 개념이 약하신 분들은 교재를 한번 더 참고하기를 바란다.