Predicting Real Estate Value in Ames, Iowa
There are many factors that determine the worth of a piece of real estate. For this project, I used The Ames Housing dataset, compiled by Dean De Cock, to predict home prices in Ames, Iowa. Real estate has always been an interest of mine, so I had some background knowledge coming into this project. I actually interned for a real estate company in the summer of 2020 and very much enjoyed it.
My goal was to create different models, find the model that best predicts housing prices, and then analyze the best model to gain insight into what features are important in changing real estate value most significantly.
I started by dealing with the null values in the dataset:
- A lot of the null values in the dataset were easy to impute because they were missing due to the house lacking that feature. For example, I changed the null values in “BsmtQual” to “NoBasement” because the null values meant that the house didn’t contain a basement.
- There were some features containing very few observations with null values, and the reason why wasn’t clear, so for those I imputed the mode or mean.
Feature Engineering: Removing Features
There were certain features that I took out right away, as they wouldn’t be fair to use for modeling. Some features, like “Street, “Utilities”, “RoofMatl”, “Heating”, and “LowQualFinSF”, were taken out right away, because at least 99% of observations had the same values for these categories.
I combined “Condition1” and “Condition2” into one feature called “BothConditions”, for a more accurate model. It wouldn’t make sense to keep them separate because some houses could have a certain condition listed under the feature “Condition1” and another house could have that same condition listed under “Condition2”, so it wouldn’t make sense for the model to split the conditions into two separate features. The categories with high enough amounts of observations to be useful for modeling all have similar distributions. So, I did not use this feature. The rest of the categories didn't have a significant enough number of samples for purposes of modelling/analysis.
I also did not use other features for different reasons:
- “LotFrontage”, i.e. linear feet of street connected to property- too many missing values
- “Alley”, i.e. type of alley access to property (gravel v paved v no alley access)- based on common knowledge it’s not a significant feature for real estate price, and far majority of the houses have no alley access
- “YearBuilt”- It’s a redundant feature because the feature
- “YearRemodAdd” lists the year the house was built if it wasn’t remodelled. What is significant to the real estate value is the last time the house was remodelled, so we only need the “YearRemodAdd” column.
- “MiscFeature”- Far majority of the features don’t have a value in this category, and the most common one, shed, doesn’t correlate with a certain sale price anyway. So there’s no use in using this feature to determine sale price.
- “LandSlope”, “BsmtFinType1”, “BsmtFinType2”, “ExterCond”, “Electrical”, “MiscVal”, “MoSold”,”YrSold”,“Functional”, “GarageQual”, “GarageCond”- I plotted catplots and they showed to be not useful in determining sale price. In “ExterCond” one category seems to have a lower mean sale price, but this is not a valid observation to take into account because there aren’t enough observations in this category for a valid analysis.
Here are some of the catplots:
- “BsmtFinSF1”, ”BsmtFinSF2”, “1stFlrSF”, “2ndFlrSF”- I didn’t use these features because we already have a different feature that tells us the total square footage of the basement, “TotalBsmtSF”, and a different features that tells us the total SF above the basement, “GrLivArea”.
- “GarageYrBlt”, GarageCars”, “TotRmsAbvGrd”- Based on common sense I assumed that “GarageYrBlt” would be highly correlated with “YearRemodAdd”, as “YearRemodAdd” is the year the house was remodeled if it was; otherwise it is the year the house was built. Likewise, based on common sense, I assumed that “GarageCars”, i.e. size of garage in car capacity, would be highly correlated with “GarageArea”, i.e. size of garage in square feet. Additionally I assumed that “TotRmsAbvGrd”, i.e. total rooms above grade (excluding bathrooms)” would be highly correlated with “BedroomAbvGr”, i.e. total bedrooms above grade. I confirmed my assumptions with scatterplots, and did not use the features “GarageYrBlt”, “GarageCars”, and “TotRmsAbvGrd” due to the idea of multicollinearity.
- “Exterior2nd”- >95% of the observations had the same exterior material listed in both “Exterior1st” and “Exterior2nd”, so I just used “Exterior1st” to avoid redundancy. I didn’t combine the two of them into one feature because it would create too many categories each consisting of very few observations, and hence complicate the modelling. The catplot showed Exterior1st as a useful feature in determining pricing.
Feature Engineering: Keeping Features
Certain features were obvious to keep, by definition of what real estate pricing is about. For example, I knew the feature “Neighborhood” would be important, as a famous rule of real estate is “location, location, location”, i.e. location is an extremely critical factor in pricing. Another example is that I knew that “LotArea”, or the size of the property, would be important, as the square footage of a property has a direct impact on real estate price. I also did research, and learned the importance of certain features, like masonry veneer type, in determining real estate value. For features I wasn’t sure about I created scatterplots or catplots to confirm I should use them.
I was concerned that perhaps the features “OverallQual” and “OverallCond”, and likewise the features “BsmtQual” and “BsmtCond” would be highly correlated, so I created a scatterplots to check if this was the case. It was not the case, so I used all these features for modelling. Catplots were used to confirm that they were all useful in determining the sale price.
Feature Engineering: Replacing Features & Creating New Features
I wanted to approach feature engineering and selection from the standpoint of someone with a real understanding of real estate. So I decided to reduce dimensionality and convert features into new ones that really have an effect on real estate price, based on what I know about real estate and based on research.
New features I created:
- “PropOfBsmtFinished” i.e. proportion of the basement that is finished- I used the features “BsmtUnfSF” and “TotalBsmtSF” to create this new feature, and then did not use “BsmtUnfSF” for modelling. I figured that a feature that tells us what proportion of the basement is finished is much more useful in determining sale price than a feature of the unfinished square footage of the basement. My thinking was that if a home with a basement of size 900 square feet has 400 square feet of unfinished space, the unfinished square footage of the basement isn’t as significant in affecting sale price as a basement of size 400 square feet with 400 square feet of unfinished space. In one house you have majority of the basement already finished, and in the other the entire basement isn’t done. Therefore, it makes much more sense to replace the feature “BsmtUnfSF” with “PropOfBsmtFinished”. The formula I used to create “PropOfBsmtFinished” =1-(“BsmtUnfSF”/'TotalBsmtSF')
- “NumFullBath”- I took the sum of “BsmtFullBath” and “FullBath” to create this feature and use it for the modelling instead of them. Each full bathroom adds a certain amount of real estate value so I figured it would make sense to have them together in one feature.
- “NumHalfBath”- I took the sum of “BsmtHalfBath” and “HalfBath” to create this feature and use it for the modelling instead of them. Each half bathroom adds a certain amount of real estate value (different from full bathroom) so I figured it would make sense to have them together in one feature.
- “QuantAndQualFP” i.e. quantity and quality of fireplaces- I combined “Fireplaces”, i.e. fireplace quantity, and “FireplaceQu”, i.e. fireplace quality, into one feature. I did this because I thought having these features separate was redundant, as most houses have zero fireplaces and hence automatically no quality factor. In “QuantAndQualFP” there were some categories with extremely few observations, so I put them into categories that seemed to be the most similar to them after plotting a catplot versus the sale price.
- “TotalPorchDeckSF”- I took the sum of all the porch/deck square footage, which is the sum of the features “WoodDeckSF”, “OpenPorchSF”, “EnclosedPorch”, “3SsnPorch”, and “ScreenPorch”. I did this to reduce dimensionality and because it makes sense to have this as one feature in determining real estate value.
- “HasPool”- Instead of having the features “PoolQC”, i.e. pool quality, and “PoolArea”, i.e size of pool in square feet, I made one feature, called “HasPool”, indicating whether or not the property has a pool. I did this because less than 1% of the data has a pool, so there weren’t enough observations for the model to accurately predict how much the value goes up based on pool quality and pool size. Based on knowledge of real estate, I knew that having a pool significantly increases the value of a home, so I wanted to have one feature that indicates whether a home has a pool or not.
Adjusting Skewed Distributions for the Linear Models
Linear models work best when the features distributions are normally distributed. The distribution of sale price was right skewed, so I took the log and changed “SalePrice” to “LogSalePrice”. I also adjusted other distributions. I took the square root of “MasVnrArea”, “TotalBsmtSF”, and “GarageArea”; and I took the log of “LotArea” and “GrLivArea”.
I removed two outliers based on “GrLivArea” V. “SalePrice”. I removed the two observations closest to the bottom right corner of the plot.
Dummifying Features for the Linear Models
For the linear modeling I dummified the categorical features. For the feature “RoofStyle”, I dropped all of the dummified columns but one, that being “RoofStyle_Hip”, because the only 2 categories with enough observations for valid analysis were Gable and Hip.
In the end I also dropped “BsmtQual_NoBasement”, “BsmtCond_NoBasement”, and “BsmtExposure_NoBasement”. I did so because I observed that they had a significant impact on the model when I checked the statistics on the linear model, thereby negatively impacting the model since we already had the feature “'SqrtTotalBsmtSF”, i.e. the basement square footage, which for a value of zero is equivalent to saying no basement. So, I wanted to avoid multicollinearity, and removing these features did in fact improve the model.
Ordinal Encoding for the Non-linear Models
For the non linear models, I used ordinal encoding instead of dummification. This led to better model performance.
- Tree-based: After performing a grid search for best parameters, Train Score: .952, Test Score: .781
- Random forest: After performing a grid search for best parameters, Train Score: .909, Test Score: .864
- Gradient boosting: After performing a grid search, Train Score: .961, Test Score: .908
- Support vector machine: Train Score: .938, Test Score: .879
- Non-penalized linear model: Train Score: .943, Test Score: .899
- Ridge linear regression model: After checking for optimal lambda, Train Score: .937, Test Score: .910
- Lasso linear regression model: After checking for optimal lambda, Train Score: .942, Test Score: .906
It turns out that the ridge regression model was the best model and gave the best score on Kaggle. The fact that a linear regression model was the best led me to believe that the way I approached feature engineering made sense. The best value for lambda was 4.
Analysis of Results
These were the features with the biggest impact on the model and their coefficients:
It turns out that the square footage of the above ground living area, the neighborhood, the zoning classification, the sale type, the basement quality, the exterior material, the building class/type of dwelling (including details such as age, presence of attic, how many stories, finished/unfinished, levels and level style), the square footage of the lot area, the presence of central air conditioning, and the house style (how many stories and whether the 2nd floor is finished) are key factors that effect real estate value.
Some of the key factors that significantly influence real estate value can't be changed by a home owner. However, some can, and it may be worth it for a home owner to take these factors into account and invest in their home.
There are two types of upgrades that are smart for a home owner to invest in:
- An upgrade that they want for themselves for personal benefit, and the upgrade is an investment that doesn't depreciate quickly.
- An upgrade that they don't necessarily want, but it is an investment that will end up increasing the value of the home by more than they spend on the upgrade. Although they don't care about the upgrade, it is a way to gain profit.
If one of the two above conditions apply, then based on the features that the model shows are most significant, it would be smartest for a home owner to invest in any of the following: Upgrading the exterior material, upgrading the quality of the basement, expanding the square footage of the home, finishing the 2nd floor if they have one, and installing central air conditioning if it is something the home lacks.