House Price Prediction

XGBoostLassoStreamlitDocker

The problem

Most feature importance analysis stops at ranking, a plot shows which features score highest and the analysis ends there. This project asked a sharper question, using a real academic study with two other researchers: does a feature's importance ranking actually reflect how much the model depends on it? Neighborhood was the test case, since it plausibly encodes location factors, school quality, amenities, that no other single column captures.

My approach

  • Method: rather than reading feature importance plots at face value, features were removed one at a time and each model retrained from scratch, comparing performance with and without each feature to measure actual impact, not just importance score.
  • Finding: in XGBoost, Neighborhood ranked only 5th by raw feature importance, behind OverallQual. But removing Neighborhood entirely hurt performance more than removing OverallQual did. A feature can matter more than its own importance ranking suggests, and the only way to know is to actually remove it and check.
  • Cross-validation across model types: the same ablation was repeated with Lasso, a linear model with a completely different mechanism from XGBoost's trees. Removing Neighborhood collapsed Lasso's R² from 0.90 to 0.096, a far steeper drop than in XGBoost, confirming the effect was not an artifact of one model's quirks.
  • Trade-off tested and reported honestly: PCA compressed the feature set down to 2 components capturing over 95% of total variance, and Neighborhood was confirmed as the dominant driver of the first component. But PCA-transformed models performed worse across the board, R² dropped from 0.90 to roughly 0.54, showing that capturing most of the variance is not the same as preserving what a model actually needs to predict well.
  • Research-to-production trade-off: the coursework version used target encoding for Neighborhood's 25 categories, since it preserves more signal than one-hot encoding. The deployed production pipeline switched to one-hot encoding instead, specifically to avoid target leakage and to handle neighborhoods the model has never seen at inference time, prioritizing reliability over raw accuracy.
  • Outlier handling: rather than dropping any row containing an outlier, removal was applied feature-by-feature and only where extreme values exceeded roughly 3% of that feature, preserving genuine rare high-value properties instead of treating them as noise.

Results

  • Removing Neighborhood increased RMSE by over 7% in XGBoost, a larger drop than removing OverallQual, the model's top-ranked feature by raw importance.
  • In Lasso, removing Neighborhood collapsed R² from 0.90 to 0.096, the single largest performance loss across every ablation tested.
  • PCA-compressed features captured over 95% of variance but reduced R² to roughly 0.54 across both model types, confirming that variance captured and predictive power retained are not the same thing.

What I would do differently

The deployed API accepts 8 user-facing inputs and silently fills the remaining features with fixed defaults drawn from typical training values. That is a reasonable simplification for a portfolio demo, but a property far outside those default assumptions, an unusually large basement or an uncommon exterior material, would get a prediction quietly built on defaults that do not describe it. A more honest version would expose which inputs are driving a given prediction versus which ones fell back to a default, rather than returning a single number with no indication of how much of the input was actually the user's.