In this project, we are presented with a train and test set of the Ames Iowa house data from Kaggle. The goal is to create a model that is capable of predicting house prices using the available variables in the dataset. The Mulitple Linear Regression model will be explored and utilized in this analysis.

Data Exploration & Cleaning

# Importing the necessary libraries and packages
library(ggplot2)
library(data.table)
library(mice)
library(caTools)
library(dplyr)
library(MASS)
# Importing the data
data <- read.csv("training.csv")
# Exploring basic information about the data
colnames(data)
 [1] "Id"            "MSSubClass"    "MSZoning"      "LotFrontage"  
 [5] "LotArea"       "Street"        "Alley"         "LotShape"     
 [9] "LandContour"   "Utilities"     "LotConfig"     "LandSlope"    
[13] "Neighborhood"  "Condition1"    "Condition2"    "BldgType"     
[17] "HouseStyle"    "OverallQual"   "OverallCond"   "YearBuilt"    
[21] "YearRemodAdd"  "RoofStyle"     "RoofMatl"      "Exterior1st"  
[25] "Exterior2nd"   "MasVnrType"    "MasVnrArea"    "ExterQual"    
[29] "ExterCond"     "Foundation"    "BsmtQual"      "BsmtCond"     
[33] "BsmtExposure"  "BsmtFinType1"  "BsmtFinSF1"    "BsmtFinType2" 
[37] "BsmtFinSF2"    "BsmtUnfSF"     "TotalBsmtSF"   "Heating"      
[41] "HeatingQC"     "CentralAir"    "Electrical"    "X1stFlrSF"    
[45] "X2ndFlrSF"     "LowQualFinSF"  "GrLivArea"     "BsmtFullBath" 
[49] "BsmtHalfBath"  "FullBath"      "HalfBath"      "BedroomAbvGr" 
[53] "KitchenAbvGr"  "KitchenQual"   "TotRmsAbvGrd"  "Functional"   
[57] "Fireplaces"    "FireplaceQu"   "GarageType"    "GarageYrBlt"  
[61] "GarageFinish"  "GarageCars"    "GarageArea"    "GarageQual"   
[65] "GarageCond"    "PavedDrive"    "WoodDeckSF"    "OpenPorchSF"  
[69] "EnclosedPorch" "X3SsnPorch"    "ScreenPorch"   "PoolArea"     
[73] "PoolQC"        "Fence"         "MiscFeature"   "MiscVal"      
[77] "MoSold"        "YrSold"        "SaleType"      "SaleCondition"
[81] "SalePrice"    
str(data)
'data.frame':   1060 obs. of  81 variables:
 $ Id           : int  1 3 4 5 8 10 11 12 14 15 ...
 $ MSSubClass   : int  60 60 70 60 60 190 20 60 20 20 ...
 $ MSZoning     : Factor w/ 5 levels "C (all)","FV",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ LotFrontage  : int  65 68 60 84 NA 50 70 85 91 NA ...
 $ LotArea      : int  8450 11250 9550 14260 10382 7420 11200 11924 10652 10920 ...
 $ Street       : Factor w/ 2 levels "Grvl","Pave": 2 2 2 2 2 2 2 2 2 2 ...
 $ Alley        : Factor w/ 2 levels "Grvl","Pave": NA NA NA NA NA NA NA NA NA NA ...
 $ LotShape     : Factor w/ 4 levels "IR1","IR2","IR3",..: 4 1 1 1 1 4 4 1 1 1 ...
 $ LandContour  : Factor w/ 4 levels "Bnk","HLS","Low",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ Utilities    : Factor w/ 1 level "AllPub": 1 1 1 1 1 1 1 1 1 1 ...
 $ LotConfig    : Factor w/ 5 levels "Corner","CulDSac",..: 5 5 1 3 1 1 5 5 5 1 ...
 $ LandSlope    : Factor w/ 3 levels "Gtl","Mod","Sev": 1 1 1 1 1 1 1 1 1 1 ...
 $ Neighborhood : Factor w/ 25 levels "Blmngtn","Blueste",..: 6 6 7 14 17 4 19 16 6 13 ...
 $ Condition1   : Factor w/ 9 levels "Artery","Feedr",..: 3 3 3 3 5 1 3 3 3 3 ...
 $ Condition2   : Factor w/ 6 levels "Artery","Feedr",..: 3 3 3 3 3 1 3 3 3 3 ...
 $ BldgType     : Factor w/ 5 levels "1Fam","2fmCon",..: 1 1 1 1 1 2 1 1 1 1 ...
 $ HouseStyle   : Factor w/ 8 levels "1.5Fin","1.5Unf",..: 6 6 6 6 6 2 3 6 3 3 ...
 $ OverallQual  : int  7 7 7 8 7 5 5 9 7 6 ...
 $ OverallCond  : int  5 5 5 5 6 6 5 5 5 5 ...
 $ YearBuilt    : int  2003 2001 1915 2000 1973 1939 1965 2005 2006 1960 ...
 $ YearRemodAdd : int  2003 2002 1970 2000 1973 1950 1965 2006 2007 1960 ...
 $ RoofStyle    : Factor w/ 6 levels "Flat","Gable",..: 2 2 2 2 2 2 4 4 2 4 ...
 $ RoofMatl     : Factor w/ 7 levels "ClyTile","CompShg",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Exterior1st  : Factor w/ 14 levels "AsbShng","AsphShn",..: 12 12 13 12 7 8 7 14 12 8 ...
 $ Exterior2nd  : Factor w/ 15 levels "AsbShng","AsphShn",..: 13 13 15 13 7 9 7 15 13 9 ...
 $ MasVnrType   : Factor w/ 4 levels "BrkCmn","BrkFace",..: 2 2 3 2 4 3 3 4 4 2 ...
 $ MasVnrArea   : int  196 162 0 350 240 0 0 286 306 212 ...
 $ ExterQual    : Factor w/ 4 levels "Ex","Fa","Gd",..: 3 3 4 3 4 4 4 1 3 4 ...
 $ ExterCond    : Factor w/ 5 levels "Ex","Fa","Gd",..: 5 5 5 5 5 5 5 5 5 5 ...
 $ Foundation   : Factor w/ 6 levels "BrkTil","CBlock",..: 3 3 1 3 2 1 2 3 3 2 ...
 $ BsmtQual     : Factor w/ 4 levels "Ex","Fa","Gd",..: 3 3 4 3 3 4 4 1 3 4 ...
 $ BsmtCond     : Factor w/ 4 levels "Fa","Gd","Po",..: 4 4 2 4 4 4 4 4 4 4 ...
 $ BsmtExposure : Factor w/ 4 levels "Av","Gd","Mn",..: 4 3 4 1 3 4 4 4 1 4 ...
 $ BsmtFinType1 : Factor w/ 6 levels "ALQ","BLQ","GLQ",..: 3 3 1 3 1 3 5 3 6 2 ...
 $ BsmtFinSF1   : int  706 486 216 655 859 851 906 998 0 733 ...
 $ BsmtFinType2 : Factor w/ 6 levels "ALQ","BLQ","GLQ",..: 6 6 6 6 2 6 6 6 6 6 ...
 $ BsmtFinSF2   : int  0 0 0 0 32 0 0 0 0 0 ...
 $ BsmtUnfSF    : int  150 434 540 490 216 140 134 177 1494 520 ...
 $ TotalBsmtSF  : int  856 920 756 1145 1107 991 1040 1175 1494 1253 ...
 $ Heating      : Factor w/ 6 levels "Floor","GasA",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ HeatingQC    : Factor w/ 5 levels "Ex","Fa","Gd",..: 1 1 3 1 1 1 1 1 1 5 ...
 $ CentralAir   : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 2 2 2 2 ...
 $ Electrical   : Factor w/ 5 levels "FuseA","FuseF",..: 5 5 5 5 5 5 5 5 5 5 ...
 $ X1stFlrSF    : int  856 920 961 1145 1107 1077 1040 1182 1494 1253 ...
 $ X2ndFlrSF    : int  854 866 756 1053 983 0 0 1142 0 0 ...
 $ LowQualFinSF : int  0 0 0 0 0 0 0 0 0 0 ...
 $ GrLivArea    : int  1710 1786 1717 2198 2090 1077 1040 2324 1494 1253 ...
 $ BsmtFullBath : int  1 1 1 1 1 1 1 1 0 1 ...
 $ BsmtHalfBath : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FullBath     : int  2 2 1 2 2 1 1 3 2 1 ...
 $ HalfBath     : int  1 1 0 1 1 0 0 0 0 1 ...
 $ BedroomAbvGr : int  3 3 3 4 3 2 3 4 3 2 ...
 $ KitchenAbvGr : int  1 1 1 1 1 2 1 1 1 1 ...
 $ KitchenQual  : Factor w/ 4 levels "Ex","Fa","Gd",..: 3 3 3 3 4 4 4 1 3 4 ...
 $ TotRmsAbvGrd : int  8 6 7 9 7 5 5 11 7 5 ...
 $ Functional   : Factor w/ 7 levels "Maj1","Maj2",..: 7 7 7 7 7 7 7 7 7 7 ...
 $ Fireplaces   : int  0 1 1 1 2 2 0 2 1 1 ...
 $ FireplaceQu  : Factor w/ 5 levels "Ex","Fa","Gd",..: NA 5 3 5 5 5 NA 3 3 2 ...
 $ GarageType   : Factor w/ 6 levels "2Types","Attchd",..: 2 2 6 2 2 2 6 4 2 2 ...
 $ GarageYrBlt  : int  2003 2001 1998 2000 1973 1939 1965 2005 2006 1960 ...
 $ GarageFinish : Factor w/ 3 levels "Fin","RFn","Unf": 2 2 3 2 2 2 3 1 2 2 ...
 $ GarageCars   : int  2 2 3 3 2 1 1 3 3 1 ...
 $ GarageArea   : int  548 608 642 836 484 205 384 736 840 352 ...
 $ GarageQual   : Factor w/ 5 levels "Ex","Fa","Gd",..: 5 5 5 5 5 3 5 5 5 5 ...
 $ GarageCond   : Factor w/ 5 levels "Ex","Fa","Gd",..: 5 5 5 5 5 5 5 5 5 5 ...
 $ PavedDrive   : Factor w/ 3 levels "N","P","Y": 3 3 3 3 3 3 3 3 3 3 ...
 $ WoodDeckSF   : int  0 0 0 192 235 0 0 147 160 0 ...
 $ OpenPorchSF  : int  61 42 35 84 204 4 0 21 33 213 ...
 $ EnclosedPorch: int  0 0 272 0 228 0 0 0 0 176 ...
 $ X3SsnPorch   : int  0 0 0 0 0 0 0 0 0 0 ...
 $ ScreenPorch  : int  0 0 0 0 0 0 0 0 0 0 ...
 $ PoolArea     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ PoolQC       : Factor w/ 3 levels "Ex","Fa","Gd": NA NA NA NA NA NA NA NA NA NA ...
 $ Fence        : Factor w/ 4 levels "GdPrv","GdWo",..: NA NA NA NA NA NA NA NA NA 2 ...
 $ MiscFeature  : Factor w/ 4 levels "Gar2","Othr",..: NA NA NA NA 3 NA NA NA NA NA ...
 $ MiscVal      : int  0 0 0 0 350 0 0 0 0 0 ...
 $ MoSold       : int  2 9 2 12 11 1 2 7 8 5 ...
 $ YrSold       : int  2008 2008 2006 2008 2009 2008 2008 2006 2007 2008 ...
 $ SaleType     : Factor w/ 9 levels "COD","Con","ConLD",..: 9 9 9 9 9 9 9 7 7 9 ...
 $ SaleCondition: Factor w/ 6 levels "Abnorml","AdjLand",..: 5 5 1 5 5 5 5 6 6 5 ...
 $ SalePrice    : int  208500 223500 140000 250000 200000 118000 129500 345000 279500 157000 ...

