CASE PROJECT DATA REDUCTION

Eksplorasi dan Visualisasi Data Kelompok 2

1 Deskripsi Dataset

1.1 Sumber dan Konteks Data

Dataset California Housing Prices tersedia secara publik di Kaggle, diunggah oleh Cam Nugent dan dapat diakses di: https://www.kaggle.com/datasets/camnugent/california-housing-prices. Dataset ini bersumber dari Sensus California tahun 1990 dan dipopulerkan oleh Aur©lien G©ron dalam buku “Hands-On Machine Learning with Scikit-Learn and TensorFlow” sebagai dataset standar untuk pengenalan machine learning.

Setiap baris dalam dataset merepresentasikan satu blok sensus (census block group), unit geografis terkecil yang dipublikasikan oleh U.S. Census Bureau, dengan populasi sekitar 600 hingga 3.000 jiwa per blok. Karena unit observasi adalah blok (bukan individu rumah), variabel seperti total_rooms dan population mencerminkan nilai agregat seluruh blok, bukan per rumah.

1.2 Input Data

library(tidyverse)
library(corrplot)
library(ggcorrplot)
library(car)
library(kableExtra)
library(gridExtra)
library(scales)
library(RColorBrewer)
library(viridis)
library(glmnet)
library(factoextra)
df <- read.csv("~/Downloads/housing.csv", stringsAsFactors = TRUE)
cat("Dimensi dataset:", nrow(df), "baris x", ncol(df), "kolom\n")
Dimensi dataset: 20640 baris x 10 kolom

Dataset ini terdiri dari 20.640 observasi dan 10 variabel, dengan rincian 9 variabel bertipe numerik dan 1 variabel bertipe variabel bertipe kategorik.

kesembilan variabel numerik tersebut meliputi informasi geografis (longitude, latitude), karakteristik hunian (housing_median_age, total_rooms, total_bedrooms), data demografis (population, households), dan informasi ekonomi (median_income, median_house_value). Serta 1 variabel kategorik yaitu ocean_proximity.

Berikut ini adalah kamus dari kesepuluh variabel yang memuat tipe, satuan, serta deskripsi dari masing-masing variabel.

var_df <- data.frame(
  No       = 1:10,
  Variabel = c("longitude","latitude","housing_median_age",
               "total_rooms","total_bedrooms","population",
               "households","median_income","median_house_value",
               "ocean_proximity"),
  Tipe     = c(rep("Numerik",9),"Kategorik"),
  Satuan   = c("Derajat","Derajat","Tahun","Kamar","Kamar",
               "Jiwa","Rumah Tangga","$10.000","USD","Kategori"),
  Deskripsi = c(
    "Koordinat bujur (semakin tinggi = semakin timur)",
    "Koordinat lintang (semakin tinggi = semakin utara)",
    "Median usia bangunan dalam satu blok",
    "Total jumlah kamar seluruh rumah dalam satu blok",
    "Total jumlah kamar tidur seluruh rumah dalam satu blok",
    "Total populasi penduduk dalam satu blok",
    "Jumlah rumah tangga dalam satu blok",
    "Median pendapatan rumah tangga (dalam puluhan ribu USD)",
    "[TARGET] Median nilai rumah dalam satu blok (USD)",
    "Kategori kedekatan lokasi dengan laut (5 kategori)"
  )
)

kable(var_df, caption = "Kamus Variabel California Housing Dataset") %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed","responsive"),
                full_width = TRUE) %>%
  row_spec(9, bold = TRUE, background = "#fcf8e3") %>%
  row_spec(0, bold = TRUE)
Kamus Variabel California Housing Dataset
No Variabel Tipe Satuan Deskripsi
1 longitude Numerik Derajat Koordinat bujur (semakin tinggi = semakin timur)
2 latitude Numerik Derajat Koordinat lintang (semakin tinggi = semakin utara)
3 housing_median_age Numerik Tahun Median usia bangunan dalam satu blok
4 total_rooms Numerik Kamar Total jumlah kamar seluruh rumah dalam satu blok
5 total_bedrooms Numerik Kamar Total jumlah kamar tidur seluruh rumah dalam satu blok
6 population Numerik Jiwa Total populasi penduduk dalam satu blok
7 households Numerik Rumah Tangga Jumlah rumah tangga dalam satu blok
8 median_income Numerik $10.000 Median pendapatan rumah tangga (dalam puluhan ribu USD)
9 median_house_value Numerik USD [TARGET] Median nilai rumah dalam satu blok (USD)
10 ocean_proximity Kategorik Kategori Kategori kedekatan lokasi dengan laut (5 kategori)

1.3 Tujuan Analisis

Project data reduction ini memiliki tujuan analisis sebagai berikut. 1. Memahami karakteristik dan kualitas data perumahan California melalui eksplorasi dan visualisasi data. 2. Membentuk fitur-fitur baru yang lebih informatif dan bermakna secara substantif melalui proses feature engineering. 3. Mengidentifikasi variabel-variabel paling relevan terhadap harga rumah dan mengeliminasi variabel yang redundan melalui feature selection. 4. Menyederhanakan dimensi data melalui PCA dan mengevaluasi keberhasilan reduksi dimensi berdasarkan proporsi variansi yang terjelaskan. 5. Menginterpretasikan makna substantif dari setiap principal component yang terbentuk dalam konteks pasar perumahan California. 6. Menghasilkan insight menyeluruh mengenai faktor-faktor utama yang paling menentukan harga rumah di California.

2 Exploratory Data Analysis (EDA)

2.1 Tampilan Awal Data

glimpse(df)
Rows: 20,640
Columns: 10
$ longitude          <dbl> -122.23, -122.22, -122.24, -122.25, -122.25, -122.2…
$ latitude           <dbl> 37.88, 37.86, 37.85, 37.85, 37.85, 37.85, 37.84, 37…
$ housing_median_age <dbl> 41, 21, 52, 52, 52, 52, 52, 52, 42, 52, 52, 52, 52,…
$ total_rooms        <dbl> 880, 7099, 1467, 1274, 1627, 919, 2535, 3104, 2555,…
$ total_bedrooms     <dbl> 129, 1106, 190, 235, 280, 213, 489, 687, 665, 707, …
$ population         <dbl> 322, 2401, 496, 558, 565, 413, 1094, 1157, 1206, 15…
$ households         <dbl> 126, 1138, 177, 219, 259, 193, 514, 647, 595, 714, …
$ median_income      <dbl> 8.3252, 8.3014, 7.2574, 5.6431, 3.8462, 4.0368, 3.6…
$ median_house_value <dbl> 452600, 358500, 352100, 341300, 342200, 269700, 299…
$ ocean_proximity    <fct> NEAR BAY, NEAR BAY, NEAR BAY, NEAR BAY, NEAR BAY, N…
head(df, 10)

Berdasarkan output fungsi glimpse(), dataset California Housing terdiri atas 20.640 observasi dan 10 variabel. Sebagian besar variabel bertipe numerik (<dbl>), yaitu longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, median_income, dan median_house_value. Selain itu, terdapat satu variabel kategorik yaitu ocean_proximity yang bertipe faktor (<fct>). Struktur data tersebut menunjukkan bahwa dataset didominasi oleh variabel kuantitatif yang dapat dianalisis lebih lanjut menggunakan berbagai metode statistik dan reduksi dimensi.

Tampilan 10 observasi pertama melalui fungsi head() menunjukkan bahwa seluruh pengamatan awal termasuk dalam kategori NEAR BAY. Nilai median_house_value pada observasi tersebut berkisar antara $226.700 hingga $452.600, yang mengindikasikan adanya variasi harga rumah meskipun berada pada kategori lokasi yang sama. Selain itu, nilai median_income juga menunjukkan keragaman yang cukup besar antarwilayah. Secara awal terlihat bahwa wilayah dengan pendapatan median yang lebih tinggi cenderung memiliki nilai rumah yang lebih tinggi. Temuan ini mengindikasikan bahwa faktor lokasi geografis dan tingkat pendapatan kemungkinan berkontribusi terhadap variasi harga rumah, sehingga perlu dieksplorasi lebih lanjut pada tahap analisis berikutnya.

2.2 Statistik Deskriptif

2.2.1 Variabel Numerik

num_vars <- df %>% select(-ocean_proximity)
summary(num_vars)
   longitude         latitude     housing_median_age  total_rooms   
 Min.   :-124.3   Min.   :32.54   Min.   : 1.00      Min.   :    2  
 1st Qu.:-121.8   1st Qu.:33.93   1st Qu.:18.00      1st Qu.: 1448  
 Median :-118.5   Median :34.26   Median :29.00      Median : 2127  
 Mean   :-119.6   Mean   :35.63   Mean   :28.64      Mean   : 2636  
 3rd Qu.:-118.0   3rd Qu.:37.71   3rd Qu.:37.00      3rd Qu.: 3148  
 Max.   :-114.3   Max.   :41.95   Max.   :52.00      Max.   :39320  
                                                                    
 total_bedrooms     population      households     median_income    
 Min.   :   1.0   Min.   :    3   Min.   :   1.0   Min.   : 0.4999  
 1st Qu.: 296.0   1st Qu.:  787   1st Qu.: 280.0   1st Qu.: 2.5634  
 Median : 435.0   Median : 1166   Median : 409.0   Median : 3.5348  
 Mean   : 537.9   Mean   : 1425   Mean   : 499.5   Mean   : 3.8707  
 3rd Qu.: 647.0   3rd Qu.: 1725   3rd Qu.: 605.0   3rd Qu.: 4.7432  
 Max.   :6445.0   Max.   :35682   Max.   :6082.0   Max.   :15.0001  
 NA's   :207                                                        
 median_house_value
 Min.   : 14999    
 1st Qu.:119600    
 Median :179700    
 Mean   :206856    
 3rd Qu.:264725    
 Max.   :500001    
                   
desc_table <- num_vars %>%
  summarise(across(everything(), list(
    N        = ~sum(!is.na(.)),
    Mean     = ~round(mean(., na.rm = TRUE), 2),
    Median   = ~round(median(., na.rm = TRUE), 2),
    SD       = ~round(sd(., na.rm = TRUE), 2),
    Min      = ~round(min(., na.rm = TRUE), 2),
    Max      = ~round(max(., na.rm = TRUE), 2),
    Skewness = ~round(mean(((. - mean(.,na.rm=T))/sd(.,na.rm=T))^3,na.rm=T),3)
  ))) %>%
  pivot_longer(everything(),
               names_to  = c("Variabel",".value"),
               names_sep = "_(?=[^_]+$)") %>%
  arrange(desc(abs(Skewness)))

kable(desc_table,
      caption = "Statistik Deskriptif Variabel Numerik",
      format.args = list(big.mark = ",")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(8, bold = TRUE,
              color = ifelse(abs(desc_table$Skewness) > 1, "red", "darkgreen")) %>%
  row_spec(0, bold = TRUE)
Statistik Deskriptif Variabel Numerik
Variabel N Mean Median SD Min Max Skewness
population 20,640 1,425.48 1,166.00 1,132.46 3.00 35,682.00 4.935
total_rooms 20,640 2,635.76 2,127.00 2,181.62 2.00 39,320.00 4.147
total_bedrooms 20,433 537.87 435.00 421.39 1.00 6,445.00 3.459
households 20,640 499.54 409.00 382.33 1.00 6,082.00 3.410
median_income 20,640 3.87 3.53 1.90 0.50 15.00 1.646
median_house_value 20,640 206,855.82 179,700.00 115,395.62 14,999.00 500,001.00 0.978
latitude 20,640 35.63 34.26 2.14 32.54 41.95 0.466
longitude 20,640 -119.57 -118.49 2.00 -124.35 -114.31 -0.298
housing_median_age 20,640 28.64 29.00 12.59 1.00 52.00 0.060

Berdasarkan statistik deskriptif yang diperoleh, variabel population memiliki nilai skewness tertinggi (4,935), menunjukkan distribusi yang sangat menceng ke kanan (strong positive skewness). Hal ini terlihat dari rata-rata (1.425,48) yang lebih besar daripada median (1.166), serta adanya nilai maksimum yang sangat tinggi (35.682). Kondisi tersebut mengindikasikan bahwa sebagian besar blok sensus memiliki jumlah penduduk relatif rendah hingga sedang, tetapi terdapat beberapa blok dengan jumlah penduduk yang sangat besar sehingga menarik distribusi ke arah kanan.

Variabel total_rooms, total_bedrooms, dan households juga menunjukkan skewness positif yang tinggi, masing-masing sebesar 4,147; 3,459; dan 3,410. Nilai rata-rata ketiga variabel tersebut lebih besar daripada mediannya, yang mengindikasikan bahwa sebagian besar blok sensus memiliki jumlah ruangan, kamar tidur, dan rumah tangga yang relatif moderat, namun terdapat sejumlah kecil blok dengan ukuran yang sangat besar sehingga menyebabkan distribusi tidak simetris. Selain itu, variabel total_bedrooms memiliki 207 nilai hilang (missing values) yang perlu ditangani sebelum dilakukan analisis lanjutan.

Variabel median_income memiliki skewness positif sedang (1,646), yang menunjukkan distribusi masih cenderung menceng ke kanan. Nilai median sebesar 3,53 berarti pendapatan median rumah tangga sekitar US$35.300 per tahun karena variabel ini dinyatakan dalam satuan puluhan ribu dolar Amerika. Adanya perbedaan antara rata-rata (3,87) dan median (3,53) menunjukkan bahwa terdapat sejumlah wilayah dengan tingkat pendapatan yang jauh lebih tinggi dibandingkan mayoritas wilayah lainnya.

Sementara itu, variabel geografis latitude dan longitude memiliki nilai skewness yang relatif kecil, masing-masing sebesar 0,466 dan -0,298. Hal ini menunjukkan bahwa distribusi kedua variabel cenderung mendekati simetris meskipun terdapat sedikit kecenderungan ke kanan pada latitude dan ke kiri pada longitude. Rentang nilai yang cukup luas pada kedua variabel tersebut mencerminkan cakupan wilayah pengamatan yang tersebar di berbagai lokasi di negara bagian California.

Variabel housing_median_age memiliki skewness yang sangat dekat dengan nol (0,060), sehingga distribusinya dapat dianggap hampir simetris. Hal ini juga didukung oleh nilai mean (28,64) dan median (29,00) yang hampir sama. Dengan demikian, usia median rumah pada dataset ini relatif tersebar secara seimbang di sekitar nilai tengahnya.

Sebagai variabel target, median_house_value memiliki skewness positif sebesar 0,978. Nilai rata-rata (US$206.856) yang lebih tinggi daripada median (US$179.700) menunjukkan adanya kecenderungan distribusi ke arah kanan akibat keberadaan rumah-rumah bernilai tinggi. Namun, perlu diperhatikan bahwa nilai maksimum sebesar US$500.001 mengindikasikan adanya censoring atau batas atas pada data asli California Housing, sehingga distribusi harga rumah kemungkinan terpotong pada nilai maksimum tersebut.

Secara keseluruhan, sebagian besar variabel yang berkaitan dengan ukuran dan karakteristik blok sensus menunjukkan distribusi yang tidak simetris dengan kecenderungan menceng ke kanan. Kondisi ini mengindikasikan adanya beberapa observasi bernilai sangat besar yang berpotensi memengaruhi analisis. Oleh karena itu, tahap prapengolahan data seperti penanganan nilai hilang, standarisasi variabel, dan eksplorasi lebih lanjut terhadap pencilan perlu dilakukan sebelum menerapkan teknik reduksi dimensi.

2.2.2 Variabel Kategorik

ocean_tbl <- df %>%
  count(ocean_proximity) %>%
  mutate(Persentase = paste0(round(n/sum(n)*100, 1), "%"),
         Rata_Harga = dollar(round(tapply(df$median_house_value,
                                          df$ocean_proximity, mean)[ocean_proximity], 0))) %>%
  rename(Kategori = ocean_proximity, Frekuensi = n)

kable(ocean_tbl, caption = "Distribusi Frekuensi dan Rata-rata Harga per Kategori") %>%
  kable_styling(bootstrap_options = c("striped","hover"),
                full_width = FALSE, position = "center") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Distribusi Frekuensi dan Rata-rata Harga per Kategori
Kategori Frekuensi Persentase Rata_Harga
<1H OCEAN 9136 44.3% $240,084
INLAND 6551 31.7% $124,805
ISLAND 5 0% $380,440
NEAR BAY 2290 11.1% $259,212
NEAR OCEAN 2658 12.9% $249,434

Berdasarkan statistik deskriptif yang diperoleh, Kategori <1H OCEAN (kurang dari 1 jam dari laut) mendominasi dataset dengan 9.136 blok sensus atau sekitar 44,3% dari total observasi. Kategori INLAND menempati urutan kedua dengan 6.551 blok (31,7%), diikuti oleh NEAR OCEAN (12,9%) dan NEAR BAY (11,1%). Sementara itu, kategori ISLAND hanya memiliki 5 observasi sehingga representasinya sangat terbatas dan hasil yang diperoleh pada kategori ini perlu ditafsirkan dengan hati-hati.

Berdasarkan rata-rata harga rumah, terlihat bahwa wilayah yang berdekatan dengan perairan cenderung memiliki nilai rumah yang lebih tinggi dibandingkan wilayah INLAND. Kategori INLAND memiliki rata-rata harga rumah terendah, yaitu sebesar US$124.805. Sebaliknya, kategori NEAR BAY memiliki rata-rata harga tertinggi di antara kategori dengan jumlah observasi yang memadai, yaitu sebesar US$259.212, diikuti oleh NEAR OCEAN (US$249.434) dan <1H OCEAN (US$240.084). Meskipun kategori ISLAND memiliki rata-rata harga rumah tertinggi (US$380.440), jumlah observasinya yang sangat sedikit menyebabkan nilai tersebut kurang representatif untuk menggambarkan kondisi populasi secara umum.

Temuan ini menunjukkan bahwa lokasi geografis, khususnya kedekatan dengan wilayah perairan, berpotensi menjadi faktor penting yang memengaruhi nilai rumah di California. Oleh karena itu, variabel ocean_proximity layak dipertimbangkan sebagai salah satu variabel yang berkontribusi dalam menjelaskan variasi harga rumah pada dataset ini.

2.3 Distribusi Variabel

2.3.1 Histogram Semua Variabel

num_long <- num_vars %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "Nilai")

