👩‍💼

3.2 보스턴 주택가격 예측분석 SAS - 2

데이터 전처리와 새로운 변수 생성, 다양한 모델을 이용한 분석을 더 진행해봅시다. 본 분석에서는 SAS 9.4를 이용합니다.
 

1. 데이터 전처리

1) 라이브러리 지정

libname 구문을 이용하여 house라는 라이브러리를 지정합니다. ' ' 사이에 본인에게 맞는 폴더 경로를 입력합니다.
libname house 'C:\Users\help\Downloads\house-prices-advanced-regression-techniques (2)';

2) 데이터 불러오기

SAS에서 데이터를 사용하기 위해 Proc Import를 사용하여 Train/Test 데이터를 불러옵시다.

2-1 Train 데이터 불러오기

PROC IMPORT DATAFILE = "C:\Users\help\Downloads\house-prices-advanced-regression-techniques (2)\TRAIN.CSV" DBMS = CSV OUT = House.TRAIN REPLACE; RUN;
 

2-2 불필요한 변수 제거

DATA House.TRAIN_1; SET House.TRAIN; DROP Street Alley Utilities Condition2 RoofMatl BsmtFinType2 BsmtFinSF2 Heating LowQualFinSF WoodDeckSF OpenPorchSF PoolArea PoolQC MiscFeature MiscVal MoSold YrSold; RUN;
 

2-3 Test 데이터 불러오기

Test 데이터는 향후 Scoring 할때 Train 데이터의 전처리된 모든 내용을 그대로 수행해야합니다.
PROC IMPORT DATAFILE = "C:\Users\help\Downloads\house-prices-advanced-regression-techniques (2)\TEST.CSV" DBMS = CSV OUT = House.TEST REPLACE; RUN;
 

2. 기초 분석

1) Target 변수 - 정규분포화

Proc sgplot은 다양한 그래프를 그릴수 있는데요. 그 중 정규분포 그래프를 그려봅시다.
proc sgplot data= House.Train_1; histogram SalePrice ; density SalePrice / type=normal legendlabel='Normal' lineattrs=(pattern=solid); density SalePrice / type=kernel legendlabel='Kernel' lineattrs=(pattern=solid); keylegend / location=inside position=topright across=1; xaxis display=(nolabel); run;
다음 코드를 실행하면 그래프가 왼쪽으로 치우쳐 있는 것을 볼 수 있습니다.
notion imagenotion image
이럴때는 변수에 로그를 취하면 정규분포 모양으로 변경됩니다. 향후 스코어링을 할때 로그를 취한 변수에 다시 지수함수를 곱하여서 원래 값으로 돌아오게 해야합니다. 로그를 취하는 방법은 다음과 같습니다. 새로운 타겟변수는 N_SalePrice로 명명합니다.
DATA HOUSE.TRAIN_TARGET; SET House.TRAIN_1; N_SalePrice = LOG(SALEPRICE); RUN;
proc sgplot data= House.TRAIN_TARGET; histogram N_SalePrice ; density N_SalePrice / type=normal legendlabel='Normal' lineattrs=(pattern=solid); density N_SalePrice / type=kernel legendlabel='Kernel' lineattrs=(pattern=solid); keylegend / location=inside position=topright across=1; xaxis display=(nolabel); run;
로그를 취한 결과 다음과 같습니다. 자료가 정규성을 띄는 것을 확인 할 수 있습니다.
notion imagenotion image
 

2) 파생변수 생성

해당 데이터에는 비슷한 종류의 변수가 많아 변수끼리 결합하여 분석하기가 좋습니다. 새로운 파생변수를 생성하여 더 좋은 결과를 얻어봅시다.

2-1 외부 컨디션