It looks like we have 1060 observations and 80 variables, excluding target variables ‘SalePrice’; 23 nominal, 23 ordinal, 14 discrete, and 20 continuous. Also, it is clear that we are missing values in our dataset. Let’s determine the distribution of the mising data.

# Checking what prcent of all the data is composed of missing values 
(sum(is.na(data))/(nrow(data)*ncol(data)))*100
[1] 5.885162
# Let's get a break down of the number of missing values in each column
missing.col <- (colSums(is.na(data))[colSums(is.na(data)) > 0]/nrow(data))*100
missing.col.names <- row.names(data.frame(missing.col))
missing.col.percent <- data.frame(missing.col)
missing.col.percent <- setorder(data.frame(missing.col), missing.col)
colnames(missing.col.percent) <- 'percent.missing'
missing.col.percent

It seems that ‘LotFrontage’, ‘FireplaceQu’, ‘Fence’, ‘Alley’, ‘MiscFeature’, and ‘PoolQC’ possess significant proportions of missing data, over 5%. More pressing is the fact that some of the variables, such as ‘PoolQC’, contain no data at all and even ‘LotFrontage’, which has the least missing data of the variables previously mentioned, is missing almost 20% of its data.

After consulting the data documentation, it seems that many of the variables use ‘NA’ as respresenting the absence of a future. For example, ‘FireplaceQu’ represents fireplace quality of a home. Now, although this variable is missing almost half of it’s data, that is due to the fact that a missing value denotes the absence of a fireplace in the home and not a missing piece of infromation of data. So, before continuing, let’s add ‘NA’ as a feature level where it is appropriate based on the data documentation.

# Adding 'NA' as a factor level
col_list <- c('Alley', 'BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2', 'FireplaceQu', 'GarageType', 'GarageFinish', 'GarageQual', 'GarageCond', 'PoolQC', 'Fence', 'MiscFeature')
data[col_list] <- lapply(data[col_list], addNA)

Now, let’s see how much missing data remains in the dataset.

(sum(is.na(data))/(nrow(data)*ncol(data)))*100
[1] 0.3004892
missing.col <- (colSums(is.na(data))[colSums(is.na(data)) > 0]/nrow(data))*100
missing.col.names <- row.names(data.frame(missing.col))
missing.col.percent <- data.frame(missing.col)
missing.col.percent <- setorder(data.frame(missing.col), missing.col)
colnames(missing.col.percent) <- 'percent.missing'
missing.col.percent
str(data[,c('MasVnrType','MasVnrArea','GarageYrBlt','LotFrontage')])
'data.frame':   1060 obs. of  4 variables:
 $ MasVnrType : Factor w/ 4 levels "BrkCmn","BrkFace",..: 2 2 3 2 4 3 3 4 4 2 ...
 $ MasVnrArea : int  196 162 0 350 240 0 0 286 306 212 ...
 $ GarageYrBlt: int  2003 2001 1998 2000 1973 1939 1965 2005 2006 1960 ...
 $ LotFrontage: int  65 68 60 84 NA 50 70 85 91 NA ...

Wow, what a difference. We have reduced the total proportion of missing data by almost half, ~6% versus ~3%, and where we had 18 variables with missing values, we now only have 4.

Now, we will use the ‘mice’ package to impute any missing values that remain. In order to prevent the injection of any personal bias, we will preemptively split the data into a train and test set first and impute the two data sets independently. Before that, let’s see if there are any columns we can, and should, remove all together because of a lack of additional predictability or information.

# Checking which variables have only a single value for all observations
for(x in colnames(data)){
  if(length(unique(data[,x])) == 1){
    print(x)
  }
}
[1] "Utilities"

We see that ‘Utilities’ gives us no additional predicitve power as the only value present for all observations is ‘AllPub’. Further more, the ‘Id’ variable serves no purpose other than identification and should o be included in the model. For these reasons, both of these columns will be removed from the dataset.

