Introduction

Supervised vs Unsupervised Learning

  • Supervised Learning

    • Memiliki target variabel
    • Tujuannya untuk membuat model prediksi dengan training data
    • Model di-evaluasi dengan testing data
  • Unsupervised Learning

    • Tidak memiliki target variabel
    • Tujuannya untuk mencari pola dalam data, yang berguna untuk menghasilkan informasi. Digunakan pada tahap pre-processing maupun Exploratory Data Analysis (EDA)
    • Tidak perlu melakukan evaluasi model, karena tidak ada label aktual

Mindmap

Secara umum, unsupervised learning yang akan kita pelajari terbagi menjadi dua cabang:

  1. Dimensionality Reduction: mereduksi dimensi dengan tetap mempertahankan informasi sebanyak-banyaknya. Algoritma: Principal Component Analysis (PCA)
  2. Clustering: pengelompokkan data berdasarkan kemiripan karakteristik data. Algoritma: K-means Clustering

Dimensionality Reduction

Tujuan dimensionality reduction adalah untuk mereduksi banyaknya variabel (dimensi) pada data dengan tetap mempertahankan informasi sebanyak mungkin. Dimensionality reduction dapat mengatasi masalah high-dimensional data. Kesulitan yang dihadapi pada high-dimensional data:

  • Memerlukan waktu dan komputasi yang besar dalam melakukan pemodelan
  • Melakukan visualisasi lebih dari tiga dimensi

Dimensionality reduction bertujuan untuk mereduksi dimensi data dengan tetap mempertahankan sebanyak mungkin informasi (informasi inti) dari data.

Dimensi: banyaknya variabel/kolom yang menjelaskan data.

Informasi: direpresentasikan oleh variance. Semakin tinggi variance (semakin beragam data) maka informasi yang diperoleh semakin banyak.

Note: Nilai variansi akan bergantung pada skala variabel.

Real Case: Image Compression

Foto berukuran 40x40 pixel memiliki 1600 kolom (dimensi).

Image compression adalah salah satu contoh nyata dimensionality reduction dimana data berupa gambar direduksi dimensinya namun tetap menghasilkan gambar yang serupa (informasi inti tidak hilang).

Algoritma yang akan kita gunakan untuk melakukan dimensionality reduction adalah Principal Component Analysis (PCA).

[Optional] Mathematics of PCA

Video: 3Blue1Brown: Eigenvectors and eigenvalues

Untuk membentuk PC dan nilai pada PC dibutuhkan eigen values & eigen vector. Secara manual, eigen values dan eigen vector didapatkan dari operasi matrix.

Teori Matrix

  • skalar: nilai yang memiliki magnitude/besaran
  • vektor: nilai yang memiliki besaran dan arah (umum digambarkan dalam suatu koordinat)
  • matrix: kumpulan nilai/bentukan data dalam baris dan kolom

Perkalian skalar-vektor: mengubah besaran vektor (tidak merubah arah kecuali berbalik arah)

\[2\left(\begin{array}{cc}2\\3\end{array}\right) = \left(\begin{array}{cc}4\\6\end{array}\right)\] \[-2\left(\begin{array}{cc}2\\3\end{array}\right) = \left(\begin{array}{cc}-4\\-6\end{array}\right)\]

Perkalian matrix-vektor: mengubah besaran dan arah

\[\left(\begin{array}{cc}1 & 2\\-1 & 2\end{array}\right)\left(\begin{array}{cc}2\\3 \end{array}\right) = \left(\begin{array}{cc}8\\4\end{array}\right)\]

Perkalian Matrix-Vektor Spesial

  • Matrix Rotasi: mengubah arah vektor tanpa mengubah besaran

\[ \left(\begin{array}{cc} 0 & -1\\ 1 & 0 \end{array}\right) \left(\begin{array}{cc} 2\\ 3 \end{array}\right)= \left(\begin{array}{cc} -3\\ 2 \end{array}\right) \]

# perkalian matrix di R
matrix(c(0,1,-1,0), nrow=2) %*% as.vector(c(2,3))
#>      [,1]
#> [1,]   -3
#> [2,]    2
  • Matrix Identitas: tidak mengubah besaran maupun arah vektor

\[ \left(\begin{array}{cc} 1 & 0\\ 0 & 1 \end{array}\right) \left(\begin{array}{cc} 2\\ 3 \end{array}\right)= \left(\begin{array}{cc} 2\\ 3 \end{array}\right) \]

# perkalian matrix di R
matrix(c(1,0,0,1), nrow=2) %*% as.vector(c(2,3))
#>      [,1]
#> [1,]    2
#> [2,]    3

Eigen dari suatu Matrix

Untuk setiap matrix, terdapat vektor spesial (eigen vector) yang jika dikalikan dengan matrixnya, hasilnya akan sama dengan vektor tersebut dikalikan suatu skalar (eigen value). Sehingga didapatkan rumus:

\[Ax = \lambda x\]

  • \(x\) adalah eigen vector
  • \(\lambda\) adalah eigen value dari matrix \(A\).

\[ \left(\begin{array}{cc} 2 & 3\\ 2 & 1 \end{array}\right) \left(\begin{array}{cc} 3\\ 2 \end{array}\right) = \left(\begin{array}{cc} 12\\ 8 \end{array}\right) =4 \left(\begin{array}{cc} 3\\ 2 \end{array}\right) \]

Teori eigen dipakai untuk menentukan PC dan nilai-nilai pada PC.

Penerapan Eigen dalam PCA:

Matrix covariance adalah matrix yang dapat merangkum informasi (variance) dari data. Kita menggunakan matrix covariance untuk mendapatkan mendapatkan eigen vector dan eigen value dari matrix tersebut, dimana:

  • eigen vector: arah sumbu tiap PC, yang menjadi formula untuk mentransformasi data awal ke PC baru.
  • eigen value: variansi yang ditangkap oleh setiap PC.
  • tiap PC memiliki 1 eigen value & 1 eigen vector.
  • alur: matrix covariance -> eigen value -> eigen vector -> nilai di tiap PC

Eigen vector akan menjadi formula untuk kalkulasi nilai di setiap PC. Contohnya, untuk data yang terdiri dari 2 variabel, bila diketahui eigen vector dari PC1 adalah:

\[x_{PC1}= \left(\begin{array}{cc}b_1\\b_2\end{array}\right)\]

Maka formula untuk menghitung nilai pada PC1 (untuk tiap barisnya) adalah:

\[PC1= b_1X_1 + b_2X_2\]

Keterangan:

  • \(x_{PC1}\) : eigen vector PC1 dari matrix covariance
  • \(b_1\), \(b_2\) : konstanta dari eigen vector
  • \(PC1\) : nilai di PC1
  • \(X_1\), \(X_2\) : nilai variabel X1 dan X2 di data awal

Principal Component Analysis (PCA)

Concept

Ide dasar dari PCA adalah untuk membuat sumbu (axis) baru yang dapat menangkap informasi sebesar mungkin. Sumbu baru ini adalah yang dinamakan sebagai Principal Component (PC). Untuk melakukan dimensionality reduction, kita akan memilih beberapa PC untuk dapat merangkum informasi yang dibutuhkan.

Kunjungi link berikut untuk ilustrasi PCA secara visual untuk data 2D dan 3D: https://setosa.io/ev/principal-component-analysis/

Figure 1A (kiri):

  • Sumbu: X1 dan X2
  • Variance data dijelaskan oleh X1 dan X2
  • Dibuatlah sumbu baru untuk menangkap informasi X1 dan X2, yang dinamakan PC1 dan PC2

Figure 1B (kanan):

  • Sumbu baru: PC1 dan PC2
  • PC1 menangkap variance lebih banyak daripada PC2

Misalkan PC1 menangkap 90% variance, dan sisanya ditangkap oleh PC2 yaitu 10%. Hanya dengan menggunakan 1 PC (yaitu PC1), kita dapat mereduksi dimensi data sebesar 50% (dari 2 dimensi menjadi 1 dimensi), namun tetap mempertahankan informasi sebesar 90% informasi data asli.