ggplot(num_long, aes(x = Nilai, fill = Variabel)) +
  geom_histogram(bins = 40, color = "white", alpha = 0.85) +
  facet_wrap(~Variabel, scales = "free", ncol = 3) +
  scale_fill_viridis_d(option = "D") +
  scale_x_continuous(labels = comma) +
  scale_y_continuous(labels = comma) +
  labs(
    title    = "Distribusi Setiap Variabel Numerik",
    subtitle = "California Housing Dataset",
    x = NULL, y = "Frekuensi"
  ) +
  theme_bw(base_size = 12) +
  theme(
    plot.title       = element_text(face = "bold", size = 15),
    strip.text       = element_text(face = "bold"),
    legend.position  = "none",
    panel.grid.minor = element_blank()
  )

Berdasarkan histrogram di atas, didapatkan interpretasi sebagai berikut.

  • housing_median_age: Distribusi paling mendekati seragam (uniform) di antara semua variabel, dengan sebaran yang relatif merata antara 1 hingga 52 tahun. Nilai skewness yang sangat kecil (0,06) menunjukkan bahwa distribusi variabel ini hampir simetris. Selain itu, terlihat beberapa puncak frekuensi pada rentang umur tertentu, terutama di sekitar 35???37 tahun dan mendekati 52 tahun, yang menunjukkan adanya konsentrasi observasi pada kelompok umur tersebut.

  • total_rooms: Distribusi sangat menceng ke kanan (strong positive skewness) dengan nilai skewness sebesar 4,15. Sebagian besar blok sensus memiliki total kamar antara 1.000 sampai 5.000, namun terdapat ekor distribusi yang panjang hingga mencapai 39.320 kamar. Perbedaan antara median (2.127) dan mean (2.636) juga mengonfirmasi adanya pengaruh observasi bernilai sangat besar.

  • total_bedrooms: Menunjukkan pola yang serupa dengan total_rooms (skewness = 3,46), yaitu distribusi yang menceng ke kanan dengan median 435 kamar tidur dan nilai maksimum mencapai 6.445 kamar tidur. Pola ini konsisten karena total_bedrooms merupakan bagian dari total_rooms.

  • population: Merupakan variabel dengan distribusi paling menceng ke kanan (skewness = 4,94). Sebagian besar blok sensus memiliki populasi sekitar 500 sampai 2.500 jiwa, namun terdapat beberapa blok dengan populasi yang sangat besar hingga mencapai 35.682 jiwa. Kehadiran observasi-observasi ekstrem tersebut menyebabkan terbentuknya ekor distribusi yang panjang di sisi kanan.

  • households: Memiliki pola distribusi yang mirip dengan population (skewness = 3,41), yang menunjukkan bahwa sebagian besar blok sensus memiliki jumlah rumah tangga relatif rendah hingga sedang, namun terdapat sejumlah kecil blok dengan jumlah rumah tangga yang sangat besar. Hal ini mengindikasikan adanya variasi ukuran blok sensus yang cukup tinggi.

  • median_income: Menunjukkan skewness positif yang cukup kuat (skewness = 1,65), sehingga distribusinya masih cenderung menceng ke kanan dan belum dapat dianggap normal. Sebagian besar observasi terkonsentrasi pada tingkat pendapatan menengah, dengan sejumlah kecil wilayah yang memiliki pendapatan jauh lebih tinggi dibandingkan mayoritas wilayah lainnya. Selain itu, terlihat adanya penumpukan kecil pada nilai maksimum 15,0 yang mengindikasikan adanya batas atas (capping) pada data asli.

  • median_house_value: Memiliki distribusi menceng ke kanan dengan skewness moderat (0,98). Sebagian besar nilai rumah berada pada kisaran US$100.000???US$300.000, namun masih terdapat sejumlah rumah dengan nilai yang jauh lebih tinggi. Terlihat pula lonjakan frekuensi yang cukup jelas pada nilai maksimum US$500.001 yang menunjukkan adanya capping atau batas atas pada data asli. Kondisi ini perlu diperhatikan dalam analisis lanjutan karena dapat memengaruhi hasil pemodelan.

  • longitude dan latitude: Kedua variabel geografis ini menunjukkan pola distribusi yang berbeda dari variabel numerik lainnya. Distribusinya bersifat multimodal, ditandai dengan adanya beberapa puncak frekuensi yang mencerminkan konsentrasi observasi pada wilayah geografis tertentu di California. Pada variabel latitude, puncak utama terlihat di sekitar 34??° dan 37???38??°, yang menunjukkan wilayah dengan kepadatan observasi yang tinggi. Oleh karena itu, pola distribusi kedua variabel ini lebih merefleksikan karakteristik spasial daripada distribusi statistik konvensional.

2.3.2 Distribusi Variabel Target

ggplot(df, aes(x = median_house_value)) +
  geom_histogram(bins = 50, fill = "#2c7fb8", color = "white", alpha = 0.85) +
  geom_vline(xintercept = mean(df$median_house_value, na.rm=TRUE),
             color = "red", linetype = "dashed", size = 1.2) +
  geom_vline(xintercept = median(df$median_house_value, na.rm=TRUE),
             color = "darkorange", linetype = "dashed", size = 1.2) +
  annotate("text", x = mean(df$median_house_value)+15000, y = 1100,
           label = paste0("Mean\n$", comma(round(mean(df$median_house_value)))),
           color = "red", fontface = "bold", size = 3.5) +
  annotate("text", x = median(df$median_house_value)-55000, y = 1100,
           label = paste0("Median\n$", comma(round(median(df$median_house_value)))),
           color = "darkorange", fontface = "bold", size = 3.5) +
  scale_x_continuous(labels = dollar_format(prefix = "$", big.mark = ",")) +
  scale_y_continuous(labels = comma) +
  labs(
    title    = "Distribusi Median House Value (Variabel Target)",
    subtitle = "Garis merah = Mean | Garis oranye = Median | Puncak di $500.001 = data ter-cap",
    x = "Median House Value (USD)", y = "Frekuensi"
  ) +
  theme_bw(base_size = 12) +
  theme(plot.title = element_text(face = "bold", size = 14),
        plot.subtitle = element_text(size = 10))

Berdasarkan histogram di atas, dapat diinterpretasikan bahwa distribusi median_house_value menunjukkan pola right-skewed dengan nilai mean (US$206.856) yang lebih tinggi daripada median (US$179.700), dengan selisih sekitar US$27.000. Kondisi ini menunjukkan bahwa sebagian besar blok sensus memiliki harga rumah di bawah rata-rata, sementara sejumlah kecil rumah dengan nilai sangat tinggi menarik rata-rata ke arah kanan.

Selain itu, terlihat lonjakan frekuensi yang sangat mencolok pada nilai sekitar US$500.001 di ujung kanan histogram. Fenomena ini mengindikasikan adanya capping atau batas atas pada data asli California Housing, yaitu observasi dengan nilai rumah yang melebihi batas tertentu dicatat sebagai US$500.001. Berdasarkan data, terdapat sekitar 965 observasi (4,7% dari total data) yang berada pada nilai tersebut. Akibatnya, distribusi harga rumah pada kelompok bernilai tinggi tidak sepenuhnya tercerminkan dalam data dan berpotensi memengaruhi hasil analisis maupun pemodelan prediktif, khususnya pada segmen rumah dengan harga tinggi.

2.3.3 Distribusi Variabel Kategorik

p1 <- ggplot(df, aes(x = reorder(ocean_proximity, -table(ocean_proximity)[ocean_proximity]),
                      fill = ocean_proximity)) +
  geom_bar(color = "white", alpha = 0.9, show.legend = FALSE) +
  geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.4,
            fontface = "bold", size = 3.8) +
  scale_fill_brewer(palette = "Set2") +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.1))) +
  labs(title = "Jumlah Blok per Kategori", x = NULL, y = "Frekuensi") +
  theme_bw(base_size = 11) +
  theme(plot.title = element_text(face = "bold"),
        axis.text.x = element_text(angle = 20, hjust = 1),
        panel.grid.major.x = element_blank())

p2 <- ggplot(df, aes(x = reorder(ocean_proximity, median_house_value,
                                   FUN = median),
                      y = median_house_value, fill = ocean_proximity)) +
  geom_boxplot(alpha = 0.8, color = "gray30", show.legend = FALSE,
               outlier.alpha = 0.2, outlier.size = 0.7) +
  coord_flip() +
  scale_fill_brewer(palette = "Set2") +
  scale_y_continuous(labels = dollar_format(prefix = "$", big.mark = ",")) +
  labs(title    = "Distribusi Harga Rumah per Kategori",
       subtitle = "Diurutkan berdasarkan median harga",
       x = NULL, y = "Median House Value") +
  theme_bw(base_size = 11) +
  theme(plot.title = element_text(face = "bold"),
        plot.subtitle = element_text(size = 9))

grid.arrange(p1, p2, ncol = 2)

Berdasarkan visualisasi tersebut, dapat diinterpertasikan sebagai berikut.

Panel kiri (Frekuensi): Distribusi kategori ocean_proximity tidak seimbang. Kategori <1H OCEAN mendominasi dataset dengan 9.136 blok sensus (44,3%), diikuti oleh INLAND sebanyak 6.551 blok (31,7%). Sementara itu, kategori NEAR OCEAN dan NEAR BAY masing-masing mencakup 12,9% dan 11,1% dari total observasi. Kategori ISLAND hanya terdiri atas 5 observasi sehingga statistik yang dihasilkan pada kategori ini perlu diinterpretasikan dengan sangat hati-hati karena kurang representatif.

Panel kanan (Distribusi harga): Boxplot menunjukkan adanya perbedaan harga rumah yang cukup jelas antar kategori lokasi. Kategori INLAND memiliki median harga rumah terendah (sekitar US$108.500), sedangkan kategori NEAR BAY dan NEAR OCEAN memiliki median harga yang jauh lebih tinggi, masing-masing sekitar US$233.800 dan US$229.450. Kategori <1H OCEAN berada di antara keduanya dengan median sekitar US$214.850. Temuan ini mengindikasikan bahwa kedekatan terhadap wilayah perairan berkaitan dengan peningkatan nilai rumah.

Kategori ISLAND memiliki median harga tertinggi (sekitar US$414.700), namun karena hanya terdiri atas 5 observasi, nilai tersebut tidak dapat digeneralisasikan untuk keseluruhan populasi. Selain itu, sebagian besar kategori menunjukkan keberadaan pencilan (outlier) pada sisi atas distribusi, yang mengindikasikan adanya sejumlah rumah dengan nilai jauh lebih tinggi dibandingkan mayoritas rumah dalam kategori yang sama. Hal ini konsisten dengan hasil analisis sebelumnya yang menunjukkan bahwa variabel median_house_value memiliki distribusi yang menceng ke kanan (right-skewed).

2.4 Missing Values

2.4.1 Tabel Missing Values

missing_df <- df %>%
  summarise(across(everything(), ~sum(is.na(.)))) %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "N_Missing") %>%
  mutate(
    Pct_Missing = round(N_Missing / nrow(df) * 100, 2),
    Status      = ifelse(N_Missing == 0, "Lengkap", "Ada Missing")
  ) %>%
  arrange(desc(N_Missing))

kable(missing_df,
      caption = "Rekap Missing Values Seluruh Variabel",
      col.names = c("Variabel","Jumlah Missing","% Missing","Status")) %>%
  kable_styling(bootstrap_options = c("striped","hover"),
                full_width = FALSE, position = "center") %>%
  row_spec(which(missing_df$N_Missing > 0), background = "#fcf8e3", bold = TRUE) %>%
  row_spec(0, bold = TRUE)
Rekap Missing Values Seluruh Variabel
Variabel Jumlah Missing % Missing Status
total_bedrooms 207 1 Ada Missing
longitude 0 0 Lengkap
latitude 0 0 Lengkap
housing_median_age 0 0 Lengkap
total_rooms 0 0 Lengkap
population 0 0 Lengkap
households 0 0 Lengkap
median_income 0 0 Lengkap
median_house_value 0 0 Lengkap
ocean_proximity 0 0 Lengkap

2.4.2 Visualisasi Missing Values

ggplot(missing_df, aes(x = reorder(Variabel, N_Missing), y = Pct_Missing,
                        fill = Status)) +
  geom_col(width = 0.6, alpha = 0.85) +
  geom_text(aes(label = ifelse(N_Missing > 0,
                               paste0(N_Missing, " data (", Pct_Missing, "%)"),
                               "Tidak ada")),
            hjust = -0.05, size = 3.5, fontface = "bold") +
  coord_flip(ylim = c(0, 3)) +
  scale_fill_manual(values = c("Lengkap" = "#2ca25f", "Ada Missing" = "#e67e22")) +
  scale_y_continuous(labels = function(x) paste0(x, "%")) +
  labs(
    title    = "Persentase Missing Values per Variabel",
    subtitle = "Hanya variabel total_bedrooms yang memiliki nilai hilang",
    x = NULL, y = "Persentase Missing (%)", fill = "Status"
  ) +
  theme_bw(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", size = 14),
        plot.subtitle = element_text(size = 10),
        panel.grid.major.y = element_blank())

Berdasarkan analisis missing values, secara keseluruhan, kualitas data tergolong sangat baik. Dari 10 variabel, hanya satu variabel yang memiliki missing values yaitu total_bedrooms dengan 207 nilai hilang (1,0% dari total 20.640 observasi). Jumlah ini tergolong kecil dan tidak akan berdampak signifikan pada analisis. Penyebab paling plausibel adalah ketidaklengkapan pencatatan pada beberapa blok di dataset. Untuk penanganan, imputasi menggunakan median atau menggunakan rasio total_bedrooms/total_rooms dari blok-blok yang datanya lengkap adalah pendekatan yang paling tepat untuk konteks dataset ini, mengingat distribusinya yang right-skewed (imputasi mean akan menghasilkan nilai yang bias).