# Dropping the 'Id' and 'Utilities' columns
data <- data[, !(names(data) %in% c('Id','Utilities'))]

Let’s continue with the imputation of the reamining missing values.

# Confirming that there are no more missing values in the dataset
train.imp.miss <- (sum(is.na(train.imputed))/(nrow(train.imputed)*ncol(train.imputed)))*100
test.imp.miss <- (sum(is.na(test.imputed))/(nrow(test.imputed)*ncol(test.imputed)))*100
print("Percent missing data in imputed train set: ")
[1] "Percent missing data in imputed train set: "
print(train.imp.miss)
[1] 0
print("Percent missing data in imputed test set: ")
[1] "Percent missing data in imputed test set: "
print(test.imp.miss)
[1] 0

Now, let’s continue by exploring the correlations between the variables in the dataset

# Creating a dataframe of correlations between SalePrice and numeric vars.
train.numeric <- dplyr::select_if(data, is.numeric)
corr.matrix <- cor(train.numeric, use = 'pairwise.complete.obs')
corr.df <- data.frame(row=rownames(corr.matrix)[row(corr.matrix)[upper.tri(corr.matrix)]], col=colnames(corr.matrix)[col(corr.matrix)[upper.tri(corr.matrix)]],corr=corr.matrix[upper.tri(corr.matrix)])
corr.df <- corr.df[order(corr.df$'corr'),]
sale.price.corr <- corr.df[corr.df$col == 'SalePrice',]
head(sale.price.corr)
tail(sale.price.corr)

It looks like ‘OverallQual’, ‘GrLivArea’, ‘GarageCars’, and ‘GarageArea’ all have “significant” correlations with the response varaible, SalePrice. By “significant”, we mean a correlation above 0.6. These may be important predictors of SalePrice. Let us also visualize the distribution of SalePrice.

# Plot of SalePrice distribution
sp.plot <- ggplot(data = train.imputed, aes(x=train.imputed$SalePrice)) + geom_histogram(bins = 50, color = 'white', fill = 'salmon')
log.sp.plot <- ggplot(data = train.imputed, aes(x=log(train.imputed$SalePrice))) + geom_histogram(bins = 50, color = 'white', fill = 'salmon') 
sp.plot

log.sp.plot

The distribution does seem to become more normal when a long transform is applied. This may improve the performance of a linear model. Will need to come back later and determine if the log trandform really has a beneficial effect.

Let’s now utilize methodical approaches to select the features that will provide us with the most predictability in our model. We will explore forward, backward, and stepwise selection as well as Lasso Regression.

# Some levels present in test set that are not accounted for in train set
 train.imputed <- train.imputed[ ,!(names(train.imputed) %in% c("Exterior1st"))]
 test.imputed <- test.imputed[ ,!(names(test.imputed) %in% c("Exterior1st"))]
# Forward Selection
forward.fit <- lm(SalePrice~1, data = train.imputed)
step.f <- stepAIC(forward.fit, direction="forward", scope = formula(lm(SalePrice~., data = train.imputed)))

# Backward Selection
backward.fit <- lm(SalePrice~.,data = train.imputed)
step.b <- stepAIC(backward.fit, direction="backward")

# Stepwise Selection
stepwise.fit <- lm(SalePrice~.,data = train.imputed)
step.s <- stepAIC(stepwise.fit, direction="both")
# Selection results
step.f$call # Forward results
lm(formula = SalePrice ~ OverallQual + GrLivArea + Neighborhood + 
    RoofMatl + BsmtFinSF1 + BsmtQual + Condition2 + MSSubClass + 
    BsmtExposure + PoolQC + KitchenQual + OverallCond + GarageArea + 
    LotArea + YearBuilt + SaleCondition + TotalBsmtSF + SaleType + 
    Functional + MSZoning + Fireplaces + ExterQual + ScreenPorch + 
    YearRemodAdd + BldgType + GarageCars + MoSold + BedroomAbvGr + 
    EnclosedPorch + WoodDeckSF + MasVnrArea + TotRmsAbvGrd + 
    LandSlope + BsmtFinType1 + X3SsnPorch, data = train.imputed)
print("######################")
[1] "######################"
step.b$call # Backward results
lm(formula = SalePrice ~ MSSubClass + MSZoning + LotArea + LandContour + 
    LandSlope + Neighborhood + Condition1 + Condition2 + BldgType + 
    OverallQual + OverallCond + YearBuilt + YearRemodAdd + RoofMatl + 
    MasVnrArea + ExterQual + BsmtQual + BsmtExposure + BsmtFinType1 + 
    BsmtFinSF1 + BsmtFinSF2 + BsmtUnfSF + X1stFlrSF + X2ndFlrSF + 
    LowQualFinSF + BedroomAbvGr + KitchenQual + TotRmsAbvGrd + 
    Functional + Fireplaces + GarageCars + WoodDeckSF + EnclosedPorch + 
    X3SsnPorch + ScreenPorch + PoolQC + MoSold + SaleType + SaleCondition, 
    data = train.imputed)
print("######################")
[1] "######################"
step.s$call # Stepwise results
lm(formula = SalePrice ~ MSSubClass + MSZoning + LotArea + LandContour + 
    LandSlope + Neighborhood + Condition1 + Condition2 + BldgType + 
    OverallQual + OverallCond + YearBuilt + YearRemodAdd + RoofMatl + 
    MasVnrArea + ExterQual + BsmtQual + BsmtExposure + BsmtFinType1 + 
    BsmtFinSF1 + BsmtFinSF2 + BsmtUnfSF + X1stFlrSF + X2ndFlrSF + 
    LowQualFinSF + BedroomAbvGr + KitchenQual + TotRmsAbvGrd + 
    Functional + Fireplaces + GarageCars + WoodDeckSF + EnclosedPorch + 
    X3SsnPorch + ScreenPorch + PoolQC + MoSold + SaleType + SaleCondition, 
    data = train.imputed)

Model Building

Let’s predict the ‘SalePrice’, of each test observation, to be the average of all the train ‘SalePrice’ values, and use the resulting metrics as a benchmark against which all following model performances will be compared.

# Let's predict the average for each test observation and use it as a benchmark
sale.price.mean <- mean(train.imputed$SalePrice)
avg.vector <- rep(c(sale.price.mean), times = 212)
cat("Bias: ", mean(avg.vector-test.y))
Bias:  7299.447
cat("\nMaximum Deviation: ", max(avg.vector-test.y))

Maximum Deviation:  146626.7
cat("\nMean Absolute Deviation: ", mean(abs(avg.vector-test.y)))

Mean Absolute Deviation:  53682.19
cat("\nMean Square Error: ", mean((avg.vector-test.y)**2))

Mean Square Error:  4959006479
cat("\nRoot Mean Square Error: ", sqrt(mean((avg.vector-test.y)**2)))

Root Mean Square Error:  70420.21

Moving on to actually building the models.

# Linear model with forward selected features
forward.lm <- lm(formula(step.f$call), data = train.imputed)
forward.predictions <- predict(forward.lm, test.imputed)
prediction from a rank-deficient fit may be misleading
summary(forward.lm)$adj.r.squared
[1] 0.9130982
cat("Bias: ", mean(forward.predictions-test.y))
Bias:  2181.083
cat("\nMaximum Deviation: ", max(forward.predictions-test.y))

Maximum Deviation:  95389.27
cat("\nMean Absolute Deviation: ", mean(abs(forward.predictions-test.y)))

Mean Absolute Deviation:  17577.36
cat("\nMean Square Error: ", mean((forward.predictions-test.y)**2))

Mean Square Error:  685348829
cat("\nRoot Mean Square Error: ", sqrt(mean((forward.predictions-test.y)**2)))

Root Mean Square Error:  26179.17

Linear model with backward selected features