Notes:

  • PC dibuat untuk merangkum sebanyak mungkin informasi (variance) data
  • PC1 menangkap variance paling besar, daripada PC2, dan seterusnya
  • Banyaknya PC yang dibentuk = Banyaknya dimensi data asli
  • Antar PC tidak saling berkorelasi, karena saling tegak lurus
  • Metode PCA cocok untuk data numerik yang saling berkorelasi

PCA Workflow

Business Question: New York Property Market

Terdapat data unit bangunan/tempat tinggal yang terjual di New York Property Market selama periode 12 bulan (sumber: New York City Department of Finance). Ingin dilakukan eksploratory data analysis untuk data ini:

# Please run the code down below
library(dplyr)

property <- read.csv("data_input/nyc.csv")
glimpse(property)
#> Rows: 84,548
#> Columns: 22
#> $ X                              <int> 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1…
#> $ BOROUGH                        <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
#> $ NEIGHBORHOOD                   <chr> "ALPHABET CITY", "ALPHABET CITY", "ALPH…
#> $ BUILDING.CLASS.CATEGORY        <chr> "07 RENTALS - WALKUP APARTMENTS        …
#> $ TAX.CLASS.AT.PRESENT           <chr> "2A", "2", "2", "2B", "2A", "2", "2B", …
#> $ BLOCK                          <int> 392, 399, 399, 402, 404, 405, 406, 407,…
#> $ LOT                            <int> 6, 26, 39, 21, 55, 16, 32, 18, 34, 153,…
#> $ EASE.MENT                      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
#> $ BUILDING.CLASS.AT.PRESENT      <chr> "C2", "C7", "C7", "C4", "C2", "C4", "C4…
#> $ ADDRESS                        <chr> "153 AVENUE B", "234 EAST 4TH   STREET"…
#> $ APARTMENT.NUMBER               <chr> " ", " ", " ", " ", " ", " ", " ", " ",…
#> $ ZIP.CODE                       <int> 10009, 10009, 10009, 10009, 10009, 1000…
#> $ RESIDENTIAL.UNITS              <int> 5, 28, 16, 10, 6, 20, 8, 44, 15, 24, 30…
#> $ COMMERCIAL.UNITS               <int> 0, 3, 1, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, …
#> $ TOTAL.UNITS                    <int> 5, 31, 17, 10, 6, 20, 8, 46, 15, 24, 34…
#> $ LAND.SQUARE.FEET               <chr> "1633", "4616", "2212", "2272", "2369",…
#> $ GROSS.SQUARE.FEET              <chr> "6440", "18690", "7803", "6794", "4615"…
#> $ YEAR.BUILT                     <int> 1900, 1900, 1900, 1913, 1900, 1900, 192…
#> $ TAX.CLASS.AT.TIME.OF.SALE      <int> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
#> $ BUILDING.CLASS.AT.TIME.OF.SALE <chr> "C2", "C7", "C7", "C4", "C2", "C4", "C4…
#> $ SALE.PRICE                     <chr> "6625000", " -  ", " -  ", "3936272", "…
#> $ SALE.DATE                      <chr> "2017-07-19 00:00:00", "2016-12-14 00:0…

Deskripsi data:

  • X: ID baris
  • BOROUGH: Kode digit untuk wilayah dimana properti berada; Manhattan (1), Bronx (2), Brooklyn (3), Queens (4), dan Staten Island (5)
  • NEIGHBORHOOD: Nama lingkungan
  • BUILDING.CLASS.CATEGORY: Kategori kelas properti
  • TAX.CLASS.AT.PRESENT, TAX.CLASS.AT.TIME.OF.SALE: Jenis pajak bangunan
  • BLOCK, LOT: Kombinasi borough, blok, dan lot membentuk kode unik untuk properti di New York City
  • EASE.MENT: Ragam hak yang dimiliki bangunan (contoh: hak jalan, dsb.)
  • BUILDING.CLASS.AT.PRESENT, BUILDING.CLASS.AT.TIME.OF.SALE: Kategori kepemilikan bangunan (A: rumah satu keluarga; O: gedung kantor; R: kondominium (kepemilikan bersama); dsb.)
  • ADDRESS: Alamat properti
  • APARTMENT.NUMBER: Nomor apartemen
  • ZIP.CODE: Kode pos properti
  • RESIDENTIAL.UNITS: Jumlah unit hunian di properti yang terdaftar
  • COMMERCIAL.UNITS: Jumlah unit komersial di properti yang terdaftar
  • TOTAL.UNITS: Jumlah unit hunian dan komersial
  • LAND.SQUARE.FEET: Luas tanah properti (square feet)
  • GROSS.SQUARE.FEET: Total area semua lantai bangunan yang diukur dari permukaan luar dinding luar bangunan, termasuk area tanah dan ruang di dalam setiap bangunan atau struktur pada properti
  • YEAR.BUILT: Tahun properti dibangun
  • SALE.PRICE: Harga properti terjual; bila 0 maka properti merupakan warisan
  • SALE.DATE: Tanggal properti dijual

Data Cleansing

1. Buang kolom yang tidak diperlukan:

  • Kolom yang tidak relevan: X
  • Kolom dengan semua nilai NA: EASE.MENT
# Please run the code down below
unique(property$EASE.MENT)
#> [1] NA

2. Memperbaiki tipe data yang belum tepat:

Perhatikan kolom yang dideteksi sebagai integer:

  • BOROUGH, BLOCK, LOT: int -> factor
    Diubah menjadi factor karena adanya sifat pengulangan
  • ZIP.CODE: int -> factor
    Diubah menjadi factor karena adanya sifat pengulangan
  • RESIDENTIAL.UNITS, COMMERCIAL.UNITS, TOTAL.UNITS: int -> tetap int
    Tidak akan diubah menjadi tipe data lainnya karena berisikan sebuah value dari penjumlahan
  • YEAR.BUILT: int -> int
    Tergantung kebutuhan, kalau misalnya kita mau cek hubungan antara year built dengan sale price, maka lebih cocok integer. kalau untuk visualisasi bar plot, factor lebih cocok
  • TAX.CLASS.AT.TIME.OF.SALE: int -> factor
    Diubah menjadi factor karena adanya sifat pengulangan

Perhatikan kolom yang dideteksi sebagai character:

  • NEIGHBORHOOD: chr -> factor
    Diubah menjadi factor karena adanya sifat pengulangan
  • BUILDING.CLASS.CATEGORY, BUILDING.CLASS.AT.PRESENT, BUILDING.CLASS.AT.TIME.OF.SALE: chr -> factor
    Diubah menjadi factor karena adanya sifat pengulangan
  • TAX.CLASS.AT.PRESENT: chr -> factor
    Diubah menjadi factor karena adanya sifat pengulangan
  • ADDRESS: chr -> tetap chr
  • APARTMENT.NUMBER: chr -> chr
  • LAND.SQUARE.FEET, GROSS.SQUARE.FEET: chr -> int
    Diubah menjadi int karena berisikan informasi mengenai luas bangunan
  • SALE.PRICE: chr -> int
    Diubah menjadi int karena berisikan informasi mengenai luas bangunan
  • SALE.DATE: chr -> Date
    Diubah menjadi date karena berisikan informasi mengenai tanggal
# Please run the code down below
property_clean <- property %>%
  
  # hapus kolom
  select(-c(X, EASE.MENT)) %>%
  
  # mengubah int -> factor dan chr -> factor
  mutate_at(vars(BOROUGH, BLOCK, LOT, ZIP.CODE, TAX.CLASS.AT.TIME.OF.SALE,
                 NEIGHBORHOOD, BUILDING.CLASS.CATEGORY, BUILDING.CLASS.AT.PRESENT, 
                 BUILDING.CLASS.AT.TIME.OF.SALE, TAX.CLASS.AT.PRESENT), as.factor) %>%
  
  # mengubah chr -> int
  mutate_at(vars(LAND.SQUARE.FEET, GROSS.SQUARE.FEET, SALE.PRICE), as.integer) %>%
  
  # mengubah chr -> date
  mutate(SALE.DATE = lubridate::ymd_hms(SALE.DATE))