사람을 만날때도 첫인상이 중요하듯이 건물도 외부 컨디션이 중요하다고 생각이 들었습니다. 외부 컨디션에 해당하는 변수는 다음과 같습니다.
OVERALLQUAL - 집의 전체 재료와 마감평가 → 1점~10점 OVERALLCOND -집의 전반적인 상태 평가 → 1점~10점 ExterQual - 외부 재료의 품질 평가 → Ex - 5점 Gd - 4점 TA - 3점 Fa - 2점 Po - 1점 NA - 0점 ExterCond - 외장재 재료의 현재 상태 평가 → Ex - 5점 Gd - 4점 TA - 3점 Fa - 2점 Po - 1점 NA - 0점
이 변수들을 결합하여 외부컨디션 변수를 생성하겠습니다. 그래프를 보면 점수가 높을 수록 가격이 높아지는 추세인 것을 볼 수 있습니다. 변수 계산을 위해 다음 코드를 실행하여 범주형 변수를 숫자형 변수로 변경합시다.
여기에서는 전처리만 수행하고, 변수 계산은 나중에 한번에 진행하도록 하겠습니다.
notion imagenotion image
DATA House.TRAIN_CONDITION; SET House.TRAIN_TARGET; IF ExterQual = 'Ex' THEN N_ExterQual = 5; ELSE IF ExterQual = 'Gd' THEN N_ExterQual = 4; ELSE IF ExterQual = 'TA' THEN N_ExterQual = 3; ELSE IF ExterQual = 'Fa' THEN N_ExterQual = 2; ELSE IF ExterQual = 'Po' THEN N_ExterQual = 1; ELSE N_ExterQual = 0; IF EXTERCOND = 'Ex' THEN N_EXTERCOND = 5; ELSE IF EXTERCOND = 'Gd' THEN N_EXTERCOND = 4; ELSE IF EXTERCOND = 'TA' THEN N_EXTERCOND = 3; ELSE IF EXTERCOND = 'Fa' THEN N_EXTERCOND = 2; ELSE IF EXTERCOND = 'Po' THEN N_EXTERCOND = 1; ELSE N_EXTERCOND = 0; RUN;

2-2 내부 면적

사람들이 부동산을 볼때 가장 먼저 보는 것이 평형 및 전체 면적입니다. 우리나라가 제곱미터/평형을 쓰는 것처럼 외국에서는 피트(feet)를 쓰곤 하는데요. 면적 변수들을 융합하여 새로운 변수를 생성해 봅시다.
내부 면적에 해당하는 변수는 다음과 같습니다.
 
TotalBsmtSF: 지하실 총 면적 1stFlrSF: 1층 평방 피트 2ndFlrSF: 2층 평방 피트 GrLivArea: 지상/거실면적 평방피트
먼저 각 변수와 타겟변수의 산점도를 그려보면 다음과 같습니다. 전반적으로 선형성을 띄는것 같으나 종종 튀는 값들이 보이는 듯합니다. 간단한 처리를 통해 이상치들을 제거하도록 하겠습니다.
 
notion imagenotion image
DATA House.Train_Area; SET House.TRAIN_CONDITION; IF TotalBsmtSF > 4000 OR _1stFlrSF > 3000 OR _2ndFlrSF > 1700 OR GrLivArea > 4000 THEN DELETE; RUN;
산점도에서 가장 치우쳐 있는 극단값들 일부를 제거해봅시다. 극단치를 제외하고 난 후의 산점도는 다음과 같습니다.
PROC SGSCATTER DATA = House.Train_Area; PLOT N_SALEPRICE * (TotalBsmtSF _1stFlrSF _2ndFlrSF GrLivArea) / ROWS = 10 COLUMNS = 1; RUN;
 
notion imagenotion image
아까보다 더 선형성을 띄는 것을 확인할 수 있습니다.
 

2-3 욕실 개수

욕실도 집을 구성하는 요소 중에 중요한 요소인데요. 보통 집의 크기가 크고 방이 많을 수록 욕실도 많을거라 예상합니다. 욕실에 대한 변수는 다음과 같은데요.
BsmtFullBath 지하 전체 욕실 BsmtHalfBath 지하 반 욕실 FullBath 등급 이상의 전체 욕실 HalfBath 등급 이상의 절반 욕실
전체 욕실을 1 반 욕실을 0.5로 정의하여 계산해봅시다. FullBath가 0인 집이 0.62%인것을 보면 대부분 화장실 1개씩은 다 가지고 있고, 여러개 있는 집도 많은 것 같습니다. 나중에 파생변수를 생성한 뒤에 결과를 확인해봅시다. 빈도표 산출 코드와 전처리 코드는 다음과 같습니다.
PROC FREQ DATA = House.TRAIN_Area; TABLE BsmtFullBath BsmtHalfBath FullBath HalfBath; RUN;
notion imagenotion image
 
DATA House.Train_Bath; SET House.Train_Area; BsmtHalfBath = BsmtHalfBath / 2; HalfBath = HalfBath / 2; RUN;

2-4 지하실 상태

우리나라는 대부분 아파트, 빌라 등의 집이 많기 때문에 지하실 개념이 많이 없지만 외국에서는 집에 지하실이 있는 것을 종종 볼 수 있습니다. 지하실에 대한 변수는 다음과 같은데요.
BsmtQual: 지하실 높이 평가 BsmtCond:지하실 일반적인 상태 평가 BsmtExposure: 지하실 벽 노출 수준 BsmtFinType1: 지하실 마감 면적 등급
평가수준과 노출수준 등급에 따라 집값이 높아지는 추세를 확인할 수 있습니다.
PROC SGSCATTER DATA = House.Train_Area; PLOT N_SALEPRICE * (BsmtQual BsmtCond BsmtExposure BsmtFinType1) / ROWS = 10 COLUMNS = 1; RUN;
 