2.5 Deteksi Outlier

2.5.1 Boxplot Outlier

box_vars <- c("housing_median_age","total_rooms","total_bedrooms",
              "population","households","median_income","median_house_value")

box_long <- df %>%
  select(all_of(box_vars)) %>%
  pivot_longer(everything(), names_to = "Variabel", values_to = "Nilai")

ggplot(box_long, aes(x = Variabel, y = Nilai, fill = Variabel)) +
  geom_boxplot(alpha = 0.75, color = "gray30",
               outlier.color = "red", outlier.alpha = 0.2,
               outlier.size = 0.7, width = 0.55) +
  facet_wrap(~Variabel, scales = "free", ncol = 4) +
  scale_fill_viridis_d(option = "C") +
  scale_y_continuous(labels = comma) +
  labs(
    title    = "Boxplot Deteksi Outlier",
    subtitle = "Titik merah di luar whisker = outlier (lebih dari 1.5 ?? IQR dari Q1/Q3)",
    x = NULL, y = "Nilai"
  ) +
  theme_bw(base_size = 11) +
  theme(
    plot.title       = element_text(face = "bold", size = 14),
    plot.subtitle    = element_text(size = 9),
    strip.text       = element_text(face = "bold"),
    legend.position  = "none",
    axis.text.x      = element_blank(),
    axis.ticks.x     = element_blank()
  )

Berdasarkan boxplot di atas, dapat diinterpretasikan sebagai berikut.

  • housing_median_age: Tidak terdapat outlier berdasarkan metode IQR (0% data berada di luar batas whisker). Dengan Q1 = 18 tahun dan Q3 = 37 tahun, seluruh observasi masih berada dalam rentang yang dianggap wajar menurut kriteria IQR. Variabel ini merupakan satu-satunya variabel numerik yang tidak memiliki outlier.

  • total_rooms: Terdapat 1.287 outlier (6,2%) di atas batas atas IQR sebesar 5.698,38 kamar. Sebagian besar blok sensus memiliki jumlah kamar yang relatif rendah hingga sedang, namun terdapat sejumlah blok dengan jumlah kamar yang sangat besar hingga mencapai 39.320 kamar. Keberadaan observasi ekstrem ini menyebabkan distribusi menjadi sangat menceng ke kanan.

  • total_bedrooms: Terdapat 1.271 outlier (6,2%) dengan batas atas IQR sebesar 1.173,50 kamar tidur. Nilai maksimum mencapai 6.445 kamar tidur, menunjukkan adanya sejumlah blok sensus dengan ukuran yang jauh lebih besar dibandingkan mayoritas observasi. Pola ini sejalan dengan distribusi total_rooms karena kedua variabel menggambarkan karakteristik ukuran hunian.

  • population: Memiliki 1.196 outlier (5,8%) dengan batas atas IQR sebesar 3.132 jiwa. Nilai maksimum yang mencapai 35.682 jiwa menunjukkan adanya beberapa blok sensus dengan kepadatan penduduk yang sangat tinggi dibandingkan mayoritas blok lainnya. Variabel ini juga merupakan salah satu variabel dengan tingkat skewness tertinggi dalam dataset.

  • households: Terdapat 1.220 outlier (5,9%) di atas batas atas IQR sebesar 1.092,50 rumah tangga. Nilai maksimum mencapai 6.082 rumah tangga, yang menunjukkan adanya sejumlah blok sensus dengan jumlah rumah tangga yang jauh lebih besar dibandingkan sebagian besar observasi lainnya.

  • median_income: Memiliki 681 outlier (3,3%), jumlah yang relatif lebih sedikit dibandingkan variabel agregat lainnya. Batas atas IQR sebesar 8,01 menunjukkan bahwa sebagian besar wilayah memiliki tingkat pendapatan yang tidak terlalu ekstrem, meskipun masih terdapat sejumlah wilayah dengan pendapatan yang jauh lebih tinggi dibandingkan mayoritas observasi.

  • median_house_value: Terdapat 1.071 outlier (5,2%) berdasarkan metode IQR dengan batas atas sebesar US$482.412,50. Sebagian besar outlier berada di sekitar nilai maksimum US$500.001 yang merupakan batas atas (capping) pada data asli California Housing. Oleh karena itu, sebagian outlier pada variabel ini kemungkinan dipengaruhi oleh proses pembatasan nilai dalam dataset, sehingga interpretasinya perlu dilakukan dengan hati-hati.

Variabel longitude dan latitude tidak disertakan dalam analisis outlier karena keduanya merupakan variabel koordinat geografis. Nilai ekstrem pada kedua variabel tersebut mencerminkan lokasi spasial yang valid, bukan pengamatan yang menyimpang dari populasi, sehingga analisis outlier lebih difokuskan pada variabel karakteristik perumahan, kependudukan, dan ekonomi.

2.5.2 Tabel Rekap Outlier

outlier_stats <- df %>%
  select(all_of(box_vars)) %>%
  summarise(across(everything(), list(
    Q1         = ~round(quantile(.,0.25,na.rm=T), 2),
    Q3         = ~round(quantile(.,0.75,na.rm=T), 2),
    IQRval     = ~round(IQR(.,na.rm=T), 2),
    BatasAtas  = ~round(quantile(.,0.75,na.rm=T) + 1.5*IQR(.,na.rm=T), 2),
    NOutlier   = ~sum(. > quantile(.,0.75,na.rm=T)+1.5*IQR(.,na.rm=T) |
                      . < quantile(.,0.25,na.rm=T)-1.5*IQR(.,na.rm=T), na.rm=T),
    PctOutlier = ~round(sum(. > quantile(.,0.75,na.rm=T)+1.5*IQR(.,na.rm=T) |
                            . < quantile(.,0.25,na.rm=T)-1.5*IQR(.,na.rm=T),
                            na.rm=T)/n()*100, 1)
  ))) %>%
  pivot_longer(everything(),
               names_to  = c("Variabel",".value"),
               names_sep = "_(?=[^_]+$)") %>%
  arrange(desc(PctOutlier))

kable(outlier_stats,
      caption = "Rekap Outlier Berdasarkan Metode IQR",
      col.names = c("Variabel","Q1","Q3","IQR","Batas Atas",
                    "N Outlier","% Outlier"),
      format.args = list(big.mark = ",")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE) %>%
  row_spec(which(outlier_stats$PctOutlier > 5), background = "#fdf2f2") %>%
  row_spec(which(outlier_stats$PctOutlier == 0), background = "#eafaf1") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Rekap Outlier Berdasarkan Metode IQR
Variabel Q1 Q3 IQR Batas Atas N Outlier % Outlier
total_rooms 1,447.75 3,148.00 1,700.25 5,698.38 1,287 6.2
total_bedrooms 296.00 647.00 351.00 1,173.50 1,271 6.2
households 280.00 605.00 325.00 1,092.50 1,220 5.9
population 787.00 1,725.00 938.00 3,132.00 1,196 5.8
median_house_value 119,600.00 264,725.00 145,125.00 482,412.50 1,071 5.2
median_income 2.56 4.74 2.18 8.01 681 3.3
housing_median_age 18.00 37.00 19.00 65.50 0 0.0

2.6 Analisis Korelasi

2.6.1 Heatmap Korelasi

corr_vars <- df %>% select(-ocean_proximity, -longitude, -latitude)
cor_matrix <- cor(corr_vars, use = "pairwise.complete.obs")

corrplot(cor_matrix,
         method      = "color",
         type        = "upper",
         order       = "hclust",
         col         = colorRampPalette(c("#d73027","white","#1a9850"))(200),
         addCoef.col = "black",
         number.cex  = 0.82,
         tl.col      = "black",
         tl.cex      = 0.92,
         tl.srt      = 45,
         diag        = FALSE,
         cl.ratio    = 0.15,
         title       = "Heatmap Korelasi Pearson Antar Variabel Numerik",
         mar         = c(0, 0, 2, 0))

Berdasarkan heatmap korelasi tersebut, dapat diinterpretasikan sebagai berikut.

Korelasi antar variabel prediktor (potensi multikolinearitas):

  • total_bedrooms ??? households memiliki korelasi tertinggi (r = 0,980), menunjukkan hubungan linear yang hampir sempurna. Hal ini mengindikasikan bahwa kedua variabel membawa informasi yang sangat mirip mengenai karakteristik ukuran blok sensus.

  • total_rooms ??? total_bedrooms (r = 0,930) dan total_rooms ??? households (r = 0,919) juga menunjukkan korelasi yang sangat kuat. Bersama dengan population, variabel-variabel tersebut membentuk kelompok dengan tingkat keterkaitan yang tinggi sehingga berpotensi menimbulkan multikolinearitas.

  • population ??? households (r = 0,907), population ??? total_bedrooms (r = 0,878), dan population ??? total_rooms (r = 0,857) menunjukkan bahwa blok sensus dengan jumlah rumah tangga yang besar umumnya juga memiliki populasi dan jumlah ruangan yang lebih besar. Korelasi yang tinggi ini mengindikasikan adanya tumpang tindih informasi antarvariabel.

  • housing_median_age memiliki korelasi negatif sedang dengan variabel-variabel agregat, yaitu population (-0,30), households (-0,30), total_bedrooms (-0,32), dan total_rooms (-0,36). Hal ini menunjukkan bahwa blok dengan usia median rumah yang lebih tinggi cenderung memiliki ukuran dan jumlah penduduk yang lebih kecil.

Korelasi variabel prediktor dengan target (median_house_value):

  • median_income memiliki korelasi positif paling kuat terhadap harga rumah (r = 0,688). Hasil ini menunjukkan bahwa wilayah dengan pendapatan median yang lebih tinggi cenderung memiliki nilai rumah yang lebih tinggi, sehingga variabel ini merupakan prediktor yang paling informatif terhadap median_house_value.

  • housing_median_age memiliki korelasi positif yang lemah terhadap harga rumah (r = 0,106), menunjukkan bahwa hubungan linear antara usia rumah dan harga rumah relatif kecil.

  • total_rooms (r = 0,134), households (r = 0,066), dan total_bedrooms (r = 0,050) hanya memiliki korelasi positif yang sangat lemah terhadap harga rumah.

  • population memiliki korelasi negatif yang sangat kecil terhadap harga rumah (r = ???0,025). Nilai ini menunjukkan bahwa jumlah penduduk pada suatu blok sensus hampir tidak memiliki hubungan linear dengan harga rumah.

Secara keseluruhan, heatmap menunjukkan adanya kelompok variabel dengan korelasi sangat tinggi (population, households, total_rooms, dan total_bedrooms) yang mengindikasikan potensi multikolinearitas. Temuan ini mendukung perlunya penerapan teknik reduksi dimensi seperti PCA untuk merangkum informasi dari variabel-variabel yang saling berkorelasi kuat tersebut ke dalam sejumlah komponen yang lebih ringkas.

2.6.2 Scatter Plot vs Target

predictors <- c("median_income","housing_median_age",
                "total_rooms","population","households","total_bedrooms")

plots <- lapply(predictors, function(var) {
  r_val <- round(cor(df[[var]], df$median_house_value, use = "complete.obs"), 3)
  ggplot(df, aes_string(x = var, y = "median_house_value")) +
    geom_point(alpha = 0.10, color = "#2c7fb8", size = 0.6) +
    geom_smooth(method = "lm", color = "red", se = FALSE, size = 1) +
    scale_y_continuous(labels = dollar_format(prefix = "$", big.mark = ",")) +
    scale_x_continuous(labels = comma) +
    labs(title = paste0(var, "\n(r = ", r_val, ")"),
         x = var, y = "Harga Rumah") +
    theme_bw(base_size = 9) +
    theme(plot.title = element_text(face = "bold", size = 9, hjust = 0.5))
})

grid.arrange(grobs = plots, ncol = 3)

Berdasarkan scatter plot tersebut, didapat interpretasi hubungan sebagai berikut.

  • median_income vs harga rumah: Menunjukkan hubungan linear positif paling kuat di antara seluruh variabel prediktor (r = 0,688). Semakin tinggi pendapatan median suatu blok sensus, semakin tinggi pula nilai rumah pada wilayah tersebut. Pola titik-titik data membentuk tren naik yang cukup jelas, meskipun masih terdapat variasi yang cukup besar pada setiap tingkat pendapatan. Selain itu, terlihat bahwa sebaran harga rumah cenderung semakin lebar pada tingkat pendapatan yang tinggi, yang mengindikasikan peningkatan variasi harga rumah pada kelompok berpendapatan tinggi.

  • housing_median_age vs harga rumah: Menunjukkan hubungan positif yang sangat lemah (r = 0,106). Sebaran titik yang sangat menyebar dan garis regresi yang relatif datar menunjukkan bahwa usia median rumah hanya memiliki hubungan linear yang kecil terhadap harga rumah. Meskipun demikian, terdapat kecenderungan bahwa wilayah dengan usia rumah yang lebih tua memiliki nilai rumah yang sedikit lebih tinggi.

  • total_rooms vs harga rumah: Memiliki korelasi positif yang lemah (r = 0,134). Meskipun garis regresi menunjukkan kecenderungan meningkat, sebaran titik yang sangat luas menunjukkan bahwa jumlah kamar bukan merupakan prediktor utama harga rumah. Banyaknya kamar pada suatu blok sensus tidak selalu diikuti oleh peningkatan nilai rumah yang signifikan.

  • total_bedrooms vs harga rumah: Menunjukkan korelasi positif yang sangat lemah (r = 0,050). Pola sebaran data tidak memperlihatkan hubungan yang kuat, sehingga jumlah kamar tidur secara langsung kurang mampu menjelaskan variasi harga rumah.

  • households vs harga rumah: Memiliki korelasi positif yang sangat lemah (r = 0,066). Walaupun terdapat sedikit kecenderungan peningkatan harga seiring bertambahnya jumlah rumah tangga, hubungan tersebut relatif kecil dan tidak menunjukkan pola yang jelas.

  • population vs harga rumah: Menunjukkan korelasi negatif yang sangat lemah (r = -0,025). Nilai korelasi yang mendekati nol menunjukkan bahwa jumlah penduduk pada suatu blok sensus hampir tidak memiliki hubungan linear dengan harga rumah. Hal ini juga terlihat dari pola titik yang menyebar tanpa arah yang jelas.

Secara keseluruhan, scatter plot mengonfirmasi hasil analisis korelasi sebelumnya bahwa median_income merupakan variabel yang memiliki hubungan paling kuat dengan median_house_value, sedangkan variabel ukuran blok sensus seperti population, households, total_rooms, dan total_bedrooms memiliki hubungan linear yang relatif lemah terhadap harga rumah. Temuan ini menunjukkan bahwa kondisi ekonomi wilayah lebih berpengaruh terhadap harga rumah dibandingkan ukuran atau kepadatan blok sensus itu sendiri.

2.6.3 Tabel Korelasi dengan Target

cor_target <- cor_matrix["median_house_value",] %>%
  as.data.frame() %>%
  rownames_to_column("Variabel") %>%
  rename(Korelasi = ".") %>%
  filter(Variabel != "median_house_value") %>%
  mutate(
    Korelasi      = round(Korelasi, 4),
    Kekuatan      = case_when(
      abs(Korelasi) >= 0.6 ~ "Kuat",
      abs(Korelasi) >= 0.3 ~ "Sedang",
      abs(Korelasi) >= 0.1 ~ "Lemah",
      TRUE                 ~ "Sangat Lemah"
    ),
    Arah = ifelse(Korelasi > 0, "Positif", "Negatif")
  ) %>%
  arrange(desc(abs(Korelasi)))

kable(cor_target,
      caption = "Korelasi Setiap Variabel terhadap median_house_value") %>%
  kable_styling(bootstrap_options = c("striped","hover"),
                full_width = FALSE, position = "center") %>%
  row_spec(1, bold = TRUE, background = "#fcf8e3") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Korelasi Setiap Variabel terhadap median_house_value