glimpse(property_clean)
#> Rows: 84,548
#> Columns: 20
#> $ BOROUGH                        <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
#> $ NEIGHBORHOOD                   <fct> ALPHABET CITY, ALPHABET CITY, ALPHABET …
#> $ BUILDING.CLASS.CATEGORY        <fct> 07 RENTALS - WALKUP APARTMENTS         …
#> $ TAX.CLASS.AT.PRESENT           <fct> 2A, 2, 2, 2B, 2A, 2, 2B, 2, 2, 2, 2, 2B…
#> $ BLOCK                          <fct> 392, 399, 399, 402, 404, 405, 406, 407,…
#> $ LOT                            <fct> 6, 26, 39, 21, 55, 16, 32, 18, 34, 153,…
#> $ BUILDING.CLASS.AT.PRESENT      <fct> C2, C7, C7, C4, C2, C4, C4, C7, D5, D9,…
#> $ ADDRESS                        <chr> "153 AVENUE B", "234 EAST 4TH   STREET"…
#> $ APARTMENT.NUMBER               <chr> " ", " ", " ", " ", " ", " ", " ", " ",…
#> $ ZIP.CODE                       <fct> 10009, 10009, 10009, 10009, 10009, 1000…
#> $ RESIDENTIAL.UNITS              <int> 5, 28, 16, 10, 6, 20, 8, 44, 15, 24, 30…
#> $ COMMERCIAL.UNITS               <int> 0, 3, 1, 0, 0, 0, 0, 2, 0, 0, 4, 0, 0, …
#> $ TOTAL.UNITS                    <int> 5, 31, 17, 10, 6, 20, 8, 46, 15, 24, 34…
#> $ LAND.SQUARE.FEET               <int> 1633, 4616, 2212, 2272, 2369, 2581, 175…
#> $ GROSS.SQUARE.FEET              <int> 6440, 18690, 7803, 6794, 4615, 9730, 42…
#> $ YEAR.BUILT                     <int> 1900, 1900, 1900, 1913, 1900, 1900, 192…
#> $ TAX.CLASS.AT.TIME.OF.SALE      <fct> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
#> $ BUILDING.CLASS.AT.TIME.OF.SALE <fct> C2, C7, C7, C4, C2, C4, C4, C7, D5, D9,…
#> $ SALE.PRICE                     <int> 6625000, NA, NA, 3936272, 8000000, NA, …
#> $ SALE.DATE                      <dttm> 2017-07-19, 2016-12-14, 2016-12-09, 20…

3. Cek missing values:

colSums(is.na(property_clean))
#>                        BOROUGH                   NEIGHBORHOOD 
#>                              0                              0 
#>        BUILDING.CLASS.CATEGORY           TAX.CLASS.AT.PRESENT 
#>                              0                              0 
#>                          BLOCK                            LOT 
#>                              0                              0 
#>      BUILDING.CLASS.AT.PRESENT                        ADDRESS 
#>                              0                              0 
#>               APARTMENT.NUMBER                       ZIP.CODE 
#>                              0                              0 
#>              RESIDENTIAL.UNITS               COMMERCIAL.UNITS 
#>                              0                              0 
#>                    TOTAL.UNITS               LAND.SQUARE.FEET 
#>                              0                          26252 
#>              GROSS.SQUARE.FEET                     YEAR.BUILT 
#>                          27612                              0 
#>      TAX.CLASS.AT.TIME.OF.SALE BUILDING.CLASS.AT.TIME.OF.SALE 
#>                              0                              0 
#>                     SALE.PRICE                      SALE.DATE 
#>                          14562                              0

Kolom dengan missing values: ada 3, LAND.SQUARE.FEET, GROSS.SQUARE.FEET, dan SALE.PRICE

Beberapa cara untuk handle missing values:

  1. Imputasi: mengisi nilai NA dengan suatu nilai
  2. Drop column: hapus kolom apabila NA pada kolom tersebut terlalu banyak (di atas 5%)
  3. Drop row: hapus baris apabila mengandung nilai NA

Catatan: Pembahasan mengenai Missing Value akan dijelaskan pada materi Time Series.

Untuk menghilangkan missing value kita bisa mengunakan bantuan fungsi filter() & complete.cases().

# Please type your code down here
property_clean <- property_clean %>% 
  filter(complete.cases(.)) # titik mengacu kepada property_clean

4. Memilih kolom numerik:

Dikarenakan analisis PCA menggunakan nilai variance, kita hanya dapat mereduksi kolom bertipe data numerik.

# Please type your code down here
property_num <- property_clean %>% 
  select_if(is.numeric)

glimpse(property_num)
#> Rows: 48,243
#> Columns: 7
#> $ RESIDENTIAL.UNITS <int> 5, 10, 6, 8, 24, 10, 24, 3, 4, 5, 0, 1, 1, 1, 1, 2, …
#> $ COMMERCIAL.UNITS  <int> 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1…
#> $ TOTAL.UNITS       <int> 5, 10, 6, 8, 24, 10, 24, 4, 5, 6, 1, 1, 1, 1, 1, 3, …
#> $ LAND.SQUARE.FEET  <int> 1633, 2272, 2369, 1750, 4489, 3717, 4131, 1520, 2201…
#> $ GROSS.SQUARE.FEET <int> 6440, 6794, 4615, 4226, 18523, 12350, 16776, 3360, 5…
#> $ YEAR.BUILT        <int> 1900, 1913, 1900, 1920, 1920, 2009, 1928, 1910, 1900…
#> $ SALE.PRICE        <int> 6625000, 3936272, 8000000, 3192840, 16232000, 103500…

Exploratory Data Analysis

Apakah antar variable memiliki skala yang sama? Mari kita cek range untuk tiap kolom:

# Please run the code down here
summary(property_num)
#>  RESIDENTIAL.UNITS  COMMERCIAL.UNITS     TOTAL.UNITS       LAND.SQUARE.FEET 
#>  Min.   :   0.000   Min.   :   0.0000   Min.   :   0.000   Min.   :      0  
#>  1st Qu.:   1.000   1st Qu.:   0.0000   1st Qu.:   1.000   1st Qu.:   1413  
#>  Median :   1.000   Median :   0.0000   Median :   1.000   Median :   2140  
#>  Mean   :   2.567   Mean   :   0.2485   Mean   :   2.834   Mean   :   3357  
#>  3rd Qu.:   2.000   3rd Qu.:   0.0000   3rd Qu.:   2.000   3rd Qu.:   3071  
#>  Max.   :1844.000   Max.   :2261.0000   Max.   :2261.000   Max.   :4228300  
#>  GROSS.SQUARE.FEET   YEAR.BUILT     SALE.PRICE        
#>  Min.   :      0   Min.   :   0   Min.   :         0  
#>  1st Qu.:    828   1st Qu.:1920   1st Qu.:     80280  
#>  Median :   1620   Median :1931   Median :    480000  
#>  Mean   :   3637   Mean   :1828   Mean   :   1107496  
#>  3rd Qu.:   2520   3rd Qu.:1961   3rd Qu.:    830000  
#>  Max.   :3750565   Max.   :2017   Max.   :1040000000

Mari kita cek juga matriks covariance dari property_num, dengan menggunakan fungsi cov():