notion imagenotion image
지하실 상태 변수에 대한 전처리 코드는 다음과 같습니다.
DATA House.Train_Bsmt; SET House.Train_Bath; If BsmtQual = 'Ex' THEN N_BsmtQual = 5; else if BsmtQual = 'Gd' THEN N_BsmtQual = 4; else if BsmtQual = 'TA' THEN N_BsmtQual = 3; else if BsmtQual = 'Fa' THEN N_BsmtQual = 2; else if BsmtQual = 'Po' THEN N_BsmtQual = 1; else N_BsmtQual = 0; If BsmtCond = 'Ex' THEN N_BsmtCond = 5; else if BsmtCond = 'Gd' THEN N_BsmtCond = 4; else if BsmtCond = 'TA' THEN N_BsmtCond = 3; else if BsmtCond = 'Fa' THEN N_BsmtCond = 2; else if BsmtCond = 'Po' THEN N_BsmtCond = 1; else N_BsmtCond = 0; If BsmtExposure = 'Gd' THEN N_BsmtExposure = 4; else if BsmtExposure = 'Av' THEN N_BsmtExposure = 3; else if BsmtExposure = 'Mn' THEN N_BsmtExposure = 2; else if BsmtExposure = 'No' THEN N_BsmtExposure = 1; else N_BsmtExposure = 0; If BsmtFinType1 = 'GLQ' THEN N_BsmtFinType1 = 6; else if BsmtFinType1 = 'ALQ' THEN N_BsmtFinType1 = 5; else if BsmtFinType1 = 'BLQ' THEN N_BsmtFinType1 = 4; else if BsmtFinType1 = 'Rec' THEN N_BsmtFinType1 = 3; else if BsmtFinType1 = 'LwQ' THEN N_BsmtFinType1 = 2; else if BsmtFinType1 = 'Unf' THEN N_BsmtFinType1 = 1; else N_BsmtFinType1 = 0; RUN;
 

2-5 부지 모양 및 평탄도

집을 지을때 부지 모양이 얼마나 평평한지, 경사는 없는지에 따라 집의 균열이나 내구성에도 영향을 주게 되고, 생활하는대도 영향이 있기 마련입니다. 부지모양과 평탄도에 따른 변수는 다음과 같습니다.
LotShape: 부지 모양 → 평평함 - 1 기울어짐 - 0 LandContour: 부동산의 평탄도 → 평평함 - 1 안평평함 - 0
부지모양과 평탄도에 따른 빈도표와 타겟에 대한 산점도는 다음과 같습니다. 부지모양이 평평할 수록 집값이 높은 추세인 것을 확인할 수 있습니다. 평탄하면 1점 평평하지 않으면 0점을 부여했습니다. 두 변수의 합으로 새로운 변수를 만들 예정입니다.
notion imagenotion image
 
notion imagenotion image
전처리 코드는 다음과 같습니다.
DATA House.Train_Land; SET House.Train_Bsmt; If LotShape = 'IR1' then N_LotShape = 0; ELSE If LotShape = 'IR2' then N_LotShape = 0; ELSE If LotShape = 'IR3' then N_LotShape = 0; ELSE N_LotShape = 1; If LandContour = 'Bnk' then N_LandContour = 0; ELSE If LandContour = 'HLS' then N_LandContour =0; ELSE If LandContour = 'Low' then N_LandContour =0; ELSE N_LandContour =1; RUN;

2-6 차고 상태

외국에서 볼 수 있는 집의 특별함 중에 다른 하나는 차고가 따로 있다는 것이다. Garage라고 하는데, 여기서는 차고도 집의 일부라 생각하고 변수로 만들어 보았다. 차고 상태에 대한 변수는 다음과 같다.
GarageFinish: 차고 내부 마감 GarageQual: 차고 품질 GarageCond: 차고 상태
차고 상태에 대한 변수는 집값과의 상관성이 약간 있으므로, 이 변수도 점수화 해서 새로운 변수로 만들어 볼 것이다.
PROC SGSCATTER DATA = House.Train_Area; PLOT N_SALEPRICE * (GarageFinish GarageQual GarageCond) / ROWS = 10 COLUMNS = 1; RUN;
notion imagenotion image
DATA House.Train_GARAGE; SET House.Train_Land; IF GarageFinish = 'Fin' THEN N_GarageFinish = 3; ELSE IF GarageFinish = 'RFn' THEN N_GarageFinish = 2; ELSE IF GarageFinish = 'Unf' THEN N_GarageFinish = 1; ELSE N_GarageFinish = 0; IF GarageQual = 'Ex' THEN N_GarageQual = 5; ELSE IF GarageQual = 'Gd' THEN N_GarageQual = 4; ELSE IF GarageQual = 'TA' THEN N_GarageQual = 3; ELSE IF GarageQual = 'Fa' THEN N_GarageQual = 2; ELSE IF GarageQual = 'Po' THEN N_GarageQual = 1; ELSE N_GarageQual = 0; IF GarageCond = 'Ex' THEN N_GarageCond = 5; ELSE IF GarageCond = 'Gd' THEN N_GarageCond = 4; ELSE IF GarageCond = 'TA' THEN N_GarageCond = 3; ELSE IF GarageCond = 'Fa' THEN N_GarageCond = 2; ELSE IF GarageCond = 'Po' THEN N_GarageCond = 1; ELSE N_GarageCond = 0; RUN;