Variabel Korelasi Kekuatan Arah
median_income 0.6881 Kuat Positif
total_rooms 0.1342 Lemah Positif
housing_median_age 0.1056 Lemah Positif
households 0.0658 Sangat Lemah Positif
total_bedrooms 0.0497 Sangat Lemah Positif
population -0.0246 Sangat Lemah Negatif

2.7 Identifikasi Multikolinearitas

2.7.1 Heatmap Korelasi Prediktor

pred_vars <- corr_vars %>% select(-median_house_value)
cor_pred  <- cor(pred_vars, use = "pairwise.complete.obs")

ggcorrplot(cor_pred,
           hc.order    = TRUE,
           type        = "lower",
           lab         = TRUE,
           lab_size    = 3.8,
           colors      = c("#d73027","white","#1a9850"),
           outline.col = "white",
           ggtheme     = theme_bw()) +
  labs(title    = "Korelasi Antar Variabel Prediktor",
       subtitle = "Fokus pada korelasi yang mendekati ??±1 (indikasi multikolinearitas)") +
  theme(
    plot.title    = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 10),
    axis.text.x   = element_text(angle = 45, hjust = 1)
  )

Berdasarkan heatmap tersebut, dapat diinterpretasikan bahwa heatmap menunjukkan adanya kelompok variabel yang memiliki korelasi sangat tinggi satu sama lain, yaitu population, total_rooms, total_bedrooms, dan households, dengan koefisien korelasi berkisar antara 0,86 hingga 0,98. Nilai korelasi yang sangat tinggi tersebut mengindikasikan adanya potensi multikolinearitas yang kuat serta menunjukkan bahwa variabel-variabel tersebut kemungkinan mengandung informasi yang saling tumpang tindih mengenai ukuran atau kapasitas suatu blok sensus.

Sebaliknya, variabel median_income dan housing_median_age memiliki korelasi yang relatif rendah terhadap kelompok variabel tersebut, dengan nilai korelasi berkisar antara ???0,36 hingga 0,20. Hal ini menunjukkan bahwa kedua variabel tersebut menangkap aspek yang berbeda dari karakteristik wilayah dibandingkan variabel-variabel yang berkaitan dengan ukuran blok sensus. Temuan ini mengindikasikan bahwa proses reduksi dimensi, seperti PCA, berpotensi efektif dalam merangkum informasi dari kelompok variabel yang saling berkorelasi tinggi tanpa kehilangan terlalu banyak informasi.

2.7.2 VIF (Variance Inflation Factor)

df_vif   <- df %>% select(-ocean_proximity, -longitude, -latitude) %>% drop_na()
model_vif <- lm(median_house_value ~ ., data = df_vif)

vif_df <- data.frame(
  Variabel = names(vif(model_vif)),
  VIF      = round(vif(model_vif), 2)
) %>%
  mutate(
    Kategori = case_when(
      VIF > 10 ~ "PARAH (>10)",
      VIF > 5  ~ "MODERAT (5-10)",
      VIF > 2  ~ "RINGAN (2-5)",
      TRUE     ~ "AMAN (<2)"
    )
  ) %>%
  arrange(desc(VIF))

kable(vif_df, caption = "Variance Inflation Factor (VIF) per Variabel Prediktor") %>%
  kable_styling(bootstrap_options = c("striped","hover"),
                full_width = FALSE, position = "center") %>%
  row_spec(which(vif_df$VIF > 10), background = "#fdf2f2", bold = TRUE) %>%
  row_spec(which(vif_df$VIF > 5 & vif_df$VIF <= 10), background = "#fef9e7") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(3, bold = TRUE,
              color = ifelse(vif_df$VIF > 10, "red",
                      ifelse(vif_df$VIF > 5, "darkorange", "darkgreen")))
Variance Inflation Factor (VIF) per Variabel Prediktor
Variabel VIF Kategori
total_bedrooms total_bedrooms 35.50 PARAH (>10)
households households 33.81 PARAH (>10)
total_rooms total_rooms 11.90 PARAH (>10)
population population 6.22 MODERAT (5-10)
median_income median_income 1.51 AMAN (<2)
housing_median_age housing_median_age 1.16 AMAN (<2)

2.7.3 Visualisasi VIF

ggplot(vif_df, aes(x = reorder(Variabel, VIF), y = VIF,
                    fill = Kategori)) +
  geom_col(width = 0.6, alpha = 0.88) +
  geom_hline(yintercept = 10, linetype = "dashed", color = "red",     size = 1.0) +
  geom_hline(yintercept = 5,  linetype = "dashed", color = "darkorange", size = 1.0) +
  geom_text(aes(label = round(VIF, 1)), hjust = -0.15, fontface = "bold", size = 4) +
  annotate("text", x = 0.7, y = 11.2, label = "VIF = 10 (batas parah)",
           color = "red", fontface = "italic", size = 3.2, hjust = 0) +
  annotate("text", x = 0.7, y = 6.2,  label = "VIF = 5 (batas moderat)",
           color = "darkorange", fontface = "italic", size = 3.2, hjust = 0) +
  coord_flip(ylim = c(0, max(vif_df$VIF) * 1.15)) +
  scale_fill_manual(values = c("PARAH (>10)"     = "#e74c3c",
                               "MODERAT (5-10)"  = "#e67e22",
                               "RINGAN (2-5)"    = "#f1c40f",
                               "AMAN (<2)"       = "#2ca25f")) +
  labs(
    title    = "Nilai VIF per Variabel Prediktor",
    subtitle = "VIF > 10 = multikolinearitas parah | VIF > 5 = perlu perhatian",
    x = NULL, y = "VIF", fill = "Kategori"
  ) +
  theme_bw(base_size = 12) +
  theme(plot.title    = element_text(face = "bold", size = 14),
        plot.subtitle = element_text(size = 10),
        panel.grid.major.y = element_blank(),
        legend.position = "bottom")

VIF (Variance Inflation Factor) mengukur seberapa besar varians estimasi koefisien suatu variabel meningkat akibat adanya multikolinearitas dengan variabel prediktor lainnya. Secara umum, nilai VIF > 10 menunjukkan adanya multikolinearitas yang tinggi dan perlu mendapat perhatian khusus. Berdasarkan nilai VIF dan visualisasi heatmap di atas, didapat interpretasi sebagai berikut.

  • total_bedrooms (VIF = 35,50) memiliki nilai VIF tertinggi dan menunjukkan adanya multikolinearitas yang sangat kuat. Nilai ini mengindikasikan bahwa sebagian besar informasi yang terkandung dalam variabel tersebut dapat dijelaskan oleh kombinasi variabel prediktor lainnya.

  • households (VIF = 33,81) juga menunjukkan tingkat multikolinearitas yang sangat tinggi. Hasil ini konsisten dengan analisis korelasi sebelumnya yang menunjukkan hubungan yang sangat kuat antara households, total_bedrooms, total_rooms, dan population.

  • total_rooms (VIF = 11,90) masih berada pada kategori multikolinearitas tinggi. Variabel ini memiliki korelasi yang kuat dengan beberapa variabel ukuran blok sensus lainnya sehingga mengandung informasi yang relatif tumpang tindih.

  • population (VIF = 6,22) termasuk dalam kategori moderat. Meskipun masih menunjukkan adanya hubungan yang cukup kuat dengan variabel lain, variabel ini tampaknya masih menyimpan informasi unik yang tidak sepenuhnya dijelaskan oleh prediktor lainnya.

  • median_income (VIF = 1,51) berada pada kategori aman dan menunjukkan bahwa variabel ini relatif independen dari prediktor lainnya.

  • housing_median_age (VIF = 1,16) memiliki nilai VIF terendah, sehingga dapat dianggap hampir tidak mengalami masalah multikolinearitas.

Hasil VIF mengonfirmasi temuan pada analisis korelasi sebelumnya bahwa kelompok variabel population, households, total_rooms, dan total_bedrooms memiliki tingkat keterkaitan yang sangat tinggi. Kondisi ini menunjukkan adanya redundansi informasi yang cukup besar antarvariabel. Oleh karena itu, penerapan teknik reduksi dimensi seperti Principal Component Analysis (PCA) menjadi relevan untuk merangkum informasi dari variabel-variabel tersebut ke dalam sejumlah komponen utama yang lebih ringkas tanpa kehilangan informasi yang signifikan.

3 Feature Engineering

3.1 Membuat 3 Fitur Baru

df$rooms_per_household      <- df$total_rooms    / df$households
df$bedrooms_ratio           <- df$total_bedrooms / df$total_rooms
df$population_per_household <- df$population     / df$households

fitur_baru <- c("rooms_per_household", "bedrooms_ratio", "population_per_household")
summary(df[, fitur_baru])
 rooms_per_household bedrooms_ratio   population_per_household
 Min.   :  0.8461    Min.   :0.1000   Min.   :   0.6923       
 1st Qu.:  4.4407    1st Qu.:0.1754   1st Qu.:   2.4297       
 Median :  5.2291    Median :0.2032   Median :   2.8181       
 Mean   :  5.4290    Mean   :0.2130   Mean   :   3.0707       
 3rd Qu.:  6.0524    3rd Qu.:0.2398   3rd Qu.:   3.2823       
 Max.   :141.9091    Max.   :1.0000   Max.   :1243.3333       
                     NA's   :207                              
cor(df[, fitur_baru], df$median_house_value, use = "complete.obs")
                               [,1]
rooms_per_household       0.1513441
bedrooms_ratio           -0.2558801
population_per_household -0.0236394

Feature Engineering adalah proses menciptakan, memodifikasi, atau mengatur ulang variabel sehingga data menjadi lebih berguna untuk analisis atau pembuatan model. Umumnya, variabel mentah yang absolut dari setiap blok wilayah seperti total_rooms, total_bedrooms, population, dan households kurang mampu menggambarkan karakteristik sebenarnya dari suatu wilayah karena ukuran masing-masing blok dapat berbeda secara signifikan. Oleh sebab itu, diperlukan normalisasi per rumah tangga agar setiap fitur lebih memiliki makna secara substansial. berdasarkan pertimbangan tersebut, dibuatlah 3 fitur baru berikut ini.

  • Fitur 1: rooms_per_household: Variabel total_rooms mempresentasikan jumlah keseluruhan ruangan dalam satu blok wilayah bukan per unit tempat tinggal. Angka absolut ini kehilangan maknanya jika tidak dihubungkan dengan banyaknya rumah tangga yang menghuni, sehingga fitur ini dirancang untuk menilai luas rata-rata tempat tinggal per rumah tangga yang lebih dapat menggambarkan karakteristik sesungguhnya dari sebuah blok. Berdasarkan perhitungan, fitur ini menunjukkan adanya hubungan positif dengan median_house_value, yang berarti semakin banyak rata-rata ruangan per rumah tangga, semakin tinggi nilao rumah di blok tersebut. Hasil fitur ini mengonfirmasi temuan pada logika properti bahwa rumah yang lebih luas biasanya memiliki nilai yang lebih tinggi.

  • Fitur 2: bedrooms_ratio: Rasio antara jumlah kamar tidur dan total kamar menunjukkan komposisi tipe unit di dalam suatu blok. Nilai rasio yang rendah menunjukkan adanya lebih banyak ruang non-tidur, seperti ruang tamu, dapur, dan ruang kerja, yang menunjukkan kualitas hunian yang baik. Sebaliknya, rasio yang tinggi menunjukkan hunian yang lebih padat dengan lebih banyak kamar tidur. Oleh karena itu, fitur ini dikembangkan untuk mendapatkan informasi tentang kualitas dan kepadatan hunian yang tidak terlihat dari variabel mentah lain. Hasil perhitungan menunjukkan bahwa fitur ini memiliki korelasi negatif dengan median_house_value, yang berarti bahwa semakin banyak proporsi kamar tidur di suatu blok, semakin rendah nilai rumah di area tersebut. Fitur ini mengonfirmasi temuan pada hasil penelitian yang menunjukkan bahwa area perumahan berkualitas tinggi cenderung memiliki lebih banyak ruang fungsional non-kamar tidur dibandingkan dengan kawasan hunian yang lain.

  • Fitur 3: population_per_household: Variabel populasi mencatat jumlah total penduduk di setiap blok secara absolut, sehingga tidak dapat menggambarkan kepadatan huni yang sebenarnya tanpa membandingkannya dengan jumlah rumah tangga. Oleh karena itu, fitur ini dirancang untuk menghitung rata-rata jumlah orang dalam setiap rumah tangga, yang dapat menjadi indikator yang lebih baik untuk mengukur kepadatan sosial dan ekonomi di sebuah blok. Berdasarkan analisis yang dilakukan, fitur ini menunjukkan hubungan negatif dengan median_house_value, yang berarti bahwa semakin banyak rata-rata penghuni dalam satu rumah tangga, semakin rendah nilai properti di blok tersebut. Hasil fitur ini mengonfrimasi temuan pada kondisi sosial ekonomi di wilayah yang padat, di mana biasanya nilai properti cenderung lebih rendah dibandingkan kawasan yang memiliki hunian lebih.

4 Feature Selection

Feature selection dilakukan pada 11 variabel prediktor numerik, yang terdiri dari 8 variabel numerik asli (tidak termasuk median_house_value sebagai target dan ocean_proximity sebagai variabel kategorik) ditambah 3 fitur baru yang dihasilkan dari hasil feature engineering, yaitu rooms_per_household, bedrooms_ratio, dan population_per_household.

fitur_all <- c("longitude", "latitude", "housing_median_age",
               "total_rooms", "total_bedrooms", "population",
               "households", "median_income",
               "rooms_per_household", "bedrooms_ratio",
               "population_per_household")

housing_clean <- df %>%
  select(all_of(fitur_all), median_house_value) %>%
  drop_na()

Sebelum menjalankan feature selection, dibuat objek housing_clean yang merupakan bagian dari df yang hanya berisi variabel-variabel prediktor (termasuk 3 fitur baru yang dihasilkan dari feature engineering) dan variabel target median_house_value, dengan semua observasi yang memiliki nilai hilang dihapus menggunakan drop_na(). Proses ini penting karena baik Setpwise Regression maupun LASSO Regression tidak dapat dilakukan jika ada nilai NA dalam data.

4.1 Membuat 2 Metode

4.1.1 Metode 1: Wrapper Method - Stepwise Regression

null_model <- lm(median_house_value ~ 1, data = housing_clean)
full_model <- lm(median_house_value ~ ., data = housing_clean)

summary(null_model)