cov(property_num)
#>                   RESIDENTIAL.UNITS COMMERCIAL.UNITS   TOTAL.UNITS
#> RESIDENTIAL.UNITS        305.049227         2.453481      307.4475
#> COMMERCIAL.UNITS           2.453481       120.715187      123.1605
#> TOTAL.UNITS              307.447502       123.160464      430.5673
#> LAND.SQUARE.FEET      220200.789429     18207.148349   238340.8764
#> GROSS.SQUARE.FEET     318448.577564     21369.667678   339744.6630
#> YEAR.BUILT               220.670165        27.677915      242.5530
#> SALE.PRICE          28836512.816272   5362349.231994 34181339.6879
#>                   LAND.SQUARE.FEET GROSS.SQUARE.FEET     YEAR.BUILT
#> RESIDENTIAL.UNITS        220200.79         318448.58      220.67016
#> COMMERCIAL.UNITS          18207.15          21369.67       27.67792
#> TOTAL.UNITS              238340.88         339744.66      242.55301
#> LAND.SQUARE.FEET      988110040.59      545385900.86   138633.73804
#> GROSS.SQUARE.FEET     545385900.86      816829031.72   425290.83095
#> YEAR.BUILT               138633.74         425290.83   215635.35375
#> SALE.PRICE          13967140344.87   107371730683.06 41381457.84525
#>                       SALE.PRICE
#> RESIDENTIAL.UNITS       28836513
#> COMMERCIAL.UNITS         5362349
#> TOTAL.UNITS             34181340
#> LAND.SQUARE.FEET     13967140345
#> GROSS.SQUARE.FEET   107371730683
#> YEAR.BUILT              41381458
#> SALE.PRICE        78460695562213

Variance dari masing-masing variabel berbeda jauh karena range dari tiap variabel berbeda, begitu pula dengan covariance. Nilai variance dan covariance dipengaruhi oleh skala dari data. Semakin tinggi skala, nilai variance atau covariance akan semakin tinggi.

Notes: Data dengan perbedaan skala antar variabel yang tinggi tidak baik untuk langsung dianalisis PCA karena dapat menimbulkan bias.

Data Preprocessing: Scaling

Scaling dilakukan agar antar variabel memiliki skala yang tidak jauh berbeda. Nilai scaling yang digunakan adalah Z-score (mean = 0, standar deviasi = 1) atau kita bisa langsung menggunakan fungsi scale().

\[Z = \frac{x-mean}{sd}\]

# scaling
property_num_z <- scale(property_num)

Dikarenakan kita tidak membagi data, maka dari itu kita cukup melakukan scalling sekali saja.

Principal Component Analysis using prcomp()

PCA dapat dilakukan dengan menggunakan fungsi prcomp().

Cara 1: menggunakan data yang di-scale secara terpisah, yaitu property_num_z

prcomp(property_num_z)
#> Standard deviations (1, .., p=7):
#> [1] 1.705249835 1.085811853 0.999591384 0.987849523 0.840500004 0.481289277
#> [7] 0.005136074
#> 
#> Rotation (n x k) = (7 x 7):
#>                           PC1         PC2          PC3         PC4         PC5
#> RESIDENTIAL.UNITS -0.50138338  0.08936884 -0.019241647  0.16910059  0.56011546
#> COMMERCIAL.UNITS  -0.18548792 -0.81557172  0.014975088 -0.14893618 -0.36170475
#> TOTAL.UNITS       -0.52014300 -0.35665390 -0.008860518  0.06344165  0.27995828
#> LAND.SQUARE.FEET  -0.37554855  0.26238205 -0.074578580  0.39235388 -0.66005834
#> GROSS.SQUARE.FEET -0.50070661  0.28523938 -0.004506179 -0.08135536 -0.18003718
#> YEAR.BUILT        -0.02601345  0.01616302  0.994397509  0.09723184 -0.02509751
#> SALE.PRICE        -0.22636182  0.22178116  0.070130740 -0.88044527 -0.09134236
#>                           PC6            PC7
#> RESIDENTIAL.UNITS -0.20433374 -0.59680907352
#> COMMERCIAL.UNITS   0.07891239 -0.37546407004
#> TOTAL.UNITS       -0.13009145  0.70911598103
#> LAND.SQUARE.FEET  -0.44151829  0.00005541206
#> GROSS.SQUARE.FEET  0.79301891 -0.00002543141
#> YEAR.BUILT        -0.01231074  0.00042440835
#> SALE.PRICE        -0.33335840  0.00005903831

Cara 2: menggunakan data yang belum discale, lalu tambahkan parameter scale = T

pca <- prcomp(property_num, scale = TRUE)
pca
#> Standard deviations (1, .., p=7):
#> [1] 1.705249835 1.085811853 0.999591384 0.987849523 0.840500004 0.481289277
#> [7] 0.005136074
#> 
#> Rotation (n x k) = (7 x 7):
#>                           PC1         PC2          PC3         PC4         PC5
#> RESIDENTIAL.UNITS -0.50138338  0.08936884 -0.019241647  0.16910059  0.56011546
#> COMMERCIAL.UNITS  -0.18548792 -0.81557172  0.014975088 -0.14893618 -0.36170475
#> TOTAL.UNITS       -0.52014300 -0.35665390 -0.008860518  0.06344165  0.27995828
#> LAND.SQUARE.FEET  -0.37554855  0.26238205 -0.074578580  0.39235388 -0.66005834
#> GROSS.SQUARE.FEET -0.50070661  0.28523938 -0.004506179 -0.08135536 -0.18003718
#> YEAR.BUILT        -0.02601345  0.01616302  0.994397509  0.09723184 -0.02509751
#> SALE.PRICE        -0.22636182  0.22178116  0.070130740 -0.88044527 -0.09134236
#>                           PC6            PC7
#> RESIDENTIAL.UNITS -0.20433374 -0.59680907352
#> COMMERCIAL.UNITS   0.07891239 -0.37546407004
#> TOTAL.UNITS       -0.13009145  0.70911598103
#> LAND.SQUARE.FEET  -0.44151829  0.00005541206
#> GROSS.SQUARE.FEET  0.79301891 -0.00002543141
#> YEAR.BUILT        -0.01231074  0.00042440835
#> SALE.PRICE        -0.33335840  0.00005903831

Terdapat tiga komponen utama dalam objek pca:

  1. pca$sdev: standar deviasi (akar variance) yang ditangkap oleh masing-masing PC, disebut juga sebagai eigen value. Digunakan untuk mengetahui seberapa besaran informasi yang ditangkap masing-masing PC.
# nilai variance yang ditangkap oleh setiap PC
pca$sdev
#> [1] 1.705249835 1.085811853 0.999591384 0.987849523 0.840500004 0.481289277
#> [7] 0.005136074

Atau kita bisa juga untuk menggunakan bantuan fungsi plot().

# variance yang ditampilkan dalam bentuk plot
plot(pca)

  1. pca$rotation: matrix rotasi, dimana matrix ini terdiri dari eigen vectors. Digunakan untuk mengetahui kontribusi masing-masing variabel ke PC.
pca$rotation
#>                           PC1         PC2          PC3         PC4         PC5
#> RESIDENTIAL.UNITS -0.50138338  0.08936884 -0.019241647  0.16910059  0.56011546
#> COMMERCIAL.UNITS  -0.18548792 -0.81557172  0.014975088 -0.14893618 -0.36170475
#> TOTAL.UNITS       -0.52014300 -0.35665390 -0.008860518  0.06344165  0.27995828
#> LAND.SQUARE.FEET  -0.37554855  0.26238205 -0.074578580  0.39235388 -0.66005834
#> GROSS.SQUARE.FEET -0.50070661  0.28523938 -0.004506179 -0.08135536 -0.18003718
#> YEAR.BUILT        -0.02601345  0.01616302  0.994397509  0.09723184 -0.02509751
#> SALE.PRICE        -0.22636182  0.22178116  0.070130740 -0.88044527 -0.09134236
#>                           PC6            PC7
#> RESIDENTIAL.UNITS -0.20433374 -0.59680907352
#> COMMERCIAL.UNITS   0.07891239 -0.37546407004
#> TOTAL.UNITS       -0.13009145  0.70911598103
#> LAND.SQUARE.FEET  -0.44151829  0.00005541206
#> GROSS.SQUARE.FEET  0.79301891 -0.00002543141
#> YEAR.BUILT        -0.01231074  0.00042440835
#> SALE.PRICE        -0.33335840  0.00005903831