2-7 건축연도/리모델링

집은 아무래도 새거일수록 좋고, 오래 되도 리모델링 된 집이 안한 집보다 가격을 더 받는 경향이 있다. 집에 대한 변수는 다음과 같다.
YearBuilt - 건축연도 YearRemodAdd - 리모델링 연도
리모델링 연도는 리모델링을 하지 않았을때 건축연도와 값이 같다. 추세를 봐도 최신의 집과 리모델링 한 집이 가격이 더 높아지는 경향이 을 확인할 수 있다. 여기서는 간단하게 2020년에 건축연도와 리모델링 연도를 뺀 뒤 두 값의 차를 2로 나누어 평균을 구하는 방법으로 새로운 변수를 만들 것이다. 그렇게 되면 최근에 지어질 수록 값이 적어지고 리모델링을 하면 약간 값이 적어지는 효과를 낼 수 있다.
PROC SGSCATTER DATA = House.Train_Area; PLOT N_SALEPRICE * (YearBuilt YearRemodAdd) / ROWS = 10 COLUMNS = 1; RUN;
 
notion imagenotion image

2-8 Missing 및 NA값 처리

SAS에서는 Missing 값과 NA값이 있으면 제대로 분석이 안되므로 해당 값을 처리하고자 합니다.
 
연속형 변수 중 Missing 값을 가지고 있는 것은 GarageYtBlt와 MasVnrArea인데, 이 변수들의 결측치는 평균값으로 대체합니다. 다음 코드를 실행하여 변수의 평균을 내보면
GarageYrBlt - 평균 값 1,978 MasVnrArea - 평균 값 101
인 것을 알 수 있다.
PROC MEANS DATA = House.Train_Land; VAR GarageYrBlt MasVnrArea; RUN;
연속 형 변수와 그 외의 나머지 범주형 변수는 다음과 같이 처리합니다.
DATA House.Train_Other; SET House.Train_GARAGE; /* MasVnrType의 NA는 None값으로 지정 */ If MasVnrType = 'NA' THEN MasVnrType = 'None'; /* Electrical의 NA는 None값을 신설하여 대체 */ If Electrical = 'NA' THEN Electrical ='None'; /* GarageType의 NA는 None값을 신설하여 대체 */ If GarageType = 'NA' THEN GarageType= 'None'; /* GarageYrBlt의 Missing값은 평균값으로 대체 */ If GarageYrBlt = '' THEN GarageYrBlt = 1978; /* GarageYrBlt의 Missing값은 평균값으로 대체 */ If MasVnrArea = '' THEN GarageYrBlt = 101; /* GarageYrBlt의 Missing값은 0으로 대체 */ If LotFrontage = 'NA' THEN LotFrontage = 0; /* FireplaceQu는 NA를 없애기 위해 등급화로 진행 */ IF FireplaceQu = 'Ex' THEN FireplaceQu = 5; ELSE IF FireplaceQu = 'Gd' THEN FireplaceQu = 4; ELSE IF FireplaceQu = 'TA' THEN FireplaceQu = 3; ELSE IF FireplaceQu = 'Fa' THEN FireplaceQu = 2; ELSE IF FireplaceQu = 'Po' THEN FireplaceQu = 1; ELSE FireplaceQu = 0; /* Fence는 NA를 없애기 위해 등급화로 진행 */ IF Fence = 'GdPrv' THEN Fence = 4; ELSE IF Fence = 'MnPrv' THEN Fence = 3; ELSE IF Fence = 'GdWo' THEN Fence = 2; ELSE IF Fence = 'MnWw' THEN Fence = 1; ELSE IF Fence = 'NA' THEN Fence = 0; ELSE Fence = 0; RUN;

2-9 파생변수 종합