# Linear model with backward selected features
backward.lm <- lm(formula(step.b$call), data = train.imputed)
backward.predictions <- predict(backward.lm, test.imputed)
prediction from a rank-deficient fit may be misleading
summary(backward.lm)$adj.r.squared
[1] 0.9139194
cat("Bias: ", mean(backward.predictions-test.y))
Bias:  2065.778
cat("\nMaximum Deviation: ", max(backward.predictions-test.y))

Maximum Deviation:  101633.9
cat("\nMean Absolute Deviation: ", mean(abs(backward.predictions-test.y)))

Mean Absolute Deviation:  17648.72
cat("\nMean Square Error: ", mean((backward.predictions-test.y)**2))

Mean Square Error:  679802238
cat("\nRoot Mean Square Error: ", sqrt(mean((backward.predictions-test.y)**2)))

Root Mean Square Error:  26073.02

Linear model with stepwise selected features

# Linear model with stepwise selected features
stepwise.lm <- lm(formula(step.s$call), data = train.imputed)
stepwise.predictions <- predict(stepwise.lm, test.imputed)
prediction from a rank-deficient fit may be misleading
summary(stepwise.lm)$adj.r.squared
[1] 0.9139194
cat("Bias: ", mean(stepwise.predictions-test.y))
Bias:  2065.778
cat("\nMaximum Deviation: ", max(stepwise.predictions-test.y))

Maximum Deviation:  101633.9
cat("\nMean Absolute Deviation: ", mean(abs(stepwise.predictions-test.y)))

Mean Absolute Deviation:  17648.72
cat("\nMean Square Error: ", mean((stepwise.predictions-test.y)**2))

Mean Square Error:  679802238
cat("\nRoot Mean Square Error: ", sqrt(mean((stepwise.predictions-test.y)**2)))

Root Mean Square Error:  26073.02

LASSO Regression

lr.test <- as.matrix(data = test.imputed)
Error in as.matrix.data.frame(data = test.imputed) : 
  argument "x" is missing, with no default
fit.lasso <- glmnet(lr.train, train.imputed$SalePrice, alpha = 1, lambda = 7564.633)
lasso.predict <- predict(fit.lasso, lr.test)
cat("Bias: ", mean(lasso.predict-test.y))
Bias:  -786.1837
cat("\nMaximum Deviation: ", max(lasso.predict-test.y))

Maximum Deviation:  76364.84
cat("\nMean Absolute Deviation: ", mean(abs(lasso.predict-test.y)))

Mean Absolute Deviation:  21316.57
cat("\nMean Square Error: ", mean((lasso.predict-test.y)**2))

Mean Square Error:  902434514
cat("\nRoot Mean Square Error: ", sqrt(mean((lasso.predict-test.y)**2)))

Root Mean Square Error:  30040.55

Results

Now let’s check the diagnostics for the forward selected linear model as it seems to be the ‘best’ model because it contains the variable with the strongest correlation with the response variable.

plot(forward.lm)
not plotting observations with leverage one:
  6, 76, 330, 351, 357, 397, 454, 559, 592, 689, 739, 755, 807, 831

not plotting observations with leverage one:
  6, 76, 330, 351, 357, 397, 454, 559, 592, 689, 739, 755, 807, 831

plot(test.y,forward.predictions) +
abline(0,1, col = 'red');
integer(0)

Conclusions