Intuisi dari hasil pca$rotation sama seperti membuat interpretasi pada saat membuat regresi linear, namun tanpa intercept:

  • PC1 = -0.501 * RESIDENTIAL.UNITS - 0.185 * COMMERCIAL.UNITS + ... - 0.226 * SALE.PRICE
  • PC2 = 0.089 * RESIDENTIAL.UNITS - 0.816 * COMMERCIAL.UNITS + ... + 0.222 * SALE.PRICE
  • PC3 = -0.019 * RESIDENTIAL.UNITS + 0.015 * COMMERCIAL.UNITS + ... + 0.070 * SALE.PRICE
  • dan seterusnya sampai PC7

Notes: Semakin besar koefisiennya, semakin banyak informasi variable awal yang terangkum pada PC tersebut.

  1. pca$x: nilai hasil proyeksi titik ke PC untuk tiap baris. Digunakan untuk mendapatkan nilai data yang baru.
# data hasil proyeksi
head(pca$x)
#>             PC1         PC2       PC3        PC4        PC5        PC6
#> [1,] -0.2935209 0.147914662 0.1980776 -0.5292426 0.07317298 -0.1514114
#> [2,] -0.5082436 0.029557658 0.1954128 -0.1886026 0.31235609 -0.1395631
#> [3,] -0.3592532 0.158199961 0.2059768 -0.6387936 0.10059704 -0.2821036
#> [4,] -0.3308636 0.005343151 0.2092174 -0.1379256 0.25565902 -0.1397556
#> [5,] -1.8076583 0.304228385 0.2792419 -1.2366689 0.70260516 -0.5597454
#> [6,] -0.7921258 0.260997656 0.4474640 -0.8037906 0.17568724 -0.2496183
#>                PC7
#> [1,] -0.0005407990
#> [2,] -0.0005278135
#> [3,] -0.0005250705
#> [4,] -0.0005322914
#> [5,] -0.0003949946
#> [6,] -0.0003997224

Reduce Dimension

Misal kita hanya ingin mereduksi 7 dimensi menjadi 5 dimensi, maka kita gunakan kolom PC1 sampai PC5 pada pca$x. Namun bagaimana cara kita memilih sebaiknya berapa dimensi yang kita pertahankan? Untuk itu, Kita melihat cumulative variance dengan fungsi summary().

summary(pca)
#> Importance of components:
#>                           PC1    PC2    PC3    PC4    PC5     PC6      PC7
#> Standard deviation     1.7052 1.0858 0.9996 0.9878 0.8405 0.48129 0.005136
#> Proportion of Variance 0.4154 0.1684 0.1427 0.1394 0.1009 0.03309 0.000000
#> Cumulative Proportion  0.4154 0.5838 0.7266 0.8660 0.9669 1.00000 1.000000

Keterangan:

  • Standard deviation: standar deviasi (akar variance) yang ditangkap oleh masing-masing PC, sama seperti pca$sdev
  • Proportion of Variance: persentase informasi yang ditangkap oleh tiap PC
  • Cumulative Proportion: jumlah persentase informasi yang ditangkap secara kumulatif dari PC1 hingga PC tersebut

Pemilihan banyaknya PC disesuaikan dengan kebutuhan. Misal, kita ingin merangkum minimal 75% informasi, maka jumlah PC yang kita gunakan adalah …

# subsetting base R: [baris, kolom]
pc_keep <- data.frame(pca$x[,1:4])
head(pc_keep)

Setelah dipilih PC yang merangkum informasi yang dibutuhkan, PC dapat digabung dengan data awal dan digunakan untuk analisis lebih lanjut (misal: supervised learning).

property_clean %>% 
  select_if(~!is.integer(.)) %>% # ambil kolom selain integer
  cbind(pc_keep) # gabungkan dengan kolom PC

Pros and Cons of PCA

Kelebihan melakukan PCA sebelum modeling:

  • Beban komputasi relatif lebih rendah, karena menggunakan fitur yang lebih sedikit
  • Bisa jadi salah satu teknik yang digunakan untuk improve performa model, namun belum tentu menjadi lebih baik
  • Mengurangi resiko terjadinya multikolinearitas, karena nilai antar PC tidak saling berkorelasi
library(GGally)

# korelasi sebelum PCA
ggcorr(property_num, label = T, hjust = 1, layout.exp = 2)

# korelasi setelah PCA
ggcorr(pca$x, label = T)

Kekurangan melakukan PCA sebelum modeling:

  • Apabila menggunakan model yang highly interpretable, maka model menjadi tidak dapat diinterpretasikan karena nilai PC merupakan nilai campuran dari beberapa variable.

Visualizing PCA

PCA tidak hanya berguna untuk dimensionality reduction namun baik untuk visualisasi high-dimensional data. Visualisasi dapat menggunakan biplot yang menampilkan:

  1. Individual factor map, yaitu sebaran data secara keseluruhan menggunakan 2 PC. Tujuannya untuk mengetahui observasi yang serupa dan outlier.
  2. Variables factor map, yaitu plot yang menunjukkan korelasi antar variable dan kontribusinya terhadap PC.

Biplot

Mari kita buat biplot dari 100 observasi pertama dari property_num

Parameter fungsi biplot():

  • x: objek hasil PCA
  • cex: ukuran font di biplot
# ambil 100 data pertama agar tidak terlalu menumpuk
property_num_100 <- head(property_num, 100)

# melakukan PCA
property_num_100_pca <- prcomp(property_num_100, scale = TRUE)
  
# membuat biplot
biplot(property_num_100_pca, cex = 0.75)

Biplot menggunakan PC1 dan PC2 secara default, karena PC tersebut yang merangkum informasi terbanyak dari data sehingga mampu mewakili data kita.

  • Titik-titik = index angka dari observasi. Semakin berdekatan maka karakteristiknya semakin mirip, sedangkan yang jauh dari gerombolan data dianggap sebagai outlier
  • Panah merah = loading score, menunjukkan kontribusi variabel tersebut terhadap PC, atau banyaknya informasi variabel tersebut yang dirangkum oleh PC.

Variables Factor Map

  1. Variable Contribution

Loading score dilihat dari jarak titik nol (pusat) ke ujung panah, secara horizontal (PC1) / vertikal (PC2). Semakin jauh panah, semakin banyak informasi yang dirangkum. Bila panah kearah negatif, maka bila nilai variabel awal tinggi, nilai di PC akan rendah, dan sebaliknya.

Berdasarkan penjelasan tersebut, maka:

Variable yang paling berkontribusi ke PC1 adalah: COMMERCIAL.UNITS, SALE.PRICE, GROSS.SQUARE.FEET, LAND.SQUARE.FEET

Variable yang paling berkontribusi ke PC2 adalah: TOTAL.UNITS, RESIDENTIAL.UNITS

Dari panah merah tersebut, kita tahu variable mana yang paling banyak berkontribusi untuk tiap PC. Mari kita gunakan fungsi fviz_contrib() untuk melihat urutan kontribusi variabel ke tiap PC

Parameter:

  • X: objek PCA
  • choice = "var": VARiable contribution
  • axes: PC keberapa yang ingin kita lihat variable contributionnya
# install.packages("factoextra")
library(factoextra)
# bar plot variable contribution
fviz_contrib(property_num_100_pca, # objek PCA
             choice = "var", # VARiable contribution
             axes = 1) # PC 1

fviz_contrib(property_num_100_pca, # objek PCA
             choice = "var", # VARiable contribution
             axes = 2) # PC 2