위에서 전처리하고 정의한대로 새로운 파생변수를 만들어 볼거에요. 파생변수는 New_Var1부터 7로 정의하고 그 외의 분석에 필요한 변수만 Keep문을 사용하여 남길거에요.
DATA HOUSE.TRAIN_DATA_FINAL; SET House.Train_Other; /* 1. 외부 컨디션 */ /* OVERALLQUAL + OVERALLCOND + N_ExterQual + N_ExterCond -30점 만점 */ New_Var1 = OVERALLQUAL + OVERALLCOND + N_ExterQual + N_ExterCond; /* 2. 내부 면적 */ /*지하실 + 1층 + 2층 + 거실*/ New_Var2 = TotalBsmtSF + _1stFlrSF + _2ndFlrSF + GrLivArea; /* 3. 욕실 */ New_Var3 = BsmtFullBath + BsmtHalfBath + FullBath + HalfBath; /* 4. 지하실 */ New_Var4 = N_BsmtQual + N_BsmtCond + N_BsmtExposure + N_BsmtFinType1; /* 5. 토지 상태 */ New_Var5 = N_LotShape +N_LandContour; /* 6. 차고 상태 */ New_Var6 = N_GarageFinish + N_GarageQual + N_GarageCond; /* 7. 지어진 날짜 */ /* 리모델링을 했을때 이점을 줌*/ New_Var7 = ((2020 - YearBuilt) + (2020-YearRemodAdd)) / 2; /* 남길 변수 목록*/ KEEP N_SALEPRICE /* 타겟변수*/ New_Var1 New_Var2 New_Var3 New_Var4 New_Var5 New_Var6 New_Var7 /* 파생변수 */ MasVnrArea BsmtFinSF1 BsmtUnfSF EnclosedPorch Fireplaces GarageArea GarageCars KitchenAbvGr LotArea LotFrontage ScreenPorch TotRmsAbvGrd _3SsnPorch ExterCond ExterQual OverallCond OverallQual BsmtFullBath BsmtHalfBath FullBath HalfBath BsmtCond BsmtExposure BsmtFinType1 BsmtQual/* 연속형 변수 */ Fence GarageType GarageYrBlt PavedDrive BldgType CentralAir Condition1 Electrical Exterior1st Exterior2nd FireplaceQu Foundation Functional HeatingQC HouseStyle KitchenQual LandSlope LotConfig MSSubClass MSZoning MasVnrType Neighborhood RoofStyle SaleCondition SaleType /* 범주형 변수 */ ; RUN;
앞서 Missing 값을 제외하고 1,453개의 데이터와 58개의 변수를 가진 분석 데이터셋이 만들어 졌습니다. 이제 새로운 모델들을 적용하여 더 나은 결과를 얻어봅시다.
 
