1. 데이터 전처리1) 라이브러리 지정2) 데이터 불러오기2-1 Train 데이터 불러오기2-2 불필요한 변수 제거2-3 Test 데이터 불러오기2. 기초 분석1) Target 변수 - 정규분포화2) 파생변수 생성2-1 외부 컨디션2-2 내부 면적2-3 욕실 개수2-4 지하실 상태2-5 부지 모양 및 평탄도2-6 차고 상태2-7 건축연도/리모델링 2-8 Missing 및 NA값 처리2-9 파생변수 종합3. 모델 적합1) Lasso1-1 모델 구축1-2 스코어링1-3 데이터 Export 및 결과 제출2) 랜덤포레스트2-1 모델 구축2-2 스코어링2-3 데이터 Export 및 결과 제출3) Mix 모델 - Lasso + 랜덤포레스트4. 결론
데이터 전처리와 새로운 변수 생성, 다양한 모델을 이용한 분석을 더 진행해봅시다. 본 분석에서는 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;
다음 코드를 실행하면 그래프가 왼쪽으로 치우쳐 있는 것을 볼 수 있습니다.

이럴때는 변수에 로그를 취하면 정규분포 모양으로 변경됩니다. 향후 스코어링을 할때 로그를 취한 변수에 다시 지수함수를 곱하여서 원래 값으로 돌아오게 해야합니다. 로그를 취하는 방법은 다음과 같습니다. 새로운 타겟변수는 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;
로그를 취한 결과 다음과 같습니다. 자료가 정규성을 띄는 것을 확인 할 수 있습니다.

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점
이 변수들을 결합하여 외부컨디션 변수를 생성하겠습니다. 그래프를 보면 점수가 높을 수록 가격이 높아지는 추세인 것을 볼 수 있습니다. 변수 계산을 위해 다음 코드를 실행하여 범주형 변수를 숫자형 변수로 변경합시다.
여기에서는 전처리만 수행하고, 변수 계산은 나중에 한번에 진행하도록 하겠습니다.

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: 지상/거실면적 평방피트
먼저 각 변수와 타겟변수의 산점도를 그려보면 다음과 같습니다. 전반적으로 선형성을 띄는것 같으나 종종 튀는 값들이 보이는 듯합니다. 간단한 처리를 통해 이상치들을 제거하도록 하겠습니다.

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;

아까보다 더 선형성을 띄는 것을 확인할 수 있습니다.
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;

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;

지하실 상태 변수에 대한 전처리 코드는 다음과 같습니다.
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점을 부여했습니다. 두 변수의 합으로 새로운 변수를 만들 예정입니다.


전처리 코드는 다음과 같습니다.
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;

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;

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위 초반대의 등수를 기록하는 수치로 기존 값보다 상당히 개선되었음을 볼 수 있습니다. 이제 다른 모델들도 적용하여 가장 좋은 모델을 찾아봅시다.

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 보다는 약간 개선된 모델이라는 것을 확인할 수 있습니다.

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


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