Catatan: garis horizontal putus-putus merah adalah batas nilai kontribusi yang diharapkan apabila PC menangkap informasi dari tiap variable dengan sama rata. Cara menghitungnya adalah 100% dibagi dengan jumlah variable. Pada kasus ini 100% / 7 = 14.28%. Bila melebihi batas tersebut, maka dapat dikatakan variable tersebut berkontribusi terhadap PC tersebut.

  1. Korelasi antar variabel dapat dilihat dari sudut antar dua panah
  • Panah saling berdekatan (sudut antar panah < 90), maka korelasi positif
  • Panah saling tegak lurus (sudut antar panah = 90), maka tidak berkorelasi
  • Panah saling bertolak belakang (sudut antar panah mendekati 180), maka korelasi negatif

Panah yang berhimpit pada biplot adalah: TOTAL.UNITS dengan RESIDENTIAL.UNITS, lalu ada juga GROSS.SQUARE.FEET dengan LAND.SQUARE.FEET (tandanya korelasi positif cukup kuat)

Panah yang (hampir) tegak lurus adalah: TOTAL.UNITS dengan SALE.PRICE (tandanya hampir tidak ada korelasi)

# opsional: cek nilai korelasi hanya untuk memastikan
ggcorr(property_num_100, label = T, hjust = 1)

Individual Factor Map

  1. Outlier detection: observasi yang jauh dari kumpulan observasi lainnya mengindikasikan outlier dari keseluruhan data. Observasi ini dapat ditandai untuk nantinya dicek karakteristik datanya untuk keperluan bisnis, atau apakah mempengaruhi performa model/clustering, dll.

Dari biplot di atas, tiga outlier terluar adalah observasi 96, 51, 39 atau 32

  1. Observasi searah panah mengindikasikan observasi tersebut nilainya tinggi pada variabel tersebut. Bila bertolak belakang, maka nilainya rendah pada variable tersebut.

Tiga outlier tersebut tinggi di nilai apa saja?

Observasi 96 ekstrim di nilai TOTAL.UNITS Observasi 51 ekstrim di nilai SALE.PRICE dan GROSS.SQUARE.FEET Observasi 39 ekstrim di nilai SALE.PRICE

Berdasarkan biplot, bagaimana kondisi properti pada observasi ke 83 apabila dibandingkan dengan observasi ke 96? Coba tinjau dari TOTAL.UNITSnya.

range(property_num_100$TOTAL.UNITS)
#> [1]  1 48
# cek observasi 83 dan 96
property_num_100[c(83, 96),]

Observasi 83 memiliki nilai TOTAL.UNITS yang sedikit, karena terletak di belakang panah TOTAL.UNITS

Observasi 96 memiliki nilai TOTAL.UNITS yang besar, karena ditunjuk oleh panah TOTAL.UNITS

Dive Deeper

Buatlah biplot dengan menggunakan 300 data pertama dari property_num:

# your code here
property_num_300 <- head(property_num,300)

# melakukan PCA
property_num_300_pca <- prcomp(property_num_300, scale = TRUE)
  
# membuat biplot
biplot(property_num_300_pca, cex = 0.5)

Insight: Outlier

Dari biplot, identifikasilah:

  • Tiga observasi yang memiliki nilai TOTAL.UNITS yang sangat tinggi: …
  • Tiga observasi yang memiliki nilai SALE.PRICE yang sangat tinggi: …

Insight: Variable Contribution

  1. Tiga variabel yang berkontribusi tinggi ke PC1: …
  2. Dua variabel yang berkontribusi tinggi ke PC2: …
  3. Variabel yang berkontribusi rendah pada PC1 maupun PC2: …

Insight: Variable Correlation

Gunakan biplot, identifikasilah:

  1. Pasangan variabel yang berkorelasi tinggi positif:
  1. Pasangan variabel yang tidak berkorelasi:

[Additional] - Fancy Biplot

Lakukan PCA untuk built-in dataset USArrests dan visualisasikan menggunakan fancy_biplot()!

# load custom function
source("R/biplot.R")

# built-in dataset
head(USArrests)

Dataset USArrests merupakan data tentang jumlah tiga tindak kejahatan (Murder, Assault, dan Rape) per 100 ribu penduduk yang berhasil dicatat di 50 negara bagian US pada tahun 1973.

Deskripsi data:

  • Murder: murder arrests per 100,000
  • Assault: assault arrests per 100,000
  • UrbanPop: percent of population in urban areas
  • Rape: rape arrests per 100,000
# biplot visualization
fancy_biplot(prcomp(USArrests, scale = T))

Insight dari biplot:

  1. Perhatikan cluster di mana Arizona, Colorado, Illinois, dan Texas berada. Negara bagian mana lagi yang ada dalam cluster tersebut? Apa insight yang dapat diperoleh?

  1. Di antara Ohio, Louisiana, North Dakota, dan Colorado, manakah yang memiliki:
  • Tingkat Murder tertinggi: …
  • Tingkat Assault tertinggi: …
  • Tingkat Rape tertinggi: …
  • Tingkat kriminalitas terendah, meliputi ketiga tipe kejahatan: …

Principal Component Analysis using FactoMiner

FactoMineR menyediakan dua fungsi utama dalam PCA:

  1. PCA() untuk melakukan analisis PCA
  2. plot.PCA() untuk visualisasi

Kelebihan FactoMineR dari fungsi base biplot() adalah:

  • Memisahkan dua grafik yang terdapat pada biplot yaitu individual factor map dan variables factor map
  • Dapat melibatkan variable kategorik untuk mewarnai plot sehingga plot lebih informatif

Workflow

Business Question: coba lakukan analisis PCA untuk Loan Dataset dari Quarter 4, 2017.

loan <- read.csv("data_input/loan2017Q4.csv", stringsAsFactors = T)

glimpse(loan)
#> Rows: 1,556
#> Columns: 16
#> $ initial_list_status <fct> w, f, w, w, w, w, w, w, w, w, w, w, w, f, w, w, w,…
#> $ purpose             <fct> debt_consolidation, debt_consolidation, debt_conso…
#> $ int_rate            <dbl> 14.08, 9.44, 28.72, 13.59, 15.05, 10.91, 15.05, 10…
#> $ installment         <dbl> 675.99, 480.08, 1010.30, 484.19, 476.33, 130.79, 3…
#> $ annual_inc          <dbl> 156700, 50000, 25000, 175000, 109992, 49000, 65000…
#> $ dti                 <dbl> 19.11, 19.35, 65.58, 12.60, 10.00, 5.12, 22.38, 33…
#> $ verification_status <fct> Source Verified, Not Verified, Verified, Not Verif…
#> $ grade               <fct> C, B, F, C, C, B, C, B, D, D, F, C, C, E, B, C, C,…
#> $ revol_bal           <int> 21936, 5457, 23453, 31740, 2284, 2016, 14330, 2758…
#> $ inq_last_12m        <int> 3, 1, 0, 0, 3, 5, 0, 1, 8, 1, 0, 12, 4, 8, 1, 3, 0…
#> $ delinq_2yrs         <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
#> $ home_ownership      <fct> MORTGAGE, RENT, OWN, MORTGAGE, MORTGAGE, MORTGAGE,…
#> $ not_paid            <int> 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,…
#> $ log_inc             <dbl> 11.962088, 10.819778, 10.126631, 12.072541, 11.608…
#> $ verified            <int> 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ grdCtoA             <int> 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,…