LS0tCnRpdGxlOiAiR0xNIFByb2plY3QgKFByZWxpbWluYXJ5IEZpbmRpbmdzKSIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKSW4gdGhpcyBwcm9qZWN0LCB3ZSBhcmUgcHJlc2VudGVkIHdpdGggYSB0cmFpbiBhbmQgdGVzdCBzZXQgb2YgdGhlIEFtZXMgSW93YSBob3VzZSBkYXRhIGZyb20gS2FnZ2xlLiBUaGUgZ29hbCBpcyB0byBjcmVhdGUgYSBtb2RlbCB0aGF0IGlzIGNhcGFibGUgb2YgcHJlZGljdGluZyBob3VzZSBwcmljZXMgdXNpbmcgdGhlIGF2YWlsYWJsZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuIFRoZSBNdWxpdHBsZSBMaW5lYXIgUmVncmVzc2lvbiBtb2RlbCB3aWxsIGJlIGV4cGxvcmVkIGFuZCB1dGlsaXplZCBpbiB0aGlzIGFuYWx5c2lzLiAKCiogW0RhdGEgRXhwbG9yYXRpb24gJiBDbGVhbmluZ10oI2RhdGFleHBsb3JhdGlvbmFuZGNsZWFuaW5nKQoqIFtNb2RlbCBCdWlsZGluZ10oI21vZGVsYnVpbGRpbmcpCiogW1Jlc3VsdHNdKCNyZXN1bHRzKQoqIFtDb25jbHVzaW9uc10oI2NvbmNsdXNpb25zKQoKIyMgRGF0YSBFeHBsb3JhdGlvbiAmIENsZWFuaW5nIHsjZGF0YWV4cGxvcmF0aW9uYW5kY2xlYW5pbmd9CgpgYGB7cixtZXNzYWdlPUZBTFNFfQojIEltcG9ydGluZyB0aGUgbmVjZXNzYXJ5IGxpYnJhcmllcyBhbmQgcGFja2FnZXMKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobWljZSkKbGlicmFyeShjYVRvb2xzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoZ2xtbmV0KQpgYGAKCmBgYHtyfQojIEltcG9ydGluZyB0aGUgZGF0YQpkYXRhIDwtIHJlYWQuY3N2KCJ0cmFpbmluZy5jc3YiKQpgYGAKCmBgYHtyfQojIEV4cGxvcmluZyBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YQpjb2xuYW1lcyhkYXRhKQpzdHIoZGF0YSkKYGBgCgpJdCBsb29rcyBsaWtlIHdlIGhhdmUgMTA2MCBvYnNlcnZhdGlvbnMgYW5kIDgwIHZhcmlhYmxlcywgZXhjbHVkaW5nIHRhcmdldCB2YXJpYWJsZXMgJ1NhbGVQcmljZSc7IDIzIG5vbWluYWwsIDIzIG9yZGluYWwsIDE0IGRpc2NyZXRlLCBhbmQgMjAgY29udGludW91cy4gQWxzbywgaXQgaXMgY2xlYXIgdGhhdCB3ZSBhcmUgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIExldCdzIGRldGVybWluZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtaXNpbmcgZGF0YS4KCmBgYHtyfQojIENoZWNraW5nIHdoYXQgcHJjZW50IG9mIGFsbCB0aGUgZGF0YSBpcyBjb21wb3NlZCBvZiBtaXNzaW5nIHZhbHVlcyAKKHN1bShpcy5uYShkYXRhKSkvKG5yb3coZGF0YSkqbmNvbChkYXRhKSkpKjEwMApgYGAKCmBgYHtyfQojIExldCdzIGdldCBhIGJyZWFrIGRvd24gb2YgdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGNvbHVtbgoKbWlzc2luZy5jb2wgPC0gKGNvbFN1bXMoaXMubmEoZGF0YSkpW2NvbFN1bXMoaXMubmEoZGF0YSkpID4gMF0vbnJvdyhkYXRhKSkqMTAwCm1pc3NpbmcuY29sLm5hbWVzIDwtIHJvdy5uYW1lcyhkYXRhLmZyYW1lKG1pc3NpbmcuY29sKSkKbWlzc2luZy5jb2wucGVyY2VudCA8LSBkYXRhLmZyYW1lKG1pc3NpbmcuY29sKQptaXNzaW5nLmNvbC5wZXJjZW50IDwtIHNldG9yZGVyKGRhdGEuZnJhbWUobWlzc2luZy5jb2wpLCBtaXNzaW5nLmNvbCkKY29sbmFtZXMobWlzc2luZy5jb2wucGVyY2VudCkgPC0gJ3BlcmNlbnQubWlzc2luZycKbWlzc2luZy5jb2wucGVyY2VudApgYGAKCkl0IHNlZW1zIHRoYXQgJ0xvdEZyb250YWdlJywgJ0ZpcmVwbGFjZVF1JywgJ0ZlbmNlJywgJ0FsbGV5JywgJ01pc2NGZWF0dXJlJywgYW5kICdQb29sUUMnIHBvc3Nlc3Mgc2lnbmlmaWNhbnQgcHJvcG9ydGlvbnMgb2YgbWlzc2luZyBkYXRhLCBvdmVyIDUlLiBNb3JlIHByZXNzaW5nIGlzIHRoZSBmYWN0IHRoYXQgc29tZSBvZiB0aGUgdmFyaWFibGVzLCBzdWNoIGFzICdQb29sUUMnLCBjb250YWluIG5vIGRhdGEgYXQgYWxsIGFuZCBldmVuICdMb3RGcm9udGFnZScsIHdoaWNoIGhhcyB0aGUgbGVhc3QgbWlzc2luZyBkYXRhIG9mIHRoZSB2YXJpYWJsZXMgcHJldmlvdXNseSBtZW50aW9uZWQsIGlzIG1pc3NpbmcgYWxtb3N0IDIwJSBvZiBpdHMgZGF0YS4KCkFmdGVyIGNvbnN1bHRpbmcgdGhlIGRhdGEgZG9jdW1lbnRhdGlvbiwgaXQgc2VlbXMgdGhhdCBtYW55IG9mIHRoZSB2YXJpYWJsZXMgdXNlICdOQScgYXMgcmVzcHJlc2VudGluZyB0aGUgYWJzZW5jZSBvZiBhIGZ1dHVyZS4gRm9yIGV4YW1wbGUsICdGaXJlcGxhY2VRdScgcmVwcmVzZW50cyBmaXJlcGxhY2UgcXVhbGl0eSBvZiBhIGhvbWUuIE5vdywgYWx0aG91Z2ggdGhpcyB2YXJpYWJsZSBpcyBtaXNzaW5nIGFsbW9zdCBoYWxmIG9mIGl0J3MgZGF0YSwgdGhhdCBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCBhIG1pc3NpbmcgdmFsdWUgZGVub3RlcyB0aGUgYWJzZW5jZSBvZiBhIGZpcmVwbGFjZSBpbiB0aGUgaG9tZSBhbmQgbm90IGEgbWlzc2luZyBwaWVjZSBvZiBpbmZyb21hdGlvbiBvZiBkYXRhLiBTbywgYmVmb3JlIGNvbnRpbnVpbmcsIGxldCdzIGFkZCAnTkEnIGFzIGEgZmVhdHVyZSBsZXZlbCB3aGVyZSBpdCBpcyBhcHByb3ByaWF0ZSBiYXNlZCBvbiB0aGUgZGF0YSBkb2N1bWVudGF0aW9uLgoKYGBge3J9CiMgQWRkaW5nICdOQScgYXMgYSBmYWN0b3IgbGV2ZWwKCmNvbF9saXN0IDwtIGMoJ0FsbGV5JywgJ0JzbXRRdWFsJywgJ0JzbXRDb25kJywgJ0JzbXRFeHBvc3VyZScsICdCc210RmluVHlwZTEnLCAnQnNtdEZpblR5cGUyJywgJ0ZpcmVwbGFjZVF1JywgJ0dhcmFnZVR5cGUnLCAnR2FyYWdlRmluaXNoJywgJ0dhcmFnZVF1YWwnLCAnR2FyYWdlQ29uZCcsICdQb29sUUMnLCAnRmVuY2UnLCAnTWlzY0ZlYXR1cmUnKQoKZGF0YVtjb2xfbGlzdF0gPC0gbGFwcGx5KGRhdGFbY29sX2xpc3RdLCBhZGROQSkKYGBgCgpOb3csIGxldCdzIHNlZSBob3cgbXVjaCBtaXNzaW5nIGRhdGEgcmVtYWlucyBpbiB0aGUgZGF0YXNldC4KCmBgYHtyfQooc3VtKGlzLm5hKGRhdGEpKS8obnJvdyhkYXRhKSpuY29sKGRhdGEpKSkqMTAwCmBgYAoKYGBge3J9Cm1pc3NpbmcuY29sIDwtIChjb2xTdW1zKGlzLm5hKGRhdGEpKVtjb2xTdW1zKGlzLm5hKGRhdGEpKSA+IDBdL25yb3coZGF0YSkpKjEwMAptaXNzaW5nLmNvbC5uYW1lcyA8LSByb3cubmFtZXMoZGF0YS5mcmFtZShtaXNzaW5nLmNvbCkpCm1pc3NpbmcuY29sLnBlcmNlbnQgPC0gZGF0YS5mcmFtZShtaXNzaW5nLmNvbCkKbWlzc2luZy5jb2wucGVyY2VudCA8LSBzZXRvcmRlcihkYXRhLmZyYW1lKG1pc3NpbmcuY29sKSwgbWlzc2luZy5jb2wpCmNvbG5hbWVzKG1pc3NpbmcuY29sLnBlcmNlbnQpIDwtICdwZXJjZW50Lm1pc3NpbmcnCm1pc3NpbmcuY29sLnBlcmNlbnQKYGBgCgpgYGB7cn0Kc3RyKGRhdGFbLGMoJ01hc1ZuclR5cGUnLCdNYXNWbnJBcmVhJywnR2FyYWdlWXJCbHQnLCdMb3RGcm9udGFnZScpXSkKYGBgCgpXb3csIHdoYXQgYSBkaWZmZXJlbmNlLiBXZSBoYXZlIHJlZHVjZWQgdGhlIHRvdGFsIHByb3BvcnRpb24gb2YgbWlzc2luZyBkYXRhIGJ5IGFsbW9zdCBoYWxmLCB+NiUgdmVyc3VzIH4zJSwgYW5kIHdoZXJlIHdlIGhhZCAxOCB2YXJpYWJsZXMgd2l0aCBtaXNzaW5nIHZhbHVlcywgd2Ugbm93IG9ubHkgaGF2ZSA0LiAKCk5vdywgd2Ugd2lsbCB1c2UgdGhlICdtaWNlJyBwYWNrYWdlIHRvIGltcHV0ZSBhbnkgbWlzc2luZyB2YWx1ZXMgdGhhdCByZW1haW4uIEluIG9yZGVyIHRvIHByZXZlbnQgdGhlIGluamVjdGlvbiBvZiBhbnkgcGVyc29uYWwgYmlhcywgd2Ugd2lsbCBwcmVlbXB0aXZlbHkgc3BsaXQgdGhlIGRhdGEgaW50byBhIHRyYWluIGFuZCB0ZXN0IHNldCBmaXJzdCBhbmQgaW1wdXRlIHRoZSB0d28gZGF0YSBzZXRzIGluZGVwZW5kZW50bHkuIEJlZm9yZSB0aGF0LCBsZXQncyBzZWUgaWYgdGhlcmUgYXJlIGFueSBjb2x1bW5zIHdlIGNhbiwgYW5kIHNob3VsZCwgcmVtb3ZlIGFsbCB0b2dldGhlciBiZWNhdXNlIG9mIGEgbGFjayBvZiBhZGRpdGlvbmFsIHByZWRpY3RhYmlsaXR5IG9yIGluZm9ybWF0aW9uLgoKYGBge3J9CiMgQ2hlY2tpbmcgd2hpY2ggdmFyaWFibGVzIGhhdmUgb25seSBhIHNpbmdsZSB2YWx1ZSBmb3IgYWxsIG9ic2VydmF0aW9ucwpmb3IoeCBpbiBjb2xuYW1lcyhkYXRhKSl7CiAgaWYobGVuZ3RoKHVuaXF1ZShkYXRhWyx4XSkpID09IDEpewogICAgcHJpbnQoeCkKICB9Cn0KYGBgCgpXZSBzZWUgdGhhdCAnVXRpbGl0aWVzJyBnaXZlcyB1cyBubyBhZGRpdGlvbmFsIHByZWRpY2l0dmUgcG93ZXIgYXMgdGhlIG9ubHkgdmFsdWUgcHJlc2VudCBmb3IgYWxsIG9ic2VydmF0aW9ucyBpcyAnQWxsUHViJy4gRnVydGhlciBtb3JlLCB0aGUgJ0lkJyB2YXJpYWJsZSBzZXJ2ZXMgbm8gcHVycG9zZSBvdGhlciB0aGFuIGlkZW50aWZpY2F0aW9uIGFuZCBzaG91bGQgbyBiZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuIEZvciB0aGVzZSByZWFzb25zLCBib3RoIG9mIHRoZXNlIGNvbHVtbnMgd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIGRhdGFzZXQuCgpgYGB7cn0KIyBEcm9wcGluZyB0aGUgJ0lkJyBhbmQgJ1V0aWxpdGllcycgY29sdW1ucwpkYXRhIDwtIGRhdGFbLCAhKG5hbWVzKGRhdGEpICVpbiUgYygnSWQnLCdVdGlsaXRpZXMnKSldCmBgYAoKTGV0J3MgY29udGludWUgd2l0aCB0aGUgaW1wdXRhdGlvbiBvZiB0aGUgcmVhbWluaW5nIG1pc3NpbmcgdmFsdWVzLgoKYGBge3IscmVzdWx0cz0naGlkZSd9CiMgQ3JlYXRpbmcgYSB0cmFpbiBhbmQgdGVzdCBzcGxpdApzZXQuc2VlZCgyMDE5KQpzcGxpdCA8LSBzYW1wbGUuc3BsaXQoZGF0YSRTYWxlUHJpY2UsIFNwbGl0UmF0aW8gPSAwLjgpCnRyYWluIDwtIHN1YnNldChkYXRhLCBzcGxpdCA9PSBUUlVFKQp0ZXN0IDwtIHN1YnNldChkYXRhLCBzcGxpdCA9PSBGQUxTRSkKdGVzdC54IDwtIHRlc3RbICwhKG5hbWVzKHRlc3QpICVpbiUgYygiU2FsZVByaWNlIikpXQp0ZXN0LnkgPC0gdGVzdCRTYWxlUHJpY2UKCiMgSW1wdXRpbmcgbWlzc2luZyB2YWx1ZXMKdHJhaW4ubWlkcyA8LSBtaWNlKHRyYWluLCBtPTEsIG1ldGhvZD0nY2FydCcsIHNlZWQ9MjAxOSkKdGVzdC5taWRzIDwtIG1pY2UodGVzdC54LCBtPTEsIG1ldGhvZD0nY2FydCcsIHNlZWQ9MjAxOSkKCnRyYWluLmltcHV0ZWQgPC0gY29tcGxldGUodHJhaW4ubWlkcywxKQp0ZXN0LmltcHV0ZWQgPC0gY29tcGxldGUodGVzdC5taWRzLDEpCmBgYCAKCmBgYHtyfQojIENvbmZpcm1pbmcgdGhhdCB0aGVyZSBhcmUgbm8gbW9yZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YXNldAp0cmFpbi5pbXAubWlzcyA8LSAoc3VtKGlzLm5hKHRyYWluLmltcHV0ZWQpKS8obnJvdyh0cmFpbi5pbXB1dGVkKSpuY29sKHRyYWluLmltcHV0ZWQpKSkqMTAwCnRlc3QuaW1wLm1pc3MgPC0gKHN1bShpcy5uYSh0ZXN0LmltcHV0ZWQpKS8obnJvdyh0ZXN0LmltcHV0ZWQpKm5jb2wodGVzdC5pbXB1dGVkKSkpKjEwMApwcmludCgiUGVyY2VudCBtaXNzaW5nIGRhdGEgaW4gaW1wdXRlZCB0cmFpbiBzZXQ6ICIpCnByaW50KHRyYWluLmltcC5taXNzKQpwcmludCgiUGVyY2VudCBtaXNzaW5nIGRhdGEgaW4gaW1wdXRlZCB0ZXN0IHNldDogIikKcHJpbnQodGVzdC5pbXAubWlzcykKYGBgCgpOb3csIGxldCdzIGNvbnRpbnVlIGJ5IGV4cGxvcmluZyB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCAKCmBgYHtyfQojIENyZWF0aW5nIGEgZGF0YWZyYW1lIG9mIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIFNhbGVQcmljZSBhbmQgbnVtZXJpYyB2YXJzLgp0cmFpbi5udW1lcmljIDwtIGRwbHlyOjpzZWxlY3RfaWYoZGF0YSwgaXMubnVtZXJpYykKY29yci5tYXRyaXggPC0gY29yKHRyYWluLm51bWVyaWMsIHVzZSA9ICdwYWlyd2lzZS5jb21wbGV0ZS5vYnMnKQpjb3JyLmRmIDwtIGRhdGEuZnJhbWUocm93PXJvd25hbWVzKGNvcnIubWF0cml4KVtyb3coY29yci5tYXRyaXgpW3VwcGVyLnRyaShjb3JyLm1hdHJpeCldXSwgY29sPWNvbG5hbWVzKGNvcnIubWF0cml4KVtjb2woY29yci5tYXRyaXgpW3VwcGVyLnRyaShjb3JyLm1hdHJpeCldXSxjb3JyPWNvcnIubWF0cml4W3VwcGVyLnRyaShjb3JyLm1hdHJpeCldKQpjb3JyLmRmIDwtIGNvcnIuZGZbb3JkZXIoY29yci5kZiQnY29ycicpLF0Kc2FsZS5wcmljZS5jb3JyIDwtIGNvcnIuZGZbY29yci5kZiRjb2wgPT0gJ1NhbGVQcmljZScsXQpoZWFkKHNhbGUucHJpY2UuY29ycikKdGFpbChzYWxlLnByaWNlLmNvcnIpCmBgYAoKSXQgbG9va3MgbGlrZSAnT3ZlcmFsbFF1YWwnLCAnR3JMaXZBcmVhJywgJ0dhcmFnZUNhcnMnLCBhbmQgJ0dhcmFnZUFyZWEnIGFsbCBoYXZlICJzaWduaWZpY2FudCIgY29ycmVsYXRpb25zIHdpdGggdGhlIHJlc3BvbnNlIHZhcmFpYmxlLCBTYWxlUHJpY2UuIEJ5ICJzaWduaWZpY2FudCIsIHdlIG1lYW4gYSBjb3JyZWxhdGlvbiBhYm92ZSAwLjYuIFRoZXNlIG1heSBiZSBpbXBvcnRhbnQgcHJlZGljdG9ycyBvZiBTYWxlUHJpY2UuIExldCB1cyBhbHNvIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIFNhbGVQcmljZS4KCmBgYHtyfQojIFBsb3Qgb2YgU2FsZVByaWNlIGRpc3RyaWJ1dGlvbgpzcC5wbG90IDwtIGdncGxvdChkYXRhID0gdHJhaW4uaW1wdXRlZCwgYWVzKHg9dHJhaW4uaW1wdXRlZCRTYWxlUHJpY2UpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCwgY29sb3IgPSAnd2hpdGUnLCBmaWxsID0gJ3NhbG1vbicpCgpsb2cuc3AucGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IHRyYWluLmltcHV0ZWQsIGFlcyh4PWxvZyh0cmFpbi5pbXB1dGVkJFNhbGVQcmljZSkpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCwgY29sb3IgPSAnd2hpdGUnLCBmaWxsID0gJ3NhbG1vbicpIAoKc3AucGxvdApsb2cuc3AucGxvdApgYGAKClRoZSBkaXN0cmlidXRpb24gZG9lcyBzZWVtIHRvIGJlY29tZSBtb3JlIG5vcm1hbCB3aGVuIGEgbG9uZyB0cmFuc2Zvcm0gaXMgYXBwbGllZC4gVGhpcyBtYXkgaW1wcm92ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBsaW5lYXIgbW9kZWwuIFdpbGwgbmVlZCB0byBjb21lIGJhY2sgbGF0ZXIgYW5kIGRldGVybWluZSBpZiB0aGUgbG9nIHRyYW5kZm9ybSByZWFsbHkgaGFzIGEgYmVuZWZpY2lhbCBlZmZlY3QuIAoKTGV0J3Mgbm93IHV0aWxpemUgbWV0aG9kaWNhbCBhcHByb2FjaGVzIHRvIHNlbGVjdCB0aGUgZmVhdHVyZXMgdGhhdCB3aWxsIHByb3ZpZGUgdXMgd2l0aCB0aGUgbW9zdCBwcmVkaWN0YWJpbGl0eSBpbiBvdXIgbW9kZWwuIFdlIHdpbGwgZXhwbG9yZSBmb3J3YXJkLCBiYWNrd2FyZCwgYW5kIHN0ZXB3aXNlIHNlbGVjdGlvbiBhcyB3ZWxsIGFzIExhc3NvIFJlZ3Jlc3Npb24uCgpgYGB7cn0KIyBTb21lIGxldmVscyBwcmVzZW50IGluIHRlc3Qgc2V0IHRoYXQgYXJlIG5vdCBhY2NvdW50ZWQgZm9yIGluIHRyYWluIHNldAogdHJhaW4uaW1wdXRlZCA8LSB0cmFpbi5pbXB1dGVkWyAsIShuYW1lcyh0cmFpbi5pbXB1dGVkKSAlaW4lIGMoIkV4dGVyaW9yMXN0IikpXQogdGVzdC5pbXB1dGVkIDwtIHRlc3QuaW1wdXRlZFsgLCEobmFtZXModGVzdC5pbXB1dGVkKSAlaW4lIGMoIkV4dGVyaW9yMXN0IikpXQpgYGAKCmBgYHtyfQojIEZvcndhcmQgU2VsZWN0aW9uCmZvcndhcmQuZml0IDwtIGxtKFNhbGVQcmljZX4xLCBkYXRhID0gdHJhaW4uaW1wdXRlZCkKc3RlcC5mIDwtIHN0ZXBBSUMoZm9yd2FyZC5maXQsIGRpcmVjdGlvbj0iZm9yd2FyZCIsIHNjb3BlID0gZm9ybXVsYShsbShTYWxlUHJpY2V+LiwgZGF0YSA9IHRyYWluLmltcHV0ZWQpKSkKCiMgQmFja3dhcmQgU2VsZWN0aW9uCmJhY2t3YXJkLmZpdCA8LSBsbShTYWxlUHJpY2V+LixkYXRhID0gdHJhaW4uaW1wdXRlZCkKc3RlcC5iIDwtIHN0ZXBBSUMoYmFja3dhcmQuZml0LCBkaXJlY3Rpb249ImJhY2t3YXJkIikKCiMgU3RlcHdpc2UgU2VsZWN0aW9uCnN0ZXB3aXNlLmZpdCA8LSBsbShTYWxlUHJpY2V+LixkYXRhID0gdHJhaW4uaW1wdXRlZCkKc3RlcC5zIDwtIHN0ZXBBSUMoc3RlcHdpc2UuZml0LCBkaXJlY3Rpb249ImJvdGgiKQpgYGAKCmBgYHtyfQojIFNlbGVjdGlvbiByZXN1bHRzCnN0ZXAuZiRjYWxsICMgRm9yd2FyZCByZXN1bHRzCnByaW50KCIjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIikKc3RlcC5iJGNhbGwgIyBCYWNrd2FyZCByZXN1bHRzCnByaW50KCIjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIikKc3RlcC5zJGNhbGwgIyBTdGVwd2lzZSByZXN1bHRzCmBgYAoKCiMjIE1vZGVsIEJ1aWxkaW5nIHsjbW9kZWxidWlsZGluZ30KCkxldCdzIHByZWRpY3QgdGhlICdTYWxlUHJpY2UnLCBvZiBlYWNoIHRlc3Qgb2JzZXJ2YXRpb24sIHRvIGJlIHRoZSBhdmVyYWdlIG9mIGFsbCB0aGUgdHJhaW4gJ1NhbGVQcmljZScgdmFsdWVzLCBhbmQgdXNlIHRoZSByZXN1bHRpbmcgbWV0cmljcyBhcyBhIGJlbmNobWFyayBhZ2FpbnN0IHdoaWNoIGFsbCBmb2xsb3dpbmcgbW9kZWwgcGVyZm9ybWFuY2VzIHdpbGwgYmUgY29tcGFyZWQuCgpgYGB7cn0KIyBMZXQncyBwcmVkaWN0IHRoZSBhdmVyYWdlIGZvciBlYWNoIHRlc3Qgb2JzZXJ2YXRpb24gYW5kIHVzZSBpdCBhcyBhIGJlbmNobWFyawpzYWxlLnByaWNlLm1lYW4gPC0gbWVhbih0cmFpbi5pbXB1dGVkJFNhbGVQcmljZSkKYXZnLnZlY3RvciA8LSByZXAoYyhzYWxlLnByaWNlLm1lYW4pLCB0aW1lcyA9IDIxMikKCmNhdCgiQmlhczogIiwgbWVhbihhdmcudmVjdG9yLXRlc3QueSkpCmNhdCgiXG5NYXhpbXVtIERldmlhdGlvbjogIiwgbWF4KGF2Zy52ZWN0b3ItdGVzdC55KSkKY2F0KCJcbk1lYW4gQWJzb2x1dGUgRGV2aWF0aW9uOiAiLCBtZWFuKGFicyhhdmcudmVjdG9yLXRlc3QueSkpKQpjYXQoIlxuTWVhbiBTcXVhcmUgRXJyb3I6ICIsIG1lYW4oKGF2Zy52ZWN0b3ItdGVzdC55KSoqMikpCmNhdCgiXG5Sb290IE1lYW4gU3F1YXJlIEVycm9yOiAiLCBzcXJ0KG1lYW4oKGF2Zy52ZWN0b3ItdGVzdC55KSoqMikpKQpgYGAKCk1vdmluZyBvbiB0byBhY3R1YWxseSBidWlsZGluZyB0aGUgbW9kZWxzLgoKYGBge3J9CiMgTGluZWFyIG1vZGVsIHdpdGggZm9yd2FyZCBzZWxlY3RlZCBmZWF0dXJlcwpmb3J3YXJkLmxtIDwtIGxtKGZvcm11bGEoc3RlcC5mJGNhbGwpLCBkYXRhID0gdHJhaW4uaW1wdXRlZCkKZm9yd2FyZC5wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGZvcndhcmQubG0sIHRlc3QuaW1wdXRlZCkKCnN1bW1hcnkoZm9yd2FyZC5sbSkkYWRqLnIuc3F1YXJlZApjYXQoIkJpYXM6ICIsIG1lYW4oZm9yd2FyZC5wcmVkaWN0aW9ucy10ZXN0LnkpKQpjYXQoIlxuTWF4aW11bSBEZXZpYXRpb246ICIsIG1heChmb3J3YXJkLnByZWRpY3Rpb25zLXRlc3QueSkpCmNhdCgiXG5NZWFuIEFic29sdXRlIERldmlhdGlvbjogIiwgbWVhbihhYnMoZm9yd2FyZC5wcmVkaWN0aW9ucy10ZXN0LnkpKSkKY2F0KCJcbk1lYW4gU3F1YXJlIEVycm9yOiAiLCBtZWFuKChmb3J3YXJkLnByZWRpY3Rpb25zLXRlc3QueSkqKjIpKQpjYXQoIlxuUm9vdCBNZWFuIFNxdWFyZSBFcnJvcjogIiwgc3FydChtZWFuKChmb3J3YXJkLnByZWRpY3Rpb25zLXRlc3QueSkqKjIpKSkKYGBgCgpMaW5lYXIgbW9kZWwgd2l0aCBiYWNrd2FyZCBzZWxlY3RlZCBmZWF0dXJlcwoKYGBge3J9CiMgTGluZWFyIG1vZGVsIHdpdGggYmFja3dhcmQgc2VsZWN0ZWQgZmVhdHVyZXMKYmFja3dhcmQubG0gPC0gbG0oZm9ybXVsYShzdGVwLmIkY2FsbCksIGRhdGEgPSB0cmFpbi5pbXB1dGVkKQpiYWNrd2FyZC5wcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGJhY2t3YXJkLmxtLCB0ZXN0LmltcHV0ZWQpCgpzdW1tYXJ5KGJhY2t3YXJkLmxtKSRhZGouci5zcXVhcmVkCmNhdCgiQmlhczogIiwgbWVhbihiYWNrd2FyZC5wcmVkaWN0aW9ucy10ZXN0LnkpKQpjYXQoIlxuTWF4aW11bSBEZXZpYXRpb246ICIsIG1heChiYWNrd2FyZC5wcmVkaWN0aW9ucy10ZXN0LnkpKQpjYXQoIlxuTWVhbiBBYnNvbHV0ZSBEZXZpYXRpb246ICIsIG1lYW4oYWJzKGJhY2t3YXJkLnByZWRpY3Rpb25zLXRlc3QueSkpKQpjYXQoIlxuTWVhbiBTcXVhcmUgRXJyb3I6ICIsIG1lYW4oKGJhY2t3YXJkLnByZWRpY3Rpb25zLXRlc3QueSkqKjIpKQpjYXQoIlxuUm9vdCBNZWFuIFNxdWFyZSBFcnJvcjogIiwgc3FydChtZWFuKChiYWNrd2FyZC5wcmVkaWN0aW9ucy10ZXN0LnkpKioyKSkpCmBgYAoKTGluZWFyIG1vZGVsIHdpdGggc3RlcHdpc2Ugc2VsZWN0ZWQgZmVhdHVyZXMKCmBgYHtyfQojIExpbmVhciBtb2RlbCB3aXRoIHN0ZXB3aXNlIHNlbGVjdGVkIGZlYXR1cmVzCnN0ZXB3aXNlLmxtIDwtIGxtKGZvcm11bGEoc3RlcC5zJGNhbGwpLCBkYXRhID0gdHJhaW4uaW1wdXRlZCkKc3RlcHdpc2UucHJlZGljdGlvbnMgPC0gcHJlZGljdChzdGVwd2lzZS5sbSwgdGVzdC5pbXB1dGVkKQoKc3VtbWFyeShzdGVwd2lzZS5sbSkkYWRqLnIuc3F1YXJlZApjYXQoIkJpYXM6ICIsIG1lYW4oc3RlcHdpc2UucHJlZGljdGlvbnMtdGVzdC55KSkKY2F0KCJcbk1heGltdW0gRGV2aWF0aW9uOiAiLCBtYXgoc3RlcHdpc2UucHJlZGljdGlvbnMtdGVzdC55KSkKY2F0KCJcbk1lYW4gQWJzb2x1dGUgRGV2aWF0aW9uOiAiLCBtZWFuKGFicyhzdGVwd2lzZS5wcmVkaWN0aW9ucy10ZXN0LnkpKSkKY2F0KCJcbk1lYW4gU3F1YXJlIEVycm9yOiAiLCBtZWFuKChzdGVwd2lzZS5wcmVkaWN0aW9ucy10ZXN0LnkpKioyKSkKY2F0KCJcblJvb3QgTWVhbiBTcXVhcmUgRXJyb3I6ICIsIHNxcnQobWVhbigoc3RlcHdpc2UucHJlZGljdGlvbnMtdGVzdC55KSoqMikpKQpgYGAKCkxBU1NPIFJlZ3Jlc3Npb24KCmBgYHtyfQojIExBU1NPIFJlZ3Jlc3Npb24KbHIudHJhaW4gPC0gbW9kZWwubWF0cml4KFNhbGVQcmljZX4uLCBkYXRhID0gdHJhaW4uaW1wdXRlZCkKdGVzdC5pbXB1dGVkJFNhbGVQcmljZSA8LSB0ZXN0LnkKbHIudGVzdCA8LSBtb2RlbC5tYXRyaXgoU2FsZVByaWNlfi4sIGRhdGEgPSB0ZXN0LmltcHV0ZWQpCmdyaWQgPC0gMTBec2VxKDQsIC0yLCBsZW5ndGggPSAxMDApCgpzZXQuc2VlZCAoMjAxOSkKY3YubGFzc28gPC0gY3YuZ2xtbmV0KGxyLnRyYWluLCB0cmFpbiRTYWxlUHJpY2UsIGFscGhhID0gMSwgbGFtYmRhID0gZ3JpZCkKYmVzdGxhbSA8LSBjdi5sYXNzbyRsYW1iZGEubWluCmJlc3RsYW0gIyBCZXN0IGxhbWJkYSA9IDc1NjQuNjMzCmBgYAoKYGBge3J9CmZpdC5sYXNzbyA8LSBnbG1uZXQobHIudHJhaW4sIHRyYWluLmltcHV0ZWQkU2FsZVByaWNlLCBhbHBoYSA9IDEsIGxhbWJkYSA9IDc1NjQuNjMzKQpsYXNzby5wcmVkaWN0IDwtIHByZWRpY3QoZml0Lmxhc3NvLCBsci50ZXN0KQoKY2F0KCJCaWFzOiAiLCBtZWFuKGxhc3NvLnByZWRpY3QtdGVzdC55KSkKY2F0KCJcbk1heGltdW0gRGV2aWF0aW9uOiAiLCBtYXgobGFzc28ucHJlZGljdC10ZXN0LnkpKQpjYXQoIlxuTWVhbiBBYnNvbHV0ZSBEZXZpYXRpb246ICIsIG1lYW4oYWJzKGxhc3NvLnByZWRpY3QtdGVzdC55KSkpCmNhdCgiXG5NZWFuIFNxdWFyZSBFcnJvcjogIiwgbWVhbigobGFzc28ucHJlZGljdC10ZXN0LnkpKioyKSkKY2F0KCJcblJvb3QgTWVhbiBTcXVhcmUgRXJyb3I6ICIsIHNxcnQobWVhbigobGFzc28ucHJlZGljdC10ZXN0LnkpKioyKSkpCmBgYAoKCiMjIFJlc3VsdHMgeyNyZXN1bHRzfQoKTm93IGxldCdzIGNoZWNrIHRoZSBkaWFnbm9zdGljcyBmb3IgdGhlIGZvcndhcmQgc2VsZWN0ZWQgbGluZWFyIG1vZGVsIGFzIGl0IHNlZW1zIHRvIGJlIHRoZSAnYmVzdCcgbW9kZWwgYmVjYXVzZSBpdCBjb250YWlucyB0aGUgdmFyaWFibGUgd2l0aCB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiAKCmBgYHtyfQojUmVzaWR1YWwgUGxvdHMKcGxvdChmb3J3YXJkLmxtKQpgYGAKCmBgYHtyfQojIFBsb3Qgb2YgcHJlZGljdGVkIHZlcnN1cyB0cnVlIHJlcG9uc2UgdGVzdCB2YWx1ZXMKcGxvdCh0ZXN0LnksZm9yd2FyZC5wcmVkaWN0aW9ucykgKwphYmxpbmUoMCwxLCBjb2wgPSAncmVkJykKYGBgCgojIyBDb25jbHVzaW9ucyB7I2NvbmNsdXNpb25zfQoKLi4uCgo=