편의를 위해 Test 데이터에 대한 코드를 적어놓았습니다. 분석에 참고하시길 바랍니다.
DATA HOUSE.Test_TARGET; SET House.Test; N_SalePrice = LOG(SALEPRICE); RUN; /* 3. 파생변수 생성*/ /*1) 외부 컨디션 - 총 30점 만점 */ DATA House.Test_CONDITION; SET House.Test_TARGET; IF ExterQual = 'Ex' THEN N_ExterQual = 5; ELSE IF ExterQual = 'Gd' THEN N_ExterQual = 4; ELSE IF ExterQual = 'TA' THEN N_ExterQual = 3; ELSE IF ExterQual = 'Fa' THEN N_ExterQual = 2; ELSE IF ExterQual = 'Po' THEN N_ExterQual = 1; ELSE N_ExterQual = 0; IF EXTERCOND = 'Ex' THEN N_EXTERCOND = 5; ELSE IF EXTERCOND = 'Gd' THEN N_EXTERCOND = 4; ELSE IF EXTERCOND = 'TA' THEN N_EXTERCOND = 3; ELSE IF EXTERCOND = 'Fa' THEN N_EXTERCOND = 2; ELSE IF EXTERCOND = 'Po' THEN N_EXTERCOND = 1; ELSE N_EXTERCOND = 0; RUN; /* 2) 내부 면적 - 전체적인 면적은 부동산의 기본이 될 수 있음 */ DATA House.Test_Area; SET House.Test_CONDITION; RUN; /* 3) 욕실 개수 - */ DATA House.Test_Bath; SET House.Test_Area; BsmtHalfBath = BsmtHalfBath / 2; HalfBath = HalfBath / 2; RUN; /* 4) 지하실 컨디션 */ DATA House.Test_Bsmt; SET House.Test_Bath; If BsmtQual = 'Ex' THEN N_BsmtQual = 5; else if BsmtQual = 'Gd' THEN N_BsmtQual = 4; else if BsmtQual = 'TA' THEN N_BsmtQual = 3; else if BsmtQual = 'Fa' THEN N_BsmtQual = 2; else if BsmtQual = 'Po' THEN N_BsmtQual = 1; else N_BsmtQual = 0; If BsmtCond = 'Ex' THEN N_BsmtCond = 5; else if BsmtCond = 'Gd' THEN N_BsmtCond = 4; else if BsmtCond = 'TA' THEN N_BsmtCond = 3; else if BsmtCond = 'Fa' THEN N_BsmtCond = 2; else if BsmtCond = 'Po' THEN N_BsmtCond = 1; else N_BsmtCond = 0; If BsmtExposure = 'Gd' THEN N_BsmtExposure = 4; else if BsmtExposure = 'Av' THEN N_BsmtExposure = 3; else if BsmtExposure = 'Mn' THEN N_BsmtExposure = 2; else if BsmtExposure = 'No' THEN N_BsmtExposure = 1; else N_BsmtExposure = 0; If BsmtFinType1 = 'GLQ' THEN N_BsmtFinType1 = 6; else if BsmtFinType1 = 'ALQ' THEN N_BsmtFinType1 = 5; else if BsmtFinType1 = 'BLQ' THEN N_BsmtFinType1 = 4; else if BsmtFinType1 = 'Rec' THEN N_BsmtFinType1 = 3; else if BsmtFinType1 = 'LwQ' THEN N_BsmtFinType1 = 2; else if BsmtFinType1 = 'Unf' THEN N_BsmtFinType1 = 1; else N_BsmtFinType1 = 0; RUN; /* 5) 부지 모양 & 평탄도 */ DATA House.Test_Land; SET House.Test_Bsmt; If LotShape = 'IR1' then N_LotShape = 0; ELSE If LotShape = 'IR2' then N_LotShape = 0; ELSE If LotShape = 'IR3' then N_LotShape = 0; ELSE N_LotShape = 1; If LandContour = 'Bnk' then N_LandContour = 0; ELSE If LandContour = 'HLS' then N_LandContour =0; ELSE If LandContour = 'Low' then N_LandContour =0; ELSE N_LandContour =1; RUN; /* 6) 차고 상태 */ DATA House.Test_GARAGE; SET House.Test_Land; IF GarageFinish = 'Fin' THEN N_GarageFinish = 3; ELSE IF GarageFinish = 'RFn' THEN N_GarageFinish = 2; ELSE IF GarageFinish = 'Unf' THEN N_GarageFinish = 1; ELSE N_GarageFinish = 0; IF GarageQual = 'Ex' THEN N_GarageQual = 5; ELSE IF GarageQual = 'Gd' THEN N_GarageQual = 4; ELSE IF GarageQual = 'TA' THEN N_GarageQual = 3; ELSE IF GarageQual = 'Fa' THEN N_GarageQual = 2; ELSE IF GarageQual = 'Po' THEN N_GarageQual = 1; ELSE N_GarageQual = 0; IF GarageCond = 'Ex' THEN N_GarageCond = 5; ELSE IF GarageCond = 'Gd' THEN N_GarageCond = 4; ELSE IF GarageCond = 'TA' THEN N_GarageCond = 3; ELSE IF GarageCond = 'Fa' THEN N_GarageCond = 2; ELSE IF GarageCond = 'Po' THEN N_GarageCond = 1; ELSE N_GarageCond = 0; RUN; /* 7) 리모델링 */ /* 8) MISSING 및 NA 처리 */ /* GarageYrBlt과 MasVnrArea는 평균값 대체*/ /* GarageYrBlt - 평균값 1978*/ /*MasVnrArea - 평균값 101*/ DATA House.Test_Other; SET House.Test_GARAGE; /* MasVnrType의 NA는 None값으로 지정 */ If MasVnrType = 'NA' THEN MasVnrType = 'None'; /* Electrical의 NA는 None값을 신설하여 대체 */ If Electrical = 'NA' THEN Electrical ='None'; /* GarageType의 NA는 None값을 신설하여 대체 */ If GarageType = 'NA' THEN GarageType= 'None'; /* GarageYrBlt의 Missing값은 평균값으로 대체 */ If GarageYrBlt = '' THEN GarageYrBlt = 1978; /* GarageYrBlt의 Missing값은 평균값으로 대체 */ If MasVnrArea = '' THEN GarageYrBlt = 101; /* GarageYrBlt의 Missing값은 0으로 대체 */ If LotFrontage = 'NA' THEN LotFrontage = 0; /* FireplaceQu는 NA를 없애기 위해 등급화로 진행 */ IF FireplaceQu = 'Ex' THEN FireplaceQu = 5; ELSE IF FireplaceQu = 'Gd' THEN FireplaceQu = 4; ELSE IF FireplaceQu = 'TA' THEN FireplaceQu = 3; ELSE IF FireplaceQu = 'Fa' THEN FireplaceQu = 2; ELSE IF FireplaceQu = 'Po' THEN FireplaceQu = 1; ELSE FireplaceQu = 0; /* Fence는 NA를 없애기 위해 등급화로 진행 */ IF Fence = 'GdPrv' THEN Fence = 4; ELSE IF Fence = 'MnPrv' THEN Fence = 3; ELSE IF Fence = 'GdWo' THEN Fence = 2; ELSE IF Fence = 'MnWw' THEN Fence = 1; ELSE IF Fence = 'NA' THEN Fence = 0; ELSE Fence = 0; RUN; /* 종합 계산*/ DATA HOUSE.Test_DATA_FINAL; SET House.Test_Other; /* 1. 외부 컨디션 */ /* OVERALLQUAL + OVERALLCOND + N_ExterQual + N_ExterCond -30점 만점 */ New_Var1 = OVERALLQUAL + OVERALLCOND + N_ExterQual + N_ExterCond; /* 2. 내부 면적 */ /*지하실 + 1층 + 2층 + 거실*/ New_Var2 = TotalBsmtSF + _1stFlrSF + _2ndFlrSF + GrLivArea; /* 3. 욕실 */ New_Var3 = BsmtFullBath + BsmtHalfBath + FullBath + HalfBath; /* 4. 지하실 */ New_Var4 = N_BsmtQual + N_BsmtCond + N_BsmtExposure + N_BsmtFinType1; /* 5. 토지 상태 */ New_Var5 = N_LotShape +N_LandContour; /* 6. 차고 상태 */ New_Var6 = N_GarageFinish + N_GarageQual + N_GarageCond; /* 7. 지어진 날짜 */ /* 리모델링을 했을때 이점을 줌*/ New_Var7 = ((2020 - YearBuilt) + (2020-YearRemodAdd)) / 2; /* 남길 변수 목록*/ KEEP ID/* 타겟변수*/ New_Var1 New_Var2 New_Var3 New_Var4 New_Var5 New_Var6 New_Var7 /* 파생변수 */ MasVnrArea BsmtFinSF1 BsmtUnfSF EnclosedPorch Fireplaces GarageArea GarageCars KitchenAbvGr LotArea ScreenPorch TotRmsAbvGrd _3SsnPorch /* 연속형 변수 */ BldgType CentralAir Condition1 Electrical Exterior1st Exterior2nd FireplaceQu Foundation Functional HeatingQC HouseStyle KitchenQual LandSlope LotConfig MSSubClass MSZoning MasVnrType Neighborhood RoofStyle SaleCondition SaleType LotFrontage/* 범주형 변수 */ ; RUN;