Data yang digunakan merupakan data peer-to-peer landing pada Q4, 2017 (digunakan saat materi “Classification 1”) yang terdiri dari 1556 obsrvasi dan 16 variabel.

  • initial_list_status: Either w (whole) or f (fractional). This variable indicates if the loan was a whole loan or fractional loan. For background: Some institutional investors have a preference to purchase loans in their entirety to obtain legal and accounting treatment specific to their situation - with the added benefit of “instant funding” to borrowers
  • purpose: Simplified from the original data; One of: credit_card, debt_consolidation, home_improvement, major_purchase and small_business
  • int_rate: Interest rate in percentages
  • installment: Monthly payment owed by the borrower
  • annual_inc: Self-reported annual income provided by the borrower / co-borrowers during application
  • dti: A ratio of the borrower’s total monthly debt payments on his/her total obligations to the self-reported monthly income
  • verification_status: is the reported income verified, not verified, or if the income source was verified
  • grade: software-assigned loan grade
  • revol_bal: total credit revolving balance (in the case of credit card, it refers to the portion of credit card spending that goes unpaid at the end of a billing cycle)
  • inq_last_12m: number of credit inquiries in the last 12 months
  • delinq_2yrs: number of 30+ days past-due incidences of delinquency in the borrower’s credit file for the past 2 years
  • home_ownership: one of MORTGAGE, OWN and RENT
  • not_paid: 0 for fully-paid loans, 1 for charged-off, past-due / grace period or defaulted
  • log_inc: log of annual_inc
  • verified: 0 for “Not verified” under verification_status, 1 otherwise
  • grdCtoA: 1 for a grade of A, B or C, 0 otherwise

FactoMiner PCA

FactoMineR adalah R package yang dibuat untuk exploratory multivariate data analysis. Mari kita lakukan analisis PCA untuk data loan dari sebuah platform di quarter ke-4 tahun 2017.

# install.packages("FactoMineR")
library(FactoMineR)

Read Data

loan <- read.csv("data_input/loan2017Q4.csv", stringsAsFactors = T)
glimpse(loan)
#> Rows: 1,556
#> Columns: 16
#> $ initial_list_status <fct> w, f, w, w, w, w, w, w, w, w, w, w, w, f, w, w, w,…
#> $ purpose             <fct> debt_consolidation, debt_consolidation, debt_conso…
#> $ int_rate            <dbl> 14.08, 9.44, 28.72, 13.59, 15.05, 10.91, 15.05, 10…
#> $ installment         <dbl> 675.99, 480.08, 1010.30, 484.19, 476.33, 130.79, 3…
#> $ annual_inc          <dbl> 156700, 50000, 25000, 175000, 109992, 49000, 65000…
#> $ dti                 <dbl> 19.11, 19.35, 65.58, 12.60, 10.00, 5.12, 22.38, 33…
#> $ verification_status <fct> Source Verified, Not Verified, Verified, Not Verif…
#> $ grade               <fct> C, B, F, C, C, B, C, B, D, D, F, C, C, E, B, C, C,…
#> $ revol_bal           <int> 21936, 5457, 23453, 31740, 2284, 2016, 14330, 2758…
#> $ inq_last_12m        <int> 3, 1, 0, 0, 3, 5, 0, 1, 8, 1, 0, 12, 4, 8, 1, 3, 0…
#> $ delinq_2yrs         <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
#> $ home_ownership      <fct> MORTGAGE, RENT, OWN, MORTGAGE, MORTGAGE, MORTGAGE,…
#> $ not_paid            <int> 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,…
#> $ log_inc             <dbl> 11.962088, 10.819778, 10.126631, 12.072541, 11.608…
#> $ verified            <int> 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ grdCtoA             <int> 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,…
table(loan$grdCtoA, loan$grade)
#>    
#>       A   B   C   D   E   F   G
#>   0   0   0 464 326 118  38  15
#>   1 210 385   0   0   0   0   0

Data berisikan 1556 customer dengan 16 variabel, dengan kolom not_paid yang mengindikasikan apakah customer tersebut gagal bayar pinjaman atau tidak. Berikut adalah deskripsi data yang lengkap:

  • initial_list_status: Either w (whole) or f (fractional). This variable indicates if the loan was a whole loan or fractional loan. For background: Some institutional investors have a preference to purchase loans in their entirety to obtain legal and accounting treatment specific to their situation - with the added benefit of “instant funding” to borrowers
  • purpose: Simplified from the original data; One of: credit_card, debt_consolidation, home_improvement, major_purchase and small_business
  • int_rate: Interest rate in percentages
  • installment: Monthly payment owed by the borrower
  • annual_inc: Self-reported annual income provided by the borrower / co-borrowers during application
  • dti: A ratio of the borrower’s total monthly debt payments on his/her total obligations to the self-reported monthly income (debt to income ratio)
  • verification_status: is the reported income verified, not verified, or if the income source was verified
  • grade: software-assigned loan grade
  • revol_bal: total credit revolving balance (in the case of credit card, it refers to the portion of credit card spending that goes unpaid at the end of a billing cycle)
  • inq_last_12m: number of credit inquiries in the last 12 months
  • delinq_2yrs: number of 30+ days past-due incidences of delinquency in the borrower’s credit file for the past 2 years
  • home_ownership: one of MORTGAGE, OWN and RENT
  • not_paid: 1 for charged-off, past-due / grace period or defaulted, 0 for fully-paid loans
  • log_inc: log of annual_inc
  • verified: 0 for “Not verified” under verification_status, 1 otherwise
  • grdCtoA: 0 for a grade of A, B or C, 1 otherwise

Data Cleansing

Adakah tipe data yang belum tepat?

Ubah not_paid, verified dan grdCtoA ke tipe data factor

loan_clean <- loan %>% 
  mutate_at(vars(not_paid, verified, grdCtoA), as.factor)

glimpse(loan_clean)
#> Rows: 1,556
#> Columns: 16
#> $ initial_list_status <fct> w, f, w, w, w, w, w, w, w, w, w, w, w, f, w, w, w,…
#> $ purpose             <fct> debt_consolidation, debt_consolidation, debt_conso…
#> $ int_rate            <dbl> 14.08, 9.44, 28.72, 13.59, 15.05, 10.91, 15.05, 10…
#> $ installment         <dbl> 675.99, 480.08, 1010.30, 484.19, 476.33, 130.79, 3…
#> $ annual_inc          <dbl> 156700, 50000, 25000, 175000, 109992, 49000, 65000…
#> $ dti                 <dbl> 19.11, 19.35, 65.58, 12.60, 10.00, 5.12, 22.38, 33…
#> $ verification_status <fct> Source Verified, Not Verified, Verified, Not Verif…
#> $ grade               <fct> C, B, F, C, C, B, C, B, D, D, F, C, C, E, B, C, C,…
#> $ revol_bal           <int> 21936, 5457, 23453, 31740, 2284, 2016, 14330, 2758…
#> $ inq_last_12m        <int> 3, 1, 0, 0, 3, 5, 0, 1, 8, 1, 0, 12, 4, 8, 1, 3, 0…
#> $ delinq_2yrs         <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
#> $ home_ownership      <fct> MORTGAGE, RENT, OWN, MORTGAGE, MORTGAGE, MORTGAGE,…
#> $ not_paid            <fct> 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1,…
#> $ log_inc             <dbl> 11.962088, 10.819778, 10.126631, 12.072541, 11.608…
#> $ verified            <fct> 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ grdCtoA             <fct> 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,…

PCA with FactoMineR

PCA dengan FactoMineR dapat melibatkan variable kategorik untuk visualisasi data. Mari kita pisahkan dulu variabel numerik dan kategorik:

# nama kolom numerik (quantitative)
quanti <- loan_clean %>% 
  select_if(is.numeric) %>% 
  colnames()

# indeks kolom numerik
quantivar <- which(colnames(loan_clean) %in% quanti)
# nama kolom kategorik (qualitative)
quali <- loan_clean %>% 
  select_if(is.factor) %>% 
  colnames()

# indeks kolom kategorik
qualivar <- which(colnames(loan_clean) %in% quali)

Melakukan PCA dengan package FactoMineR

Parameter fungsi PCA():

  • X: dataframe awal
  • scale.unit = T: untuk melakukan scaling
  • quali.sup: index kolom variable kategorikal (qualitative)
  • graph = F: untuk tidak menampilkan plot secara langsung
  • ncp: banyaknya PC yang digunakan, default = 5