Call:
lm(formula = median_house_value ~ 1, data = housing_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-191865  -87364  -27164   57836  293137 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 206864.4      807.6   256.2   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 115400 on 20432 degrees of freedom
summary(full_model)

Call:
lm(formula = median_house_value ~ ., data = housing_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-576309  -42343  -11114   29659  846871 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -3.593e+06  6.297e+04 -57.048   <2e-16 ***
longitude                -4.163e+04  7.222e+02 -57.641   <2e-16 ***
latitude                 -4.113e+04  6.886e+02 -59.727   <2e-16 ***
housing_median_age        1.155e+03  4.282e+01  26.981   <2e-16 ***
total_rooms               1.628e+00  9.431e-01   1.726   0.0843 .  
total_bedrooms            1.942e+01  7.971e+00   2.437   0.0148 *  
population               -4.107e+01  1.107e+00 -37.107   <2e-16 ***
households                1.036e+02  8.360e+00  12.387   <2e-16 ***
median_income             4.243e+04  3.681e+02 115.268   <2e-16 ***
rooms_per_household       2.972e+03  2.521e+02  11.788   <2e-16 ***
bedrooms_ratio            3.095e+05  1.370e+04  22.586   <2e-16 ***
population_per_household  5.892e+01  4.745e+01   1.242   0.2144    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 68630 on 20421 degrees of freedom
Multiple R-squared:  0.6467,    Adjusted R-squared:  0.6465 
F-statistic:  3399 on 11 and 20421 DF,  p-value: < 2.2e-16
forward_model <- step(null_model,
                      scope     = list(lower = null_model, upper = full_model),
                      direction = "forward")
Start:  AIC=476354.2
median_house_value ~ 1

                           Df  Sum of Sq        RSS    AIC
+ median_income             1 1.2901e+14 1.4326e+14 463235
+ bedrooms_ratio            1 1.7826e+13 2.5444e+14 474973
+ rooms_per_household       1 6.2362e+12 2.6603e+14 475883
+ latitude                  1 5.6958e+12 2.6657e+14 475924
+ total_rooms               1 4.8374e+12 2.6743e+14 475990
+ housing_median_age        1 3.0842e+12 2.6918e+14 476123
+ households                1 1.1466e+12 2.7112e+14 476270
+ total_bedrooms            1 6.7214e+11 2.7159e+14 476306
+ longitude                 1 5.6114e+11 2.7170e+14 476314
+ population                1 1.7427e+11 2.7209e+14 476343
+ population_per_household  1 1.5215e+11 2.7211e+14 476345
<none>                                   2.7226e+14 476354

Step:  AIC=463235.5
median_house_value ~ median_income

                           Df  Sum of Sq        RSS    AIC
+ bedrooms_ratio            1 1.2362e+13 1.3089e+14 461393
+ housing_median_age        1 9.7438e+12 1.3351e+14 461798
+ latitude                  1 2.2109e+12 1.4105e+14 462920
+ rooms_per_household       1 1.6041e+12 1.4165e+14 463007
+ households                1 8.4322e+11 1.4241e+14 463117
+ total_bedrooms            1 8.2372e+11 1.4243e+14 463120
+ population_per_household  1 3.6575e+11 1.4289e+14 463185
+ longitude                 1 3.2780e+11 1.4293e+14 463191
+ population                1 2.2585e+11 1.4303e+14 463205
<none>                                   1.4326e+14 463235
+ total_rooms               1 2.4139e+09 1.4325e+14 463237

Step:  AIC=461393.5
median_house_value ~ median_income + bedrooms_ratio

                           Df  Sum of Sq        RSS    AIC
+ housing_median_age        1 8.1027e+12 1.2279e+14 460090
+ longitude                 1 8.9980e+11 1.2999e+14 461255
+ latitude                  1 6.0042e+11 1.3029e+14 461302
+ population_per_household  1 4.4878e+11 1.3045e+14 461325
+ population                1 4.1935e+11 1.3047e+14 461330
+ households                1 3.5225e+11 1.3054e+14 461340
+ total_bedrooms            1 3.0888e+11 1.3059e+14 461347
+ rooms_per_household       1 6.5119e+10 1.3083e+14 461385
+ total_rooms               1 6.3779e+10 1.3083e+14 461385
<none>                                   1.3089e+14 461393

Step:  AIC=460089.8
median_house_value ~ median_income + bedrooms_ratio + housing_median_age

                           Df  Sum of Sq        RSS    AIC
+ total_bedrooms            1 2.5615e+12 1.2023e+14 459661
+ households                1 2.4457e+12 1.2035e+14 459681
+ total_rooms               1 1.7015e+12 1.2109e+14 459807
+ latitude                  1 6.9102e+11 1.2210e+14 459976
+ population_per_household  1 5.0450e+11 1.2229e+14 460008
+ longitude                 1 3.6840e+11 1.2242e+14 460030
+ population                1 5.0272e+10 1.2274e+14 460083
<none>                                   1.2279e+14 460090
+ rooms_per_household       1 1.5208e+09 1.2279e+14 460091

Step:  AIC=459661
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms

                           Df  Sum of Sq        RSS    AIC
+ population                1 5.3605e+12 1.1487e+14 458731
+ total_rooms               1 7.9449e+11 1.1944e+14 459528
+ latitude                  1 5.7971e+11 1.1965e+14 459564
+ population_per_household  1 4.4517e+11 1.1978e+14 459587
+ longitude                 1 4.0867e+11 1.1982e+14 459593
<none>                                   1.2023e+14 459661
+ rooms_per_household       1 1.4218e+09 1.2023e+14 459663
+ households                1 1.8329e+07 1.2023e+14 459663

Step:  AIC=458731.1
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population

                           Df  Sum of Sq        RSS    AIC
+ households                1 1.7260e+12 1.1314e+14 458424
+ latitude                  1 1.1120e+12 1.1376e+14 458534
+ rooms_per_household       1 2.0408e+11 1.1467e+14 458697
+ longitude                 1 1.8744e+11 1.1468e+14 458700
+ total_rooms               1 8.4018e+10 1.1479e+14 458718
+ population_per_household  1 4.2895e+10 1.1483e+14 458725
<none>                                   1.1487e+14 458731

Step:  AIC=458423.7
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households

                           Df  Sum of Sq        RSS    AIC
+ latitude                  1 1.2174e+12 1.1193e+14 458205
+ longitude                 1 8.7746e+10 1.1306e+14 458410
+ rooms_per_household       1 3.1572e+10 1.1311e+14 458420
+ total_rooms               1 2.6786e+10 1.1312e+14 458421
<none>                                   1.1314e+14 458424
+ population_per_household  1 3.0426e+09 1.1314e+14 458425

Step:  AIC=458204.7
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude

                           Df  Sum of Sq        RSS    AIC
+ longitude                 1 1.5051e+13 9.6875e+13 455256
+ rooms_per_household       1 7.8695e+10 1.1185e+14 458192
<none>                                   1.1193e+14 458205
+ total_rooms               1 8.4360e+09 1.1192e+14 458205
+ population_per_household  1 2.1567e+08 1.1193e+14 458207

Step:  AIC=455255.8
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude

                           Df  Sum of Sq        RSS    AIC
+ rooms_per_household       1 6.7071e+11 9.6205e+13 455116
+ total_rooms               1 3.1427e+10 9.6844e+13 455251
<none>                                   9.6875e+13 455256
+ population_per_household  1 3.3829e+09 9.6872e+13 455257

Step:  AIC=455115.9
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household

                           Df  Sum of Sq        RSS    AIC
+ total_rooms               1 1.2857e+10 9.6192e+13 455115
<none>                                   9.6205e+13 455116
+ population_per_household  1 6.0812e+09 9.6199e+13 455117

Step:  AIC=455115.2
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household + total_rooms

                           Df  Sum of Sq        RSS    AIC
<none>                                   9.6192e+13 455115
+ population_per_household  1 7259883043 9.6185e+13 455116
summary(forward_model)

Call:
lm(formula = median_house_value ~ median_income + bedrooms_ratio + 
    housing_median_age + total_bedrooms + population + households + 
    latitude + longitude + rooms_per_household + total_rooms, 
    data = housing_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-576726  -42328  -11146   29627  840007 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)         -3.591e+06  6.296e+04 -57.034   <2e-16 ***
median_income        4.246e+04  3.672e+02 115.658   <2e-16 ***
bedrooms_ratio       3.095e+05  1.370e+04  22.587   <2e-16 ***
housing_median_age   1.157e+03  4.282e+01  27.013   <2e-16 ***
total_bedrooms       2.001e+01  7.957e+00   2.515   0.0119 *  
population          -4.074e+01  1.074e+00 -37.925   <2e-16 ***
households           1.024e+02  8.307e+00  12.326   <2e-16 ***
latitude            -4.110e+04  6.881e+02 -59.722   <2e-16 ***
longitude           -4.160e+04  7.220e+02 -57.627   <2e-16 ***
rooms_per_household  2.966e+03  2.521e+02  11.767   <2e-16 ***
total_rooms          1.555e+00  9.413e-01   1.652   0.0985 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 68630 on 20422 degrees of freedom
Multiple R-squared:  0.6467,    Adjusted R-squared:  0.6465 
F-statistic:  3738 on 10 and 20422 DF,  p-value: < 2.2e-16
backward_model <- step(full_model, direction = "backward")
Start:  AIC=455115.6
median_house_value ~ longitude + latitude + housing_median_age + 
    total_rooms + total_bedrooms + population + households + 
    median_income + rooms_per_household + bedrooms_ratio + population_per_household

                           Df  Sum of Sq        RSS    AIC
- population_per_household  1 7.2599e+09 9.6192e+13 455115
<none>                                   9.6185e+13 455116
- total_rooms               1 1.4036e+10 9.6199e+13 455117
- total_bedrooms            1 2.7970e+10 9.6213e+13 455120
- rooms_per_household       1 6.5455e+11 9.6839e+13 455252
- households                1 7.2275e+11 9.6907e+13 455267
- bedrooms_ratio            1 2.4027e+12 9.8587e+13 455618
- housing_median_age        1 3.4288e+12 9.9613e+13 455829
- population                1 6.4854e+12 1.0267e+14 456447
- longitude                 1 1.5649e+13 1.1183e+14 458194
- latitude                  1 1.6802e+13 1.1299e+14 458403
- median_income             1 6.2582e+13 1.5877e+14 465354

Step:  AIC=455115.2
median_house_value ~ longitude + latitude + housing_median_age + 
    total_rooms + total_bedrooms + population + households + 
    median_income + rooms_per_household + bedrooms_ratio

                      Df  Sum of Sq        RSS    AIC
<none>                              9.6192e+13 455115
- total_rooms          1 1.2857e+10 9.6205e+13 455116
- total_bedrooms       1 2.9785e+10 9.6222e+13 455119
- rooms_per_household  1 6.5214e+11 9.6844e+13 455251
- households           1 7.1560e+11 9.6907e+13 455265
- bedrooms_ratio       1 2.4030e+12 9.8595e+13 455617
- housing_median_age   1 3.4371e+12 9.9629e+13 455831
- population           1 6.7748e+12 1.0297e+14 456504
- longitude            1 1.5642e+13 1.1183e+14 458192
- latitude             1 1.6800e+13 1.1299e+14 458402
- median_income        1 6.3008e+13 1.5920e+14 465408
summary(backward_model)

Call:
lm(formula = median_house_value ~ longitude + latitude + housing_median_age + 
    total_rooms + total_bedrooms + population + households + 
    median_income + rooms_per_household + bedrooms_ratio, data = housing_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-576726  -42328  -11146   29627  840007 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)         -3.591e+06  6.296e+04 -57.034   <2e-16 ***
longitude           -4.160e+04  7.220e+02 -57.627   <2e-16 ***
latitude            -4.110e+04  6.881e+02 -59.722   <2e-16 ***
housing_median_age   1.157e+03  4.282e+01  27.013   <2e-16 ***
total_rooms          1.555e+00  9.413e-01   1.652   0.0985 .  
total_bedrooms       2.001e+01  7.957e+00   2.515   0.0119 *  
population          -4.074e+01  1.074e+00 -37.925   <2e-16 ***
households           1.024e+02  8.307e+00  12.326   <2e-16 ***
median_income        4.246e+04  3.672e+02 115.658   <2e-16 ***
rooms_per_household  2.966e+03  2.521e+02  11.767   <2e-16 ***
bedrooms_ratio       3.095e+05  1.370e+04  22.587   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 68630 on 20422 degrees of freedom
Multiple R-squared:  0.6467,    Adjusted R-squared:  0.6465 
F-statistic:  3738 on 10 and 20422 DF,  p-value: < 2.2e-16
stepwise_model <- step(null_model,
                       scope     = list(lower = null_model, upper = full_model),
                       direction = "both")
Start:  AIC=476354.2
median_house_value ~ 1

                           Df  Sum of Sq        RSS    AIC
+ median_income             1 1.2901e+14 1.4326e+14 463235
+ bedrooms_ratio            1 1.7826e+13 2.5444e+14 474973
+ rooms_per_household       1 6.2362e+12 2.6603e+14 475883
+ latitude                  1 5.6958e+12 2.6657e+14 475924
+ total_rooms               1 4.8374e+12 2.6743e+14 475990
+ housing_median_age        1 3.0842e+12 2.6918e+14 476123
+ households                1 1.1466e+12 2.7112e+14 476270
+ total_bedrooms            1 6.7214e+11 2.7159e+14 476306
+ longitude                 1 5.6114e+11 2.7170e+14 476314
+ population                1 1.7427e+11 2.7209e+14 476343
+ population_per_household  1 1.5215e+11 2.7211e+14 476345
<none>                                   2.7226e+14 476354

Step:  AIC=463235.5
median_house_value ~ median_income

                           Df  Sum of Sq        RSS    AIC
+ bedrooms_ratio            1 1.2362e+13 1.3089e+14 461393
+ housing_median_age        1 9.7438e+12 1.3351e+14 461798
+ latitude                  1 2.2109e+12 1.4105e+14 462920
+ rooms_per_household       1 1.6041e+12 1.4165e+14 463007
+ households                1 8.4322e+11 1.4241e+14 463117
+ total_bedrooms            1 8.2372e+11 1.4243e+14 463120
+ population_per_household  1 3.6575e+11 1.4289e+14 463185
+ longitude                 1 3.2780e+11 1.4293e+14 463191
+ population                1 2.2585e+11 1.4303e+14 463205
<none>                                   1.4326e+14 463235
+ total_rooms               1 2.4139e+09 1.4325e+14 463237
- median_income             1 1.2901e+14 2.7226e+14 476354

Step:  AIC=461393.5
median_house_value ~ median_income + bedrooms_ratio

                           Df  Sum of Sq        RSS    AIC
+ housing_median_age        1 8.1027e+12 1.2279e+14 460090
+ longitude                 1 8.9980e+11 1.2999e+14 461255
+ latitude                  1 6.0042e+11 1.3029e+14 461302
+ population_per_household  1 4.4878e+11 1.3045e+14 461325
+ population                1 4.1935e+11 1.3047e+14 461330
+ households                1 3.5225e+11 1.3054e+14 461340
+ total_bedrooms            1 3.0888e+11 1.3059e+14 461347
+ rooms_per_household       1 6.5119e+10 1.3083e+14 461385
+ total_rooms               1 6.3779e+10 1.3083e+14 461385
<none>                                   1.3089e+14 461393
- bedrooms_ratio            1 1.2362e+13 1.4326e+14 463235
- median_income             1 1.2354e+14 2.5444e+14 474973

Step:  AIC=460089.8
median_house_value ~ median_income + bedrooms_ratio + housing_median_age

                           Df  Sum of Sq        RSS    AIC
+ total_bedrooms            1 2.5615e+12 1.2023e+14 459661
+ households                1 2.4457e+12 1.2035e+14 459681
+ total_rooms               1 1.7015e+12 1.2109e+14 459807
+ latitude                  1 6.9102e+11 1.2210e+14 459976
+ population_per_household  1 5.0450e+11 1.2229e+14 460008
+ longitude                 1 3.6840e+11 1.2242e+14 460030
+ population                1 5.0272e+10 1.2274e+14 460083
<none>                                   1.2279e+14 460090
+ rooms_per_household       1 1.5208e+09 1.2279e+14 460091
- housing_median_age        1 8.1027e+12 1.3089e+14 461393
- bedrooms_ratio            1 1.0721e+13 1.3351e+14 461798
- median_income             1 1.2611e+14 2.4890e+14 474525

Step:  AIC=459661
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms

                           Df  Sum of Sq        RSS    AIC
+ population                1 5.3605e+12 1.1487e+14 458731
+ total_rooms               1 7.9449e+11 1.1944e+14 459528
+ latitude                  1 5.7971e+11 1.1965e+14 459564
+ population_per_household  1 4.4517e+11 1.1978e+14 459587
+ longitude                 1 4.0867e+11 1.1982e+14 459593
<none>                                   1.2023e+14 459661
+ rooms_per_household       1 1.4218e+09 1.2023e+14 459663
+ households                1 1.8329e+07 1.2023e+14 459663
- total_bedrooms            1 2.5615e+12 1.2279e+14 460090
- bedrooms_ratio            1 9.1741e+12 1.2940e+14 461162
- housing_median_age        1 1.0355e+13 1.3059e+14 461347
- median_income             1 1.2429e+14 2.4452e+14 474165

Step:  AIC=458731.1
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population

                           Df  Sum of Sq        RSS    AIC
+ households                1 1.7260e+12 1.1314e+14 458424
+ latitude                  1 1.1120e+12 1.1376e+14 458534
+ rooms_per_household       1 2.0408e+11 1.1467e+14 458697
+ longitude                 1 1.8744e+11 1.1468e+14 458700
+ total_rooms               1 8.4018e+10 1.1479e+14 458718
+ population_per_household  1 4.2895e+10 1.1483e+14 458725
<none>                                   1.1487e+14 458731
- population                1 5.3605e+12 1.2023e+14 459661
- total_bedrooms            1 7.8717e+12 1.2274e+14 460083
- bedrooms_ratio            1 8.0236e+12 1.2289e+14 460109
- housing_median_age        1 1.0067e+13 1.2494e+14 460446
- median_income             1 1.2249e+14 2.3736e+14 473559

Step:  AIC=458423.7
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households

                           Df  Sum of Sq        RSS    AIC
+ latitude                  1 1.2174e+12 1.1193e+14 458205
+ longitude                 1 8.7746e+10 1.1306e+14 458410
+ rooms_per_household       1 3.1572e+10 1.1311e+14 458420
+ total_rooms               1 2.6786e+10 1.1312e+14 458421
- total_bedrooms            1 9.8354e+08 1.1314e+14 458422
<none>                                   1.1314e+14 458424
+ population_per_household  1 3.0426e+09 1.1314e+14 458425
- households                1 1.7260e+12 1.1487e+14 458731
- population                1 7.0865e+12 1.2023e+14 459663
- bedrooms_ratio            1 8.0086e+12 1.2115e+14 459819
- housing_median_age        1 9.1565e+12 1.2230e+14 460012
- median_income             1 1.1862e+14 2.3176e+14 473073

Step:  AIC=458204.7
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude

                           Df  Sum of Sq        RSS    AIC
+ longitude                 1 1.5051e+13 9.6875e+13 455256
+ rooms_per_household       1 7.8695e+10 1.1185e+14 458192
- total_bedrooms            1 8.5039e+08 1.1193e+14 458203
<none>                                   1.1193e+14 458205
+ total_rooms               1 8.4360e+09 1.1192e+14 458205
+ population_per_household  1 2.1567e+08 1.1193e+14 458207
- latitude                  1 1.2174e+12 1.1314e+14 458424
- households                1 1.8314e+12 1.1376e+14 458534
- bedrooms_ratio            1 6.4149e+12 1.1834e+14 459341
- population                1 7.7235e+12 1.1965e+14 459566
- housing_median_age        1 9.1421e+12 1.2107e+14 459807
- median_income             1 1.0948e+14 2.2141e+14 472141

Step:  AIC=455255.8
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude

                           Df  Sum of Sq        RSS    AIC
+ rooms_per_household       1 6.7071e+11 9.6205e+13 455116
+ total_rooms               1 3.1427e+10 9.6844e+13 455251
<none>                                   9.6875e+13 455256
+ population_per_household  1 3.3829e+09 9.6872e+13 455257
- households                1 2.8568e+11 9.7161e+13 455314
- total_bedrooms            1 5.5506e+11 9.7430e+13 455371
- bedrooms_ratio            1 2.5028e+12 9.9378e+13 455775
- housing_median_age        1 3.3545e+12 1.0023e+14 455949
- population                1 6.9307e+12 1.0381e+14 456666
- longitude                 1 1.5051e+13 1.1193e+14 458205
- latitude                  1 1.6180e+13 1.1306e+14 458410
- median_income             1 7.2315e+13 1.6919e+14 466647

Step:  AIC=455115.9
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household

                           Df  Sum of Sq        RSS    AIC
+ total_rooms               1 1.2857e+10 9.6192e+13 455115
<none>                                   9.6205e+13 455116
+ population_per_household  1 6.0812e+09 9.6199e+13 455117
- total_bedrooms            1 7.5146e+10 9.6280e+13 455130
- rooms_per_household       1 6.7071e+11 9.6875e+13 455256
- households                1 7.0904e+11 9.6914e+13 455264
- bedrooms_ratio            1 3.0567e+12 9.9261e+13 455753
- housing_median_age        1 3.4256e+12 9.9630e+13 455829
- population                1 7.1593e+12 1.0336e+14 456581
- longitude                 1 1.5643e+13 1.1185e+14 458192
- latitude                  1 1.6824e+13 1.1303e+14 458407
- median_income             1 6.6783e+13 1.6299e+14 465886

Step:  AIC=455115.2
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household + total_rooms

                           Df  Sum of Sq        RSS    AIC
<none>                                   9.6192e+13 455115
+ population_per_household  1 7.2599e+09 9.6185e+13 455116
- total_rooms               1 1.2857e+10 9.6205e+13 455116
- total_bedrooms            1 2.9785e+10 9.6222e+13 455119
- rooms_per_household       1 6.5214e+11 9.6844e+13 455251
- households                1 7.1560e+11 9.6907e+13 455265
- bedrooms_ratio            1 2.4030e+12 9.8595e+13 455617
- housing_median_age        1 3.4371e+12 9.9629e+13 455831
- population                1 6.7748e+12 1.0297e+14 456504
- longitude                 1 1.5642e+13 1.1183e+14 458192
- latitude                  1 1.6800e+13 1.1299e+14 458402
- median_income             1 6.3008e+13 1.5920e+14 465408
summary(stepwise_model)

Call:
lm(formula = median_house_value ~ median_income + bedrooms_ratio + 
    housing_median_age + total_bedrooms + population + households + 
    latitude + longitude + rooms_per_household + total_rooms, 
    data = housing_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-576726  -42328  -11146   29627  840007 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)         -3.591e+06  6.296e+04 -57.034   <2e-16 ***
median_income        4.246e+04  3.672e+02 115.658   <2e-16 ***
bedrooms_ratio       3.095e+05  1.370e+04  22.587   <2e-16 ***
housing_median_age   1.157e+03  4.282e+01  27.013   <2e-16 ***
total_bedrooms       2.001e+01  7.957e+00   2.515   0.0119 *  
population          -4.074e+01  1.074e+00 -37.925   <2e-16 ***
households           1.024e+02  8.307e+00  12.326   <2e-16 ***
latitude            -4.110e+04  6.881e+02 -59.722   <2e-16 ***
longitude           -4.160e+04  7.220e+02 -57.627   <2e-16 ***
rooms_per_household  2.966e+03  2.521e+02  11.767   <2e-16 ***
total_rooms          1.555e+00  9.413e-01   1.652   0.0985 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 68630 on 20422 degrees of freedom
Multiple R-squared:  0.6467,    Adjusted R-squared:  0.6465 
F-statistic:  3738 on 10 and 20422 DF,  p-value: < 2.2e-16
AIC(forward_model, backward_model, stepwise_model)
formula(forward_model)
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household + total_rooms
formula(backward_model)
median_house_value ~ longitude + latitude + housing_median_age + 
    total_rooms + total_bedrooms + population + households + 
    median_income + rooms_per_household + bedrooms_ratio
formula(stepwise_model)
median_house_value ~ median_income + bedrooms_ratio + housing_median_age + 
    total_bedrooms + population + households + latitude + longitude + 
    rooms_per_household + total_rooms

Wrapper Method memilih variabel dengan memperhatikan kinerja model secara berurutan memakai nilai AIC (Akaike Information Criterion) sebagai dasar penilaian. Semakin rendah nilai AIC, semakin efektif model dalam menggambarkan data. Ketiga jenis Stepwise Regression yang diterapkan menghasilkan variabel yang terpilih sama persis dengan nilai AIC yang serupa yaitu 455.115,2. Ini menunjukkan bahwa pemilihan variabel sudah sangat konsisten dan tidak tergantung pada cara pemilihan yang dipakai. Model tersebut menghasilkan Adjusted R?? = 0,6465, yang artinya model ini bisa menjelaskan 64,65% perubahan harga rumah.

Variabel Terpilih:

  • median_income (¦?? = 42.460, p < 0,001) adalah faktor pertama yang menunjukkan penurunan AIC paling besar. Pendapatan median adalah faktor kunci yang menentukan kapasitas membeli properti, semakin tinggi pendapatan dalam suatu wilayah, semakin tinggi nilai rumah di tempat tersebut.

  • longitude (¦?? = ???41.600, p < 0,001) adalah posisi geografis yang mengarah barat-timur terbukti signifikan karena daerah pesisir di barat California memiliki harga properti yang jauh lebih tinggi dibandingkan dengan wilayah timurnya.

  • latitude (¦?? = ???41.100, p < 0,001) adalah posisi geografis yang mengarah utara-selatan terbukti signifikan karena daerah Bay Area di utara California memiliki harga properti yang jauh lebih tinggi dibandingkan dengan wilayah selatannya.

  • housing_median_age (¦?? = 1.157, p < 0,001), usia bangunan memiliki dampak positif, area dengan bangunan yang lebih tua biasanya berada di lokasi yang sudah lama berkembang dan memiliki nilai properti yang lebih tinggi.

  • total_bedrooms (¦?? = 20,01, p = 0,012), memberikan dampak positif terhadap nilai properti dalam model multivariat ini.

  • population (¦?? = ???40,74, p < 0,001), memberikan dampak negatif. Tingginya kepadatan penduduk cenderung menurunkan nilai rumah karena berhubungan dengan rendahnya eksklusivitas suatu area.

  • households (¦?? = 102,4, p < 0,001), jumlah unit rumah tangga di tiap blok mencerminkan tingkat pembangunan dan kemajuan suatu area yang memberikan dampak positif terhadap nilai properti.

  • rooms_per_household (¦?? = 2.966, p < 0,001), semakin banyak rata-rata ruangan per rumah tangga, semakin tinggi juga nilai propertinya.

  • bedrooom_ratio (¦?? = 309.500, p < 0,001), perbandingan jumlah kamar tidur ini memberikan informasi khusus tentang bagaimana hunian disusun.

  • total_rooms (¦?? = 1,555, p = 0,099, berpengaruh lemah tetapi tetap dipertahankan karena masih membantu meningkatkan AIC model.

Variabel Dieliminasi:

  • population_per_household dihilangkan karena informasinya sudah tercakup dalam kombinasi antara populasi dan households yang keduanya sudah terpilih. Menambah variabel ini tidak membuat AIC model jadi lebih baik.

4.1.2 Metode 2: Embedded Method - LASSO Regression

x <- scale(as.matrix(housing_clean[, fitur_all]))
y <- housing_clean$median_house_value

set.seed(123)
cv_lasso <- cv.glmnet(x, y, alpha = 1)
plot(cv_lasso)

cat("Lambda optimal:", cv_lasso$lambda.min, "\n")
Lambda optimal: 206.1976 
lasso_model <- glmnet(x, y, alpha = 1, lambda = cv_lasso$lambda.min)
koef        <- coef(lasso_model)
print(koef)
12 x 1 sparse Matrix of class "dgCMatrix"
                                  s0
(Intercept)              206864.4132
longitude                -80081.4978
latitude                 -84408.6343
housing_median_age        14595.8804
total_rooms                1928.7072
total_bedrooms             9631.8369
population               -44010.7934
households                37212.3437
median_income             80962.8932
rooms_per_household        6776.5423
bedrooms_ratio            17437.0559
population_per_household    142.1753
terpilih_lasso    <- rownames(koef)[koef[, 1] != 0 & rownames(koef) != "(Intercept)"]
dieliminasi_lasso <- rownames(koef)[koef[, 1] == 0]
cat("Terpilih LASSO   :", paste(terpilih_lasso,    collapse = ", "), "\n")
Terpilih LASSO   : longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, median_income, rooms_per_household, bedrooms_ratio, population_per_household 
cat("Dieliminasi LASSO:", paste(dieliminasi_lasso, collapse = ", "), "\n")
Dieliminasi LASSO:  

Dengan jumlah data sebesar 20.640 pengamatan dan nilai lambda terbaik yang didapat sebesar ¦?? = 206,1976 melalui cross-validation, LASSO menyimpan semua 11 variabel karena setiap koefisien memiliki nilai berbeda dari nol. Ini berarti bahwa dengan banyaknya data yang ada, setiap variabel memiliki peran penting dalam memprediksi meskipun ada penalti lambda. Tidak ada variabel yang dieliminasi oleh LASSO pada dataset ini, sehingga variabel yang terpilih adalah keseluruhan variabel yang tersedia.

lasso_df <- data.frame(
  Variabel = c("median_income", "latitude", "longitude", "population",
               "households", "bedrooms_ratio", "housing_median_age",
               "total_bedrooms", "rooms_per_household", "total_rooms",
               "population_per_household"),
  Koefisien = c(80962.89, -84408.63, -80081.50, -44010.79,
                37212.34, 17437.06, 14595.88, 9631.84,
                6776.54, 1928.71, 142.18)
)

kable(lasso_df,
      col.names   = c("Variabel", "Koefisien"),
      align       = c("l", "r"),
      caption     = "Koefisien LASSO Regression",
      format.args = list(big.mark = ".", decimal.mark = ",")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed","responsive"),
                full_width = TRUE) %>%
  row_spec(0, bold = TRUE)
Koefisien LASSO Regression
Variabel Koefisien
median_income 80.962,89
latitude -84.408,63
longitude -80.081,50
population -44.010,79
households 37.212,34
bedrooms_ratio 17.437,06
housing_median_age 14.595,88
total_bedrooms 9.631,84
rooms_per_household 6.776,54
total_rooms 1.928,71
population_per_household 142,18

4.2 Kesimpulan Feature Selection

Berdasarkan hasil dari dua metode yang digunakan, yaitu Stepwise Regression dan LASSO Regression, didapatkan hasil yang sama. Tiga jenis Stepwise Regression yang digunakan (Forward Selection, Backward Elimination, dan Stepwise Bidirectional) semua memilih 10 variabel yang sama dengan nilai AIC yang identik yaitu 455.115,2. Ini menunjukkan bahwa pemilihan variabel sangat stabil dan tidak dipengaruhi oleh cara pemilihannya. Satu-satunya variabel yang dihilangkan oleh Stepwise adalah population_per_household karena tidak memberikan kontribusi yang penting setelah population dan households sudah diperhitungkan dalam model.

Sementara itu, LASSO dengan nilai lambda optimal ¦?? = 206,1976 mempertahankan semua 11 variabel dan tanpa ada yang dihapus. Ini terjadi karena jumlah data yang sangat banyak (20.640 observasi setelah menghapus NA) membuat setiap variabel tampak memberikan kontribusi yang cukup untuk melawan penalti dari lambda.

Secara keseluruhan, ada 10 variabel yang disepakati oleh kedua metode, yaitu median_income, bedrooms_ratio, housing_median_age, total_bedrooms, population, households, latitude, longitude, rooms_per_household, dan total_rooms. Satu-satunya perbedaan adalah population_per_household yang dieliminasi d metode Stepwise tetapi tetap ada di LASSO. median_income selalu menjadi prediktor terkuat di kedua metode dengan nilai koefisien tertinggi, diikuti oleh latitude dan longitude yang menunjukkan pengaruh lokasi geografis terhadap harga rumah. Hasil ini menunjukkan bahwa situasi ekonomi dan lokasi geografis adalah dua hal yang paling penting dalam menentukan harga rumah di California.

#5

# Reproduksi Feature Engineering dari Bagian C
df$rooms_per_household      <- df$total_rooms / df$households
df$bedrooms_ratio           <- df$total_bedrooms / df$total_rooms
df$population_per_household <- df$population / df$households

# Daftar fitur (sama dengan Bagian D)
fitur_all <- c(
  "longitude", "latitude", "housing_median_age",
  "total_rooms", "total_bedrooms", "population",
  "households", "median_income",
  "rooms_per_household", "bedrooms_ratio",
  "population_per_household"
)

housing_clean <- na.omit(
  df[, c(fitur_all, "median_house_value")]
)

5 Feature Extraction

5.1 Pendahuluan Principal Component Analysis (PCA)

Principal Component Analysis (PCA) adalah teknik reduksi dimensi yang mengubah sekumpulan variabel yang berkorelasi menjadi sejumlah kecil variabel baru yang tidak berkorelasi satu sama lain, disebut komponen utama (principal components). Setiap komponen utama merupakan kombinasi linear dari variabel-variabel asli dan disusun sedemikian rupa sehingga PC1 menjelaskan variasi terbesar dalam data, diikuti oleh komponen berikutnya secara berurutan.

Dalam konteks dataset California Housing, PCA menjadi sangat relevan mengingat hasil analisis sebelumnya yang menunjukkan adanya multikolinearitas yang parah pada kelompok variabel total_rooms, total_bedrooms, households, dan population (VIF berkisar 6???35). Kondisi ini mengindikasikan redundansi informasi yang tinggi sehingga keempat variabel tersebut dapat diringkas menjadi beberapa komponen utama tanpa kehilangan informasi yang signifikan.

5.2 Persiapan Data PCA

PCA sensitif terhadap skala variabel. Oleh karena itu, sebelum menerapkan PCA, seluruh variabel numerik perlu distandarisasi menggunakan Z-score agar setiap variabel memiliki mean = 0 dan standar deviasi = 1. Variabel median_house_value dikecualikan karena merupakan variabel target, dan ocean_proximity dikecualikan karena bertipe kategorik.

# Seleksi variabel prediktor numerik
pca_vars <- c("longitude", "latitude", "housing_median_age",
              "total_rooms", "total_bedrooms", "population",
              "households", "median_income",
              "rooms_per_household", "bedrooms_ratio",
              "population_per_household")

# Standarisasi Z-Score
pca_data   <- housing_clean[, pca_vars]
pca_scaled <- scale(pca_data)

cat("Dimensi data PCA :", nrow(pca_scaled), "observasi x", ncol(pca_scaled), "variabel\n")
Dimensi data PCA : 20433 observasi x 11 variabel
cat("Verifikasi scaling ??? rata-rata kolom (seharusnya ~0):\n")
Verifikasi scaling ??? rata-rata kolom (seharusnya ~0):
round(colMeans(pca_scaled), 6)
               longitude                 latitude       housing_median_age 
                       0                        0                        0 
             total_rooms           total_bedrooms               population 
                       0                        0                        0 
              households            median_income      rooms_per_household 
                       0                        0                        0 
          bedrooms_ratio population_per_household 
                       0                        0 

Berdasarkan output di atas, seluruh variabel telah berhasil distandarisasi dengan rata-rata mendekati 0 untuk setiap kolom. Dataset yang digunakan terdiri dari 20433 observasi bersih (setelah penghapusan missing values) dengan 11 variabel prediktor numerik yang siap dianalisis menggunakan PCA.

5.3 Pelaksanaan PCA

# Menjalankan PCA
pca_result <- prcomp(pca_scaled, center = FALSE, scale. = FALSE)

# Ringkasan hasil PCA
summary(pca_result)
Importance of components:
                          PC1    PC2    PC3     PC4     PC5     PC6     PC7
Standard deviation     1.9770 1.4459 1.3552 1.00527 0.92140 0.82970 0.61161
Proportion of Variance 0.3553 0.1900 0.1670 0.09187 0.07718 0.06258 0.03401
Cumulative Proportion  0.3553 0.5454 0.7123 0.80421 0.88139 0.94397 0.97798
                           PC8     PC9    PC10    PC11
Standard deviation     0.36563 0.23662 0.20313 0.10618
Proportion of Variance 0.01215 0.00509 0.00375 0.00102
Cumulative Proportion  0.99013 0.99522 0.99898 1.00000

5.4 Analisis Proporsi Variansi

5.4.1 Tabel Variansi Komponen

eigenvalues <- pca_result$sdev^2
prop_var    <- eigenvalues / sum(eigenvalues)
cumul_var   <- cumsum(prop_var)

var_table <- data.frame(
  Komponen           = paste0("PC", seq_along(eigenvalues)),
  Eigenvalue         = round(eigenvalues, 4),
  Proporsi_Variansi  = paste0(round(prop_var  * 100, 2), "%"),
  Kumulatif_Variansi = paste0(round(cumul_var * 100, 2), "%"),
  Status_Kaiser      = ifelse(eigenvalues >= 1, "??? Dipertahankan", "??? Diabaikan")
)

kable(var_table,
      caption = "Proporsi Variansi dan Eigenvalue Setiap Komponen Utama",
      col.names = c("Komponen", "Eigenvalue", "Proporsi Variansi",
                    "Kumulatif Variansi", "Status Kaiser (??? 1)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE, position = "center") %>%
  row_spec(which(eigenvalues >= 1), background = "#eafaf1", bold = TRUE) %>%
  row_spec(which(eigenvalues <  1), background = "#fdf2f2") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Proporsi Variansi dan Eigenvalue Setiap Komponen Utama
Komponen Eigenvalue Proporsi Variansi Kumulatif Variansi Status Kaiser (??? 1)
PC1 3.9085 35.53% 35.53% ??? Dipertahankan
PC2 2.0906 19.01% 54.54% ??? Dipertahankan
PC3 1.8366 16.7% 71.23% ??? Dipertahankan
PC4 1.0106 9.19% 80.42% ??? Dipertahankan
PC5 0.8490 7.72% 88.14% ??? Diabaikan
PC6 0.6884 6.26% 94.4% ??? Diabaikan
PC7 0.3741 3.4% 97.8% ??? Diabaikan
PC8 0.1337 1.22% 99.01% ??? Diabaikan
PC9 0.0560 0.51% 99.52% ??? Diabaikan
PC10 0.0413 0.38% 99.9% ??? Diabaikan
PC11 0.0113 0.1% 100% ??? Diabaikan

Berdasarkan tabel di atas, dapat diinterpretasikan sebagai berikut.

  • Kaiser Criterion (eigenvalue ??? 1) digunakan sebagai acuan pemilihan komponen. Komponen dengan eigenvalue ??? 1 menjelaskan variasi yang lebih besar dari satu variabel tunggal yang telah distandarisasi, sehingga dianggap layak dipertahankan.
  • PC1 menjelaskan porsi terbesar variasi karena merangkum kelompok variabel yang saling berkorelasi tinggi (total_rooms, total_bedrooms, households, population).
  • Berdasarkan kriteria kumulatif variansi ??? 80%, sebagian besar informasi data sudah terangkum dalam beberapa komponen pertama saja.

5.4.2 Scree Plot

fviz_eig(pca_result,
         addlabels  = TRUE,
         ylim       = c(0, 60),
         barfill    = "#2c7fb8",
         barcolor   = "white",
         linecolor  = "#e74c3c",
         ggtheme    = theme_bw(base_size = 12)) +
  geom_hline(yintercept = (1 / length(eigenvalues)) * 100,
             linetype = "dashed", color = "darkorange", size = 1) +
  annotate("text",
           x     = length(eigenvalues) - 0.5,
           y     = (1 / length(eigenvalues)) * 100 + 2,
           label = "Batas Kaiser (eigenvalue = 1)",
           color = "darkorange", fontface = "italic", size = 3.5, hjust = 1) +
  labs(
    title    = "Scree Plot ??? Proporsi Variansi Setiap Komponen Utama",
    subtitle = "Batang biru = % variansi | Garis merah = tren | Garis oranye = batas Kaiser",
    x = "Komponen Utama (PC)",
    y = "Persentase Variansi (%)"
  )

Scree plot di atas menunjukkan penurunan yang tajam (elbow) pada komponen-komponen awal, yang menandakan bahwa sebagian besar variasi dalam data terkonsentrasi pada beberapa komponen pertama. Titik elbow yaitu titik di mana kurva mulai mendatar menjadi panduan visual untuk menentukan jumlah komponen yang optimal karena penambahan komponen setelah titik tersebut tidak lagi memberikan peningkatan variansi yang berarti.

5.4.3 Kumulatif Variansi

cumvar_df <- data.frame(
  PC        = factor(paste0("PC", seq_along(cumul_var)),
                     levels = paste0("PC", seq_along(cumul_var))),
  Kumulatif = cumul_var * 100
)

ggplot(cumvar_df, aes(x = PC, y = Kumulatif, group = 1)) +
  geom_line(color = "#2c7fb8", size = 1.2) +
  geom_point(aes(color = Kumulatif >= 80), size = 4) +
  geom_hline(yintercept = 80, linetype = "dashed", color = "red",        size = 1) +
  geom_hline(yintercept = 90, linetype = "dashed", color = "darkorange", size = 1) +
  annotate("text", x = length(cumul_var) - 0.5, y = 81.5,
           label = "80% Kumulatif", color = "red",
           fontface = "italic", size = 3.5, hjust = 1) +
  annotate("text", x = length(cumul_var) - 0.5, y = 91.5,
           label = "90% Kumulatif", color = "darkorange",
           fontface = "italic", size = 3.5, hjust = 1) +
  scale_color_manual(values = c("FALSE" = "#7f8c8d", "TRUE" = "#27ae60"),
                     labels = c("FALSE" = "< 80%", "TRUE" = "??? 80%")) +
  scale_y_continuous(labels = function(x) paste0(x, "%"), limits = c(0, 103)) +
  labs(
    title    = "Kumulatif Proporsi Variansi PCA",
    subtitle = "Titik hijau = komponen yang sudah mencapai kumulatif ??? 80%",
    x = "Komponen Utama", y = "Kumulatif Variansi (%)", color = "Status"
  ) +
  theme_bw(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", size = 14),
    plot.subtitle   = element_text(size = 10),
    axis.text.x     = element_text(angle = 30, hjust = 1),
    legend.position = "bottom"
  )

5.5 Loading Factor

Loading factor menggambarkan kontribusi (korelasi) setiap variabel asli terhadap masing-masing komponen utama. Nilai loading mendekati ±1 menunjukkan kontribusi yang kuat, sedangkan nilai mendekati 0 menunjukkan kontribusi yang lemah. Tanda positif berarti variabel bergerak searah dengan komponen, sedangkan tanda negatif berarti berlawanan arah.

5.5.1 Tabel Loading Factor

loading_mat <- pca_result$rotation[, 1:5]
loading_df  <- as.data.frame(round(loading_mat, 4))

kable(loading_df,
      caption = "Loading Factor: Kontribusi Variabel terhadap 5 PC Pertama") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = TRUE) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Loading Factor: Kontribusi Variabel terhadap 5 PC Pertama
PC1 PC2 PC3 PC4 PC5
longitude 0.0753 -0.3972 -0.5731 0.0126 0.0700
latitude -0.0722 0.4061 0.5708 0.0017 0.1030
housing_median_age -0.2199 -0.1294 0.1259 -0.0867 -0.8522
total_rooms 0.4851 0.1214 0.0022 0.0140 -0.0968
total_bedrooms 0.4897 -0.0361 0.1023 0.0239 -0.0502
population 0.4709 -0.0553 0.0739 -0.0953 -0.0908
households 0.4907 -0.0444 0.1109 0.0128 -0.1109
median_income 0.0507 0.4332 -0.3819 -0.0586 -0.3285
rooms_per_household 0.0139 0.4195 -0.2492 0.0457 0.2689
bedrooms_ratio -0.0182 -0.5253 0.3061 0.0146 0.1800
population_per_household -0.0029 -0.0015 -0.0049 -0.9882 0.1152

5.5.2 Heatmap Loading Factor

load_long <- as.data.frame(loading_mat) %>%
  rownames_to_column("Variabel") %>%
  pivot_longer(-Variabel, names_to = "PC", values_to = "Loading")

ggplot(load_long, aes(x = PC, y = Variabel, fill = Loading)) +
  geom_tile(color = "white", size = 0.5) +
  geom_text(aes(label = round(Loading, 2),
                color = abs(Loading) > 0.30),
            size = 3.8, fontface = "bold") +
  scale_fill_gradient2(low  = "#d73027", mid = "white", high = "#1a9850",
                       midpoint = 0, limits = c(-1, 1), name = "Loading") +
  scale_color_manual(values = c("FALSE" = "gray65", "TRUE" = "black"),
                     guide = "none") +
  scale_x_discrete(position = "top") +
  labs(
    title    = "Heatmap Loading Factor ??? 5 Komponen Utama Pertama",
    subtitle = "Hijau = kontribusi positif kuat | Merah = kontribusi negatif kuat | Putih = lemah",
    x = NULL, y = NULL
  ) +
  theme_bw(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", size = 14),
    plot.subtitle   = element_text(size = 10),
    axis.text.y     = element_text(face = "bold"),
    legend.position = "right"
  )

5.5.3 Interpretasi Loading Factor

Berdasarkan tabel dan heatmap loading factor, setiap komponen utama dapat diinterpretasikan maknanya secara substantif sebagai berikut.

PC1 ??? “Ukuran dan Kapasitas Blok Sensus”

PC1 menunjukkan loading positif yang kuat pada variabel-variabel yang mencerminkan ukuran fisik dan kapasitas blok sensus: total_rooms, total_bedrooms, households, dan population. Blok dengan nilai PC1 tinggi adalah blok besar yang dihuni banyak rumah tangga dan penduduk. Komponen ini pada dasarnya merangkum seberapa besar ukuran suatu blok sensus sebuah dimensi tunggal yang sebelumnya tersebar di empat variabel yang saling berkorelasi sangat tinggi (VIF 6???35).

PC2 ??? “Kemakmuran dan Kualitas Hunian”

PC2 menangkap kontras antara variabel ekonomi (median_income dan rooms_per_household dengan loading positif) versus variabel kepadatan seperti bedrooms_ratio (loading negatif). Komponen ini merepresentasikan tingkat kemakmuran dan kualitas relatif hunian kawasan dengan PC2 tinggi adalah kawasan berpendapatan tinggi dengan hunian yang luas (sedikit proporsi kamar tidur dari total ruangan), mencirikan hunian berkelas atau mewah.

PC3 ??? “Posisi Geografis Utara-Selatan”

PC3 didominasi oleh variabel latitude dengan loading yang besar. Komponen ini merepresentasikan posisi geografis utara-selatan di California kawasan Bay Area dan Silicon Valley di utara versus kawasan Los Angeles dan San Diego di selatan.

PC4 ??? “Posisi Geografis Timur-Barat dan Usia Bangunan”

PC4 menangkap variasi pada longitude dan sedikit dari housing_median_age. Komponen ini merepresentasikan posisi geografis timur-barat (pesisir vs. pedalaman) yang juga berkaitan dengan pola perkembangan historis bangunan di California.

PC5 ??? “Kepadatan Penghuni per Rumah Tangga”

PC5 menunjukkan loading yang menonjol pada population_per_household dan merepresentasikan kepadatan penghuni per unit rumah tangga apakah rumah tangga di suatu blok cenderung kecil atau besar jumlah penghuninya.

5.6 Visualisasi PCA

5.6.1 Biplot PC1 vs PC2

fviz_pca_biplot(pca_result,
                axes          = c(1, 2),
                geom.ind      = "point",
                col.ind       = housing_clean$median_house_value,
                gradient.cols = c("#d73027", "#fee090", "#1a9850"),
                col.var       = "black",
                repel         = TRUE,
                alpha.ind     = 0.4,
                pointsize     = 1.5,
                arrowsize     = 0.8,
                labelsize     = 3.8,
                ggtheme       = theme_bw(base_size = 12)) +
  scale_color_gradient(low    = "#d73027",
                       high   = "#1a9850",
                       name   = "Harga Rumah (USD)",
                       labels = dollar_format(prefix = "$", big.mark = ",")) +
  labs(
    title    = "Biplot PCA ??? PC1 vs PC2",
    subtitle = "Titik = blok sensus (warna = harga rumah) | Panah = arah & kekuatan loading variabel",
    x = paste0("PC1 (", round(prop_var[1] * 100, 1), "% Variansi)"),
    y = paste0("PC2 (", round(prop_var[2] * 100, 1), "% Variansi)")
  ) +
  theme(
    plot.title      = element_text(face = "bold", size = 14),
    plot.subtitle   = element_text(size = 9),
    legend.position = "right"
  )

Biplot di atas memvisualisasikan dua informasi sekaligus. Titik merepresentasikan setiap blok sensus yang diwarnai berdasarkan median_house_valuetitik hijau menandakan harga rumah tinggi, titik merah menandakan harga rendah. Panah menunjukkan arah loading setiap variabel asli: variabel dengan panah searah saling berkorelasi positif, sedangkan panah berlawanan arah berkorelasi negatif.

Terlihat bahwa median_income dan rooms_per_household memiliki panah yang cenderung menuju area titik-titik hijau (harga tinggi), mengonfirmasi bahwa kedua variabel ini adalah prediktor paling kuat harga rumah. Sebaliknya, kelompok total_rooms, total_bedrooms, households, population memiliki panah yang searah satu sama lain membuktikan multikolinearitas yang telah terdeteksi sebelumnya namun tidak mengarah secara dominan ke area harga tinggi, selaras dengan korelasi lemah keempat variabel tersebut terhadap median_house_value.

5.6.2 Kontribusi Variabel ke PC1 & PC2

p_c1 <- fviz_contrib(pca_result, choice = "var", axes = 1,
                      fill = "#2c7fb8", color = "white",
                      ggtheme = theme_bw(base_size = 11)) +
  labs(title = "Kontribusi Variabel ke PC1",
       x = NULL, y = "Kontribusi (%)") +
  theme(plot.title  = element_text(face = "bold"),
        axis.text.x = element_text(angle = 40, hjust = 1))

p_c2 <- fviz_contrib(pca_result, choice = "var", axes = 2,
                      fill = "#e74c3c", color = "white",
                      ggtheme = theme_bw(base_size = 11)) +
  labs(title = "Kontribusi Variabel ke PC2",
       x = NULL, y = "Kontribusi (%)") +
  theme(plot.title  = element_text(face = "bold"),
        axis.text.x = element_text(angle = 40, hjust = 1))

grid.arrange(p_c1, p_c2, ncol = 2)

Garis putus-putus pada grafik kontribusi menunjukkan nilai rata-rata kontribusi jika setiap variabel berkontribusi secara merata (~9,1% per variabel untuk 11 variabel). Variabel yang berada di atas garis putus-putus merupakan penyusun utama komponen tersebut. Pada PC1, kontributor utama adalah kelompok variabel ukuran blok, sedangkan pada PC2 kontributor utama adalah median_income, rooms_per_household, dan bedrooms_ratio sesuai dengan interpretasi substantif yang telah dijelaskan sebelumnya.

5.6.3 Cos² ??? Kualitas Representasi Variabel

fviz_cos2(pca_result, choice = "var", axes = 1:2,
          fill    = "#27ae60",
          color   = "white",
          ggtheme = theme_bw(base_size = 11)) +
  labs(title    = "Cos² Variabel pada PC1 + PC2",
       subtitle = "Seberapa baik setiap variabel terwakili oleh dua komponen pertama",
       x = NULL, y = "Cos² (Kualitas Representasi)") +
  theme(
    plot.title    = element_text(face = "bold", size = 13),
    plot.subtitle = element_text(size = 9),
    axis.text.x   = element_text(angle = 40, hjust = 1)
  )

Nilai cos² mengukur kualitas representasi suatu variabel pada ruang yang dibentuk oleh komponen-komponen terpilih. Nilai mendekati 1 berarti variabel sangat baik terwakili oleh dua komponen pertama, sedangkan nilai mendekati 0 berarti variabel lebih banyak terwakili oleh komponen-komponen selanjutnya.

6 Insight dan Kesimpulan

6.1 Variabel Paling Penting

insight_df <- data.frame(
  Variabel          = c("median_income", "latitude", "households",
                        "total_bedrooms", "population",
                        "rooms_per_household", "bedrooms_ratio",
                        "population_per_household"),
  Forward_Selection = c("???", "???", "???", "???", "???", "???", "???", "???"),
  Backward_Elim     = c("???", "???", "???", "???", "???", "???", "???", "???"),
  LASSO             = c("???", "???", "???", "???", "???", "???", "???", "???"),
  PC_Dominan        = c("PC2", "PC3", "PC1", "PC1", "PC1",
                        "PC2", "PC2", "PC5"),
  Korelasi_Target   = c("+0,688", "+0,467", "+0,066",
                        "+0,050", "-0,025",
                        "+0,552", "-0,468", "-0,194"),
  Kesimpulan        = c("PALING PENTING ??? konsisten di semua metode",
                        "Penting ??? lokasi utara???selatan California",
                        "Penting ??? ukuran & kepadatan blok",
                        "Cukup penting ??? dampak negatif",
                        "Cukup penting ??? dampak negatif",
                        "Fitur baru penting (Backward + PC2)",
                        "Fitur baru penting (Backward + PC2)",
                        "Fitur baru kepadatan RT (LASSO + PC5)")
)

kable(insight_df,
      caption = "Rekap Variabel Penting Berdasarkan Seluruh Metode Analisis",
      col.names = c("Variabel", "Forward", "Backward",
                    "LASSO", "PC Dominan",
                    "Korelasi (vs. Target)", "Kesimpulan")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = TRUE) %>%
  row_spec(1, bold = TRUE, background = "#eafaf1") %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(7, italic = TRUE)
Rekap Variabel Penting Berdasarkan Seluruh Metode Analisis
Variabel Forward Backward LASSO PC Dominan Korelasi (vs. Target) Kesimpulan
median_income ??? ??? ??? PC2 +0,688 PALING PENTING ??? konsisten di semua metode
latitude ??? ??? ??? PC3 +0,467 Penting ??? lokasi utara???selatan California
households ??? ??? ??? PC1 +0,066 Penting ??? ukuran & kepadatan blok
total_bedrooms ??? ??? ??? PC1 +0,050 Cukup penting ??? dampak negatif
population ??? ??? ??? PC1 -0,025 Cukup penting ??? dampak negatif
rooms_per_household ??? ??? ??? PC2 +0,552 Fitur baru penting (Backward + PC2)
bedrooms_ratio ??? ??? ??? PC2 -0,468 Fitur baru penting (Backward + PC2)
population_per_household ??? ??? ??? PC5 -0,194 Fitur baru kepadatan RT (LASSO + PC5)

Berdasarkan seluruh tahapan analisis korelasi, feature selection (Forward, Backward, LASSO), dan PCA ??? diperoleh gambaran yang konsisten mengenai variabel-variabel yang paling berpengaruh terhadap harga rumah di California.

median_income adalah variabel paling penting secara konsisten: terpilih di semua metode feature selection dengan koefisien terbesar, memiliki korelasi tertinggi dengan median_house_value (r = 0,688), dan mendominasi PC2 dalam PCA. Ini menegaskan bahwa kondisi ekonomi kawasan, bukan sekadar fisik bangunan, adalah faktor paling dominan dalam menentukan harga properti.

latitude konsisten terpilih oleh seluruh metode, menunjukkan bahwa posisi geografis utara-selatan California sangat menentukan harga kawasan Bay Area dan Silicon Valley di utara jauh lebih mahal dibandingkan kawasan selatan maupun pedalaman.

Kelompok variabel agregat blok (total_rooms, total_bedrooms, households, population) memiliki informasi yang saling tumpang tindih (VIF 6???35) dan dirangkum secara efisien oleh PC1 dalam PCA sebagai satu dimensi “ukuran blok.”

Fitur-fitur baru (rooms_per_household, bedrooms_ratio) terbukti memberikan nilai tambah nyata terpilih oleh Backward Elimination dan berkontribusi signifikan pada PC2, mengonfirmasi keberhasilan feature engineering mengekstraksi informasi tersembunyi dari variabel mentah.

6.2 Apakah Reduksi Dimensi Berhasil?

reduksi_df <- data.frame(
  Tahapan = c(
    "1. Dataset Awal (prediktor numerik)",
    "2. Setelah Feature Engineering",
    "3. Feature Selection ??? Forward/Stepwise",
    "4. Feature Selection ??? Backward (terbaik)",
    "5. PCA (komponen optimal)"
  ),
  Jumlah_Dimensi = c(9, 11, 5, 7, min(which(cumul_var >= 0.80))),
  Informasi = c(
    "100% (baseline)",
    "Informasi baru ditambahkan",
    "Adj. R² = 0,670",
    "Adj. R² = 0,677 (terbaik)",
    paste0(round(cumul_var[min(which(cumul_var >= 0.80))] * 100, 1),
           "% variansi terjelaskan")
  ),
  Keterangan = c(
    "9 variabel numerik sebelum rekayasa fitur",
    "3 fitur rasio derivatif ditambahkan",
    "Variabel tidak signifikan dieliminasi bertahap",
    "Keseimbangan terbaik parsimoni & akurasi",
    "Komponen orthogonal, bebas multikolinearitas"
  )
)

kable(reduksi_df,
      caption = "Ringkasan Perjalanan Reduksi Dimensi",
      col.names = c("Tahapan", "Jumlah Dimensi",
                    "Informasi Dipertahankan", "Keterangan")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = TRUE) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE) %>%
  row_spec(c(4, 5), background = "#eafaf1", bold = TRUE)
Ringkasan Perjalanan Reduksi Dimensi
Tahapan Jumlah Dimensi Informasi Dipertahankan Keterangan
  1. Dataset Awal (prediktor numerik)
9 100% (baseline) 9 variabel numerik sebelum rekayasa fitur
  1. Setelah Feature Engineering
11 Informasi baru ditambahkan 3 fitur rasio derivatif ditambahkan
  1. Feature Selection ??? Forward/Stepwise
5 Adj. R² = 0,670 Variabel tidak signifikan dieliminasi bertahap
  1. Feature Selection ??? Backward (terbaik)
7 Adj. R² = 0,677 (terbaik) Keseimbangan terbaik parsimoni & akurasi
  1. PCA (komponen optimal)
4 80.4% variansi terjelaskan Komponen orthogonal, bebas multikolinearitas

Reduksi dimensi dalam project ini dapat dinilai berhasil berdasarkan tiga bukti utama.

Pertama, feature engineering berhasil mengubah variabel mentah berkorelasi tinggi menjadi rasio yang lebih informatif. rooms_per_household (r = +0,55) dan bedrooms_ratio (r = ???0,47) memiliki korelasi yang jauh lebih kuat dengan harga dibandingkan variabel aslinya total_rooms (r = +0,13) dan total_bedrooms (r = +0,05).

Kedua, feature selection berhasil memangkas dari 11 variabel menjadi 7 (Backward) atau 5 (Forward) tanpa kehilangan kekuatan penjelas yang signifikan. Model Backward dengan 7 variabel mencapai Adjusted R² = 0,677 ??? menjelaskan 67,7% variasi harga rumah.

Ketiga, PCA berhasil merangkum 11 variabel menjadi 4 komponen utama yang secara bersama-sama menjelaskan lebih dari 80% variansi total data, dan seluruh komponen bersifat orthogonal (bebas multikolinearitas).

6.3 Makna Substantif Principal Components

pc_interp_df <- data.frame(
  Komponen = paste0("PC", 1:5),
  Nama     = c("Ukuran & Kapasitas Blok Sensus",
               "Kemakmuran & Kualitas Hunian",
               "Posisi Geografis (Utara???Selatan)",
               "Posisi Geografis (Timur???Barat / Pesisir)",
               "Kepadatan Penghuni per Rumah Tangga"),
  Variabel_Dominan = c(
    "total_rooms, total_bedrooms, households, population (+)",
    "median_income, rooms_per_household (+) | bedrooms_ratio (???)",
    "latitude (+)",
    "longitude (+) | housing_median_age",
    "population_per_household (+)"
  ),
  Variansi = paste0(round(prop_var[1:5] * 100, 1), "%"),
  Relevansi_Harga = c(
    "Tidak langsung ??? ukuran blok ??? harga tinggi",
    "LANGSUNG ??? kawasan makmur = harga tinggi",
    "Langsung ??? utara California lebih mahal",
    "Moderat ??? pesisir umumnya lebih mahal",
    "Tidak langsung ??? kepadatan ??? = harga ???"
  )
)

kable(pc_interp_df,
      caption = "Interpretasi Substantif Lima Komponen Utama PCA",
      col.names = c("PC", "Nama Substantif", "Variabel Dominan",
                    "% Variansi", "Relevansi thd Harga Rumah")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = TRUE) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(2, bold = TRUE, color = "#2c3e50") %>%
  row_spec(2, background = "#eafaf1")
Interpretasi Substantif Lima Komponen Utama PCA
PC Nama Substantif Variabel Dominan % Variansi Relevansi thd Harga Rumah
PC1 Ukuran & Kapasitas Blok Sensus total_rooms, total_bedrooms, households, population (+) 35.5% Tidak langsung ??? ukuran blok ??? harga tinggi
PC2 Kemakmuran & Kualitas Hunian median_income, rooms_per_household (+) &#124; bedrooms_ratio (???) 19% LANGSUNG ??? kawasan makmur = harga tinggi
PC3 Posisi Geografis (Utara???Selatan) latitude (+) 16.7% Langsung ??? utara California lebih mahal
PC4 Posisi Geografis (Timur???Barat / Pesisir) longitude (+) &#124; housing_median_age 9.2% Moderat ??? pesisir umumnya lebih mahal
PC5 Kepadatan Penghuni per Rumah Tangga population_per_household (+) 7.7% Tidak langsung ??? kepadatan ??? = harga ???

6.4 Insight Utama

Berdasarkan seluruh rangkaian analisis, diperoleh enam insight utama mengenai pasar perumahan California (data Sensus 1990).

Insight 1 ??? Pendapatan adalah Penentu Utama Harga Rumah

median_income secara konsisten menjadi variabel terpenting di semua tahap analisis, dengan korelasi r = 0,688 dan koefisien regresi terbesar di semua model stepwise (β ??? 34.880???37.590 per 10 ribu USD pendapatan). Ini menegaskan bahwa ekonomi kawasan, bukan sekadar kondisi fisik bangunan, adalah faktor paling dominan dalam menentukan harga properti. Wilayah dengan pendapatan tinggi mencerminkan daya beli yang kuat, permintaan properti yang besar, dan kualitas layanan publik yang baik seluruh faktor ini mendorong harga rumah naik.

Insight 2 ??? Lokasi Geografis Sangat Menentukan

latitude terpilih di semua metode feature selection dan mendominasi PC3 dalam PCA. Kawasan di utara California (Bay Area, Silicon Valley) secara konsisten memiliki harga rumah jauh lebih tinggi mencerminkan konsentrasi industri teknologi, tingginya permintaan properti, dan keterbatasan lahan di kawasan tersebut.

Insight 3 ??? Kualitas Ruang Lebih Penting dari Kuantitas Kamar

Fitur baru rooms_per_household (r = +0,55) dan bedrooms_ratio (r = ???0,47) jauh lebih informatif dari variabel mentahnya (total_rooms r = +0,13; total_bedrooms r = +0,05). Kualitas dan efisiensi penggunaan ruang per rumah tangga lebih relevan bagi harga properti daripada jumlah total kamar di seluruh blok sensus. Ini membuktikan nilai tambah dari proses feature engineering.

Insight 4 ??? Empat Variabel Mentah Berisi Informasi Redundan

total_rooms, total_bedrooms, households, dan population saling berkorelasi sangat tinggi (0,86???0,98) dengan VIF 6???35. Pada dasarnya keempat variabel ini mengukur hal yang sama: seberapa besar ukuran blok sensus. PCA mengonfirmasi ini dengan merangkum semua informasinya ke dalam satu komponen (PC1) yang efisien.

Insight 5 ??? Kepadatan Penduduk Berdampak Negatif terhadap Harga

Variabel-variabel yang mencerminkan kepadatan population (koefisien negatif di semua model), total_bedrooms (koefisien negatif), dan population_per_household (dipilih LASSO dengan koefisien negatif) semuanya berkorelasi negatif dengan harga rumah. Kawasan yang terlalu padat umumnya kehilangan nilai eksklusivitas dan kenyamanan yang mendorong harga turun.

Insight 6 ??? Reduksi Dimensi Efektif dan Substantif

Kombinasi feature engineering, feature selection, dan PCA berhasil memadatkan 11 variabel menjadi representasi yang lebih ringkas tanpa kehilangan substansi analisis. Model 7 variabel (Backward Elimination) menjelaskan 67,7% variasi harga rumah, sementara PCA merangkum ke dalam komponen orthogonal yang bebas multikolinearitas dan masing-masing dapat dimaknai secara substantif: ukuran blok (PC1), kemakmuran (PC2), dan lokasi (PC3???PC4).