3. 모델 적합

이제 모델을 적합해 봅시다. 전처리한 내용이 얼마나 효과가 있는지 아까와 같은 Lasso 모델을 적용하여 분석해봅시다.

1) Lasso

Lasso는 아까 분석한 코드와 달라진 부분은 변수가 달라졌다는 것 빼고는 동일합니다.

1-1 모델 구축

proc glmselect data=HOUSE.TRAIN_DATA_FINAL plots=asePlot; class BldgType CentralAir Condition1 Electrical Exterior1st Exterior2nd FireplaceQu Foundation Functional HeatingQC HouseStyle KitchenQual LandSlope LotConfig MSSubClass MSZoning MasVnrType Neighborhood RoofStyle SaleCondition SaleType LotFrontage ; model N_SALEPRICE = New_Var1 New_Var2 New_Var3 New_Var4 New_Var5 New_Var6 New_Var7 /* 파생변수 */ MasVnrArea BsmtFinSF1 BsmtUnfSF EnclosedPorch Fireplaces GarageArea GarageCars KitchenAbvGr LotArea ScreenPorch TotRmsAbvGrd _3SsnPorch /* 연속형 변수 */ BldgType CentralAir Condition1 Electrical Exterior1st Exterior2nd FireplaceQu Foundation Functional HeatingQC HouseStyle KitchenQual LandSlope LotConfig MSSubClass MSZoning MasVnrType Neighborhood RoofStyle SaleCondition SaleType LotFrontage/* 범주형 변수 */ / selection=lasso; store out=house_New_model_Lasso; run;

1-2 스코어링