# melakukan PCA dengan FactoMineR
loan_pca <- PCA(
  X = loan_clean, # dataframe
  scale.unit = TRUE, # melakukan scaling
  quali.sup = qualivar, # indeks kolom kategori
  graph = FALSE, # tidak langsung menampilkan plot
  ncp = 8 # jumlah kolom numerik pada data
)

# cumulative variance
# ekuivalen dengan summary(pca) saat menggunakan prcomp()
loan_pca$eig
#>        eigenvalue percentage of variance cumulative percentage of variance
#> comp 1  2.3492124              29.365155                          29.36515
#> comp 2  1.4050728              17.563410                          46.92857
#> comp 3  1.0985925              13.732406                          60.66097
#> comp 4  1.0131555              12.664444                          73.32542
#> comp 5  0.8294041              10.367552                          83.69297
#> comp 6  0.6467516               8.084395                          91.77736
#> comp 7  0.4922722               6.153403                          97.93076
#> comp 8  0.1655389               2.069236                         100.00000
data.frame(loan_pca$ind$coord)
# cek nilai di tiap PC
# ekuivalen dengan pca$x
head(loan_pca$ind$coord)
#>        Dim.1       Dim.2      Dim.3      Dim.4      Dim.5       Dim.6
#> 1  1.8726407 -0.02630256 -0.2620212 -0.3818516 -0.1440523  0.29379806
#> 2 -0.7953612 -0.59637757  0.1002547  0.9009347  0.4275856  0.77186601
#> 3 -1.3593014  4.73853507 -0.5401945  0.8218723 -0.5799966  0.74642058
#> 4  2.0152497 -0.64397562 -0.9941929  0.4797471 -0.3253111 -0.52434576
#> 5  0.6545361 -0.71599161  0.3267262 -0.5351297 -0.6294665 -0.01941924
#> 6 -0.9730541 -1.37420746  0.4126093 -1.2286800  0.1841724 -0.34258151
#>        Dim.7      Dim.8
#> 1  0.4142317  0.2487058
#> 2 -0.1167101 -0.1040927
#> 3  0.8142216 -0.2268024
#> 4  0.5452035  0.2257019
#> 5  0.3436425  0.2451064
#> 6 -0.5383739 -0.1755210

Visualisasi PCA

Biplot dapat divisualisasikan menggunakan plot.PCA()

Kelebihan plot.PCA() dari fungsi base biplot() adalah:

  • memisahkan grafik observasi dan variable agar bisa dilihat lebih mudah
  • bisa melibatkan variable kategorik untuk mewarnai plot sehingga plot lebih informatif

Individual Factor Map

Plot sebaran observasi untuk mengetahui index yang dianggap sebagai outlier

Parameter fungsi plot.PCA():

  • x: object PCA (FactoMineR)
  • choix = "ind": plot INDividual factor map
  • invisible = "quali": menghilangkan label kolom kategorik, agar tidak mengganggu visualisasi
  • select = "contrib n": hanya menampilkan indeks dari n outlier terluar
  • habillage: mewarnai titik berdasarkan index kolom (bisa kategorikal maupun numerik)
# individual factor map
plot.PCA(
  x = loan_pca,
  choix = "ind", # INDividual factor map
  invisible = "quali", # menghilangkan visualisasi kualitatif
  select = "contrib 10", # menampilkan 10 outlier terluar
  habillage = 13 # indeks kolom untuk pewarnaan, yaitu not_paid
)

Note: persentase yang tampil pada sumbu Dim 1 (29.37%) dan Dim 2 (17.56%) menunjukkan seberapa besar sumbu merangkum informasi. Secara kolektif, biplot di atas menjelaskan 29.37% + 17.56% = 46.93% informasi data asli.

Tindak lanjut terhadap outlier ini:

  • Identifikasi nilai apa yang menyebabkan observasi tersebut disebut outlier, lihat dari variable factor map (panah searah dengan observasi)
  • Apabila masuk ke ranah supervised learning, outlier ini bisa dicoba untuk dihandle (misal remove/transformasi nilai) sebagai salah satu strategi model improvement

Variables Factor Map

  • Mengetahui variable contribution ke tiap PC, serta besar informasi yang dirangkum dari tiap variable ke tiap PC (loading)
  • Mengetahui correlation antar variable awal

Parameter fungsi plot.PCA():

  • x: object PCA (FactoMineR)
  • choix = "var": plot VARiable factor map
# variables factor map
plot.PCA(
  x = loan_pca,
  choix = "var"
)

Insight:

  • PC1 paling banyak merangkum dua variable: annual_inc dan log_inc
  • PC2 paling banyak merangkum dua variable: int_rate dan dti
  • Pasangan variabel yang saling berkorelasi tinggi positif:
    • annual_inc dan log_inc
    • installment dan revol_bal
    • dti dan int_rate

[Additional] Dimension Description

Untuk lebih jelas dan objektif, kontribusi/korelasi tiap variable ke tiap PC dapat dilihat menggunakan dimdesc(). Semakin tinggi nilai korelasi, semakin banyak informasi yang dirangkum pada PC tersebut.

# dimension description
loan_dim <- dimdesc(loan_pca)

# variable yang berkontribusi untuk PC1
as.data.frame(loan_dim$Dim.1$quanti) %>% 
  arrange(desc(abs(correlation)))

Reduce Dimension

Menampilkan cumulative proportion dari PCA:

# summary PCA untuk FactoMineR
loan_pca$eig
#>        eigenvalue percentage of variance cumulative percentage of variance
#> comp 1  2.3492124              29.365155                          29.36515
#> comp 2  1.4050728              17.563410                          46.92857
#> comp 3  1.0985925              13.732406                          60.66097
#> comp 4  1.0131555              12.664444                          73.32542
#> comp 5  0.8294041              10.367552                          83.69297
#> comp 6  0.6467516               8.084395                          91.77736
#> comp 7  0.4922722               6.153403                          97.93076
#> comp 8  0.1655389               2.069236                         100.00000

Misalkan kita ingin mempertahankan informasi sebesar 80%, maka berapa PC yang kita gunakan?

Menggunakan PC1 sampai PC5

Subsetting base di R: df[baris,kolom]

# mengambil data hasil PCA sebanyak PC yang dibutuhkan
loan_keep <- as.data.frame(loan_pca$ind$coord[,1:5])
head(loan_keep)

loan_keep kemudian dapat dikembalikan ke data frame awal (menggantikan variable numerik awal) dan digunakan untuk supervised learning.

[Additional] Reconstruction

Fungsi reconst() memungkinkan kita untuk melakukan rekonstruksi data hasil PCA ke data semula. Umumnya rekonstruksi ini digunakan pada data gambar untuk kasus image compression.

Misalnya gambar asli adalah 40x40 pixel (1600 kolom), lalu direduksi dengan menggunakan 50 PC saja. Untuk menampilkan data berupa gambar, tetap diperlukan struktur data yang 1600 kolom yang mewakili tiap pixel. Kita bisa melakukan rekonstruksi gambar dari 50 PC ke 1600 kolom kembali. Namun, nilai hasil rekonstruksi tersebut menjadi sedikit berbeda dari nilai awal.

Catatan: rekonstruksi data jarang dilakukan pada data tabular, karena nilai hasil rekonstruksi tidak merepresentasikan nilai asli, sehingga kurang ada manfaatnya.

Berikut contoh rekonstruksi data dengan menggunakan data loan:

# reconstruct data menggunakan PC1 - PC5
loan_reconst <- reconst(loan_pca, 
                        ncp = 5) # jumlah PC yang ingin di reconstruct

data.frame(loan_reconst)

Bandingkan dengan nilai asli sebelum dilakukan PCA:

# data awal (coba bandingkan dengan hasil reconstruct di atas)
loan_clean %>% 
  select_if(is.numeric)

Kita juga dapat rekonstruksi data hingga 100% sama persis bila menggunakan seluruh PC, dengan menggunakan parameter ncp = 8 di fungsi reconst().