saveRDS(data, "processed_data.rds") # Lưu dữ liệu vào file
Trong bối cảnh thị trường bất động sản ngày càng biến động, việc dự báo giá nhà chính xác đóng vai trò quan trọng đối với nhiều bên liên quan, bao gồm các nhà đầu tư, ngân hàng, tổ chức tài chính, cũng như cá nhân có nhu cầu mua nhà. Các nghiên cứu trước đây đã chỉ ra rằng giá nhà bị ảnh hưởng bởi nhiều yếu tố như đặc điểm của bất động sản, vị trí địa lý, điều kiện kinh tế vĩ mô, cũng như tâm lý thị trường (Zhou & Li, 2021; Malpezzi, 1999).
Việc xây dựng mô hình dự báo giá nhà không chỉ giúp cải thiện các quyết định tài chính mà còn hỗ trợ xây dựng các chính sách kinh tế vĩ mô liên quan đến thị trường bất động sản. Theo nghiên cứu của Kok, Monkkonen và Quigley (2014), một mô hình dự báo giá nhà hiệu quả có thể giúp giảm thiểu rủi ro đầu tư, tối ưu hóa danh mục tín dụng bất động sản của ngân hàng, cũng như cung cấp thông tin hữu ích cho các nhà hoạch định chính sách.
Để giải quyết vấn đề này, nghiên cứu này trước tiên tập trung vào
việc hiểu rõ bộ dữ liệu thông qua phân tích khám phá dữ liệu
(Exploratory Data Analysis - EDA). Giai đoạn này giúp đánh giá
cấu trúc dữ liệu, phát hiện các xu hướng quan trọng, kiểm tra sự phân bố
của các biến, cũng như xác định các yếu tố có ảnh hưởng lớn đến giá nhà.
Theo nghiên cứu của Gupta, Das và Sen (2020), EDA đóng vai trò thiết yếu
trong việc nhận diện các vấn đề như dữ liệu ngoại lệ, thiếu dữ liệu và
đa cộng tuyến – những yếu tố có thể ảnh hưởng đến độ chính xác của mô
hình dự báo.
Sau khi hoàn tất quá trình EDA, nghiên cứu này thử nghiệm nhiều mô hình
khác nhau và nhận thấy hồi quy Lasso là phương pháp có
hiệu suất tốt nhất với điểm Root Mean Squared Error (RMSE) trên
kiểm định chéo là 0.1121. Điều này phù hợp với các nghiên cứu
trước đây, khi Tibshirani (1996) chỉ ra rằng Lasso là một phương pháp
hồi quy mạnh mẽ giúp giải quyết vấn đề đa cộng tuyến và chọn lọc các
biến quan trọng. Do bộ dữ liệu có nhiều biến có tương quan cao, Lasso
trở thành phương pháp tối ưu vì nó có khả năng loại bỏ các biến
dư thừa, giúp giảm độ phức tạp của mô hình và tăng độ chính xác
của dự báo (Hastie, Tibshirani & Friedman, 2009).
Ngoài hồi quy Lasso, nghiên cứu này cũng thử nghiệm mô hình XGBoost, một thuật toán học máy dựa trên cây quyết định có hiệu suất cao trong các bài toán dự báo phi tuyến. Kết quả cho thấy XGBoost đạt điểm RMSE kiểm định chéo là 0.1162, thấp hơn một chút so với Lasso. Tuy nhiên, theo nghiên cứu của Chen và Guestrin (2016), XGBoost có lợi thế lớn trong việc phát hiện các mối quan hệ phi tuyến giữa biến đầu vào và biến mục tiêu, giúp tăng cường hiệu suất dự báo trong những tình huống phức tạp.
Nhận thấy rằng hai mô hình này có bản chất khác nhau – Lasso tối ưu hóa bằng cách loại bỏ biến không quan trọng, trong khi XGBoost tận dụng sức mạnh của cây quyết định để phát hiện các mẫu phức tạp trong dữ liệu, nghiên cứu này đề xuất một phương pháp kết hợp dự báo từ hai mô hình. Theo hướng dẫn của Dietterich (2000), việc kết hợp nhiều mô hình có thể giúp cải thiện độ chính xác của dự báo bằng cách giảm phương sai trong kết quả. Trong nghiên cứu này, dự báo từ Lasso được gán trọng số cao hơn XGBoost do hiệu suất kiểm định chéo tốt hơn.
Bộ dữ liệu bao gồm cả biến dạng ký tự và số nguyên. Phần lớn các biến ký tự thực chất là các yếu tố có thứ tự,dữ liệu được xử lý dưới dạng chuỗi ký tự để thuận tiện cho quá trình làm sạch dữ liệu và kỹ thuật tính năng sau này. Tổng cộng, bộ dữ liệu có 81 cột, trong đó cột cuối cùng đại diện cho biến mục tiêu (Giá bán). Dưới đây là một cái nhìn sơ lược về các biến, và tôi sẽ trình bày chi tiết về từng biến trong phần sau của tài liệu. ### **1.4. Kết cấu các chương
#2. Phân tích và trực quan hoá các biến trong mô hình ## 2.1. Danh sách các biến được sử dụng và mô tả
SalePrice: Giá bán của ngôi nhà (tính bằng USD). Đây là biến mục tiêu cần dự đoán.
Thông tin chung về bất động sản
MSSubClass: Loại hình nhà ở (ví dụ: nhà đơn lập, song
lập, chung cư, v.v.).
MSZoning: Quy hoạch khu vực (dân cư, thương mại, nông
nghiệp, v.v.).
LotFrontage: Chiều rộng mặt tiền của lô đất giáp với
đường (tính bằng feet).
LotArea: Tổng diện tích của lô đất (tính bằng square
feet).
Street: Loại đường tiếp cận bất động sản (ví dụ: đường
trải nhựa, đường sỏi).
Alley: Loại ngõ phía sau hoặc bên hông nhà (nếu
có).
LotShape: Hình dạng tổng thể của lô đất (vuông vắn,
không đối xứng, v.v.).
LandContour: Độ cao và độ bằng phẳng của khu đất (bằng
phẳng, đồi núi, trũng, v.v.).
Utilities: Các dịch vụ tiện ích có sẵn (điện, nước, hệ
thống thoát nước, v.v.).
LotConfig: Cách lô đất được sắp xếp (góc đường, trong
hẻm, đối diện công viên, v.v.).
LandSlope: Độ dốc tổng thể của khu đất.
Vị trí & môi trường xung quanh
Neighborhood: Khu vực hoặc vùng lân cận trong thành phố
Ames.
Condition1: Mức độ gần đường lớn, đường sắt hoặc các
yếu tố tác động khác.
Condition2: Một yếu tố bổ sung về môi trường xung quanh
(nếu có thêm ảnh hưởng khác).
Kiểu dáng và cấu trúc nhà
BldgType: Loại hình nhà (nhà đơn lập, nhà song lập,
chung cư, v.v.).
HouseStyle: Kiểu thiết kế của ngôi nhà (1 tầng, 2 tầng,
có gác lửng, v.v.).
OverallQual: Chất lượng tổng thể của vật liệu và hoàn
thiện nội thất (theo thang điểm).
OverallCond: Đánh giá tổng thể về tình trạng hiện tại
của ngôi nhà.
YearBuilt: Năm xây dựng ngôi nhà ban đầu.
YearRemodAdd: Năm cải tạo hoặc nâng cấp gần nhất (nếu
có).
Mái nhà & vật liệu bên ngoài
RoofStyle: Kiểu dáng mái nhà (bằng, dốc, nhiều mái,
v.v.).
RoofMatl: Loại vật liệu được sử dụng cho mái nhà.
Exterior1st: Vật liệu chính được sử dụng cho ngoại thất
ngôi nhà.
Exterior2nd: Vật liệu phụ (nếu có) được sử dụng cho
ngoại thất.
MasVnrType: Loại lớp phủ gạch trang trí phía ngoài (nếu
có).
MasVnrArea: Diện tích lớp phủ gạch trang trí (tính bằng
square feet).
Tầng hầm
Foundation: Loại nền móng của ngôi nhà.
BsmtQual: Đánh giá về chiều cao tầng hầm.
BsmtCond: Đánh giá chung về tình trạng tầng hầm.
BsmtExposure: Mức độ tiếp xúc của tầng hầm với không
gian bên ngoài (cửa sổ, lối thoát).
BsmtFinType1: Chất lượng và mức độ hoàn thiện của tầng
hầm.
BsmtFinSF1: Diện tích đã hoàn thiện của tầng hầm (tính
bằng square feet).
BsmtFinType2: Phần tầng hầm hoàn thiện bổ sung (nếu
có).
BsmtFinSF2: Diện tích phần tầng hầm hoàn thiện bổ
sung.
BsmtUnfSF: Diện tích tầng hầm chưa hoàn thiện.
TotalBsmtSF: Tổng diện tích tầng hầm.
Hệ thống tiện ích & nội thất
Heating: Loại hệ thống sưởi trong nhà.
HeatingQC: Chất lượng và tình trạng của hệ thống
sưởi.
CentralAir: Nhà có hệ thống điều hòa trung tâm hay
không (Có/Không).
Electrical: Loại hệ thống điện được lắp đặt.
Diện tích không gian sống
1stFlrSF: Diện tích tầng 1 (tính bằng square
feet).
2ndFlrSF: Diện tích tầng 2 (nếu có).
LowQualFinSF: Diện tích hoàn thiện chất lượng thấp (tất
cả các tầng).
GrLivArea: Tổng diện tích sinh hoạt trên mặt đất.
Phòng tắm & phòng ngủ
BsmtFullBath: Số phòng tắm đầy đủ ở tầng hầm.
BsmtHalfBath: Số phòng tắm nửa ở tầng hầm.
FullBath: Số phòng tắm đầy đủ trên tầng trệt trở
lên.
HalfBath: Số phòng tắm nửa trên tầng trệt trở
lên.
Bedroom: Số lượng phòng ngủ (không tính tầng
hầm).
Kitchen: Số lượng bếp trong nhà.
KitchenQual: Chất lượng của khu bếp.
TotRmsAbvGrd: Tổng số phòng (không tính phòng tắm) trên
tầng trệt trở lên.
Functional: Đánh giá chức năng tổng thể của ngôi nhà
(bình thường, cần sửa chữa, v.v.).
Lò sưởi & gara
Fireplaces: Số lượng lò sưởi trong nhà.
FireplaceQu: Chất lượng của lò sưởi.
GarageType: Kiểu thiết kế và vị trí của gara.
GarageYrBlt: Năm xây dựng gara.
GarageFinish: Mức độ hoàn thiện nội thất bên trong
gara.
GarageCars: Số lượng xe ô tô có thể chứa trong
gara.
GarageArea: Tổng diện tích gara (tính bằng square
feet).
GarageQual: Chất lượng tổng thể của gara.
GarageCond: Đánh giá về tình trạng tổng thể của
gara.
PavedDrive: Đường lái xe có được lát đá hay không.
Không gian ngoài trời
WoodDeckSF: Diện tích sàn gỗ ngoài trời (tính bằng
square feet).
OpenPorchSF: Diện tích hiên mở trước/sau nhà.
EnclosedPorch: Diện tích hiên kín (nếu có).
3SsnPorch: Diện tích hiên ba mùa (có thể sử dụng vào 3
mùa trong năm).
ScreenPorch: Diện tích hiên có màn chắn.
PoolArea: Diện tích hồ bơi (nếu có).
PoolQC: Chất lượng hồ bơi (nếu có).
Fence: Loại và chất lượng hàng rào xung quanh nhà.
Các đặc điểm khác
MiscFeature: Các đặc điểm đặc biệt khác không thuộc
danh mục trên.
MiscVal: Giá trị của các đặc điểm đặc biệt này (tính
bằng USD).
Thông tin về giao dịch mua bán
MoSold: Tháng bán nhà.
YrSold: Năm bán nhà.
SaleType: Loại giao dịch mua bán (bán thông thường, bán
đấu giá, v.v.).
SaleCondition: Điều kiện bán nhà (bình thường, bán do
tài chính khó khăn, v.v.).
##2.2.Cài đặt các thư viện cần thiết và tiến hành đọc dữ liệu
library(knitr)
library(ggplot2)
library(plyr)
## Warning: package 'plyr' was built under R version 4.4.1
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:plyr':
##
## arrange, count, desc, failwith, id, mutate, rename, summarise,
## summarize
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(corrplot)
## Warning: package 'corrplot' was built under R version 4.4.1
## corrplot 0.95 loaded
library(caret)
## Loading required package: lattice
library(gridExtra)
##
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
##
## combine
library(scales)
library(Rmisc)
library(ggrepel)
## Warning: package 'ggrepel' was built under R version 4.4.1
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:gridExtra':
##
## combine
## The following object is masked from 'package:dplyr':
##
## combine
## The following object is masked from 'package:ggplot2':
##
## margin
library(psych)
##
## Attaching package: 'psych'
## The following object is masked from 'package:randomForest':
##
## outlier
## The following objects are masked from 'package:scales':
##
## alpha, rescale
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
library(xgboost)
##
## Attaching package: 'xgboost'
## The following object is masked from 'package:dplyr':
##
## slice
library(readr)
##
## Attaching package: 'readr'
## The following object is masked from 'package:scales':
##
## col_factor
train <- read_csv("Downloads/house-prices-advanced-regression-techniques/train.csv")
## Rows: 1460 Columns: 81
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConf...
## dbl (38): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, Ye...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
library(readr)
test <- read_csv("Downloads/house-prices-advanced-regression-techniques/test.csv")
## Rows: 1459 Columns: 80
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConf...
## dbl (37): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, Ye...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Getting rid of the IDs but keeping the test IDs in a vector. These are needed to compose the submission file
test_labels <- test$Id
test$Id <- NULL
train$Id <- NULL
test$SalePrice <- NA
all <- rbind(train, test)
dim(all)
## [1] 2919 80
##2.3.Một số biến quan trọng trong phân tích ###2.3.1 Biến phụ thuộc SalePrice
ggplot(data=all[!is.na(all$SalePrice),], aes(x=SalePrice)) +
geom_histogram(fill="blue", binwidth = 10000) +
scale_x_continuous(breaks= seq(0, 800000, by=100000), labels = comma)
Như có thể quan sát, biến Giá bán (SalePrice) có phân phối lệch về bên trái. Điều này có nghĩa là phần lớn các ngôi nhà có mức giá trung bình hoặc thấp, trong khi chỉ có một số ít ngôi nhà có giá trị rất cao, tạo ra một đuôi dài ở phía bên phải của biểu đồ phân phối. Hiện tượng này là hợp lý vì trong thực tế, số lượng người có khả năng mua nhà giá cao thường ít hơn nhiều so với những người mua nhà ở mức giá phổ thông.
Do tính chất lệch của phân phối, nếu sử dụng trực tiếp giá bán làm đầu vào cho các mô hình dự báo, có thể dẫn đến việc mô hình không hoạt động tối ưu. Các thuật toán hồi quy tuyến tính hoặc mô hình dự báo có thể bị ảnh hưởng bởi các giá trị cực đoan (outliers), dẫn đến dự báo thiếu chính xác.
summary(all$SalePrice)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 34900 129975 163000 180921 214000 755000 1459
##2.3.2 Một số biến quan trọng trong dự báo
Các biến dạng ký tự cần được xử lý trước khi có thể đưa vào mô hình. Điều này có thể bao gồm việc mã hóa lại, làm sạch dữ liệu hoặc áp dụng các kỹ thuật tính năng phù hợp để đảm bảo chúng có thể được sử dụng hiệu quả trong mô hình dự báo.
Trước khi thực hiện các bước xử lý chi tiết, cần có cái nhìn tổng quan về bộ dữ liệu bằng cách kiểm tra mối quan hệ giữa các biến số với biến mục tiêu SalePrice. Cụ thể, hệ số tương quan giữa các biến số và SalePrice sẽ được tính toán để xác định những biến có ảnh hưởng lớn đến giá bán. Điều này giúp hiểu rõ hơn về các yếu tố quan trọng trong tập dữ liệu và đưa ra chiến lược phù hợp để xử lý cũng như chọn lọc biến cho mô hình dự báo. ##2.3.3 Tương quan với biến SalePrice
numericVars <- which(sapply(all, is.numeric)) #index vector numeric variables
numericVarNames <- names(numericVars) #saving names vector for use later on
cat('There are', length(numericVars), 'numeric variables')
## There are 37 numeric variables
all_numVar <- all[, numericVars]
cor_numVar <- cor(all_numVar, use="pairwise.complete.obs") #correlations of all numeric variables
#sort on decreasing correlations with SalePrice
cor_sorted <- as.matrix(sort(cor_numVar[,'SalePrice'], decreasing = TRUE))
#select only high corelations
CorHigh <- names(which(apply(cor_sorted, 1, function(x) abs(x)>0.5)))
cor_numVar <- cor_numVar[CorHigh, CorHigh]
corrplot.mixed(cor_numVar, tl.col="black", tl.pos = "lt")
Nhận xét: Tổng cộng, có 10 biến số có hệ số tương quan từ 0,5 trở lên với SalePrice, cho thấy mối quan hệ tương đối mạnh giữa các biến này và giá bán. Đáng chú ý, tất cả các mối tương quan này đều là dương, có nghĩa là khi giá trị của các biến này tăng, SalePrice cũng có xu hướng tăng theo.
Điều này gợi ý rằng các đặc điểm liên quan đến những biến số này có thể đóng vai trò quan trọng trong việc xác định giá trị của một ngôi nhà. Vì vậy, tôi sẽ xem xét kỹ hơn từng biến để hiểu rõ hơn về mức độ ảnh hưởng của chúng và cách chúng có thể được sử dụng trong mô hình dự báo. Bước tiếp theo sẽ là trực quan hóa dữ liệu và kiểm tra tính chất của từng biến nhằm đảm bảo rằng chúng thực sự phù hợp với mô hình. (Tương quan 0,5 là mức độ tương quan trung bình giữa hai biến, thể hiện mối quan hệ tuyến tính giữa chúng. Trong trường hợp này, hệ số tương quan được tính bằng hệ số tương quan Pearson, thường được ký hiệu là r.
Hệ số này có giá trị từ -1 đến 1:
- r > 0: Hai biến có mối tương quan dương, nghĩa là
khi một biến tăng, biến còn lại cũng có xu hướng tăng.
- r < 0: Hai biến có mối tương quan âm, nghĩa là khi
một biến tăng, biến còn lại có xu hướng giảm.
- r = 0: Hai biến không có mối quan hệ tuyến tính.
Với r = 0,5, mức độ tương quan được coi là trung bình đến khá mạnh. Điều này có nghĩa là khi biến độc lập tăng lên, SalePrice cũng có xu hướng tăng, nhưng mối quan hệ này không hoàn toàn chặt chẽ. Trong phân tích dữ liệu, các biến có tương quan từ 0,5 trở lên thường được coi là có ảnh hưởng đáng kể đến biến mục tiêu và có thể được chọn để đưa vào mô hình dự báo.) Phân tích mối quan hệ giữa SalePrice và các yếu tố dự báo quan trọng
Trong phần này, tôi sẽ trực quan hóa mối quan hệ giữa SalePrice (giá bán) và hai biến có tương quan cao nhất với nó:
Những trực quan hóa này sẽ giúp làm rõ mức độ ảnh hưởng của hai yếu tố này đối với giá nhà, từ đó hỗ trợ việc lựa chọn biến quan trọng trong mô hình dự báo.
Vấn đề đa cộng tuyến trong dữ liệu
Khi kiểm tra mối tương quan giữa các biến số, tôi nhận thấy vấn đề đa cộng tuyến (multicollinearity). Đa cộng tuyến xảy ra khi có hai hoặc nhiều biến độc lập có mối quan hệ rất chặt chẽ với nhau, điều này có thể gây ảnh hưởng tiêu cực đến các mô hình hồi quy do làm tăng phương sai của ước lượng và giảm tính ổn định của mô hình.
Ví dụ, hai biến GarageCars (số lượng xe có thể đỗ trong gara) và GarageArea (tổng diện tích gara) có hệ số tương quan rất cao (r = 0,89). Điều này có nghĩa là hai biến này gần như cung cấp cùng một thông tin – một gara lớn thường có sức chứa nhiều xe hơn. Đồng thời, cả hai biến này đều có mối tương quan mạnh với SalePrice, dẫn đến khả năng dư thừa thông tin nếu đưa cả hai vào mô hình.
Các biến số khác có tương quan cao với SalePrice
Ngoài OverallQual và GrLivArea, có sáu biến số khác có hệ số tương quan lớn hơn 0,5 với SalePrice, bao gồm:
Những biến số này đều có mối quan hệ chặt chẽ với SalePrice, vì vậy tôi sẽ cân nhắc sử dụng chúng trong mô hình dự báo, đồng thời xử lý vấn đề đa cộng tuyến để đảm bảo mô hình hoạt động tốt nhất.
##2.3.4.Chất lượng tổng thể (Overall Quality)
ggplot(data=all[!is.na(all$SalePrice),], aes(x=factor(OverallQual), y=SalePrice))+
geom_boxplot(col='blue') + labs(x='Overall Quality') +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma)
Nhận xét: Chất lượng tổng thể và mối quan hệ với Giá
bán
Trong tất cả các biến số trong tập dữ liệu, OverallQual (Chất lượng tổng thể) có mối tương quan mạnh nhất với SalePrice (Giá bán), với hệ số tương quan lên đến 0,79. Điều này cho thấy rằng chất lượng tổng thể của ngôi nhà đóng vai trò rất quan trọng trong việc xác định giá trị của nó.
OverallQual là một thang đo đánh giá tổng thể về
chất lượng vật liệu xây dựng và mức độ hoàn thiện của ngôi nhà. Biến này
được xếp hạng theo thang điểm từ 1 đến 10, trong
đó:
- 1: Chất lượng rất kém (Very Poor)
- 2-4: Chất lượng kém đến trung bình (Poor to Below
Average)
- 5-6: Chất lượng trung bình (Average)
- 7-8: Chất lượng khá đến tốt (Above Average to
Good)
- 9-10: Chất lượng rất tốt đến xuất sắc (Very Good to
Excellent)
Mối quan hệ giữa OverallQual và SalePrice
Mối quan hệ mạnh giữa OverallQual và SalePrice có nghĩa là những ngôi nhà có chất lượng tổng thể cao hơn thường có giá bán cao hơn đáng kể. Điều này hoàn toàn hợp lý, vì người mua thường sẵn sàng trả nhiều tiền hơn cho những ngôi nhà có chất lượng xây dựng tốt, sử dụng vật liệu cao cấp và được hoàn thiện một cách tỉ mỉ.
Ảnh hưởng đến mô hình dự báo
Vì OverallQual có tác động lớn đến giá nhà, đây sẽ là một biến quan
trọng trong mô hình dự báo. Tôi sẽ kiểm tra phân phối của biến này, đồng
thời kết hợp nó với các yếu tố khác như diện tích sinh hoạt, năm xây
dựng và số lượng tiện nghi để xây dựng một mô hình chính xác hơn.
Mối quan hệ giữa OverallQual và SalePrice: Xu hướng và Ngoại
lệ
Mối quan hệ giữa OverallQual (Chất lượng tổng thể) và SalePrice (Giá bán) thể hiện một tương quan dương rõ ràng, nghĩa là khi chất lượng tổng thể của ngôi nhà tăng lên, giá bán của nó cũng có xu hướng tăng theo. Tuy nhiên, thay vì là một đường thẳng hoàn toàn tuyến tính, mối quan hệ này có dạng đường cong tăng nhẹ, cho thấy rằng sự gia tăng về chất lượng tổng thể càng cao thì mức tăng của giá nhà càng lớn. Điều này phản ánh thực tế rằng những ngôi nhà có chất lượng tốt hơn không chỉ có giá cao hơn, mà còn có thể được định giá theo cấp số nhân do sử dụng vật liệu cao cấp và thiết kế cao cấp hơn.
Kiểm tra các giá trị ngoại lệ (Outliers)
Khi kiểm tra dữ liệu,không phát hiện ra bất kỳ giá trị cực đoan nào gây ảnh hưởng lớn đến phân tích. Tuy nhiên, nếu xét về các ứng viên có thể được coi là ngoại lệ sau này, một trường hợp đáng chú ý là ngôi nhà có OverallQual = 4 nhưng lại có giá bán rất cao. Điều này khá bất thường, vì các ngôi nhà có chất lượng tổng thể thấp thường không có giá trị cao như vậy.
Có thể có một số lý do khiến ngôi nhà này có giá cao dù chất lượng
tổng thể không cao, chẳng hạn như:
- Vị trí đắc địa hoặc có diện tích đất lớn.
- Được cải tạo hoặc có các yếu tố đặc biệt không được phản ánh đầy đủ
trong OverallQual.
- Dữ liệu bị ghi sai hoặc có sự khác biệt trong cách đánh giá chất
lượng.
Nếu trong quá trình xây dựng mô hình, ngôi nhà này gây ảnh hưởng lớn đến kết quả dự báo, có thể cân nhắc loại bỏ nó khỏi tập dữ liệu để đảm bảo mô hình hoạt động chính xác hơn. Tuy nhiên, trước khi quyết định, xem xét kỹ hơn về các đặc điểm của ngôi nhà này để hiểu rõ hơn về lý do tại sao giá bán của nó cao bất thường.
ggplot(data=all[!is.na(all$SalePrice),], aes(x=GrLivArea, y=SalePrice))+
geom_point(col='blue') + geom_smooth(method = "lm", se=FALSE, color="black", aes(group=1)) +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma) +
geom_text_repel(aes(label = ifelse(all$GrLivArea[!is.na(all$SalePrice)]>4500, rownames(all), '')))
## `geom_smooth()` using formula = 'y ~ x'
Nhận xét:Mối quan hệ giữa GrLivArea và SalePrice
Biến số có mối tương quan cao thứ hai với SalePrice (Giá bán) là GrLivArea (Diện tích khu vực sinh hoạt trên mặt đất), với hệ số tương quan khá cao. Điều này hoàn toàn hợp lý, vì những ngôi nhà có diện tích sinh hoạt lớn hơn thường có giá trị cao hơn, do không gian rộng rãi hơn mang lại sự thoải mái và tiện nghi hơn cho người ở.
GrLivArea đại diện cho tổng diện tích của tất cả các tầng trên mặt đất, không bao gồm tầng hầm. Nhìn chung, diện tích này càng lớn thì giá trị của ngôi nhà càng cao, vì vậy đây là một yếu tố quan trọng cần xem xét khi định giá nhà.
Xu hướng và mối quan hệ với SalePrice
Mối quan hệ giữa GrLivArea và SalePrice là tương quan dương, nghĩa là khi diện tích khu vực sinh hoạt tăng, giá bán cũng có xu hướng tăng. Trong thực tế, những ngôi nhà rộng hơn có nhiều không gian sử dụng hơn, giúp cải thiện chất lượng sống và do đó thường có giá cao hơn.
Tuy nhiên, cần kiểm tra xem xu hướng này có tuyến tính hay không, vì có thể có một số dấu hiệu cho thấy sự tăng giá có thể chậm lại ở những mức diện tích rất lớn. Điều này có thể xảy ra do thị trường có giới hạn về mức giá mà người mua sẵn sàng trả, ngay cả đối với những căn nhà có diện tích rất lớn.
Kiểm tra các giá trị ngoại lệ
Một điểm quan trọng cần lưu ý khi phân tích
GrLivArea là sự xuất hiện của các giá trị ngoại
lệ (outliers). Những ngôi nhà có diện tích sinh hoạt quá lớn
nhưng lại có giá bán thấp hơn nhiều so với xu hướng chung có thể là dữ
liệu bất thường hoặc phản ánh các yếu tố đặc biệt như:
- Ngôi nhà có diện tích rộng nhưng vị trí không thuận lợi.
- Chất lượng xây dựng hoặc tiện ích không tương xứng với diện
tích.
- Ghi nhận sai dữ liệu.
Nếu có những giá trị này trong dữ liệu, cần kiểm tra kỹ hơn để xác định xem có cần loại bỏ chúng hay không để tránh ảnh hưởng tiêu cực đến mô hình dự báo. Xác định các giá trị ngoại lệ trong GrLivArea và SalePrice
Khi kiểm tra mối quan hệ giữa GrLivArea (Diện tích khu vực
sinh hoạt trên mặt đất) và SalePrice (Giá
bán), có hai ngôi nhà đặc biệt bất thường:
- Nhà số 524
- Nhà số 1299
Hai ngôi nhà này có diện tích sinh hoạt rất lớn nhưng lại có giá bán thấp hơn đáng kể so với xu hướng chung. Điều này khiến chúng trở thành ứng viên tiềm năng cho các giá trị ngoại lệ (outliers).
Lý do chưa loại bỏ ngay hai ngôi nhà này
Việc loại bỏ ngoại lệ cần được cân nhắc kỹ lưỡng, vì có thể có lý do hợp lý khiến những ngôi nhà này có giá thấp. Một trong những giả thuyết có thể là OverallQual (Chất lượng tổng thể) của hai ngôi nhà này thấp, khiến giá bán không cao mặc dù có diện tích rộng.
Tuy nhiên, khi kiểm tra dữ liệu, cả nhà 524 và nhà 1299 đều
có điểm tối đa về Chất lượng tổng thể (OverallQual = 10). Điều
này có nghĩa là:
- Không thể giải thích giá thấp của chúng bằng chất lượng xây dựng
kém.
- Có khả năng cao hai ngôi nhà này thực sự là ngoại lệ, do một yếu tố
nào đó không phản ánh trong dữ liệu (ví dụ: vị trí xấu, tình trạng hư
hỏng nặng, hoặc lỗi nhập dữ liệu).
Thay vì loại bỏ ngay lập tức hai ngôi nhà này khỏi tập dữ liệu,
cần:
1. Tiếp tục theo dõi và kiểm tra xem chúng có ảnh hưởng
lớn đến mô hình dự báo hay không.
2. Kiểm tra thêm các đặc điểm khác như vị trí địa lý,
lịch sử giao dịch hoặc các biến số khác có thể giúp giải thích giá thấp
của chúng.
3. Nếu chúng gây ảnh hưởng tiêu cực đến mô hình, cân nhắc loại
bỏ để tránh làm lệch dự báo.
Việc xử lý đúng các giá trị ngoại lệ như thế này rất quan trọng để đảm bảo độ chính xác và tính tổng quát của mô hình phân tích giá nhà.
all[c(524, 1299), c('SalePrice', 'GrLivArea', 'OverallQual')]
## # A tibble: 2 × 3
## SalePrice GrLivArea OverallQual
## <dbl> <dbl> <dbl>
## 1 184750 4676 10
## 2 160000 5642 10
###2.3.6 Xử lý dữ liệu thiếu và mã hoá các biến phân loại Trước khi tiến hành phân tích chi tiết, điều quan trọng đầu tiên là xác định các biến có giá trị bị thiếu trong tập dữ liệu. Việc này giúp hiểu được mức độ thiếu dữ liệu và có chiến lược phù hợp để xử lý, tránh ảnh hưởng đến mô hình dự báo sau này.
Các giá trị bị thiếu có thể xuất hiện vì nhiều lý do, chẳng hạn
như:
- Dữ liệu không được cung cấp hoặc không được ghi nhận đầy đủ trong quá
trình thu thập.
- Một số trường hợp giá trị không áp dụng cho tất cả các quan sát (ví
dụ: diện tích garage có thể bị thiếu nếu nhà không có garage).
- Lỗi nhập dữ liệu hoặc sai sót trong quá trình xử lý.
NAcol <- which(colSums(is.na(all)) > 0)
sort(colSums(sapply(all[NAcol], is.na)), decreasing = TRUE)
## PoolQC MiscFeature Alley Fence SalePrice FireplaceQu
## 2909 2814 2721 2348 1459 1420
## LotFrontage GarageYrBlt GarageFinish GarageQual GarageCond GarageType
## 486 159 159 159 159 157
## BsmtCond BsmtExposure BsmtQual BsmtFinType2 BsmtFinType1 MasVnrType
## 82 82 81 80 79 24
## MasVnrArea MSZoning Utilities BsmtFullBath BsmtHalfBath Functional
## 23 4 2 2 2 2
## Exterior1st Exterior2nd BsmtFinSF1 BsmtFinSF2 BsmtUnfSF TotalBsmtSF
## 1 1 1 1 1 1
## Electrical KitchenQual GarageCars GarageArea SaleType
## 1 1 1 1 1
cat('There are', length(NAcol), 'columns with missing values')
## There are 35 columns with missing values
Xử lý giá trị thiếu trong tập dữ liệu
Khi kiểm tra dữ liệu, nhận thấy rằng SalePrice (Giá bán) có 1459 giá trị thiếu, và số lượng này trùng khớp với kích thước của tập thử nghiệm. Điều này hoàn toàn hợp lý vì SalePrice chỉ có trong tập huấn luyện, còn tập thử nghiệm không có giá trị mục tiêu này. Do đó, sẽ không sửa giá trị thiếu trong SalePrice, mà chỉ tập trung vào việc xử lý các giá trị thiếu trong 34 biến dự báo.
Kế hoạch xử lý giá trị thiếu
Với 35 biến dự báo có giá trị thiếu, các bước xử lý hợp lý bao gồm:
Việc xử lý các giá trị thiếu một cách hợp lý sẽ giúp mô hình dự báo hoạt động chính xác hơn và tránh các vấn đề về chất lượng dữ liệu trong quá trình phân tích. # 3.Điền dữ liệu thiếu ##3.1Xử lý 35 biến dự báo có giá trị thiếu trong tập dữ liệu**
Trong phần này, tôi sẽ tiến hành sửa 34 biến dự báo có giá trị bị thiếu trong tập dữ liệu. Tôi sẽ xử lý từng biến theo thứ tự từ biến có nhiều giá trị thiếu nhất cho đến khi tất cả đều được xử lý.
Nếu tôi phát hiện ra một nhóm biến có liên quan chặt chẽ với
nhau, tôi sẽ xử lý chúng cùng lúc để đảm bảo tính nhất quán
trong dữ liệu. Ví dụ:
- Nhóm biến liên quan đến hồ bơi (PoolQC, PoolArea,
v.v.)
- Nhóm biến liên quan đến nhà để xe (GarageType,
GarageCars, GarageArea, v.v.)
- Nhóm biến liên quan đến tầng hầm (BsmtQual, BsmtCond,
BsmtExposure, v.v.)
Cách trình bày và xử lý để dễ theo dõi
Để giúp tài liệu dễ đọc và trực quan hơn, tôi sẽ sử dụng “Tabs” trong knitr để tổ chức các nhóm biến. Điều này giúp bạn có thể chọn xem xét từng nhóm một cách linh hoạt mà không cần phải đọc toàn bộ tài liệu.
Bạn có thể tập trung vào những phần quan trọng, đặc
biệt là:
- Nhóm biến về nhà để xe (Garage)
- Nhóm biến về tầng hầm (Basement)
Tôi đã dành nhiều thời gian để xác định chính xác những ngôi nhà thực sự không có tầng hầm hoặc nhà để xe, thay vì đơn giản cho rằng giá trị NA là dữ liệu bị thiếu do lỗi nhập.
Các bước xử lý dữ liệu
Bên cạnh việc xử lý các giá trị thiếu, tôi cũng thực hiện:
1. Chuyển đổi các biến ký tự thành dạng số nếu có thứ
tự rõ ràng (ví dụ: chất lượng nhà, điều kiện tầng hầm).
2. Chuyển đổi các biến danh mục không có thứ tự thành các yếu tố
(factors) để chuẩn bị cho quá trình mã hóa sau này.
3. Sử dụng One-Hot Encoding (mã hóa nhị phân) để biến
các biến danh mục thành dạng số, giúp mô hình máy học xử lý tốt hơn.
Việc thực hiện các bước này sẽ giúp cải thiện chất lượng dữ liệu và
đảm bảo mô hình hoạt động hiệu quả hơn khi dự báo giá nhà ### 3.1.1 Pool
variables
Xử lý biến PoolQC và PoolArea
1. Giới thiệu về hai biến liên quan đến hồ bơi
Trong tập dữ liệu, có hai biến liên quan đến hồ bơi:
. Xử lý giá trị thiếu trong PoolQC
Khi kiểm tra dữ liệu, tôi nhận thấy đa số các giá trị của PoolQC
bị thiếu (NA). Tuy nhiên, điều này hoàn toàn hợp lý vì
chỉ một tỷ lệ nhỏ ngôi nhà có hồ bơi.
Để xử lý NA trong PoolQC, tôi sẽ:
- Gán tất cả các giá trị NA thành “No Pool” để biểu thị
rằng những ngôi nhà này không có hồ bơi thay vì coi đó
là dữ liệu bị thiếu.
- Mã hóa lại PoolQC thành dạng số để phục vụ mô hình sau này, với quy
tắc:
- No Pool → 0
- Fa (Fair) → 1
- TA (Typical/Average) → 2
- Gd (Good) → 3
- Ex (Excellent) → 4
. Xử lý giá trị thiếu trong PoolArea
PoolArea là một biến số học (numerical variable), nên việc xử lý sẽ đơn
giản hơn:
- Nếu PoolQC là “No Pool”, thì PoolArea phải
bằng 0.
- Nếu PoolQC có giá trị khác “No Pool”, nhưng PoolArea
bị thiếu (NA), tôi sẽ thay thế bằng giá trị trung bình của PoolArea cho
từng nhóm chất lượng hồ bơi.
all$PoolQC[is.na(all$PoolQC)] <- 'None'
Qualities <- c('None' = 0, 'Po' = 1, 'Fa' = 2, 'TA' = 3, 'Gd' = 4, 'Ex' = 5)
all$PoolQC<-as.integer(revalue(all$PoolQC, Qualities))
## The following `from` values were not present in `x`: Po, TA
table(all$PoolQC)
##
## 0 2 4 5
## 2909 2 4 4
Phân tích mối quan hệ giữa PoolQC và PoolArea
Trong bộ dữ liệu, ngoài biến PoolQC (đánh giá chất lượng hồ bơi), còn có một biến số khác liên quan đến hồ bơi là PoolArea, được đo bằng feet vuông. Để đảm bảo tính nhất quán trong dữ liệu, tôi đã kiểm tra xem có mối quan hệ rõ ràng nào giữa hai biến này hay không.
1. Kiểm tra dữ liệu thiếu của PoolQC
Trước tiên, tôi nhận thấy có 3 ngôi nhà không có giá trị trong biến
PoolQC. Điều này đặt ra câu hỏi liệu những ngôi nhà này
thực sự không có hồ bơi hay chỉ bị thiếu thông tin về chất lượng hồ
bơi.
2. Mối quan hệ giữa PoolQC và PoolArea
Để kiểm tra sự liên kết giữa PoolQC và
PoolArea, tôi tiến hành phân tích dữ liệu để xem liệu
kích thước hồ bơi có ảnh hưởng đến đánh giá chất lượng hồ bơi hay không.
Một số giả định hợp lý có thể là:
- Hồ bơi có diện tích lớn thường có chất lượng cao hơn (PoolQC
cao).
- Hồ bơi nhỏ hơn có thể có chất lượng thấp hơn.
Tuy nhiên, sau khi kiểm tra dữ liệu, tôi không tìm thấy một mối quan
hệ rõ ràng nào giữa PoolQC và
PoolArea. Cụ thể:
- Một số hồ bơi nhỏ vẫn nhận được đánh giá PoolQC
cao.
- Một số hồ bơi lớn lại có PoolQC trung bình hoặc
thấp.
Điều này cho thấy rằng diện tích hồ bơi không phải là yếu tố quyết định duy nhất đối với chất lượng hồ bơi.
3. Điền giá trị thiếu cho PoolQC
Do không có mối quan hệ rõ ràng giữa PoolQC và
PoolArea, tôi quyết định điền giá trị thiếu của
PoolQC dựa trên một biến khác có liên quan: **Chất
lượng tổng thể của ngôi nhà (OverallQual)*
all[all$PoolArea>0 & all$PoolQC==0, c('PoolArea', 'PoolQC', 'OverallQual')]
## # A tibble: 3 × 3
## PoolArea PoolQC OverallQual
## <dbl> <int> <dbl>
## 1 368 0 4
## 2 444 0 6
## 3 561 0 3
all$PoolQC[2421] <- 2
all$PoolQC[2504] <- 3
all$PoolQC[2600] <- 2
Lý do cho lựa chọn này:
- Một ngôi nhà có chất lượng tổng thể cao thường có cơ sở vật chất tốt,
bao gồm hồ bơi có chất lượng cao hơn.
- Ngược lại, những ngôi nhà có chất lượng tổng thể thấp thường không đầu
tư quá nhiều vào tiện nghi như hồ bơi cao cấp.
Vì vậy, đối với 3 ngôi nhà thiếu giá trị PoolQC, tôi sẽ gán giá trị PoolQC dựa trên mức Chất lượng tổng thể của chúng. Nếu chất lượng tổng thể của các ngôi nhà này không quá cao, thì tôi sẽ gán mức chất lượng hồ bơi tương ứng ở mức trung bình hoặc thấp.
Xử lý Biến Miscellaneous Feature
Biến Miscellaneous Feature (Đặc điểm phụ) chứa các đặc điểm phụ không được đề cập trong các danh mục khác của bộ dữ liệu. Đây là những đặc điểm bổ sung của ngôi nhà, có thể ảnh hưởng đến giá trị của bất động sản nhưng không thuộc các nhóm đặc tính chính như diện tích, số phòng, hoặc chất lượng xây dựng.
Trong tập dữ liệu, có 2.814 giá trị NA trong biến này. Việc có nhiều giá trị NA cho thấy rằng phần lớn các ngôi nhà không có bất kỳ đặc điểm nào thuộc danh mục Miscellaneous Feature. Điều này có nghĩa là NA trong biến này không phải là dữ liệu bị thiếu, mà thực chất là đại diện cho “Không có đặc điểm đặc biệt”. Do đó, thay vì loại bỏ hoặc thay thế bằng giá trị trung bình, tôi sẽ giữ nguyên những NA này và coi chúng như một nhóm riêng biệt trong quá trình xử lý dữ liệu.
Vì các giá trị trong Miscellaneous Feature không có thứ tự (không thể sắp xếp theo mức độ tốt/xấu hoặc cao/thấp), tôi quyết định chuyển đổi chúng thành một biến phân loại (categorical variable) bằng cách sử dụng factor() trong R hoặc one-hot encoding nếu sử dụng mô hình học máy yêu cầu dữ liệu số.
Dưới đây là danh sách các giá trị có thể có trong biến Miscellaneous Feature cùng với ý nghĩa của chúng:
**Hướng Xử Lý Dữ Liệu
Việc xử lý biến Miscellaneous Feature theo cách này giúp tôi duy trì tính toàn vẹn của dữ liệu trong khi vẫn đảm bảo rằng mọi thông tin quan trọng đều được sử dụng một cách hợp lý trong mô hình dự báo giá nhà.
all$MiscFeature[is.na(all$MiscFeature)] <- 'None'
all$MiscFeature <- as.factor(all$MiscFeature)
ggplot(all[!is.na(all$SalePrice),], aes(x=MiscFeature, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..))
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## No summary function supplied, defaulting to `mean_se()`
table(all$MiscFeature)
##
## Gar2 None Othr Shed TenC
## 5 2814 4 95 1
Nhận xét: Biểu đồ cột thể hiện mối quan hệ giữa Đặc điểm phụ (Miscellaneous Features) và giá bán nhà (SalePrice) cho thấy phần lớn các căn nhà trong tập dữ liệu không có bất kỳ đặc điểm phụ nào (1406 mẫu), và nhóm này có mức giá trung bình khá cao. Trong khi đó, nhà có sân quần vợt (TenC) có giá bán trung bình cao nhất, nhưng do chỉ có 1 mẫu nên khó đưa ra kết luận tổng quát. Nhóm nhà có nhà kho lớn (Shed) chiếm số lượng đáng kể (49 mẫu) với giá bán trung bình thấp hơn so với nhóm không có đặc điểm nào. Các nhóm còn lại như Gar2 (garage thứ hai) và Other (các đặc điểm khác) có số lượng rất ít (chỉ 2 mẫu mỗi nhóm), khiến việc đánh giá tác động đến giá bán trở nên khó khăn. Nhìn chung, do số lượng mẫu của một số nhóm quá nhỏ, rất khó để khẳng định rõ ràng ảnh hưởng của các đặc điểm phụ này đến giá bán nhà.
all$Alley[is.na(all$Alley)] <- 'None'
all$Alley <- as.factor(all$Alley)
ggplot(all[!is.na(all$SalePrice),], aes(x=Alley, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue')+
scale_y_continuous(breaks= seq(0, 200000, by=50000), labels = comma)
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
table(all$Alley)
##
## Grvl None Pave
## 120 2721 78
Biến số Alley (Lối đi phía sau của căn nhà) có các giá trị sau:
Loại lối vào Số lượng mẫu Grvl (Đường sỏi) X mẫu Pave (Đường trải nhựa) Y mẫu NA (Không có lối đi phía sau của căn nhà) 2721 Do phần lớn giá trị của biến Alley là NA (Không có lối vào hẻm), điều này có nghĩa rằng phần lớn các ngôi nhà trong dữ liệu không có đường hẻm phía sau. 2. Nhận xét về mối quan hệ với giá bán nhà
Những ngôi nhà không có lối đi phía sau (NA) chiếm phần lớn dữ liệu, vì vậy có thể biến này không có nhiều ảnh hưởng đáng kể đến giá nhà. Những ngôi nhà có lối đi phía sau bằng sỏi (Grvl) thường có giá bán thấp hơn do đường sỏi có thể gây bất tiện hơn so với đường trải nhựa. Những ngôi nhà có lối đi phía sau trải nhựa (Pave) có thể có giá cao hơn vì lối đi phía sau được trải nhựa giúp việc đi lại dễ dàng hơn, đặc biệt đối với ô tô. Tuy nhiên, cần kiểm tra dữ liệu để xác minh giả thuyết này. 3. Bảng tần số của biến Alley
Kết quả của table(all$Alley) cho thấy tần số xuất hiện của từng loại hẻm: “Grvl”: 120 ngôi nhà có lối vào hẻm bằng sỏi. “Pave”: 78 ngôi nhà có lối vào hẻm được lát nhựa/bê tông. “None”: 2721 ngôi nhà không có hẻm (giá trị “NA” đã được thay thế bằng “None”). ⟹ Phần lớn các ngôi nhà trong tập dữ liệu không có hẻm (chiếm phần lớn trong 2920 dữ liệu tổng).
Biểu đồ cột minh họa mối quan hệ giữa loại lối đi phía sau (Alley) và giá nhà (SalePrice). Nhìn vào chiều cao của các cột: Nhà có lối vào bằng sỏi (“Grvl”) có giá trung bình thấp nhất. Nhà có lối vào được lát nhựa (“Pave”) có giá trung bình cao hơn so với “Grvl”. Nhà không có lối đi phía sau (“None”) có giá trung bình cao nhất. 4. Nhận xét và giải thích
Nhà không có hẻm có giá trung bình cao nhất: Điều này có thể là do các ngôi nhà không có lối đi phía sau thường nằm trong khu dân cư cao cấp hơn hoặc có diện tích mặt tiền lớn hơn, trong khi nhà có hẻm thường là các ngôi nhà nhỏ, trong ngõ. Nhà có lối đi phía sau trải nhựa có giá cao hơn nhà có lối đi phía sau trải sỏi: Việc có một lối đi phía sau được lát nhựa có thể làm tăng giá trị tài sản vì nó tạo điều kiện di chuyển tốt hơn, giảm bụi bẩn và nâng cao thẩm mỹ. Nhà có lối đi phía sau trải sỏi có giá thấp nhất: Điều này có thể là do các ngôi nhà này thường thuộc các khu vực kém phát triển hơn hoặc lối đi phía sau trải sỏi không thuận tiện bằng lối đi phía sau trải nhựa.
Biến Fence mô tả chất lượng hàng rào của một ngôi nhà. Đây là một biến thứ tự (ordinal variable), có các mức giá trị khác nhau, từ chất lượng tốt đến kém: GdPrv (Good Privacy): Hàng rào chất lượng cao, đảm bảo sự riêng tư tốt. MnPrv (Minimum Privacy): Hàng rào có chất lượng trung bình, mang lại sự riêng tư ở mức tối thiểu. GdWo (Good Wood): Hàng rào gỗ chất lượng tốt. MnWw (Minimum Wood/Wire): Hàng rào gỗ hoặc dây kẽm chất lượng thấp. NA (No Fence): Không có hàng rào. 2. Số lượng từng loại hàng rào trong tập dữ liệu
Dữ liệu cho thấy có 2348 giá trị NA, tức là phần lớn các ngôi nhà không có hàng rào. Các giá trị còn lại là một số ít nhà có hàng rào với chất lượng khác nhau. 3. Ảnh hưởng của chất lượng hàng rào đến giá nhà (SalePrice)
Vì hàng rào có thể cung cấp sự riêng tư, bảo vệ và cải thiện thẩm mỹ của ngôi nhà, nên nó có thể ảnh hưởng đến giá trị bất động sản. Dự đoán xu hướng tác động của hàng rào đến giá nhà: Nhà có hàng rào “GdPrv” có thể có giá cao nhất, vì hàng rào cao cấp tạo ra sự riêng tư tối đa. Nhà có hàng rào “MnPrv” hoặc “GdWo” có thể có giá trung bình, vì vẫn có sự bảo vệ nhất định. Nhà có hàng rào “MnWw” có thể có giá thấp hơn, vì đây là loại hàng rào đơn giản, ít giá trị thẩm mỹ. Nhà không có hàng rào (“NA”) có thể có giá thấp hơn so với nhà có hàng rào cao cấp, trừ khi ngôi nhà nằm trong một khu cao cấp, không cần hàng rào.
all$Fence[is.na(all$Fence)] <- 'None'
table(all$Fence)
##
## GdPrv GdWo MnPrv MnWw None
## 118 112 329 12 2348
all[!is.na(all$SalePrice),] %>% group_by(Fence) %>% summarise(median = median(SalePrice), counts=n())
## # A tibble: 5 × 3
## Fence median counts
## <chr> <dbl> <int>
## 1 GdPrv 167500 59
## 2 GdWo 138750 54
## 3 MnPrv 137450 157
## 4 MnWw 130000 11
## 5 None 173000 1179
Nhận xét chính
1.Phần lớn các ngôi nhà không có hàng rào: Số lượng nhà không có hàng rào (2348) chiếm đa số so với các loại hàng rào khác. Điều này cho thấy hàng rào không phải là một tính năng phổ biến trong dữ liệu này, có thể do vị trí của các ngôi nhà (ví dụ: khu đô thị, nơi hàng rào không cần thiết) hoặc do sở thích của chủ nhà. Nhà có hàng rào “Minimum Privacy” (MnPrv) chiếm tỷ lệ cao nhất trong số các nhà có hàng rào: Có 329 ngôi nhà có hàng rào MnPrv, cao hơn so với các loại hàng rào chất lượng tốt hơn. Điều này có thể phản ánh rằng phần lớn chủ nhà chỉ cần một mức độ riêng tư cơ bản mà không cần đầu tư vào hàng rào chất lượng cao. Hàng rào chất lượng cao ít phổ biến hơn: GdPrv (118) và GdWo (112) có số lượng thấp hơn MnPrv nhưng cao hơn MnWw. Điều này có thể là do hàng rào chất lượng cao tốn kém hơn và chỉ xuất hiện ở một số ngôi nhà cao cấp hơn. MnWw (Minimum Wood/Wire) có số lượng thấp nhất (12): Loại hàng rào này rất ít gặp, có thể vì nó không phổ biến hoặc không được ưa chuộng do không mang lại giá trị lớn cho ngôi nhà. 2. Ảnh hưởng đến giá nhà (SalePrice)
Nhà có hàng rào tốt (GdPrv, GdWo) có thể có giá cao hơn do tạo sự riêng tư và tăng tính thẩm mỹ. Nhà có hàng rào tối thiểu (MnPrv, MnWw) có thể không làm tăng giá trị đáng kể hoặc thậm chí không ảnh hưởng nhiều. Nhà không có hàng rào (None) có thể có giá trị trung bình cao hơn nếu nằm trong các khu vực cao cấp, nơi hàng rào không quan trọng. 3. Nhận xét về phân bố giá trị trung vị SalePrice theo hàng rào Nhà không có hàng rào (None) có giá trung vị cao nhất (173,000) Có tới 1,179 ngôi nhà không có hàng rào, và giá bán trung vị của nhóm này cao nhất. Điều này có thể cho thấy hàng rào không phải là một yếu tố quan trọng trong việc định giá nhà, hoặc những ngôi nhà không có hàng rào thường nằm ở các khu vực cao cấp hơn, nơi không cần thiết phải có hàng rào. Nhà có hàng rào riêng tư tốt (GdPrv) có giá trung vị cao thứ hai (167,500) Với 59 ngôi nhà, nhóm này có mức giá trung vị cao hơn hẳn các loại hàng rào khác ngoại trừ nhóm None. Điều này có thể do hàng rào riêng tư tốt góp phần tăng giá trị ngôi nhà vì nó mang lại sự an toàn và riêng tư. Hàng rào gỗ tốt (GdWo) và hàng rào riêng tư tối thiểu (MnPrv) có mức giá trung vị gần nhau (~138,000) GdWo (138,750) và MnPrv (137,450) có giá trị trung vị thấp hơn đáng kể so với GdPrv. Điều này cho thấy hàng rào bằng gỗ tốt không mang lại nhiều lợi thế về giá trị so với hàng rào có mức riêng tư tối thiểu. Hàng rào tối thiểu bằng gỗ hoặc dây kẽm (MnWw) có giá trung vị thấp nhất (130,000) Chỉ có 11 ngôi nhà trong nhóm này, nhưng giá trung vị thấp nhất. Điều này phản ánh rằng loại hàng rào này không có nhiều giá trị về mặt tài sản, hoặc nó thường xuất hiện ở những ngôi nhà có giá trị thấp hơn.
all$Fence <- as.factor(all$Fence)
GdPrv (Good Privacy - Riêng tư tốt nhất) GdWo (Good Wood - Gỗ tốt) MnPrv (Minimum Privacy - Riêng tư tối thiểu) MnWw (Minimum Wood/Wire - Gỗ/dây kẽm tối thiểu) None (Không có hàng rào) Nhận xét quan trọng:
Không có sự sắp xếp rõ ràng giữa các loại hàng rào theo giá trị SalePrice Nếu hàng rào có ảnh hưởng lớn đến giá trị nhà, ta kỳ vọng các loại hàng rào chất lượng cao sẽ có giá nhà trung bình cao hơn. Tuy nhiên, dữ liệu cho thấy nhà không có hàng rào (None) lại có giá trung vị cao nhất (173,000), trong khi hàng rào chất lượng cao như GdPrv có giá trung vị thấp hơn (167,500). Các giá trị của Fence không có tính thứ tự (Ordinal), mà là phân loại (Categorical) Nếu biến có thứ tự rõ ràng (Ordinal), ta có thể gán giá trị số (ví dụ: 1 = MnWw, 2 = MnPrv, …, 5 = GdPrv). Nhưng trong trường hợp này, “không có hàng rào” lại có giá trị cao nhất, nên việc sắp xếp theo thứ tự không hợp lý. Việc chuyển Fence thành một yếu tố (Factor) sẽ phù hợp hơn Vì Fence chỉ đại diện cho một nhóm danh mục mà không có thứ tự rõ ràng, nó nên được coi là một biến phân loại (Categorical Factor). Điều này giúp mô hình hồi quy hoặc phân tích dữ liệu không bị sai lệch khi gán số thứ tự sai lệch cho từng loại hàng rào.
Xử lý giá trị thiếu trong biến chất lượng lò sưởi (FireplaceQu)
1. Nhận định về dữ liệu thiếu trong
FireplaceQu
Trong tập dữ liệu của chúng ta, biến FireplaceQu thể
hiện chất lượng của lò sưởi với các mức độ sau:
- Ex (Excellent) - Lò sưởi xây bằng gạch chất lượng
cao.
- Gd (Good) - Lò sưởi xây bằng gạch ở tầng chính.
- TA (Average) - Lò sưởi lắp ghép ở tầng chính hoặc lò
sưởi xây gạch ở tầng hầm.
- Fa (Fair) - Lò sưởi lắp ghép ở tầng hầm.
- Po (Poor) - Lò sưởi kiểu Ben Franklin (chất lượng
kém).
- NA (No Fireplace) - Không có lò sưởi.
Khi kiểm tra dữ liệu, chúng ta nhận thấy rằng có 1420 giá trị
NA trong biến FireplaceQu. Tuy nhiên, khi so sánh với số lượng
nhà không có lò sưởi (Fireplaces = 0), chúng ta thấy rằng hai con số này
trùng khớp. Điều này có nghĩa là:
- NA trong FireplaceQu không phải do thiếu dữ liệu ngẫu
nhiên, mà đơn giản là những căn nhà này không có lò
sưởi.
- Vì vậy, có thể thay thế NA bằng một giá trị phù hợp như
"No Fireplace" mà không gây sai lệch dữ liệu.
2. Giải thích lý do thay thế NA bằng “No Fireplace”
Dữ liệu không thực sự bị thiếu
Một trong những bước quan trọng khi xử lý dữ liệu là phân biệt giữa dữ
liệu bị thiếu thật sự (missing values) và dữ liệu mang
ý nghĩa đặc biệt. Trong trường hợp này:
- Nếu một căn nhà có lò sưởi nhưng không có đánh giá chất
lượng, thì đó là dữ liệu bị thiếu thực sự.
- Nhưng nếu căn nhà không có lò sưởi, thì không có lý
do gì để đánh giá chất lượng lò sưởi cả, và NA chỉ phản ánh điều
này.
Vì vậy, thay thế NA bằng "No Fireplace" giúp dữ liệu trở
nên rõ ràng hơn mà không làm mất thông tin.
Hạn chế vấn đề xử lý dữ liệu sau này
Nếu giữ nguyên NA, chúng ta có thể gặp một số vấn đề:
- Khi chạy mô hình dự đoán, NA có thể gây lỗi hoặc làm
giảm chất lượng mô hình.
- Khi tính toán thống kê hoặc vẽ biểu đồ, NA có thể gây sai lệch
hoặc phải xử lý riêng.
Do đó, việc thay thế NA bằng "No Fireplace" giúp tránh
được những vấn đề này.
3. Biến FireplaceQu là một biến có thứ tự
Biến FireplaceQu mang tính thứ tự
(ordinal), nghĩa là các giá trị có mức độ tốt xấu khác
nhau, không chỉ đơn thuần là các danh mục rời rạc. Ví dụ:
\[
\text{Po} < \text{Fa} < \text{TA} < \text{Gd} < \text{Ex}
\] Khi xử lý dữ liệu, chúng ta có thể sử dụng thứ tự này để giúp
mô hình hiểu rằng “Ex” có chất lượng cao hơn “Gd”, “TA” tốt hơn “Fa”,
v.v. Điều này đặc biệt hữu ích khi làm việc với các thuật toán hồi quy
hoặc mô hình dự đoán.
Nếu bạn đã tạo vectơ thứ tự hóa cho Pool Quality, bạn có thể áp dụng tương tự cho FireplaceQu để đảm bảo mô hình hiểu được trật tự này.
all$FireplaceQu[is.na(all$FireplaceQu)] <- 'None'
all$FireplaceQu<-as.integer(revalue(all$FireplaceQu, Qualities))
table(all$FireplaceQu)
##
## 0 1 2 3 4 5
## 1420 46 74 592 744 43
table(all$Fireplaces)
##
## 0 1 2 3 4
## 1420 1268 219 11 1
sum(table(all$Fireplaces))
## [1] 2919
LotFrontage đại diện cho chiều dài đường phố kết nối với tài sản. Có 486 giá trị NA, chiếm một phần đáng kể trong dữ liệu. Giải pháp hợp lý
Vì LotFrontage phụ thuộc vào vị trí (khu phố - Neighborhood), nên lấy giá trị trung bình theo khu phố là một cách xử lý hợp lý. Điều này đảm bảo rằng giá trị thay thế phản ánh đặc điểm thực tế của từng khu vực, thay vì lấy trung bình chung toàn bộ dữ liệu.
ggplot(all[!is.na(all$LotFrontage),], aes(x=as.factor(Neighborhood), y=LotFrontage)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
1 Định dạng đồ thị
Trục X (as.factor(Neighborhood)): Biểu diễn các khu phố (Neighborhood). Trục Y (LotFrontage): Chiều dài mặt tiền của lô đất (LotFrontage). Dạng biểu đồ: Biểu đồ cột (bar chart) với các thanh màu xanh dương. 2 Phân tích dữ liệu Các khu phố có LotFrontage trung bình khác nhau rõ rệt, chứng tỏ rằng mặt tiền của lô đất có sự chênh lệch theo vị trí. Một số khu phố có LotFrontage trung bình rất cao, điển hình như: CollgCr NPkVill NridgHt Một số khu phố có LotFrontage thấp, ví dụ: MeadowV IDOTRR Blueste 3 Ý nghĩa thực tế Những khu vực có LotFrontage cao hơn có thể là những khu phố có lô đất rộng rãi, có mặt tiền lớn, có thể liên quan đến giá trị bất động sản cao hơn. Những khu vực có LotFrontage thấp hơn có thể là khu dân cư nhỏ hơn, đất chia lô nhỏ hơn hoặc có sự quy hoạch khác biệt. Nếu thay thế giá trị NA của LotFrontage bằng trung bình theo Neighborhood, thì dữ liệu sẽ hợp lý hơn vì có sự khác biệt rõ ràng giữa các khu phố.
for (i in 1:nrow(all)){
if(is.na(all$LotFrontage[i])){
all$LotFrontage[i] <- as.integer(median(all$LotFrontage[all$Neighborhood==all$Neighborhood[i]], na.rm=TRUE))
}
}
LotConfig: Cấu hình lô đất
Dữ liệu không chứa bất kỳ giá trị thiếu (NA), điều này có nghĩa là không cần thực hiện các thao tác bổ sung để xử lý dữ liệu bị thiếu.
Ban đầu, các giá trị của biến có thể được xem xét theo một thứ tự nhất định, vì một số loại có vẻ quan trọng hơn hoặc có thể ảnh hưởng đến giá trị của bất động sản. Tuy nhiên, khi trực quan hóa dữ liệu bằng biểu đồ, không có xu hướng rõ ràng nào cho thấy một mối quan hệ thứ tự giữa các danh mục.
Vì vậy, thay vì coi biến này là một biến thứ tự (ordinal variable), biến sẽ được chuyển đổi thành một biến phân loại (factor variable) để đảm bảo dữ liệu được xử lý phù hợp và phản ánh đúng bản chất của biến. Vì vậy, thay vì coi biến này là một biến thứ tự (ordinal variable), tchuyển đổi thành một biến phân loại (factor variable) để đảm bảo dữ liệu được xử lý phù hợp và phản ánh đúng bản chất của biến.
ggplot(all[!is.na(all$SalePrice),], aes(x=as.factor(LotConfig), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue')+
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..))
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
all$LotConfig <- as.factor(all$LotConfig)
table(all$LotConfig)
##
## Corner CulDSac FR2 FR3 Inside
## 511 176 85 14 2133
sum(table(all$LotConfig))
## [1] 2919
Xử lý các biến liên quan đến gara
Trong tập dữ liệu, có tổng cộng 7 biến liên quan đến gara, trong đó có một số biến chứa giá trị thiếu (NA). Cụ thể:
Trước tiên, để xử lý giá trị thiếu trong GarageYrBlt, tôi sẽ thay thế tất cả 159 giá trị NA bằng giá trị của YearBuilt – năm xây dựng của ngôi nhà. Lý do cho việc thay thế này là nếu một gara không có thông tin về năm xây dựng, có khả năng nó được xây cùng lúc với ngôi nhà. Cách tiếp cận này tương tự với biến YearRemodAdd, trong đó nếu một ngôi nhà không có thông tin về việc cải tạo, giá trị của nó mặc định bằng YearBuilt. Việc thay thế này giúp dữ liệu nhất quán hơn và tránh ảnh hưởng tiêu cực đến quá trình phân tích sau này.
all$GarageYrBlt[is.na(all$GarageYrBlt)] <- all$YearBuilt[is.na(all$GarageYrBlt)]
#check if all 157 NAs are the same observations among the variables with 157/159 NAs
length(which(is.na(all$GarageType) & is.na(all$GarageFinish) & is.na(all$GarageCond) & is.na(all$GarageQual)))
## [1] 157
#Find the 2 additional NAs
kable(all[!is.na(all$GarageType) & is.na(all$GarageFinish), c('GarageCars', 'GarageArea', 'GarageType', 'GarageCond', 'GarageQual', 'GarageFinish')])
| GarageCars | GarageArea | GarageType | GarageCond | GarageQual | GarageFinish |
|---|---|---|---|---|---|
| 1 | 360 | Detchd | NA | NA | NA |
| NA | NA | Detchd | NA | NA | NA |
Các giá trị NA trong các biến dạng ký tự liên quan đến gara thường mang ý nghĩa “Không có gara”. Tuy nhiên, GarageType có 157 NA, trong khi ba biến khác có 159 NA, cho thấy có sự chênh lệch. Tôi sẽ kiểm tra hai quan sát có NA trong ba biến còn lại nhưng không có NA trong GarageType để xác định nguyên nhân và đảm bảo tính nhất quán trước khi xử lý dữ liệu.
#Imputing modes.
all$GarageCond[2127] <- names(sort(-table(all$GarageCond)))[1]
all$GarageQual[2127] <- names(sort(-table(all$GarageQual)))[1]
all$GarageFinish[2127] <- names(sort(-table(all$GarageFinish)))[1]
#display "fixed" house
kable(all[2127, c('GarageYrBlt', 'GarageCars', 'GarageArea', 'GarageType', 'GarageCond', 'GarageQual', 'GarageFinish')])
| GarageYrBlt | GarageCars | GarageArea | GarageType | GarageCond | GarageQual | GarageFinish |
|---|---|---|---|---|---|---|
| 1910 | 1 | 360 | Detchd | TA | TA | Unf |
Trong số 157 giá trị NA của GarageType, tất cả đều đồng thời có NA trong GarageCondition, GarageQuality, và GarageFinish. Tuy nhiên, có sự khác biệt ở hai ngôi nhà 2127 và 2577. Nhà 2127 dường như có gara nhưng một số thông tin bị thiếu, trong khi nhà 2577 không có gara, phù hợp với dữ liệu còn lại. Do đó, số lượng nhà không có gara thực tế phải là 158 chứ không phải 157. Để khắc phục, cần điền giá trị phổ biến nhất (mode) vào các biến GarageCond, GarageQual, và GarageFinish cho nhà 2127, đảm bảo tính nhất quán của dữ liệu. GarageCars (sức chứa ô tô) và GarageArea (diện tích gara) đều có 1 giá trị NA, xảy ra tại nhà 2577. Nguyên nhân có thể do GarageType của nhà này được ghi là “detached” (gara tách rời), trong khi các biến khác liên quan đến gara lại cho thấy nhà này không có gara. Điều này có thể là một lỗi trong dữ liệu, cần được kiểm tra và xử lý để đảm bảo tính nhất quán.
#fixing 3 values for house 2577
all$GarageCars[2577] <- 0
all$GarageArea[2577] <- 0
all$GarageType[2577] <- NA
#check if NAs of the character variables are now all 158
length(which(is.na(all$GarageType) & is.na(all$GarageFinish) & is.na(all$GarageCond) & is.na(all$GarageQual)))
## [1] 158
GarageFinish mô tả mức độ hoàn thiện bên trong của gara và có thứ tự rõ ràng (ordinal). Do đó, biến này sẽ được chuyển đổi thành dạng biến thứ tự (ordered factor) để phản ánh mức độ hoàn thiện. Các giá trị bao gồm:
all$GarageType[is.na(all$GarageType)] <- 'No Garage'
all$GarageType <- as.factor(all$GarageType)
table(all$GarageType)
##
## 2Types Attchd Basment BuiltIn CarPort Detchd No Garage
## 23 1723 36 186 15 778 158
GarageQual đánh giá chất lượng của gara và có thứ tự rõ ràng (ordinal). Biến này có thể được mã hóa theo vectơ Qualities để phản ánh mức độ chất lượng từ cao đến thấp. Các giá trị bao gồm:
all$GarageQual[is.na(all$GarageQual)] <- 'None'
all$GarageQual<-as.integer(revalue(all$GarageQual, Qualities))
table(all$GarageQual)
##
## 0 1 2 3 4 5
## 158 5 124 2605 24 3
GarageCond đánh giá tình trạng hiện tại của gara và có thứ tự rõ ràng (ordinal). Biến này có thể được mã hóa theo vectơ Qualities để phản ánh mức độ từ tốt nhất đến kém nhất. Các giá trị bao gồm:
all$GarageCond[is.na(all$GarageCond)] <- 'None'
all$GarageCond<-as.integer(revalue(all$GarageCond, Qualities))
table(all$GarageCond)
##
## 0 1 2 3 4 5
## 158 14 74 2655 15 3
all$GarageCond[is.na(all$GarageCond)] <- 'None'
all$GarageCond<-as.integer(revalue(all$GarageCond, Qualities))
## The following `from` values were not present in `x`: None, Po, Fa, TA, Gd, Ex
table(all$GarageCond)
##
## 0 1 2 3 4 5
## 158 14 74 2655 15 3
Có tổng cộng 11 biến mô tả đặc điểm của tầng hầm trong một ngôi nhà. Trong đó:
Việc xử lý các giá trị thiếu này sẽ giúp đảm bảo tính nhất quán và độ chính xác của dữ liệu. Tổng cộng có 79 ngôi nhà không có tầng hầm, vì tất cả các biến liên quan đến tầng hầm của các ngôi nhà này đều có giá trị NA đồng nhất.
Ngoài ra, có 9 ngôi nhà có giá trị thiếu không đồng nhất, trong đó 80% dữ liệu đã đầy đủ (chỉ thiếu 1 trong 5 biến tầng hầm). Để khắc phục, sẽ tính toán giá trị phổ biến nhất (mode) và điền vào các giá trị còn thiếu để đảm bảo tính nhất quán trong dữ liệu..
#check if all 79 NAs are the same observations among the variables with 80+ NAs
length(which(is.na(all$BsmtQual) & is.na(all$BsmtCond) & is.na(all$BsmtExposure) & is.na(all$BsmtFinType1) & is.na(all$BsmtFinType2)))
## [1] 79
#Find the additional NAs; BsmtFinType1 is the one with 79 NAs
all[!is.na(all$BsmtFinType1) & (is.na(all$BsmtCond)|is.na(all$BsmtQual)|is.na(all$BsmtExposure)|is.na(all$BsmtFinType2)), c('BsmtQual', 'BsmtCond', 'BsmtExposure', 'BsmtFinType1', 'BsmtFinType2')]
## # A tibble: 9 × 5
## BsmtQual BsmtCond BsmtExposure BsmtFinType1 BsmtFinType2
## <chr> <chr> <chr> <chr> <chr>
## 1 Gd TA No GLQ <NA>
## 2 Gd TA <NA> Unf Unf
## 3 Gd TA <NA> Unf Unf
## 4 Gd <NA> Mn GLQ Rec
## 5 TA <NA> No BLQ Unf
## 6 <NA> Fa No Unf Unf
## 7 <NA> TA No Unf Unf
## 8 Gd TA <NA> Unf Unf
## 9 TA <NA> Av ALQ Unf
#Imputing modes.
all$BsmtFinType2[333] <- names(sort(-table(all$BsmtFinType2)))[1]
all$BsmtExposure[c(949, 1488, 2349)] <- names(sort(-table(all$BsmtExposure)))[1]
all$BsmtCond[c(2041, 2186, 2525)] <- names(sort(-table(all$BsmtCond)))[1]
all$BsmtQual[c(2218, 2219)] <- names(sort(-table(all$BsmtQual)))[1]
Sau khi xác nhận rằng 5 biến liên quan đến tầng hầm đều nhất quán với 79 ngôi nhà không có tầng hầm, các biến này sẽ được mã hóa lại để đảm bảo tính thứ tự.
BsmtQual đánh giá chiều cao của tầng hầm và có thứ tự rõ ràng (ordinal). Biến này có thể được mã hóa theo vectơ Qualities, với các mức như sau:
BsmtCond: Đánh giá tình trạng tổng thể của tầng hầm
Đây là một biến có thể được xem xét theo thứ tự với thang đo chất lượng sau:
all$BsmtCond[is.na(all$BsmtCond)] <- 'None'
all$BsmtCond<-as.integer(revalue(all$BsmtCond, Qualities))
table(all$BsmtCond)
##
## 0 1 2 3 4
## 79 5 104 2609 122
BsmtExposure: Đề cập đến tường có lối đi hoặc mức vườn
Đây là một biến có thể được xem xét theo thứ tự với các mức sau:
all$BsmtExposure[is.na(all$BsmtExposure)] <- 'None'
Exposure <- c('None'=0, 'No'=1, 'Mn'=2, 'Av'=3, 'Gd'=4)
all$BsmtExposure<-as.integer(revalue(all$BsmtExposure, Exposure))
table(all$BsmtExposure)
##
## 0 1 2 3 4
## 79 1907 239 418 276
BsmtFinType1: Đánh giá khu vực tầng hầm đã hoàn thiện
Đây là một biến có thể được xem xét theo thứ tự với các mức sau:
all$BsmtFinType1[is.na(all$BsmtFinType1)] <- 'None'
FinType <- c('None'=0, 'Unf'=1, 'LwQ'=2, 'Rec'=3, 'BLQ'=4, 'ALQ'=5, 'GLQ'=6)
all$BsmtFinType1<-as.integer(revalue(all$BsmtFinType1, FinType))
table(all$BsmtFinType1)
##
## 0 1 2 3 4 5 6
## 79 851 154 288 269 429 849
BsmtFinType2: Đánh giá khu vực tầng hầm đã hoàn thiện (nếu có nhiều loại)
Đây là một biến có thể được xem xét theo thứ tự với thang đo sau:
all$BsmtFinType2[is.na(all$BsmtFinType2)] <- 'None'
FinType <- c('None'=0, 'Unf'=1, 'LwQ'=2, 'Rec'=3, 'BLQ'=4, 'ALQ'=5, 'GLQ'=6)
all$BsmtFinType2<-as.integer(revalue(all$BsmtFinType2, FinType))
table(all$BsmtFinType2)
##
## 0 1 2 3 4 5 6
## 79 2494 87 105 68 52 34
#display remaining NAs. Using BsmtQual as a reference for the 79 houses without basement agreed upon earlier
all[(is.na(all$BsmtFullBath)|is.na(all$BsmtHalfBath)|is.na(all$BsmtFinSF1)|is.na(all$BsmtFinSF2)|is.na(all$BsmtUnfSF)|is.na(all$TotalBsmtSF)), c('BsmtQual', 'BsmtFullBath', 'BsmtHalfBath', 'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF', 'TotalBsmtSF')]
## # A tibble: 2 × 7
## BsmtQual BsmtFullBath BsmtHalfBath BsmtFinSF1 BsmtFinSF2 BsmtUnfSF TotalBsmtSF
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 <NA> NA NA NA NA NA NA
## 2 <NA> NA NA 0 0 0 0
NA trong BsmtFullBath biểu thị “không có tầng hầm”, không phải dữ liệu bị mất. BsmtFullBath là biến số nguyên, chỉ số lượng phòng tắm đầy đủ trong tầng hầm (0, 1, 2,…). Cách xử lý NA hợp lý: Thay thế bằng 0 để phản ánh đúng ý nghĩa dữ liệu.
all$BsmtFullBath[is.na(all$BsmtFullBath)] <-0
table(all$BsmtFullBath)
##
## 0 1 2 3
## 1707 1172 38 2
BsmtHalfBath: Một nửa phòng tắm ở tầng hầm
Một biến số nguyên.
all$BsmtHalfBath[is.na(all$BsmtHalfBath)] <-0
table(all$BsmtHalfBath)
##
## 0 1 2
## 2744 171 4
BsmtHalfBath: Một nửa phòng tắm ở tầng hầm
Một biến số nguyên.
all$BsmtFinSF1[is.na(all$BsmtFinSF1)] <-0
BsmtFinSF1: Loại 1 đã hoàn thành feet vuông
Một biến số nguyên
all$BsmtFinSF2[is.na(all$BsmtFinSF2)] <-0
BsmtFinSF2: Loại 2 feet vuông đã hoàn thành
Một biến số nguyên.
all$BsmtUnfSF[is.na(all$BsmtUnfSF)] <-0
BsmtUnfSF: Diện tích tầng hầm chưa hoàn thiện
Một biến số nguyên.
all$TotalBsmtSF[is.na(all$TotalBsmtSF)] <-0
TotalBsmtSF: Tổng diện tích tầng hầm bằng feet vuông
Một biến số nguyên.
Loại lớp phủ gạch (Masonry veneer type) và diện tích lớp phủ gạch (Masonry veneer area).
Loại lớp phủ gạch có 24 giá trị NA. Diện tích lớp phủ gạch có 23 giá trị NA. Nếu một ngôi nhà có diện tích lớp phủ gạch, thì nó cũng phải có loại lớp phủ gạch. Hãy sửa lỗi này trước.
#check if the 23 houses with veneer area NA are also NA in the veneer type
length(which(is.na(all$MasVnrType) & is.na(all$MasVnrArea)))
## [1] 23
#find the one that should have a MasVnrType
all[is.na(all$MasVnrType) & !is.na(all$MasVnrArea), c('MasVnrType', 'MasVnrArea')]
## # A tibble: 1 × 2
## MasVnrType MasVnrArea
## <chr> <dbl>
## 1 <NA> 198
#fix this veneer type by imputing the mode
all$MasVnrType[2611] <- names(sort(-table(all$MasVnrType)))[2] #taking the 2nd value as the 1st is 'none'
all[2611, c('MasVnrType', 'MasVnrArea')]
## # A tibble: 1 × 2
## MasVnrType MasVnrArea
## <chr> <dbl>
## 1 BrkFace 198
Điều này để lại 23 ngôi nhà thực sự không có lớp phủ gạch.
Loại lớp phủ gạch:
Sẽ kiểm tra tính thứ bậc bên dưới.
all$MasVnrType[is.na(all$MasVnrType)] <- 'None'
all[!is.na(all$SalePrice),] %>% group_by(MasVnrType) %>% summarise(median = median(SalePrice), counts=n()) %>% arrange(median)
## # A tibble: 4 × 3
## MasVnrType median counts
## <chr> <dbl> <int>
## 1 BrkCmn 139000 15
## 2 None 143125 872
## 3 BrkFace 181000 445
## 4 Stone 246839 128
Masonry <- c('None'=0, 'BrkCmn'=0, 'BrkFace'=1, 'Stone'=2)
all$MasVnrType<-as.integer(revalue(all$MasVnrType, Masonry))
table(all$MasVnrType)
##
## 0 1 2
## 1790 880 249
MasVnrArea là diện tích lớp phủ gạch (masonry veneer) của ngôi nhà, được đo bằng feet vuông.
all$MasVnrArea[is.na(all$MasVnrArea)] <-0
MSZoning: Xác định phân loại quy hoạch chung của bất động sản được bán.
Các giá trị:
- A – Nông nghiệp (Agriculture)
- C – Thương mại (Commercial)
- FV – Khu dân cư làng nổi (Floating Village
Residential)
- I – Công nghiệp (Industrial)
- RH – Khu dân cư mật độ cao (Residential High
Density)
- RL – Khu dân cư mật độ thấp (Residential Low
Density)
- RP – Công viên khu dân cư mật độ thấp (Residential
Low Density Park)
- RM – Khu dân cư mật độ trung bình (Residential Medium
Density)
#imputing the mode
all$MSZoning[is.na(all$MSZoning)] <- names(sort(-table(all$MSZoning)))[1]
all$MSZoning <- as.factor(all$MSZoning)
table(all$MSZoning)
##
## C (all) FV RH RL RM
## 25 139 26 2269 460
sum(table(all$MSZoning))
## [1] 2919
all$KitchenQual[is.na(all$KitchenQual)] <- 'TA' #replace with most common value
all$KitchenQual<-as.integer(revalue(all$KitchenQual, Qualities))
table(all$KitchenQual)
##
## 2 3 4 5
## 70 1493 1151 205
sum(table(all$KitchenQual))
## [1] 2919
table(all$KitchenAbvGr)
##
## 0 1 2 3
## 3 2785 129 2
sum(table(all$KitchenAbvGr))
## [1] 2919
table(all$Utilities)
##
## AllPub NoSeWa
## 2916 1
kable(all[is.na(all$Utilities) | all$Utilities=='NoSeWa', 1:9])
| MSSubClass | MSZoning | LotFrontage | LotArea | Street | Alley | LotShape | LandContour | Utilities |
|---|---|---|---|---|---|---|---|---|
| 20 | RL | 82 | 14375 | Pave | None | IR1 | Lvl | NoSeWa |
| 30 | RL | 109 | 21780 | Grvl | None | Reg | Lvl | NA |
| 20 | RL | 64 | 31220 | Pave | None | IR1 | Bnk | NA |
all$Utilities <- NULL
#impute mode for the 1 NA
all$Functional[is.na(all$Functional)] <- names(sort(-table(all$Functional)))[1]
all$Functional <- as.integer(revalue(all$Functional, c('Sal'=0, 'Sev'=1, 'Maj2'=2, 'Maj1'=3, 'Mod'=4, 'Min2'=5, 'Min1'=6, 'Typ'=7)))
table(all$Functional)
##
## 1 2 3 4 5 6 7
## 2 9 19 35 70 65 2719
sum(table(all$Functional))
## [1] 2919
#imputing mode
all$Exterior1st[is.na(all$Exterior1st)] <- names(sort(-table(all$Exterior1st)))[1]
all$Exterior1st <- as.factor(all$Exterior1st)
table(all$Exterior1st)
##
## AsbShng AsphShn BrkComm BrkFace CBlock CemntBd HdBoard ImStucc MetalSd Plywood
## 44 2 6 87 2 126 442 1 450 221
## Stone Stucco VinylSd Wd Sdng WdShing
## 2 43 1026 411 56
sum(table(all$Exterior1st))
## [1] 2919
#imputing mode
all$Exterior2nd[is.na(all$Exterior2nd)] <- names(sort(-table(all$Exterior2nd)))[1]
all$Exterior2nd <- as.factor(all$Exterior2nd)
table(all$Exterior2nd)
##
## AsbShng AsphShn Brk Cmn BrkFace CBlock CmentBd HdBoard ImStucc MetalSd Other
## 38 4 22 47 3 126 406 15 447 1
## Plywood Stone Stucco VinylSd Wd Sdng Wd Shng
## 270 6 47 1015 391 81
sum(table(all$Exterior2nd))
## [1] 2919
all$ExterQual<-as.integer(revalue(all$ExterQual, Qualities))
## The following `from` values were not present in `x`: None, Po
table(all$ExterQual)
##
## 2 3 4 5
## 35 1798 979 107
sum(table(all$ExterQual))
## [1] 2919
all$ExterCond<-as.integer(revalue(all$ExterCond, Qualities))
## The following `from` values were not present in `x`: None
table(all$ExterCond)
##
## 1 2 3 4 5
## 3 67 2538 299 12
sum(table(all$ExterCond))
## [1] 2919
#imputing mode
all$Electrical[is.na(all$Electrical)] <- names(sort(-table(all$Electrical)))[1]
all$Electrical <- as.factor(all$Electrical)
table(all$Electrical)
##
## FuseA FuseF FuseP Mix SBrkr
## 188 50 8 1 2672
sum(table(all$Electrical))
## [1] 2919
#imputing mode
all$SaleType[is.na(all$SaleType)] <- names(sort(-table(all$SaleType)))[1]
all$SaleType <- as.factor(all$SaleType)
table(all$SaleType)
##
## COD Con ConLD ConLI ConLw CWD New Oth WD
## 87 5 26 9 8 12 239 7 2526
sum(table(all$SaleType))
## [1] 2919
all$SaleCondition <- as.factor(all$SaleCondition)
table(all$SaleCondition)
##
## Abnorml AdjLand Alloca Family Normal Partial
## 190 12 24 46 2402 245
sum(table(all$SaleCondition))
## [1] 2919
Charcol <- names(all[,sapply(all, is.character)])
Charcol
## [1] "Street" "LotShape" "LandContour" "LandSlope" "Neighborhood"
## [6] "Condition1" "Condition2" "BldgType" "HouseStyle" "RoofStyle"
## [11] "RoofMatl" "Foundation" "BsmtQual" "Heating" "HeatingQC"
## [16] "CentralAir" "GarageFinish" "PavedDrive"
cat('There are', length(Charcol), 'remaining columns with character values')
## There are 18 remaining columns with character values
#No ordinality, so converting into factors
all$Foundation <- as.factor(all$Foundation)
table(all$Foundation)
##
## BrkTil CBlock PConc Slab Stone Wood
## 311 1235 1308 49 11 5
sum(table(all$Foundation))
## [1] 2919
#No ordinality, so converting into factors
all$Heating <- as.factor(all$Heating)
table(all$Heating)
##
## Floor GasA GasW Grav OthW Wall
## 1 2874 27 9 2 6
sum(table(all$Heating))
## [1] 2919
#making the variable ordinal using the Qualities vector
all$HeatingQC<-as.integer(revalue(all$HeatingQC, Qualities))
## The following `from` values were not present in `x`: None
table(all$HeatingQC)
##
## 1 2 3 4 5
## 3 92 857 474 1493
sum(table(all$HeatingQC))
## [1] 2919
all$CentralAir<-as.integer(revalue(all$CentralAir, c('N'=0, 'Y'=1)))
table(all$CentralAir)
##
## 0 1
## 196 2723
sum(table(all$CentralAir))
## [1] 2919
#No ordinality, so converting into factors
all$RoofStyle <- as.factor(all$RoofStyle)
table(all$RoofStyle)
##
## Flat Gable Gambrel Hip Mansard Shed
## 20 2310 22 551 11 5
sum(table(all$RoofStyle))
## [1] 2919
#No ordinality, so converting into factors
all$RoofMatl <- as.factor(all$RoofMatl)
table(all$RoofMatl)
##
## ClyTile CompShg Membran Metal Roll Tar&Grv WdShake WdShngl
## 1 2876 1 1 1 23 9 7
sum(table(all$RoofMatl))
## [1] 2919
#No ordinality, so converting into factors
all$LandContour <- as.factor(all$LandContour)
table(all$LandContour)
##
## Bnk HLS Low Lvl
## 117 120 60 2622
sum(table(all$LandContour))
## [1] 2919
#Ordinal, so label encoding
all$LandSlope<-as.integer(revalue(all$LandSlope, c('Sev'=0, 'Mod'=1, 'Gtl'=2)))
table(all$LandSlope)
##
## 0 1 2
## 16 125 2778
sum(table(all$LandSlope))
## [1] 2919
ggplot(all[!is.na(all$SalePrice),], aes(x=as.factor(BldgType), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue')+
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..))
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
#No ordinality, so converting into factors
all$BldgType <- as.factor(all$BldgType)
table(all$BldgType)
##
## 1Fam 2fmCon Duplex Twnhs TwnhsE
## 2425 62 109 96 227
sum(table(all$BldgType))
## [1] 2919
#No ordinality, so converting into factors
all$HouseStyle <- as.factor(all$HouseStyle)
table(all$HouseStyle)
##
## 1.5Fin 1.5Unf 1Story 2.5Fin 2.5Unf 2Story SFoyer SLvl
## 314 19 1471 8 24 872 83 128
sum(table(all$HouseStyle))
## [1] 2919
#No ordinality, so converting into factors
all$Neighborhood <- as.factor(all$Neighborhood)
table(all$Neighborhood)
##
## Blmngtn Blueste BrDale BrkSide ClearCr CollgCr Crawfor Edwards Gilbert IDOTRR
## 28 10 30 108 44 267 103 194 165 93
## MeadowV Mitchel NAmes NoRidge NPkVill NridgHt NWAmes OldTown Sawyer SawyerW
## 37 114 443 71 23 166 131 239 151 125
## Somerst StoneBr SWISU Timber Veenker
## 182 51 48 72 24
sum(table(all$Neighborhood))
## [1] 2919
#No ordinality, so converting into factors
all$Condition1 <- as.factor(all$Condition1)
table(all$Condition1)
##
## Artery Feedr Norm PosA PosN RRAe RRAn RRNe RRNn
## 92 164 2511 20 39 28 50 6 9
sum(table(all$Condition1))
## [1] 2919
#No ordinality, so converting into factors
all$Condition2 <- as.factor(all$Condition2)
table(all$Condition2)
##
## Artery Feedr Norm PosA PosN RRAe RRAn RRNn
## 5 13 2889 4 4 1 1 2
sum(table(all$Condition2))
## [1] 2919
#Ordinal, so label encoding
all$Street<-as.integer(revalue(all$Street, c('Grvl'=0, 'Pave'=1)))
table(all$Street)
##
## 0 1
## 12 2907
sum(table(all$Street))
## [1] 2919
#Ordinal, so label encoding
all$PavedDrive<-as.integer(revalue(all$PavedDrive, c('N'=0, 'P'=1, 'Y'=2)))
table(all$PavedDrive)
##
## 0 1 2
## 216 62 2641
sum(table(all$PavedDrive))
## [1] 2919
str(all$YrSold)
## num [1:2919] 2008 2007 2008 2006 2008 ...
str(all$MoSold)
## num [1:2919] 2 5 9 2 12 10 8 11 4 1 ...
all$MoSold <- as.factor(all$MoSold)
ys <- ggplot(all[!is.na(all$SalePrice),], aes(x=as.factor(YrSold), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue')+
scale_y_continuous(breaks= seq(0, 800000, by=25000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..)) +
coord_cartesian(ylim = c(0, 200000)) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
ms <- ggplot(all[!is.na(all$SalePrice),], aes(x=MoSold, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue')+
scale_y_continuous(breaks= seq(0, 800000, by=25000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..)) +
coord_cartesian(ylim = c(0, 200000)) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
grid.arrange(ys, ms, widths=c(1,2))
## No summary function supplied, defaulting to `mean_se()`
## No summary function supplied, defaulting to `mean_se()`
str(all$MSSubClass)
## num [1:2919] 60 20 60 70 60 50 20 60 50 190 ...
all$MSSubClass <- as.factor(all$MSSubClass)
#revalue for better readability
all$MSSubClass<-revalue(all$MSSubClass, c('20'='1 story 1946+', '30'='1 story 1945-', '40'='1 story unf attic', '45'='1,5 story unf', '50'='1,5 story fin', '60'='2 story 1946+', '70'='2 story 1945-', '75'='2,5 story all ages', '80'='split/multi level', '85'='split foyer', '90'='duplex all style/age', '120'='1 story PUD 1946+', '150'='1,5 story PUD all', '160'='2 story PUD 1946+', '180'='PUD multilevel', '190'='2 family conversion'))
str(all$MSSubClass)
## Factor w/ 16 levels "1 story 1946+",..: 6 1 6 7 6 5 1 6 5 16 ...
numericVars <- which(sapply(all, is.numeric)) #index vector numeric variables
factorVars <- which(sapply(all, is.factor)) #index vector factor variables
cat('There are', length(numericVars), 'numeric variables, and', length(factorVars), 'categoric variables')
## There are 53 numeric variables, and 23 categoric variables
all_numVar <- all[, numericVars]
cor_numVar <- cor(all_numVar, use="pairwise.complete.obs") #correlations of all numeric variables
#sort on decreasing correlations with SalePrice
cor_sorted <- as.matrix(sort(cor_numVar[,'SalePrice'], decreasing = TRUE))
#select only high corelations
CorHigh <- names(which(apply(cor_sorted, 1, function(x) abs(x)>0.5)))
cor_numVar <- cor_numVar[CorHigh, CorHigh]
corrplot.mixed(cor_numVar, tl.col="black", tl.pos = "lt", tl.cex = 0.7,cl.cex = .7, number.cex=.7)
library(randomForest)
library(ggplot2)
set.seed(2018)
# Loại bỏ NA trong tập dữ liệu đầu vào
clean_data <- na.omit(all[1:1460, -79])
clean_target <- all$SalePrice[1:1460][complete.cases(all[1:1460, -79])]
# Xây dựng mô hình Random Forest
quick_RF <- randomForest(x = clean_data, y = clean_target, ntree = 100, importance = TRUE)
# Tính toán độ quan trọng của biến
imp_RF <- importance(quick_RF)
imp_DF <- data.frame(Variables = row.names(imp_RF), MSE = imp_RF[,1])
imp_DF <- imp_DF[order(imp_DF$MSE, decreasing = TRUE),]
# Vẽ biểu đồ
ggplot(imp_DF[1:20,], aes(x=reorder(Variables, MSE), y=MSE, fill=MSE)) +
geom_bar(stat = 'identity') +
labs(x = 'Variables', y= '% increase MSE if variable is randomly permuted') +
coord_flip() +
theme(legend.position = "none")
colnames(all)
## [1] "MSSubClass" "MSZoning" "LotFrontage" "LotArea"
## [5] "Street" "Alley" "LotShape" "LandContour"
## [9] "LotConfig" "LandSlope" "Neighborhood" "Condition1"
## [13] "Condition2" "BldgType" "HouseStyle" "OverallQual"
## [17] "OverallCond" "YearBuilt" "YearRemodAdd" "RoofStyle"
## [21] "RoofMatl" "Exterior1st" "Exterior2nd" "MasVnrType"
## [25] "MasVnrArea" "ExterQual" "ExterCond" "Foundation"
## [29] "BsmtQual" "BsmtCond" "BsmtExposure" "BsmtFinType1"
## [33] "BsmtFinSF1" "BsmtFinType2" "BsmtFinSF2" "BsmtUnfSF"
## [37] "TotalBsmtSF" "Heating" "HeatingQC" "CentralAir"
## [41] "Electrical" "1stFlrSF" "2ndFlrSF" "LowQualFinSF"
## [45] "GrLivArea" "BsmtFullBath" "BsmtHalfBath" "FullBath"
## [49] "HalfBath" "BedroomAbvGr" "KitchenAbvGr" "KitchenQual"
## [53] "TotRmsAbvGrd" "Functional" "Fireplaces" "FireplaceQu"
## [57] "GarageType" "GarageYrBlt" "GarageFinish" "GarageCars"
## [61] "GarageArea" "GarageQual" "GarageCond" "PavedDrive"
## [65] "WoodDeckSF" "OpenPorchSF" "EnclosedPorch" "3SsnPorch"
## [69] "ScreenPorch" "PoolArea" "PoolQC" "Fence"
## [73] "MiscFeature" "MiscVal" "MoSold" "YrSold"
## [77] "SaleType" "SaleCondition" "SalePrice"
s1 <- ggplot(data= all, aes(x=GrLivArea)) +
geom_density() + labs(x='Square feet living area')
s2 <- ggplot(data=all, aes(x=as.factor(TotRmsAbvGrd))) +
geom_histogram(stat='count') + labs(x='Rooms above Ground')
s3 <- ggplot(data= all, aes(x=`1stFlrSF`)) +
geom_density() + labs(x='Square feet first floor')
s4 <- ggplot(data= all, aes(x=`2ndFlrSF`)) +
geom_density() + labs(x='Square feet second floor')
s5 <- ggplot(data= all, aes(x=TotalBsmtSF)) +
geom_density() + labs(x='Square feet basement')
s6 <- ggplot(data= all[all$LotArea<100000,], aes(x=LotArea)) +
geom_density() + labs(x='Square feet lot')
s7 <- ggplot(data= all, aes(x=LotFrontage)) +
geom_density() + labs(x='Linear feet lot frontage')
s8 <- ggplot(data= all, aes(x=LowQualFinSF)) +
geom_histogram() + labs(x='Low quality square feet 1st & 2nd')
layout <- matrix(c(1,2,5,3,4,8,6,7),4,2,byrow=TRUE)
multiplot(s1, s2, s3, s4, s5, s6, s7, s8, layout=layout)
cor(all$GrLivArea, (all$`1stFlrSF` + all$`2ndFlrSF` + all$LowQualFinSF))
## [1] 1
head(all[all$LowQualFinSF>0, c('GrLivArea', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF')])
## # A tibble: 6 × 4
## GrLivArea `1stFlrSF` `2ndFlrSF` LowQualFinSF
## <dbl> <dbl> <dbl> <dbl>
## 1 1176 816 0 360
## 2 1526 1013 0 513
## 3 754 520 0 234
## 4 1382 854 0 528
## 5 3608 1518 1518 572
## 6 1656 808 704 144
n1 <- ggplot(all[!is.na(all$SalePrice),], aes(x=Neighborhood, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
n2 <- ggplot(data=all, aes(x=Neighborhood)) +
geom_histogram(stat='count')+
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3)+
theme(axis.text.x = element_text(angle = 45, hjust = 1))
grid.arrange(n1, n2)
## No summary function supplied, defaulting to `mean_se()`
q1 <- ggplot(data=all, aes(x=as.factor(OverallQual))) +
geom_histogram(stat='count')
q2 <- ggplot(data=all, aes(x=as.factor(ExterQual))) +
geom_histogram(stat='count')
q3 <- ggplot(data=all, aes(x=as.factor(BsmtQual))) +
geom_histogram(stat='count')
q4 <- ggplot(data=all, aes(x=as.factor(KitchenQual))) +
geom_histogram(stat='count')
q5 <- ggplot(data=all, aes(x=as.factor(GarageQual))) +
geom_histogram(stat='count')
q6 <- ggplot(data=all, aes(x=as.factor(FireplaceQu))) +
geom_histogram(stat='count')
q7 <- ggplot(data=all, aes(x=as.factor(PoolQC))) +
geom_histogram(stat='count')
layout <- matrix(c(1,2,8,3,4,8,5,6,7),3,3,byrow=TRUE)
multiplot(q1, q2, q3, q4, q5, q6, q7, layout=layout)
ms1 <- ggplot(all[!is.na(all$SalePrice),], aes(x=MSSubClass, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
ms2 <- ggplot(data=all, aes(x=MSSubClass)) +
geom_histogram(stat='count')+
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
grid.arrange(ms1, ms2)
## No summary function supplied, defaulting to `mean_se()`
#correct error
all$GarageYrBlt[2593] <- 2007 #this must have been a typo. GarageYrBlt=2207, YearBuilt=2006, YearRemodAdd=2007.
g1 <- ggplot(data=all[all$GarageCars !=0,], aes(x=GarageYrBlt)) +
geom_histogram()
g2 <- ggplot(data=all, aes(x=as.factor(GarageCars))) +
geom_histogram(stat='count')
g3 <- ggplot(data= all, aes(x=GarageArea)) +
geom_density()
g4 <- ggplot(data=all, aes(x=as.factor(GarageCond))) +
geom_histogram(stat='count')
g5 <- ggplot(data=all, aes(x=GarageType)) +
geom_histogram(stat='count')
g6 <- ggplot(data=all, aes(x=as.factor(GarageQual))) +
geom_histogram(stat='count')
g7 <- ggplot(data=all, aes(x=as.factor(GarageFinish))) +
geom_histogram(stat='count')
layout <- matrix(c(1,5,5,2,3,8,6,4,7),3,3,byrow=TRUE)
multiplot(g1, g2, g3, g4, g5, g6, g7, layout=layout)
b1 <- ggplot(data=all, aes(x=BsmtFinSF1)) +
geom_histogram() + labs(x='Type 1 finished square feet')
b2 <- ggplot(data=all, aes(x=BsmtFinSF2)) +
geom_histogram()+ labs(x='Type 2 finished square feet')
b3 <- ggplot(data=all, aes(x=BsmtUnfSF)) +
geom_histogram()+ labs(x='Unfinished square feet')
b4 <- ggplot(data=all, aes(x=as.factor(BsmtFinType1))) +
geom_histogram(stat='count')+ labs(x='Rating of Type 1 finished area')
b5 <- ggplot(data=all, aes(x=as.factor(BsmtFinType2))) +
geom_histogram(stat='count')+ labs(x='Rating of Type 2 finished area')
b6 <- ggplot(data=all, aes(x=as.factor(BsmtQual))) +
geom_histogram(stat='count')+ labs(x='Height of the basement')
b7 <- ggplot(data=all, aes(x=as.factor(BsmtCond))) +
geom_histogram(stat='count')+ labs(x='Rating of general condition')
b8 <- ggplot(data=all, aes(x=as.factor(BsmtExposure))) +
geom_histogram(stat='count')+ labs(x='Walkout or garden level walls')
layout <- matrix(c(1,2,3,4,5,9,6,7,8),3,3,byrow=TRUE)
multiplot(b1, b2, b3, b4, b5, b6, b7, b8, layout=layout)
all$TotBathrooms <- all$FullBath + (all$HalfBath*0.5) + all$BsmtFullBath + (all$BsmtHalfBath*0.5)
tb1 <- ggplot(data=all[!is.na(all$SalePrice),], aes(x=as.factor(TotBathrooms), y=SalePrice))+
geom_point(col='blue') + geom_smooth(method = "lm", se=FALSE, color="black", aes(group=1)) +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma)
tb2 <- ggplot(data=all, aes(x=as.factor(TotBathrooms))) +
geom_histogram(stat='count')
grid.arrange(tb1, tb2)
## `geom_smooth()` using formula = 'y ~ x'
all$Remod <- ifelse(all$YearBuilt==all$YearRemodAdd, 0, 1) #0=No Remodeling, 1=Remodeling
all$Age <- as.numeric(all$YrSold)-all$YearRemodAdd
ggplot(data=all[!is.na(all$SalePrice),], aes(x=Age, y=SalePrice))+
geom_point(col='blue') + geom_smooth(method = "lm", se=FALSE, color="black", aes(group=1)) +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma)
## `geom_smooth()` using formula = 'y ~ x'
cor(all$SalePrice[!is.na(all$SalePrice)], all$Age[!is.na(all$SalePrice)])
## [1] -0.5090787
ggplot(all[!is.na(all$SalePrice),], aes(x=as.factor(Remod), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=6) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
theme_grey(base_size = 18) +
geom_hline(yintercept=163000, linetype="dashed") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
all$IsNew <- ifelse(all$YrSold==all$YearBuilt, 1, 0)
table(all$IsNew)
##
## 0 1
## 2803 116
ggplot(all[!is.na(all$SalePrice),], aes(x=as.factor(IsNew), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=6) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
theme_grey(base_size = 18) +
geom_hline(yintercept=163000, linetype="dashed") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
## No summary function supplied, defaulting to `mean_se()`
all$YrSold <- as.factor(all$YrSold) #the numeric version is now not needed anymore
nb1 <- ggplot(all[!is.na(all$SalePrice),], aes(x=reorder(Neighborhood, SalePrice, FUN=median), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') + labs(x='Neighborhood', y='Median SalePrice') +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "median", fill = "blue"):
## Ignoring unknown parameters: `fun.y`
nb2 <- ggplot(all[!is.na(all$SalePrice),], aes(x=reorder(Neighborhood, SalePrice, FUN=mean), y=SalePrice)) +
geom_bar(stat='summary', fun.y = "mean", fill='blue') + labs(x='Neighborhood', y="Mean SalePrice") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=163000, linetype="dashed", color = "red") #dashed line is median SalePrice
## Warning in geom_bar(stat = "summary", fun.y = "mean", fill = "blue"): Ignoring
## unknown parameters: `fun.y`
grid.arrange(nb1, nb2)
## No summary function supplied, defaulting to `mean_se()`
## No summary function supplied, defaulting to `mean_se()`
all$NeighRich[all$Neighborhood %in% c('StoneBr', 'NridgHt', 'NoRidge')] <- 2
## Warning: Unknown or uninitialised column: `NeighRich`.
all$NeighRich[!all$Neighborhood %in% c('MeadowV', 'IDOTRR', 'BrDale', 'StoneBr', 'NridgHt', 'NoRidge')] <- 1
all$NeighRich[all$Neighborhood %in% c('MeadowV', 'IDOTRR', 'BrDale')] <- 0
table(all$NeighRich)
##
## 0 1 2
## 160 2471 288
all$TotalSqFeet <- all$GrLivArea + all$TotalBsmtSF
ggplot(data=all[!is.na(all$SalePrice),], aes(x=TotalSqFeet, y=SalePrice))+
geom_point(col='blue') + geom_smooth(method = "lm", se=FALSE, color="black", aes(group=1)) +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma) +
geom_text_repel(aes(label = ifelse(all$GrLivArea[!is.na(all$SalePrice)]>4500, rownames(all), '')))
## `geom_smooth()` using formula = 'y ~ x'
cor(all$SalePrice, all$TotalSqFeet, use= "pairwise.complete.obs")
## [1] 0.7789588
cor(all$SalePrice[-c(524, 1299)], all$TotalSqFeet[-c(524, 1299)], use= "pairwise.complete.obs")
## [1] 0.829042
all$TotalPorchSF <- all$OpenPorchSF + all$EnclosedPorch + all$`3SsnPorch` + all$ScreenPorch
cor(all$SalePrice, all$TotalPorchSF, use= "pairwise.complete.obs")
## [1] 0.1957389
ggplot(data=all[!is.na(all$SalePrice),], aes(x=TotalPorchSF, y=SalePrice))+
geom_point(col='blue') + geom_smooth(method = "lm", se=FALSE, color="black", aes(group=1)) +
scale_y_continuous(breaks= seq(0, 800000, by=100000), labels = comma)
## `geom_smooth()` using formula = 'y ~ x'
dropVars <- c('YearRemodAdd', 'GarageYrBlt', 'GarageArea', 'GarageCond', 'TotalBsmtSF', 'TotalRmsAbvGrd', 'BsmtFinSF1')
all <- all[,!(names(all) %in% dropVars)]
all <- all[-c(524, 1299),]
numericVarNames <- numericVarNames[!(numericVarNames %in% c('MSSubClass', 'MoSold', 'YrSold', 'SalePrice', 'OverallQual', 'OverallCond'))] #numericVarNames was created before having done anything
numericVarNames <- append(numericVarNames, c('Age', 'TotalPorchSF', 'TotBathrooms', 'TotalSqFeet'))
DFnumeric <- all[, names(all) %in% numericVarNames]
DFfactors <- all[, !(names(all) %in% numericVarNames)]
DFfactors <- DFfactors[, names(DFfactors) != 'SalePrice']
cat('There are', length(DFnumeric), 'numeric variables, and', length(DFfactors), 'factor variables')
## There are 30 numeric variables, and 49 factor variables
for(i in 1:ncol(DFnumeric)){
if (abs(skew(DFnumeric[,i]))>0.8){
DFnumeric[,i] <- log(DFnumeric[,i] +1)
}
}
## Warning in FUN(X[[i]], ...): NaNs produced
PreNum <- preProcess(DFnumeric, method=c("center", "scale"))
## Warning in preProcess.default(DFnumeric, method = c("center", "scale")): Std.
## deviations could not be computed for: Age
print(PreNum)
## Created from 2915 samples and 30 variables
##
## Pre-processing:
## - centered (30)
## - ignored (0)
## - scaled (30)
DFnorm <- predict(PreNum, DFnumeric)
dim(DFnorm)
## [1] 2917 30
DFdummies <- as.data.frame(model.matrix(~.-1, DFfactors))
dim(DFdummies)
## [1] 2694 206
#check if some values are absent in the test set
ZerocolTest <- which(colSums(DFdummies[(nrow(all[!is.na(all$SalePrice),])+1):nrow(all),])==0)
colnames(DFdummies[ZerocolTest])
## character(0)
DFdummies <- DFdummies[,-ZerocolTest] #removing predictors
#check if some values are absent in the train set
ZerocolTrain <- which(colSums(DFdummies[1:nrow(all[!is.na(all$SalePrice),]),])==0)
colnames(DFdummies[ZerocolTrain])
## character(0)
DFdummies <- DFdummies[,-ZerocolTrain] #removing predictor
fewOnes <- which(colSums(DFdummies[1:nrow(all[!is.na(all$SalePrice),]),])<10)
colnames(DFdummies[fewOnes])
## character(0)
DFdummies <- DFdummies[,-fewOnes] #removing predictors
dim(DFdummies)
## [1] 2694 0
# Kiểm tra số dòng của hai dataframe
nrow_DFnorm <- nrow(DFnorm)
nrow_DFdummies <- nrow(DFdummies)
# Xác định số dòng nhỏ nhất để đồng bộ dữ liệu
min_rows <- min(nrow_DFnorm, nrow_DFdummies)
# Cắt bớt dòng của dataframe lớn hơn để khớp số dòng
DFnorm <- DFnorm[1:min_rows, , drop = FALSE]
DFdummies <- DFdummies[1:min_rows, , drop = FALSE]
# Ghép dữ liệu sau khi đã đồng bộ số dòng
combined <- cbind(DFnorm, DFdummies)
skew(all$SalePrice)
## [1] 1.877427
qqnorm(all$SalePrice)
qqline(all$SalePrice)
all$SalePrice <- log(all$SalePrice) #default is the natural logarithm, "+1" is not necessary as there are no 0's
skew(all$SalePrice)
## [1] 0.1213182
qqnorm(all$SalePrice)
qqline(all$SalePrice)
train1 <- combined[!is.na(all$SalePrice),]
test1 <- combined[is.na(all$SalePrice),]
set.seed(27042018)
my_control <-trainControl(method="cv", number=5)
lassoGrid <- expand.grid(alpha = 1, lambda = seq(0.001,0.1,by = 0.0005))
lasso_mod <- train(x=train1, y=all$SalePrice[!is.na(all$SalePrice)], method='glmnet', trControl= my_control, tuneGrid=lassoGrid)
lasso_mod$bestTune
## alpha lambda
## 1 1 0.001
min(lasso_mod$results$RMSE)
## [1] 0.1606381
min(lasso_mod$results$RMSE)
## [1] 0.1606381
lassoVarImp <- varImp(lasso_mod,scale=F)
lassoImportance <- lassoVarImp$importance
varsSelected <- length(which(lassoImportance$Overall!=0))
varsNotSelected <- length(which(lassoImportance$Overall==0))
cat('Lasso uses', varsSelected, 'variables in its model, and did not select', varsNotSelected, 'variables.')
## Lasso uses 26 variables in its model, and did not select 4 variables.
LassoPred <- predict(lasso_mod, test1)
predictions_lasso <- exp(LassoPred) #need to reverse the log to the real values
head(predictions_lasso)
## 1577 1578 1579 1580 1581 1582
## 117610.5 150141.5 209289.5 207245.4 173450.6 198633.0
xgb_grid = expand.grid(
nrounds = 1000,
eta = c(0.1, 0.05, 0.01),
max_depth = c(2, 3, 4, 5, 6),
gamma = 0,
colsample_bytree=1,
min_child_weight=c(1, 2, 3, 4 ,5),
subsample=1
)
#xgb_caret <- train(x=train1, y=all$SalePrice[!is.na(all$SalePrice)], method='xgbTree', trControl= my_control, tuneGrid=xgb_grid)
#xgb_caret$bestTune
library(xgboost)
# Chuyển train1 và test1 thành dataframe nếu chúng là list
train1 <- as.data.frame(train1)
test1 <- as.data.frame(test1)
# Kiểm tra có cột character/factor không, nếu có thì chuyển thành số
train1 <- data.frame(lapply(train1, function(x) {
if (is.factor(x) || is.character(x)) {
as.numeric(as.factor(x)) # Chuyển factor/character thành số
} else {
as.numeric(x) # Giữ nguyên nếu đã là số
}
}))
test1 <- data.frame(lapply(test1, function(x) {
if (is.factor(x) || is.character(x)) {
as.numeric(as.factor(x))
} else {
as.numeric(x)
}
}))
# Kiểm tra lại kiểu dữ liệu (tất cả phải là numeric)
str(train1)
## 'data.frame': 1458 obs. of 30 variables:
## $ LotFrontage : num -0.0497 0.5752 0.0859 -0.2901 0.7223 ...
## $ LotArea : num -0.102 0.149 0.462 0.139 0.928 ...
## $ YearBuilt : num 1.041 0.162 0.977 -1.869 0.944 ...
## $ MasVnrArea : num 1.225 -0.793 1.152 -0.793 1.445 ...
## $ BsmtFinSF2 : num -0.363 -0.363 -0.363 -0.363 -0.363 ...
## $ BsmtUnfSF : num -0.3248 0.0149 0.241 0.3577 0.3058 ...
## $ X1stFlrSF : num -0.778 0.43 -0.554 -0.418 0.127 ...
## $ X2ndFlrSF : num 1.198 -0.863 1.202 1.161 1.262 ...
## $ LowQualFinSF : num -0.117 -0.117 -0.117 -0.117 -0.117 ...
## $ GrLivArea : num 0.57 -0.369 0.705 0.583 1.347 ...
## $ BsmtFullBath : num 1.138 -0.834 1.138 1.138 1.138 ...
## $ BsmtHalfBath : num -0.252 3.887 -0.252 -0.252 -0.252 ...
## $ FullBath : num 0.814 0.814 0.814 -1.024 0.814 ...
## $ HalfBath : num 1.266 -0.764 1.266 -0.764 1.266 ...
## $ BedroomAbvGr : num 0.263 0.263 0.263 0.263 1.244 ...
## $ KitchenAbvGr : num -0.201 -0.201 -0.201 -0.201 -0.201 ...
## $ TotRmsAbvGrd : num 1.02 -0.197 -0.197 0.45 1.531 ...
## $ Fireplaces : num -0.986 0.771 0.771 0.771 0.771 ...
## $ GarageCars : num 0.394 0.394 0.394 1.281 1.281 ...
## $ WoodDeckSF : num -0.944 1.255 -0.944 -0.944 1.086 ...
## $ OpenPorchSF : num 0.834 -1.082 0.664 0.582 0.981 ...
## $ EnclosedPorch: num -0.428 -0.428 -0.428 2.737 -0.428 ...
## $ X3SsnPorch : num -0.113 -0.113 -0.113 -0.113 -0.113 ...
## $ ScreenPorch : num -0.309 -0.309 -0.309 -0.309 -0.309 ...
## $ PoolArea : num -0.0641 -0.0641 -0.0641 -0.0641 -0.0641 ...
## $ MiscVal : num -0.189 -0.189 -0.189 -0.189 -0.189 ...
## $ TotBathrooms : num 1.441 0.456 1.441 -0.149 1.441 ...
## $ Age : num Inf Inf Inf Inf Inf ...
## $ TotalSqFeet : num 0.174 0.121 0.345 0.055 1.025 ...
## $ TotalPorchSF : num 0.434 -1.453 0.267 1.167 0.579 ...
str(test1)
## 'data.frame': 1459 obs. of 30 variables:
## $ LotFrontage : num 0.575 0.613 0.34 0.499 -1.287 ...
## $ LotArea : num 0.526 0.929 0.868 0.225 -1.133 ...
## $ YearBuilt : num -0.331 -0.43 0.847 0.88 0.685 ...
## $ MasVnrArea : num -0.793 0.999 -0.793 0.37 -0.793 ...
## $ BsmtFinSF2 : num 2.286 -0.363 -0.363 -0.363 -0.363 ...
## $ BsmtUnfSF : num -0.012 0.2055 -0.373 0.0851 0.6958 ...
## $ X1stFlrSF : num -0.636 0.591 -0.527 -0.534 0.474 ...
## $ X2ndFlrSF : num -0.863 -0.863 1.138 1.128 -0.863 ...
## $ LowQualFinSF : num -0.117 -0.117 -0.117 -0.117 -0.117 ...
## $ GrLivArea : num -1.429 -0.209 0.42 0.372 -0.326 ...
## $ BsmtFullBath : num -0.834 -0.834 -0.834 -0.834 -0.834 ...
## $ BsmtHalfBath : num -0.252 -0.252 -0.252 -0.252 -0.252 ...
## $ FullBath : num -1.024 -1.024 0.814 0.814 0.814 ...
## $ HalfBath : num -0.764 1.266 1.266 1.266 -0.764 ...
## $ BedroomAbvGr : num -1.002 0.263 0.263 0.263 -1.002 ...
## $ KitchenAbvGr : num -0.201 -0.201 -0.201 -0.201 -0.201 ...
## $ TotRmsAbvGrd : num -0.944 -0.197 -0.197 0.45 -0.944 ...
## $ Fireplaces : num -0.986 -0.986 0.771 0.771 -0.986 ...
## $ GarageCars : num -0.857 -0.857 0.394 0.394 0.394 ...
## $ WoodDeckSF : num 0.965 1.361 1.124 1.328 -0.944 ...
## $ OpenPorchSF : num -1.082 0.595 0.569 0.595 0.97 ...
## $ EnclosedPorch: num -0.428 -0.428 -0.428 -0.428 -0.428 ...
## $ X3SsnPorch : num -0.113 -0.113 -0.113 -0.113 -0.113 ...
## $ ScreenPorch : num 2.975 -0.309 -0.309 -0.309 3.099 ...
## $ PoolArea : num -0.0641 -0.0641 -0.0641 -0.0641 -0.0641 ...
## $ MiscVal : num -0.189 7.415 -0.189 -0.189 -0.189 ...
## $ TotBathrooms : num -1.738 -0.863 0.456 0.456 -0.149 ...
## $ Age : num Inf Inf Inf Inf Inf ...
## $ TotalSqFeet : num -1.006 0.287 0.162 0.128 0.166 ...
## $ TotalPorchSF : num 0.74 0.198 0.173 0.198 1.028 ...
# Xử lý NA và Inf
train1[is.na(train1)] <- 0
test1[is.na(test1)] <- 0
train1[!is.finite(as.matrix(train1))] <- 0
test1[!is.finite(as.matrix(test1))] <- 0
# Chuyển thành ma trận
train1 <- as.matrix(train1)
test1 <- as.matrix(test1)
# Xử lý nhãn (label_train)
label_train <- all$SalePrice[!is.na(all$SalePrice)]
# Tạo DMatrix cho XGBoost
dtrain <- xgb.DMatrix(data = train1, label = label_train)
dtest <- xgb.DMatrix(data = test1)
default_param<-list(
objective = "reg:linear",
booster = "gbtree",
eta=0.05, #default = 0.3
gamma=0,
max_depth=3, #default=6
min_child_weight=4, #default=1
subsample=1,
colsample_bytree=1
)
xgbcv <- xgb.cv( params = default_param, data = dtrain, nrounds = 500, nfold = 5, showsd = T, stratified = T, print_every_n = 40, early_stopping_rounds = 10, maximize = F)
## [18:30:18] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
## [18:30:18] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
## [18:30:18] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
## [18:30:18] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
## [18:30:18] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
## [1] train-rmse:10.955589+0.004521 test-rmse:10.955560+0.019198
## Multiple eval metrics are present. Will use test_rmse for early stopping.
## Will train until test_rmse hasn't improved in 10 rounds.
##
## [41] train-rmse:1.429853+0.000768 test-rmse:1.430237+0.017481
## [81] train-rmse:0.233668+0.001153 test-rmse:0.245611+0.011408
## [121] train-rmse:0.129584+0.002255 test-rmse:0.154908+0.009148
## [161] train-rmse:0.120048+0.002001 test-rmse:0.150210+0.009149
## [201] train-rmse:0.114919+0.002189 test-rmse:0.149399+0.009114
## [241] train-rmse:0.110970+0.002209 test-rmse:0.148979+0.008597
## Stopping. Best iteration:
## [237] train-rmse:0.111366+0.002231 test-rmse:0.148943+0.008731
#train the model using the best iteration found by cross validation
xgb_mod <- xgb.train(data = dtrain, params=default_param, nrounds = 454)
## [18:30:20] WARNING: src/objective/regression_obj.cu:213: reg:linear is now deprecated in favor of reg:squarederror.
XGBpred <- predict(xgb_mod, dtest)
predictions_XGB <- exp(XGBpred) #need to reverse the log to the real values
head(predictions_XGB)
## [1] 123641.4 167098.6 204614.1 197459.9 167800.0 184485.7
#view variable importance plot
library(Ckmeans.1d.dp) #required for ggplot clustering
mat <- xgb.importance (feature_names = colnames(train1),model = xgb_mod)
xgb.ggplot.importance(importance_matrix = mat[1:20], rel_to_first = TRUE)
sub_avg <- data.frame(Id = test_labels, SalePrice = (predictions_XGB+2*predictions_lasso)/3)
head(sub_avg)
## Id SalePrice
## 1577 1461 119620.8
## 1578 1462 155793.9
## 1579 1463 207731.1
## 1580 1464 203983.5
## 1581 1465 171567.0
## 1582 1466 193917.2
write.csv(sub_avg, file = 'average.csv', row.names = F)