스코어링을 할때 전과 동일하게 값이 Null(.)으로 나오는 부분이 있는데 이 부분은 평균값으로 대체 할 것입니다. 잊지 말고 해야할 것은 예측값에 지수함수를 곱하여서 로그화된 값을 다시 돌려놔야한다는 것입니다.
proc plm restore=house_New_model_1; score data=HOUSE.Test_DATA_FINAL out=House.score1 pred=Predicted lcl=Lower ucl=Upper; run;
/* 평균값 - 12.1153808*/ proc means data=house.score1; var predicted; run;
DATA RESULT_New_1; SET House.score1; IF Predicted = . then predicted = 12.1153808; Predicted = exp(predicted); KEEP ID Predicted; RENAME PREDICTED = SalePrice; RUN;

1-3 데이터 Export 및 결과 제출

다음 코드를 실행하여 Result_Lasso_new.csv를 생성할 것이고, 이 파일을 캐글에 제출합니다.
PROC EXPORT DATA = Result_New_1 Outfile = 'C:/Users/help/Downloads/house-prices-advanced-regression-techniques (2)/Result_Lasso_new.csv' DBMS=CSV REPLACE; RUN;
 
캐글에 제출한 결과 0.16471 스코어를 얻었습니다. 약 3,000위 초반대의 등수를 기록하는 수치로 기존 값보다 상당히 개선되었음을 볼 수 있습니다. 이제 다른 모델들도 적용하여 가장 좋은 모델을 찾아봅시다.
 
notion imagenotion image

2) 랜덤포레스트

2-1 모델 구축

proc hpforest data=HOUSE.TRAIN_DATA_FINAL maxtrees= 2000 vars_to_try=6 seed=1500 trainfraction=0.3 maxdepth=50 leafsize=4 alpha= 0.3; target N_SALEPRICE/ level=interval; input MasVnrArea BsmtFinSF1 BsmtUnfSF EnclosedPorch Fireplaces GarageArea GarageCars KitchenAbvGr LotArea ScreenPorch TotRmsAbvGrd _3SsnPorch New_Var1 New_Var2 New_Var3 New_Var4 New_Var5 New_Var6 New_Var7 / level=interval; input BldgType CentralAir Condition1 Electrical Exterior1st Exterior2nd FireplaceQu Foundation Functional HeatingQC HouseStyle KitchenQual LandSlope LotConfig MSSubClass MSZoning MasVnrType Neighborhood RoofStyle SaleCondition SaleType LotFrontage/level = nominal; ods output fitstatistics = fitstats; save file = "C:\Users\help\Downloads\house-prices-advanced-regression-techniques (2)\model_fit.bin"; run;
 

2-2 스코어링

proc hp4score data=HOUSE.Test_DATA_FINAL; ID Id; score file= "C:\Users\help\Downloads\house-prices-advanced-regression-techniques (2)\model_fit.bin" out=Scored_house; run;
여기서도 잊지말고 지수함수를 곱하여서 원래 가격으로 돌려놓습니다.
DATA Result_house; SET Scored_house; SalePrice = exp(P_N_SalePrice); KEEP Id SalePrice; RUN;

2-3 데이터 Export 및 결과 제출

다음 코드를 실행하여 Result_house_RF.csv를 생성할 것이고, 이 파일을 캐글에 제출합니다.
PROC EXPORT DATA = Result_house Outfile = 'C:/Users/help/Downloads/house-prices-advanced-regression-techniques (2)/Result_house_RF.csv' DBMS=CSV REPLACE; RUN;
랜덤포레스트가 Lasso 보다는 약간 개선된 모델이라는 것을 확인할 수 있습니다.
 
notion imagenotion image

3) Mix 모델 - Lasso + 랜덤포레스트

두 모델의 값의 평균을 적용시키면 어떨까? 혹시 더 좋아질까? 아니면 오히려 나빠질까? 라는 생각이 들 수 있는데 일단 진행해보자. 방법은 간단합니다. 두가지 값의 평균을 낸 값을 캐글에 제출하면 되는데요. 결과는 어떨까요?
놀랍게도 다음과 같이 0.15950 Lasso와 랜덤포레스트보다 좋은 결과를 얻게 되었습니다. 만약 모델이 더 많이 있다면 혹시나 더 나을 값을 냈을지도 모르지만 이번 분석에서는 여기까지 진행하도록 하겠습니다. 최종 결과는 다음과 같이 3,055등으로 마무리 지었습니다.
notion imagenotion image
notion imagenotion image

4. 결론

이로써 SAS를 이용한 Kaggle 분석을 마치도록 하겠습니다. 더욱더 무궁무진한 데이터의 세계에서 통계 소프트웨어 중 최고라고 꼽히는 SAS가 Kaggle과 데이콘 같은 데이터 분석 대회에서 조금 더 영향력을 발휘하는 날이 있었으면 좋겠습니다.