Dataset yang digunakan adalah AmesHousing, yaitu data penjualan rumah di Kota Ames, Iowa, yang dikumpulkan oleh Dean De Cock (2011) sebagai alternatif modern dari dataset Boston Housing.
Berbeda dengan dataset bawaan R pada umumnya (iris,
mtcars, airquality) yang dapat langsung
dipanggil dengan data(), package AmesHousing
tidak menyediakan objek siap pakai bernama
ames. Package ini menyediakan fungsi
make_ames() yang akan membangkitkan versi data yang sudah
dibersihkan (cleaned) dan siap dianalisis. Oleh karena itu, dataset
diperoleh melalui:
ames <- AmesHousing::make_ames()
bukan melalui data(ames).
Dataset asli (ames_raw) memiliki 2.930 observasi
dan 82 variabel dengan banyak missing value (misalnya pada
kolom Pool QC, Alley,
Fireplace Qu). Namun versi make_ames() yang
dipakai pada mini project ini sudah melalui proses cleaning,
sehingga jumlah variabel menjadi 81 (kolom
Order dan PID dihapus) dan praktis
tidak memiliki missing value karena nilai NA pada
variabel kategorik sudah dikodekan ulang menjadi level eksplisit
(misalnya "No_Pool", "No_Alley_Access",
"No_Garage").
install.packages("AmesHousing")
install.packages("tidyverse")
install.packages("caret")
install.packages("corrplot")
install.packages("rpart")
install.packages("rpart.plot")
install.packages("cluster")
install.packages("factoextra")
library(AmesHousing)
library(tidyverse)
library(caret)
library(corrplot)
library(rpart)
library(rpart.plot)
library(cluster)
library(factoextra)
# install.packages("AmesHousing")
library(AmesHousing)
library(dplyr)
library(ggplot2)
library(corrplot)
library(rpart)
library(rpart.plot)
library(cluster)
library(factoextra)
library(caret)
library(gridExtra)
library(scales)
set.seed(123)
ames <- make_ames()
ames <- as.data.frame(ames)
dim(ames)
## [1] 2930 81
str(ames[, 1:15])
## 'data.frame': 2930 obs. of 15 variables:
## $ MS_SubClass : Factor w/ 16 levels "One_Story_1946_and_Newer_All_Styles",..: 1 1 1 1 6 6 12 12 12 6 ...
## $ MS_Zoning : Factor w/ 7 levels "Floating_Village_Residential",..: 3 2 3 3 3 3 3 3 3 3 ...
## $ Lot_Frontage: num 141 80 81 93 74 78 41 43 39 60 ...
## $ Lot_Area : int 31770 11622 14267 11160 13830 9978 4920 5005 5389 7500 ...
## $ Street : Factor w/ 2 levels "Grvl","Pave": 2 2 2 2 2 2 2 2 2 2 ...
## $ Alley : Factor w/ 3 levels "Gravel","No_Alley_Access",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ Lot_Shape : Factor w/ 4 levels "Regular","Slightly_Irregular",..: 2 1 2 1 2 2 1 2 2 1 ...
## $ Land_Contour: Factor w/ 4 levels "Bnk","HLS","Low",..: 4 4 4 4 4 4 4 2 4 4 ...
## $ Utilities : Factor w/ 3 levels "AllPub","NoSeWa",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Lot_Config : Factor w/ 5 levels "Corner","CulDSac",..: 1 5 1 1 5 5 5 5 5 5 ...
## $ Land_Slope : Factor w/ 3 levels "Gtl","Mod","Sev": 1 1 1 1 1 1 1 1 1 1 ...
## $ Neighborhood: Factor w/ 29 levels "North_Ames","College_Creek",..: 1 1 1 1 7 7 17 17 17 7 ...
## $ Condition_1 : Factor w/ 9 levels "Artery","Feedr",..: 3 2 3 3 3 3 3 3 3 3 ...
## $ Condition_2 : Factor w/ 8 levels "Artery","Feedr",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ Bldg_Type : Factor w/ 5 levels "OneFam","TwoFmCon",..: 1 1 1 1 1 1 5 5 5 1 ...
# Ringkasan tipe data pada seluruh kolom
table(sapply(ames, class))
##
## factor integer numeric
## 46 23 12
Interpretasi: Dataset terdiri dari 81 variabel,
gabungan antara variabel numerik (luas bangunan, jumlah kamar, tahun
dibangun, dll.) dan variabel kategorik/faktor (zona, tipe bangunan,
kualitas material, lingkungan/Neighborhood, dll.). Variabel
target untuk prediksi adalah Sale_Price.
na_count <- sapply(ames, function(x) sum(is.na(x)))
na_count[na_count > 0]
## named integer(0)
cat("Total missing value pada seluruh dataset (hasil make_ames()):", sum(na_count), "\n")
## Total missing value pada seluruh dataset (hasil make_ames()): 0
Interpretasi: Hasil make_ames() tidak
memiliki missing value sama sekali. Hal ini bukan
berarti data mentahnya bersih, melainkan karena package
AmesHousing sudah melakukan proses data cleaning
di balik layar.
summary(
ames %>%
select(
Sale_Price,
Gr_Liv_Area,
Total_Bsmt_SF,
Lot_Area,
Year_Built,
TotRms_AbvGrd
)
)
## Sale_Price Gr_Liv_Area Total_Bsmt_SF Lot_Area Year_Built
## Min. : 12789 Min. : 334 Min. : 0 Min. : 1300 Min. :1872
## 1st Qu.:129500 1st Qu.:1126 1st Qu.: 793 1st Qu.: 7440 1st Qu.:1954
## Median :160000 Median :1442 Median : 990 Median : 9436 Median :1973
## Mean :180796 Mean :1500 Mean :1051 Mean : 10148 Mean :1971
## 3rd Qu.:213500 3rd Qu.:1743 3rd Qu.:1302 3rd Qu.: 11555 3rd Qu.:2001
## Max. :755000 Max. :5642 Max. :6110 Max. :215245 Max. :2010
## TotRms_AbvGrd
## Min. : 2.000
## 1st Qu.: 5.000
## Median : 6.000
## Mean : 6.443
## 3rd Qu.: 7.000
## Max. :15.000
1. Sale_Price (Harga Rumah)
Harga rumah memiliki nilai minimum sebesar 12.789 dan maksimum sebesar 755.000, menunjukkan rentang harga yang sangat lebar. Nilai rata-rata (180.796) lebih tinggi dibanding median (160.000), yang mengindikasikan distribusi harga rumah cenderung miring ke kanan (right-skewed) akibat adanya beberapa rumah dengan harga sangat tinggi.
2. Gr_Liv_Area (Luas Area Hunian)
Luas area hunian berkisar antara 334 hingga 5.642 kaki persegi. Nilai median sebesar 1.442 dan rata-rata sebesar 1.500 menunjukkan bahwa sebagian besar rumah memiliki luas hunian sekitar 1.400–1.500 kaki persegi. Adanya perbedaan antara nilai maksimum dan rata-rata mengindikasikan keberadaan beberapa rumah berukuran sangat besar.
3. Total_Bsmt_SF (Luas Basement)
Luas basement memiliki nilai minimum 0, yang berarti terdapat rumah tanpa basement. Rata-rata luas basement adalah 1.051 kaki persegi dengan median 990 kaki persegi, menunjukkan bahwa sebagian besar rumah memiliki basement dengan ukuran yang cukup besar.
4. Lot_Area (Luas Tanah)
Luas tanah bervariasi dari 1.300 hingga 215.245 kaki persegi. Nilai maksimum yang sangat tinggi dibandingkan kuartil ketiga (11.555) menunjukkan adanya beberapa properti dengan luas tanah yang jauh lebih besar dibanding mayoritas rumah, sehingga kemungkinan terdapat outlier pada variabel ini.
5. Year_Built (Tahun Pembangunan)
Rumah tertua dibangun pada tahun 1872, sedangkan rumah terbaru dibangun pada tahun 2010. Median tahun pembangunan adalah 1973, yang menunjukkan bahwa sekitar setengah dari rumah dalam dataset dibangun sebelum tahun tersebut dan setengahnya lagi setelahnya.
6. TotRms_AbvGrd (Jumlah Ruangan di Atas Permukaan Tanah)
Jumlah ruangan berkisar antara 2 hingga 15 ruangan, dengan rata-rata 6,44 ruangan dan median 6 ruangan. Hal ini menunjukkan bahwa sebagian besar rumah memiliki sekitar 5–7 ruangan, yang dapat merepresentasikan ukuran rumah menengah.
ggplot(ames, aes(x = Sale_Price)) +
geom_histogram(bins = 40, fill = "#2C7FB8", color = "white") +
scale_x_continuous(
labels = label_dollar(prefix = "$")
) +
labs(
title = "Distribusi Harga Jual Rumah",
x = "Harga Jual (USD)",
y = "Frekuensi"
) +
theme_minimal()
Interpretasi: Grafik menunjukkan ekor yang memanjang ke arah kanan (nilai harga yang lebih tinggi). Mayoritas rumah di Ames dijual dengan harga yang relatif rendah hingga menengah, sementara hanya ada sebagian kecil rumah mewah yang memiliki harga sangat tinggi. Konsentrasi penjualan rumah paling banyak berada pada kisaran harga sekitar $130,000 hingga $160,000 (ditunjukkan oleh dua batang tertinggi yang mendekati frekuensi 450. Terdapat beberapa data titik (batang-batang kecil) yang terisolasi di sisi kanan, mulai dari kisaran $500,000 hingga hampir $800,000.
ggplot(ames, aes(y = Sale_Price)) +
geom_boxplot(fill = "#41B6C4") +
scale_y_continuous(
labels = label_dollar(prefix = "$")
) +
labs(
title = "Boxplot Harga Jual Rumah",
y = "Harga Jual (USD)"
) +
theme_minimal()
Interpretasi: Sebesar 50% dari total seluruh rumah di dataset ini terkonsentrasi di rentang harga yang cukup ketat, yaitu antara $130,000 hingga $215,000. Setiap rumah yang terjual dengan harga di atas $340,000 secara statistik dianggap sebagai pencilan (outliers). Terlihat ada penumpukan outliers yang sangat padat dari harga $340,000 hingga $500,000, serta beberapa pencilan ekstrem yang mendekati angka $600,000 dan bahkan $750,000
num_vars <- ames %>% select(where(is.numeric))
corr_matrix <- cor(num_vars, use = "complete.obs")
corrplot(corr_matrix, method = "color", type = "upper", tl.cex = 0.6, tl.col = "black",
order = "hclust")
# Overall_Qual disimpan sebagai factor (ordinal), sehingga perlu diubah ke numerik
# secara eksplisit agar ikut serta dalam analisis korelasi numerik.
ames$Overall_Qual_num <- as.numeric(ames$Overall_Qual)
num_vars2 <- ames %>% select(where(is.numeric))
cor_target <- sort(cor(num_vars2)[, "Sale_Price"], decreasing = TRUE)
head(cor_target, 11)
## Sale_Price Overall_Qual_num Gr_Liv_Area Garage_Cars
## 1.0000000 0.7992618 0.7067799 0.6475616
## Garage_Area Total_Bsmt_SF First_Flr_SF Year_Built
## 0.6401383 0.6325288 0.6216761 0.5584261
## Full_Bath Year_Remod_Add Mas_Vnr_Area
## 0.5456039 0.5329738 0.5021960
Interpretasi: Variabel yang paling berkorelasi
positif dengan Sale_Price adalah
Overall_Qual (kualitas material & finishing
keseluruhan, r ≈ 0.80), diikuti oleh Gr_Liv_Area (luas
area hunian di atas tanah, r ≈ 0.71), Garage_Cars,
Garage_Area, dan Total_Bsmt_SF. Temuan
ini konsisten dengan intuisi domain: rumah yang lebih luas dan
berkualitas lebih tinggi cenderung dijual lebih mahal. Variabel-variabel
inilah yang akan dijadikan prediktor utama pada tahap pemodelan
regresi.
ggplot(ames, aes(x = Gr_Liv_Area, y = Sale_Price)) +
geom_point(alpha = 0.4, color = "#225EA8") +
geom_smooth(method = "lm", color = "red", se = FALSE) +
scale_y_continuous(labels = scales::dollar_format()) +
labs(title = "Hubungan Luas Hunian (Gr_Liv_Area) dengan Harga Rumah",
x = "Luas Area Hunian (sqft)",
y = "Sale Price (USD)") +
theme_minimal()
Interpretasi: Median Garis merah geom_smooth(method = “lm”) akan bergerak naik dari kiri bawah ke kanan atas. Ini membuktikan secara visual bahwa variabel Gr_Liv_Area (luas lantai di atas tanah) memiliki hubungan linear positif yang sangat kuat dengan Sale_Price. Semakin luas rumah, harganya semakin mahal. Penggunaan parameter alpha = 0.4 sangat tepat. Bagian transparan yang menumpuk menjadi warna biru pekat menunjukkan di mana mayoritas ukuran dan harga rumah di Ames berada (biasanya menumpuk di rentang 1,000–2,000 sqft)
ggplot(ames, aes(x = factor(Overall_Qual_num), y = Sale_Price)) +
geom_boxplot(fill = "#7FCDBB") +
scale_y_continuous(labels = scales::dollar_format()) +
labs(title = "Harga Rumah Berdasarkan Overall Quality",
x = "Overall Quality (1 = Sangat Rendah, 10 = Sangat Tinggi)",
y = "Sale Price (USD)") +
theme_minimal()
Interpretasi: Median harga rumah meningkat secara
konsisten seiring naiknya skor kualitas bangunan
(Overall_Qual). Pola monoton ini memperkuat temuan korelasi
sebelumnya bahwa kualitas bangunan adalah salah satu prediktor harga
yang paling kuat dan dapat diandalkan.
# Variabel numerik yang merepresentasikan karakteristik fisik rumah dipilih
# untuk segmentasi (klastering), TANPA menyertakan Sale_Price -- karena tujuan
# klastering adalah mengelompokkan rumah berdasarkan karakteristik, bukan harga.
cluster_vars <- c("Gr_Liv_Area", "Total_Bsmt_SF", "Garage_Area", "Lot_Area",
"Overall_Qual_num", "Year_Built", "TotRms_AbvGrd", "Full_Bath")
cluster_data <- ames[, cluster_vars]
summary(cluster_data)
## Gr_Liv_Area Total_Bsmt_SF Garage_Area Lot_Area
## Min. : 334 Min. : 0 Min. : 0.0 Min. : 1300
## 1st Qu.:1126 1st Qu.: 793 1st Qu.: 320.0 1st Qu.: 7440
## Median :1442 Median : 990 Median : 480.0 Median : 9436
## Mean :1500 Mean :1051 Mean : 472.7 Mean : 10148
## 3rd Qu.:1743 3rd Qu.:1302 3rd Qu.: 576.0 3rd Qu.: 11555
## Max. :5642 Max. :6110 Max. :1488.0 Max. :215245
## Overall_Qual_num Year_Built TotRms_AbvGrd Full_Bath
## Min. : 1.000 Min. :1872 Min. : 2.000 Min. :0.000
## 1st Qu.: 5.000 1st Qu.:1954 1st Qu.: 5.000 1st Qu.:1.000
## Median : 6.000 Median :1973 Median : 6.000 Median :2.000
## Mean : 6.095 Mean :1971 Mean : 6.443 Mean :1.567
## 3rd Qu.: 7.000 3rd Qu.:2001 3rd Qu.: 7.000 3rd Qu.:2.000
## Max. :10.000 Max. :2010 Max. :15.000 Max. :4.000
Interpretasi: Delapan variabel di atas dipilih
karena merepresentasikan tiga dimensi utama karakteristik rumah:
ukuran (Gr_Liv_Area,
Total_Bsmt_SF, Lot_Area,
TotRms_AbvGrd), fasilitas
(Garage_Area, Full_Bath), dan
kualitas/usia (Overall_Qual_num,
Year_Built). Sale_Price sengaja tidak
diikutsertakan agar segmentasi murni didasarkan pada karakteristik fisik
rumah, bukan harga jualnya — hasil klaster nanti justru akan dipakai
untuk menjelaskan pola harga (lihat bagian Profiling
Cluster).
cluster_scaled <- scale(cluster_data) # standarisasi wajib sebelum k-means
fviz_nbclust(cluster_scaled, kmeans, method = "wss", k.max = 8) +
labs(title = "Elbow Method untuk Menentukan Jumlah Cluster Optimal")
Interpretasi: Grafik elbow method menunjukkan penurunan tajam within-cluster sum of squares (WSS) hingga k = 3, setelah itu penurunan melandai. Titik “siku” (elbow) pada k = 3 mengindikasikan bahwa 3 cluster merupakan jumlah yang cukup optimal — menambah cluster lebih banyak tidak memberikan perbaikan yang signifikan, namun menambah kompleksitas interpretasi.
# Label kelas dibuat dari Sale_Price menggunakan pembagian tertile (33%/66%)
# sehingga didapatkan 3 kelas yang seimbang jumlah anggotanya.
batas <- quantile(ames$Sale_Price, probs = c(1/3, 2/3))
batas
## 33.33333% 66.66667%
## 139000 190000
ames$Price_Class <- cut(ames$Sale_Price,
breaks = c(-Inf, batas[1], batas[2], Inf),
labels = c("Murah", "Sedang", "Mahal"))
table(ames$Price_Class)
##
## Murah Sedang Mahal
## 981 980 969
Interpretasi: Karena Sale_Price adalah
variabel numerik kontinu, untuk keperluan klasifikasi
variabel ini perlu diubah menjadi kategori. Digunakan pembagian tertile
(persentil ke-33 dan ke-66) sehingga rumah terbagi rata ke dalam tiga
kelas: Murah (di bawah 139,000),
Sedang, dan Mahal (di atas 190,000).
Pendekatan tertile dipilih (dibanding median split 2 kelas) agar masalah
klasifikasi lebih menantang dan lebih informatif untuk segmentasi
pasar.
Setelah tahap EDA selesai dilakukan, alur kerja data mining yang sesuai dengan kerangka CRISP-DM (Cross-Industry Standard Process for Data Mining) adalah melanjutkan ke tahap Modeling. Pada mini project ini, tahap Modeling mencakup tiga pendekatan sekaligus pada satu dataset yang sama:
Sale_Price) sebagai variabel numerik kontinu.Ketiga pendekatan ini saling melengkapi: regresi menjawab “berapa harganya”, klasifikasi menjawab “termasuk kategori harga apa”, dan klastering menjawab “rumah ini mirip dengan kelompok rumah seperti apa”.
Variabel target (response) pada tahap regresi adalah
Sale_Price, yaitu harga jual rumah dalam
satuan dolar — variabel numerik kontinu.
# Prediktor dipilih berdasarkan hasil analisis korelasi pada tahap EDA
# (variabel dengan korelasi tertinggi terhadap Sale_Price).
pred_vars <- c("Overall_Qual_num", "Gr_Liv_Area", "Garage_Cars", "Total_Bsmt_SF",
"Year_Built", "Full_Bath", "TotRms_AbvGrd", "Year_Remod_Add", "Fireplaces")
reg_data <- ames[, c("Sale_Price", pred_vars)]
head(reg_data, 3)
## Sale_Price Overall_Qual_num Gr_Liv_Area Garage_Cars Total_Bsmt_SF Year_Built
## 1 215000 6 1656 2 1080 1960
## 2 105000 5 896 1 882 1961
## 3 172000 6 1329 1 1329 1958
## Full_Bath TotRms_AbvGrd Year_Remod_Add Fireplaces
## 1 1 7 1960 2
## 2 1 5 1961 0
## 3 1 6 1958 0
Interpretasi: Sembilan prediktor dipilih berdasarkan
kekuatan korelasinya dengan Sale_Price pada tahap EDA
sebelumnya, mencerminkan dimensi kualitas
(Overall_Qual_num), ukuran (Gr_Liv_Area,
Total_Bsmt_SF, TotRms_AbvGrd), fasilitas
(Garage_Cars, Full_Bath,
Fireplaces), dan usia bangunan (Year_Built,
Year_Remod_Add).
set.seed(123)
idx <- sample(1:nrow(reg_data), size = 0.8 * nrow(reg_data))
train_reg <- reg_data[idx, ]
test_reg <- reg_data[-idx, ]
cat("Jumlah data training:", nrow(train_reg), "\n")
## Jumlah data training: 2344
cat("Jumlah data testing :", nrow(test_reg), "\n")
## Jumlah data testing : 586
Interpretasi: Data dibagi 80% untuk training dan 20% untuk testing menggunakan random sampling. Pembagian ini perlu dilakukan agar performa model dapat dievaluasi secara objektif pada data yang belum pernah dilihat model (unseen data), sehingga mencerminkan kemampuan generalisasi model.
model_lm <- lm(Sale_Price ~ ., data = train_reg)
summary(model_lm)
##
## Call:
## lm(formula = Sale_Price ~ ., data = train_reg)
##
## Residuals:
## Min 1Q Median 3Q Max
## -512863 -20042 -2467 15740 276960
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.273e+06 9.838e+04 -12.934 < 2e-16 ***
## Overall_Qual_num 1.876e+04 8.856e+02 21.182 < 2e-16 ***
## Gr_Liv_Area 5.803e+01 3.257e+00 17.820 < 2e-16 ***
## Garage_Cars 1.246e+04 1.353e+03 9.208 < 2e-16 ***
## Total_Bsmt_SF 2.975e+01 2.138e+00 13.917 < 2e-16 ***
## Year_Built 2.923e+02 3.860e+01 7.572 5.25e-14 ***
## Full_Bath -8.088e+03 2.003e+03 -4.038 5.57e-05 ***
## TotRms_AbvGrd -2.047e+03 8.434e+02 -2.427 0.0153 *
## Year_Remod_Add 3.240e+02 4.937e+01 6.563 6.46e-11 ***
## Fireplaces 8.969e+03 1.361e+03 6.591 5.38e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 36510 on 2334 degrees of freedom
## Multiple R-squared: 0.7952, Adjusted R-squared: 0.7944
## F-statistic: 1007 on 9 and 2334 DF, p-value: < 2.2e-16
Interpretasi: Model regresi linear berganda
(multiple linear regression) dibangun dengan
Sale_Price sebagai variabel dependen dan kesembilan
variabel di atas sebagai prediktor. Nilai R-squared
pada data training sebesar 0.795 menunjukkan bahwa model mampu
menjelaskan sekitar 79.5% variasi harga rumah. Koefisien
Overall_Qual_num dan Gr_Liv_Area memiliki
nilai p-value yang sangat kecil (signifikan), menegaskan kembali bahwa
keduanya merupakan prediktor harga yang paling kuat.
pred_reg <- predict(model_lm, newdata = test_reg)
head(data.frame(Aktual = test_reg$Sale_Price, Prediksi = round(pred_reg)), 10)
## Aktual Prediksi
## 3 172000 155485
## 6 195500 195402
## 15 212000 240965
## 22 170000 189150
## 26 142000 127894
## 42 275000 296872
## 43 259000 287277
## 50 205000 239524
## 53 160000 192046
## 55 184500 188385
rmse_val <- sqrt(mean((test_reg$Sale_Price - pred_reg)^2))
mae_val <- mean(abs(test_reg$Sale_Price - pred_reg))
r2_test <- cor(test_reg$Sale_Price, pred_reg)^2
cat("RMSE (testing):", round(rmse_val, 1), "\n")
## RMSE (testing): 34118.4
cat("MAE (testing):", round(mae_val, 1), "\n")
## MAE (testing): 23210.4
cat("R-squared (testing):", round(r2_test, 3), "\n")
## R-squared (testing): 0.803
ggplot(data.frame(Aktual = test_reg$Sale_Price, Prediksi = pred_reg),
aes(x = Aktual, y = Prediksi)) +
geom_point(alpha = 0.4, color = "#2C7FB8") +
geom_abline(slope = 1, intercept = 0, color = "red", linetype = "dashed") +
labs(title = "Harga Aktual vs Harga Prediksi", x = "Harga Aktual", y = "Harga Prediksi") +
theme_minimal()
Interpretasi: Pada data testing, model menghasilkan RMSE ≈ 34,118 dan R² ≈ 0.803. Artinya, secara rata-rata prediksi model meleset sekitar 34,118 dari harga aktual, dan model mampu menjelaskan sekitar 80.3% variasi harga pada data baru. Titik-titik pada scatter plot yang mengumpul di sekitar garis diagonal (merah putus-putus) mengindikasikan prediksi yang cukup akurat, meskipun terdapat penyimpangan lebih besar pada rumah-rumah dengan harga sangat tinggi — konsisten dengan outlier yang ditemukan pada tahap EDA.
Label kelas (Price_Class: Murah/Sedang/Mahal) telah
dibuat pada tahap Persiapan Klasifikasi di bagian EDA
menggunakan pembagian tertile dari Sale_Price.
table(ames$Price_Class)
##
## Murah Sedang Mahal
## 981 980 969
# Variabel prediktor untuk klasifikasi TIDAK menyertakan Sale_Price (sumber label)
# agar tidak terjadi data leakage.
class_vars <- c("Overall_Qual_num", "Gr_Liv_Area", "Garage_Cars", "Total_Bsmt_SF",
"Year_Built", "Full_Bath")
class_data <- ames[, c("Price_Class", class_vars)]
head(class_data, 3)
## Price_Class Overall_Qual_num Gr_Liv_Area Garage_Cars Total_Bsmt_SF Year_Built
## 1 Mahal 6 1656 2 1080 1960
## 2 Murah 5 896 1 882 1961
## 3 Sedang 6 1329 1 1329 1958
## Full_Bath
## 1 1
## 2 1
## 3 1
Interpretasi: Variabel prediktor untuk klasifikasi sengaja dipilih subset dari variabel regresi (enam variabel paling kuat) untuk menjaga model tetap sederhana dan mudah diinterpretasikan dalam bentuk pohon keputusan.
set.seed(123)
idx2 <- sample(1:nrow(class_data), size = 0.8 * nrow(class_data))
train_class <- class_data[idx2, ]
test_class <- class_data[-idx2, ]
cat("Jumlah data training:", nrow(train_class), "\n")
## Jumlah data training: 2344
cat("Jumlah data testing :", nrow(test_class), "\n")
## Jumlah data testing : 586
tree_model <- rpart(Price_Class ~ ., data = train_class, method = "class")
rpart.plot(tree_model, type = 2, extra = 104, fallen.leaves = TRUE,
main = "Decision Tree - Klasifikasi Kategori Harga Rumah")
Interpretasi: Pohon keputusan menunjukkan bahwa
Overall_Qual_num dan Gr_Liv_Area menjadi
split (pemisah) utama di simpul-simpul teratas, sekali lagi
menegaskan bahwa kualitas bangunan dan luas hunian adalah faktor paling
diskriminatif dalam menentukan kategori harga rumah.
pred_class <- predict(tree_model, newdata = test_class, type = "class")
head(data.frame(Aktual = test_class$Price_Class, Prediksi = pred_class), 10)
## Aktual Prediksi
## 3 Sedang Sedang
## 6 Mahal Sedang
## 15 Mahal Mahal
## 22 Sedang Sedang
## 26 Sedang Murah
## 42 Mahal Mahal
## 43 Mahal Mahal
## 50 Mahal Mahal
## 53 Sedang Sedang
## 55 Sedang Sedang
cm <- table(Prediksi = pred_class, Aktual = test_class$Price_Class)
cm
## Aktual
## Prediksi Murah Sedang Mahal
## Murah 130 43 7
## Sedang 39 128 45
## Mahal 3 22 169
Interpretasi - Total prediksi yang tepat: 130+128+169=427 rumah. - Total prediksi yang meleset: 43+7+45+39+3+22=159 rumah.
Kenapa kelas Sedang akurasinya paling rendah? Ini hal yang wajar dalam data mining. Rumah kelas Sedang secara karakteristik berada di tengah-tengah, sehingga fiturnya sering kali tumpang tindih (overlap) dengan batas atas kelas Murah atau batas bawah kelas Mahal.
accuracy <- sum(diag(cm)) / sum(cm)
cat("Akurasi model Decision Tree:", round(accuracy, 3), "atau", round(accuracy*100,1), "%\n")
## Akurasi model Decision Tree: 0.729 atau 72.9 %
# Precision & recall per kelas sebagai pelengkap evaluasi
precision <- diag(cm) / rowSums(cm)
recall <- diag(cm) / colSums(cm)
data.frame(Kelas = names(precision), Precision = round(precision,3), Recall = round(recall,3))
## Kelas Precision Recall
## Murah Murah 0.722 0.756
## Sedang Sedang 0.604 0.663
## Mahal Mahal 0.871 0.765
Interpretasi: Model Decision Tree berhasil mengklasifikasikan kategori harga rumah dengan akurasi sekitar 72.9%. Dari confusion matrix terlihat bahwa kesalahan klasifikasi paling banyak terjadi antara kelas yang berdekatan (misalnya “Sedang” tertukar dengan “Mahal” atau “Murah”), sementara kesalahan antara “Murah” dan “Mahal” (dua kelas yang paling jauh) sangat jarang terjadi. Pola ini wajar karena batas antar kategori harga bersifat kontinu, bukan benar-benar terpisah secara alami.
Variabel numerik yang digunakan untuk klastering sama dengan yang
telah dipilih pada tahap Persiapan untuk Klastering di
bagian EDA (cluster_vars), yaitu kombinasi variabel ukuran,
fasilitas, dan kualitas/usia rumah — tanpa menyertakan
Sale_Price.
cluster_vars
## [1] "Gr_Liv_Area" "Total_Bsmt_SF" "Garage_Area" "Lot_Area"
## [5] "Overall_Qual_num" "Year_Built" "TotRms_AbvGrd" "Full_Bath"
# Standarisasi (z-score) WAJIB dilakukan sebelum k-means karena variabel-variabel
# di atas memiliki satuan dan skala yang sangat berbeda (mis. Lot_Area dalam ribuan
# sqft vs Full_Bath dalam satuan 0-4). Tanpa standarisasi, variabel berskala besar
# akan mendominasi perhitungan jarak Euclidean.
cluster_scaled <- scale(cluster_data)
head(round(cluster_scaled, 2), 3)
## Gr_Liv_Area Total_Bsmt_SF Garage_Area Lot_Area Overall_Qual_num Year_Built
## [1,] 0.31 0.07 0.26 2.74 -0.07 -0.38
## [2,] -1.19 -0.38 1.20 0.19 -0.78 -0.34
## [3,] -0.34 0.63 -0.75 0.52 -0.07 -0.44
## TotRms_AbvGrd Full_Bath
## [1,] 0.35 -1.02
## [2,] -0.92 -1.02
## [3,] -0.28 -1.02
Berdasarkan elbow method pada tahap EDA, dipilih k = 3 sebagai jumlah cluster optimal.
set.seed(123)
km_model <- kmeans(cluster_scaled, centers = 3, nstart = 25)
km_model$centers
## Gr_Liv_Area Total_Bsmt_SF Garage_Area Lot_Area Overall_Qual_num Year_Built
## 1 -0.6448838 -0.46045001 -0.5462595 -0.14688945 -0.6813153 -0.6585472
## 2 0.2143664 0.06590425 0.1220086 -0.08688994 0.3084168 0.5019332
## 3 1.3947342 1.19587606 1.3181622 0.62917557 1.2854648 0.7747096
## TotRms_AbvGrd Full_Bath
## 1 -0.5012393 -0.8920637
## 2 0.1282737 0.7303585
## 3 1.1720677 0.9336439
table(Cluster = km_model$cluster)
## Cluster
## 1 2 3
## 1378 1081 471
fviz_cluster(km_model, data = cluster_scaled,
geom = "point", ellipse.type = "convex", palette = "jco",
ggtheme = theme_minimal(),
main = "Visualisasi Cluster Rumah (PCA 2D)")
Interpretasi: 1. Cluster 1 (Warna Biru - Sisi Kanan)
2. Cluster 2 (Warna Kuning - Sisi Tengah)
3. Cluster 3 (Warna Abu-abu - Sisi Kiri & Menyebar)
Analisis Poligon yang Lebar: Bentuk poligon abu-abu yang sangat luas dan renggang mengindikasikan bahwa data di Cluster 3 memiliki variabilitas (keberagaman) yang sangat tinggi dibandingkan cluster lainnya. Ada rumah yang sangat ekstrem di ujung kiri bawah (jarak antar titik berjauhan), yang bisa dikaitkan dengan properti bernilai rendah namun memiliki karakteristik tanah/kondisi yang sangat tidak biasa (outliers dalam clustering).
profil <- aggregate(cluster_data, by = list(Cluster = km_model$cluster), mean)
profil_n <- table(km_model$cluster)
profil$Jumlah_Rumah <- as.integer(profil_n)
# Tambahkan rata-rata Sale_Price per cluster untuk membantu interpretasi
profil$Avg_Sale_Price <- tapply(ames$Sale_Price, km_model$cluster, mean)
profil <- profil %>% mutate(across(where(is.numeric), ~round(., 1)))
profil
## Cluster Gr_Liv_Area Total_Bsmt_SF Garage_Area Lot_Area Overall_Qual_num
## 1 1 1173.7 848.2 355.1 8990.4 5.1
## 2 2 1608.1 1080.3 498.9 9463.2 6.5
## 3 3 2204.7 1578.6 756.3 15105.8 7.9
## Year_Built TotRms_AbvGrd Full_Bath Jumlah_Rumah Avg_Sale_Price
## 1 1951.4 5.7 1.1 1378 128389.7
## 2 1986.5 6.6 2.0 1081 191952.5
## 3 1994.8 8.3 2.1 471 308515.5
Interpretasi profil cluster:
ord <- order(profil$Avg_Sale_Price)
profil_sorted <- profil[ord, ]
profil_sorted
## Cluster Gr_Liv_Area Total_Bsmt_SF Garage_Area Lot_Area Overall_Qual_num
## 1 1 1173.7 848.2 355.1 8990.4 5.1
## 2 2 1608.1 1080.3 498.9 9463.2 6.5
## 3 3 2204.7 1578.6 756.3 15105.8 7.9
## Year_Built TotRms_AbvGrd Full_Bath Jumlah_Rumah Avg_Sale_Price
## 1 1951.4 5.7 1.1 1378 128389.7
## 2 1986.5 6.6 2.0 1081 191952.5
## 3 1994.8 8.3 2.1 471 308515.5
Berdasarkan rata-rata Sale_Price per cluster, dapat
diberikan profil/segmentasi sebagai berikut (urutan dari harga rata-rata
terendah ke tertinggi):
Gr_Liv_Area dan
Total_Bsmt_SF lebih rendah), usia bangunan lebih tua
(Year_Built lebih kecil), dan skor
Overall_Qual lebih rendah — dapat dilabeli sebagai segmen
“Rumah Ekonomis/Tua”.meskipun klastering tidak menggunakan
Sale_Price sama sekali sebagai input, hasil cluster yang
terbentuk tetap selaras dengan tingkatan harga rumah
yang sebenarnya. Ini menjadi bukti tambahan (validasi tidak langsung)
bahwa karakteristik fisik rumah (ukuran, kualitas, usia) memang
berasosiasi kuat dengan harga jualnya.
Berdasarkan keseluruhan analisis pada dataset AmesHousing (2.930
observasi, 81 variabel hasil make_ames()), dapat
disimpulkan beberapa insight utama:
Faktor utama yang memengaruhi harga rumah:
Overall_Qual (kualitas bangunan) dan
Gr_Liv_Area (luas area hunian) secara konsisten muncul
sebagai prediktor terkuat di ketiga pendekatan (korelasi tertinggi pada
EDA, koefisien paling signifikan pada regresi, dan node pemisah utama
pada decision tree).
Kemampuan memprediksi harga rumah: Model regresi linear dengan 9 prediktor mampu menjelaskan sekitar 80.3% variasi harga rumah pada data testing (R² ≈ 0.803), dengan rata-rata kesalahan prediksi (RMSE) sebesar 34,118. Untuk klasifikasi kategori harga (Murah/Sedang/ Mahal), model Decision Tree mencapai akurasi 72.9%, dengan kesalahan prediksi mayoritas terjadi pada kategori yang berdekatan, bukan kategori yang berlawanan.
Segmentasi rumah berdasarkan karakteristik: K-Means dengan k = 3 berhasil mengelompokkan rumah menjadi tiga segmen alami — rumah ekonomis/tua, rumah standar/keluarga, dan rumah premium/mewah — tanpa menggunakan informasi harga sama sekali. Keselarasan segmen ini dengan tingkatan harga aktual menjadi validasi tambahan terhadap konsistensi temuan di seluruh tahap analisis.
Secara keseluruhan, dataset AmesHousing memungkinkan satu siklus penuh proses Data Mining sesuai kerangka CRISP-DM — mulai dari pemahaman data (EDA), hingga tiga jenis tugas pemodelan (Regression, Classification, Clustering) — dipraktikkan secara terpadu dalam satu studi kasus, dengan temuan yang saling memperkuat satu sama lain di setiap tahapnya.