Data Preprocessing Menggunakan RStudio

Pendahuluan

Dalam analisis data, data yang tersedia tidak selalu berada dalam kondisi siap digunakan. Pada praktikum ini, saya melakukan data preprocessing menggunakan RStudio dengan bahasa pemrograman R pada data pelanggan dan data transaksi sebuah perusahaan e-commerce. Data pelanggan memuat informasi identitas, usia, kota, pendapatan, dan status pelanggan, sedangkan data transaksi memuat jumlah transaksi dan total pembelian.

Kedua sumber data tersebut masih memiliki beberapa permasalahan, seperti missing values, kategori yang tidak konsisten, duplikasi, nilai ekstrem, serta perbedaan nama identifier. Oleh karena itu, dilakukan serangkaian tahapan preprocessing yang meliputi pemeriksaan kualitas data, data cleaning, penanganan missing values dan outlier, transformasi data numerik, serta integrasi kedua sumber data hingga diperoleh satu dataset yang lebih bersih dan terstruktur.

Dalam prosesnya, setiap perubahan terhadap data dilakukan berdasarkan alasan dan aturan yang jelas, bukan hanya karena suatu nilai terlihat berbeda atau tidak biasa. Dengan demikian, proses preprocessing tidak hanya bertujuan menghasilkan data yang lebih rapi, tetapi juga menjaga agar informasi yang terkandung di dalam data tetap dapat dipertanggungjawabkan dan siap digunakan untuk analisis selanjutnya.


Memeriksa Lingkungan Kerja

Praktikum dilakukan menggunakan RStudio dengan bahasa pemrograman R sebagai perangkat pengolahan data. Pemeriksaan lingkungan kerja dilakukan untuk mengetahui lokasi working directory dan versi R yang digunakan.

getwd()
## [1] "C:/Sem 5"
R.version.string
## [1] "R version 4.5.1 (2025-06-13 ucrt)"


Membangun Dataset Praktikum

Dataset praktikum terdiri atas dua sumber data, yaitu data pelanggan dan data transaksi yang dibentuk menggunakan R sebagai data awal untuk proses preprocessing.

pelanggan_raw <- data.frame(
  customer_id = c(
    "C001", "C002", "C003", "C004", "C005", "C006",
    "C007", "C008", "C009", "C010", "C010", "C011",
    "C012", "C013", "C014", "C015", "C015", "C016",
    "C017", "C018", "C019", "C020", "C021", "C022",
    "C023", "C024", "C025", "C026", "C027", "C028"
  ),

  nama = c(
    "Ani", "Budi", "Citra", "Dodi", "Eka", "Fani",
    "Gilang", "Hana", "Indra", "Joko", "Joko", "Kiki",
    "Lina", "Maya", "Nanda", "Oki", "Oki", "Putri",
    "Raka", "Salsa", "Tio", "Uli", "Vina", "Wawan",
    "Yani", "Zaki", "Aldi", "Bella", "Caca", "Dian"
  ),

  usia = c(
    21, 25, 23, 150, 27, NA,
    31, 29, 22, 35, 35, 28,
    24, 26, 30, 32, 32, 27,
    34, 23, 29, NA, 31, 28,
    25, 33, 140, 22, 36, 27
  ),

  pendapatan = c(
    4500000, NA, 5200000, 4800000, 4900000, 5100000,
    500000000, 4700000, 4600000, 5300000, 5300000, NA,
    4800000, 5500000, NA, 5100000, 5100000, 4900000,
    6000000, 4700000, 5200000, NA, 5400000, 5000000,
    5600000, 4800000, 350000000, 4500000, NA, 5100000
  ),

  kota = c(
    "Pekanbaru", " PKU", "PEKANBARU", "Dumai", "pekanbaru",
    "DUMAI", "Pekanbaru ", "Siak", "PKU", "Dumai",
    "Dumai", NA,
    "Pekanbaru", "pekanbaru", " PKU", "DUMAI", "DUMAI",
    "Siak", "Pekanbaru ", "PEKANBARU", "Dumai", "PKU",
    "pekanbaru", "Pekanbaru", "DUMAI", "Siak", NA,
    "PEKANBARU", "Dumai", "Pekanbaru"
  ),

  status = c(
    "Aktif", "aktif", "ACTIVE", "A", "Tidak Aktif", "nonaktif",
    "Aktif", "AKTIF", "A", "Tidak aktif", "Tidak aktif", "Aktif",
    "aktif", "ACTIVE", "A", "Tidak Aktif", "Tidak Aktif", "AKTIF",
    "Aktif", "active", "A", "nonaktif", "Tidak aktif", "AKTIF",
    "aktif", "ACTIVE", "A", "Tidak Aktif", "nonaktif", "Aktif"
  ),

  stringsAsFactors = FALSE
)

pelanggan_raw
##    customer_id   nama usia pendapatan       kota      status
## 1         C001    Ani   21    4.5e+06  Pekanbaru       Aktif
## 2         C002   Budi   25         NA        PKU       aktif
## 3         C003  Citra   23    5.2e+06  PEKANBARU      ACTIVE
## 4         C004   Dodi  150    4.8e+06      Dumai           A
## 5         C005    Eka   27    4.9e+06  pekanbaru Tidak Aktif
## 6         C006   Fani   NA    5.1e+06      DUMAI    nonaktif
## 7         C007 Gilang   31    5.0e+08 Pekanbaru        Aktif
## 8         C008   Hana   29    4.7e+06       Siak       AKTIF
## 9         C009  Indra   22    4.6e+06        PKU           A
## 10        C010   Joko   35    5.3e+06      Dumai Tidak aktif
## 11        C010   Joko   35    5.3e+06      Dumai Tidak aktif
## 12        C011   Kiki   28         NA       <NA>       Aktif
## 13        C012   Lina   24    4.8e+06  Pekanbaru       aktif
## 14        C013   Maya   26    5.5e+06  pekanbaru      ACTIVE
## 15        C014  Nanda   30         NA        PKU           A
## 16        C015    Oki   32    5.1e+06      DUMAI Tidak Aktif
## 17        C015    Oki   32    5.1e+06      DUMAI Tidak Aktif
## 18        C016  Putri   27    4.9e+06       Siak       AKTIF
## 19        C017   Raka   34    6.0e+06 Pekanbaru        Aktif
## 20        C018  Salsa   23    4.7e+06  PEKANBARU      active
## 21        C019    Tio   29    5.2e+06      Dumai           A
## 22        C020    Uli   NA         NA        PKU    nonaktif
## 23        C021   Vina   31    5.4e+06  pekanbaru Tidak aktif
## 24        C022  Wawan   28    5.0e+06  Pekanbaru       AKTIF
## 25        C023   Yani   25    5.6e+06      DUMAI       aktif
## 26        C024   Zaki   33    4.8e+06       Siak      ACTIVE
## 27        C025   Aldi  140    3.5e+08       <NA>           A
## 28        C026  Bella   22    4.5e+06  PEKANBARU Tidak Aktif
## 29        C027   Caca   36         NA      Dumai    nonaktif
## 30        C028   Dian   27    5.1e+06  Pekanbaru       Aktif
transaksi_raw <- data.frame(
  cust_id = c(
    "C001", "C002", "C003", "C004", "C005", "C006",
    "C007", "C008", "C009", "C010", "C012",
    "C013", "C014", "C015", "C016", "C017", "C018",
    "C019", "C020", "C021", "C022", "C023", "C024",
    "C025", "C026", "C027", "C028", "C029", "C030", "C031"
  ),

  jumlah_transaksi = c(
    5, 3, 7, 2, 6, 4,
    20, 5, 3, 8, 1,
    4, 6, 5, 9, 3, 7,
    2, 8, 4, 6, 5, 10,
    3, 7, 25, 4, 6, 2, 5
  ),

  total_purchase = c(
    1500000, 900000, 2700000, 600000, 2100000, 1300000,
    25000000, 1700000, 800000, 3200000, 250000,
    1200000, 2400000, 1800000, 3500000, 900000, 2800000,
    700000, 3100000, 1500000, 2200000, 1900000, 4200000,
    30000000, 1300000, 2700000, 950000, 2100000, 600000, 1600000
  ),

  stringsAsFactors = FALSE
)

transaksi_raw
##    cust_id jumlah_transaksi total_purchase
## 1     C001                5        1.5e+06
## 2     C002                3        9.0e+05
## 3     C003                7        2.7e+06
## 4     C004                2        6.0e+05
## 5     C005                6        2.1e+06
## 6     C006                4        1.3e+06
## 7     C007               20        2.5e+07
## 8     C008                5        1.7e+06
## 9     C009                3        8.0e+05
## 10    C010                8        3.2e+06
## 11    C012                1        2.5e+05
## 12    C013                4        1.2e+06
## 13    C014                6        2.4e+06
## 14    C015                5        1.8e+06
## 15    C016                9        3.5e+06
## 16    C017                3        9.0e+05
## 17    C018                7        2.8e+06
## 18    C019                2        7.0e+05
## 19    C020                8        3.1e+06
## 20    C021                4        1.5e+06
## 21    C022                6        2.2e+06
## 22    C023                5        1.9e+06
## 23    C024               10        4.2e+06
## 24    C025                3        3.0e+07
## 25    C026                7        1.3e+06
## 26    C027               25        2.7e+06
## 27    C028                4        9.5e+05
## 28    C029                6        2.1e+06
## 29    C030                2        6.0e+05
## 30    C031                5        1.6e+06


Bagian I — Konsep Data Preprocessing

Memahami Struktur Data

Sebelum melakukan perubahan, periksa struktur, dimensi, tipe atribut, dan beberapa observasi awal.

dim(pelanggan_raw)
## [1] 30  6

Interpretasi: Dataset pelanggan terdiri atas 30 observasi dan 6 atribut, sehingga terdapat enam variabel yang akan diperiksa dan diproses pada tahap selanjutnya.

dim(transaksi_raw)
## [1] 30  3

Interpretasi: Dataset transaksi terdiri atas 30 observasi dan 3 atribut.

names(pelanggan_raw)
## [1] "customer_id" "nama"        "usia"        "pendapatan"  "kota"       
## [6] "status"

Interpretasi: Dataset pelanggan memiliki atribut customer_id, nama, usia, pendapatan, kota, dan status yang mencakup informasi identitas maupun karakteristik pelanggan.

names(transaksi_raw)
## [1] "cust_id"          "jumlah_transaksi" "total_purchase"

Interpretasi: Dataset transaksi memiliki atribut cust_id, jumlah_transaksi, dan total_purchase.

str(pelanggan_raw)
## 'data.frame':    30 obs. of  6 variables:
##  $ customer_id: chr  "C001" "C002" "C003" "C004" ...
##  $ nama       : chr  "Ani" "Budi" "Citra" "Dodi" ...
##  $ usia       : num  21 25 23 150 27 NA 31 29 22 35 ...
##  $ pendapatan : num  4.5e+06 NA 5.2e+06 4.8e+06 4.9e+06 5.1e+06 5.0e+08 4.7e+06 4.6e+06 5.3e+06 ...
##  $ kota       : chr  "Pekanbaru" " PKU" "PEKANBARU" "Dumai" ...
##  $ status     : chr  "Aktif" "aktif" "ACTIVE" "A" ...

Interpretasi: Struktur data menunjukkan bahwa customer_id, nama, kota, dan status bertipe karakter, sedangkan usia dan pendapatan bertipe numerik sehingga dapat digunakan untuk pemeriksaan statistik.

str(transaksi_raw)
## 'data.frame':    30 obs. of  3 variables:
##  $ cust_id         : chr  "C001" "C002" "C003" "C004" ...
##  $ jumlah_transaksi: num  5 3 7 2 6 4 20 5 3 8 ...
##  $ total_purchase  : num  1.5e+06 9.0e+05 2.7e+06 6.0e+05 2.1e+06 1.3e+06 2.5e+07 1.7e+06 8.0e+05 3.2e+06 ...

Interpretasi: Struktur data menunjukkan bahwa customer_id bertipe karakter, sedangkan jumlah_transaksi dan total_purchase bertipe numerik sehingga dapat digunakan untuk pemeriksaan statistik.

head(pelanggan_raw)
##   customer_id  nama usia pendapatan      kota      status
## 1        C001   Ani   21    4500000 Pekanbaru       Aktif
## 2        C002  Budi   25         NA       PKU       aktif
## 3        C003 Citra   23    5200000 PEKANBARU      ACTIVE
## 4        C004  Dodi  150    4800000     Dumai           A
## 5        C005   Eka   27    4900000 pekanbaru Tidak Aktif
## 6        C006  Fani   NA    5100000     DUMAI    nonaktif

Interpretasi: Beberapa observasi awal memperlihatkan bahwa data memiliki variasi penulisan pada atribut kategori serta terdapat nilai yang perlu diperiksa lebih lanjut.

head(transaksi_raw)
##   cust_id jumlah_transaksi total_purchase
## 1    C001                5        1500000
## 2    C002                3         900000
## 3    C003                7        2700000
## 4    C004                2         600000
## 5    C005                6        2100000
## 6    C006                4        1300000
summary(pelanggan_raw)
##  customer_id            nama                usia          pendapatan       
##  Length:30          Length:30          Min.   : 21.00   Min.   :  4500000  
##  Class :character   Class :character   1st Qu.: 25.00   1st Qu.:  4800000  
##  Mode  :character   Mode  :character   Median : 28.50   Median :  5100000  
##                                        Mean   : 36.61   Mean   : 38644000  
##                                        3rd Qu.: 32.25   3rd Qu.:  5300000  
##                                        Max.   :150.00   Max.   :500000000  
##                                        NA's   :2        NA's   :5          
##      kota              status         
##  Length:30          Length:30         
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Interpretasi: Ringkasan data menunjukkan adanya nilai hilang pada usia dan pendapatan, serta rentang nilai yang cukup ekstrem sehingga kedua atribut tersebut perlu diperiksa lebih lanjut.

summary(transaksi_raw)
##    cust_id          jumlah_transaksi total_purchase    
##  Length:30          Min.   : 1.000   Min.   :  250000  
##  Class :character   1st Qu.: 3.250   1st Qu.: 1012500  
##  Mode  :character   Median : 5.000   Median : 1750000  
##                     Mean   : 6.167   Mean   : 3516667  
##                     3rd Qu.: 7.000   3rd Qu.: 2700000  
##                     Max.   :25.000   Max.   :30000000

Interpretasi: Ringkasan menunjukkan bahwa kedua atribut numerik memiliki rentang nilai yang cukup lebar, sehingga perlu diperiksa lebih lanjut untuk memastikan adanya atau tidaknya nilai ekstrem.


Mengukur Kualitas Awal

Setelah mengetahui struktur awal dataset, tahap berikutnya adalah memeriksa kualitas data untuk mengidentifikasi permasalahan yang perlu ditangani sebelum proses preprocessing dilakukan. Pemeriksaan ini berfokus pada kondisi yang dapat diketahui langsung dari dataset, seperti kelengkapan data, konsistensi nilai, duplikasi, serta kesesuaian nilai dengan aturan yang berlaku.

# Banyak missing value per atribut
colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           2           5           2           0

Interpretasi: Hasil pemeriksaan menunjukkan bahwa terdapat missing value pada atribut usia sebanyak 2 nilai, pendapatan sebanyak 5 nilai, dan kota sebanyak 2 nilai. Atribut lainnya tidak memiliki nilai yang hilang.

# Persentase missing value per atribut
round(colMeans(is.na(pelanggan_raw)) * 100, 2)
## customer_id        nama        usia  pendapatan        kota      status 
##        0.00        0.00        6.67       16.67        6.67        0.00

Interpretasi: Secara persentase, missing value terdapat pada usia sebesar 6,67%, pendapatan sebesar 16,67%, dan kota sebesar 6,67%, dengan persentase tertinggi terdapat pada atribut pendapatan.

# Banyak missing value per atribut
colSums(is.na(transaksi_raw))
##          cust_id jumlah_transaksi   total_purchase 
##                0                0                0

Interpretasi: Tidak ditemukan missing value pada seluruh atribut data transaksi, sehingga ketiga atribut memiliki kelengkapan data sebesar 100%.

# Banyak baris duplikat penuh
sum(duplicated(pelanggan_raw))
## [1] 2

Interpretasi: Ditemukan 2 baris yang teridentifikasi sebagai duplikat penuh pada data pelanggan. Kondisi ini perlu diperiksa lebih lanjut untuk menentukan apakah baris tersebut merupakan data yang benar-benar berulang.

# Banyak customer_id yang berulang
sum(duplicated(pelanggan_raw$customer_id))
## [1] 2

Interpretasi: Ditemukan 2 customer_id yang berulang, sehingga keunikan identifier pelanggan belum sepenuhnya terpenuhi. Kondisi ini perlu ditangani sebelum proses integrasi dengan data transaksi.

# Banyak baris duplikat penuh
sum(duplicated(transaksi_raw))
## [1] 0

Interpretasi: Tidak ditemukan baris duplikat pada data transaksi, sehingga setiap observasi tercatat secara unik berdasarkan seluruh atribut yang diperiksa.

# Kategori yang tercatat pada atribut kota dan status
sort(unique(pelanggan_raw$kota))
## [1] " PKU"       "Dumai"      "DUMAI"      "pekanbaru"  "Pekanbaru" 
## [6] "PEKANBARU"  "Pekanbaru " "PKU"        "Siak"

Interpretasi: Atribut kota memiliki beberapa variasi penulisan untuk kategori yang sama, seperti PKU dan Pekanbaru dengan perbedaan kapitalisasi maupun spasi. Hal ini menunjukkan adanya ketidakkonsistenan kategori yang perlu distandardisasi.

sort(unique(pelanggan_raw$status))
## [1] "A"           "active"      "ACTIVE"      "aktif"       "Aktif"      
## [6] "AKTIF"       "nonaktif"    "Tidak aktif" "Tidak Aktif"

Interpretasi: Atribut status memiliki beberapa bentuk penulisan yang merujuk pada kategori yang sama, seperti Aktif, aktif, ACTIVE, dan A. Variasi tersebut menunjukkan adanya ketidakkonsistenan kategori yang perlu diperbaiki.

# Rentang atribut numerik
range(pelanggan_raw$usia, na.rm = TRUE)
## [1]  21 150

Interpretasi: Atribut usia memiliki rentang nilai 21–150 tahun. Nilai maksimum 150 tahun terlihat tidak wajar sehingga perlu dievaluasi lebih lanjut berdasarkan aturan domain dan metode deteksi yang digunakan.

range(pelanggan_raw$pendapatan, na.rm = TRUE)
## [1] 4.5e+06 5.0e+08

Interpretasi: Atribut pendapatan memiliki rentang Rp4,5 juta hingga Rp500 juta, dengan nilai maksimum yang jauh lebih tinggi dibandingkan sebagian besar data. Nilai tersebut menjadi kandidat outlier yang perlu diperiksa lebih lanjut.

range(transaksi_raw$jumlah_transaksi, na.rm = TRUE)
## [1]  1 25

Interpretasi: Atribut jumlah_transaksi memiliki rentang 1–25 transaksi. Nilai maksimum 25 relatif lebih tinggi dibandingkan sebagian besar observasi sehingga dapat diperiksa lebih lanjut sebagai kandidat nilai ekstrem.

range(transaksi_raw$total_purchase, na.rm = TRUE)
## [1] 2.5e+05 3.0e+07

Interpretasi: Atribut total_purchase memiliki rentang Rp250 ribu hingga Rp30 juta. Nilai maksimum Rp30 juta terlihat cukup jauh dari sebagian besar observasi sehingga perlu dievaluasi lebih lanjut sebagai kandidat outlier.


Membuat Fungsi Ringkasan Kualitas

audit_data <- function(data) {
  data.frame(
    atribut = names(data),
    tipe = sapply(data, function(x) class(x)[1]),
    jumlah_missing = sapply(data, function(x) sum(is.na(x))),
    persen_missing = round(sapply(data, function(x) mean(is.na(x)) * 100), 2),
    jumlah_unik = sapply(data, function(x) length(unique(x))),
    row.names = NULL
  )
}

audit_awal <- audit_data(pelanggan_raw)
audit_awal
##       atribut      tipe jumlah_missing persen_missing jumlah_unik
## 1 customer_id character              0           0.00          28
## 2        nama character              0           0.00          28
## 3        usia   numeric              2           6.67          19
## 4  pendapatan   numeric              5          16.67          16
## 5        kota character              2           6.67          10
## 6      status character              0           0.00           9
audit_data <- function(data) {
  data.frame(
    atribut = names(data),
    tipe = sapply(data, function(x) class(x)[1]),
    jumlah_missing = sapply(data, function(x) sum(is.na(x))),
    persen_missing = round(sapply(data, function(x) mean(is.na(x)) * 100), 2),
    jumlah_unik = sapply(data, function(x) length(unique(x))),
    row.names = NULL
  )
}

audit_awal <- audit_data(transaksi_raw)
audit_awal
##            atribut      tipe jumlah_missing persen_missing jumlah_unik
## 1          cust_id character              0              0          30
## 2 jumlah_transaksi   numeric              0              0          12
## 3   total_purchase   numeric              0              0          24

Interpretasi: hasil audit bukan keputusan cleaning. Hasil audit digunakan untuk mengidentifikasi atribut yang memerlukan pemeriksaan lebih lanjut sebelum menentukan tindakan preprocessing.


Bagian II — Implementasi Data Cleaning

Membuat Salinan Kerja

Data mentah dipertahankan sebagai data awal, sedangkan proses pembersihan dilakukan pada salinan kerja agar perubahan yang dilakukan tetap dapat ditelusuri.

pelanggan <- pelanggan_raw
transaksi <- transaksi_raw


Membersihkan Spasi dan Kapitalisasi

Berdasarkan hasil pemeriksaan sebelumnya, atribut kota dan status memiliki variasi spasi dan kapitalisasi. Oleh karena itu, dilakukan pembersihan spasi terlebih dahulu dan penyeragaman huruf untuk memudahkan proses standardisasi kategori.

# Menghapus spasi di awal dan akhir
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)

# Menyeragamkan huruf menjadi kecil
pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)
# Memeriksa hasil
sort(unique(pelanggan$kota))
## [1] "dumai"     "pekanbaru" "pku"       "siak"
# Memeriksa hasil 
sort(unique(pelanggan$status))
## [1] "a"           "active"      "aktif"       "nonaktif"    "tidak aktif"

Interpretasi: Setelah proses tersebut, kategori akan menjadi lebih mudah dibandingkan karena perbedaan seperti “Pekanbaru”, “PEKANBARU”, dan “pekanbaru” sudah diseragamkan menjadi “pekanbaru”.


Menyeragamkan Kategori

Berdasarkan hasil pemeriksaan, terdapat beberapa kategori yang sebenarnya merujuk pada wilayah atau status yang sama. Oleh karena itu, kategori tersebut distandardisasi berdasarkan aturan yang telah ditentukan.

Standardisasi Kota

pelanggan$kota[pelanggan$kota %in% c("pku", "pekanbaru")] <- "Pekanbaru"
pelanggan$kota[pelanggan$kota == "dumai"] <- "Dumai"
pelanggan$kota[pelanggan$kota == "siak"] <- "Siak"

sort(unique(pelanggan$kota))
## [1] "Dumai"     "Pekanbaru" "Siak"

Standardisasi Status

pelanggan$status[pelanggan$status %in% c("aktif", "active", "a")] <- "Aktif"
pelanggan$status[pelanggan$status %in% c("tidak aktif", "nonaktif")] <- "Tidak Aktif"

sort(unique(pelanggan$status))
## [1] "Aktif"       "Tidak Aktif"

Catatan: NA pada kota tidak ikut diubah karena merupakan missing value dan akan ditangani pada tahap tersendiri.


Mendeteksi dan Menangani Duplikasi

Pada pemeriksaan awal ditemukan 2 baris duplikat penuh dan terdapat 2 customer_id yang berulang, yaitu C010 dan C015.

pelanggan[
  duplicated(pelanggan$customer_id) |
  duplicated(pelanggan$customer_id, fromLast = TRUE),
]
##    customer_id nama usia pendapatan  kota      status
## 10        C010 Joko   35    5300000 Dumai Tidak Aktif
## 11        C010 Joko   35    5300000 Dumai Tidak Aktif
## 16        C015  Oki   32    5100000 Dumai Tidak Aktif
## 17        C015  Oki   32    5100000 Dumai Tidak Aktif

Karena setiap customer_id dalam data ini merepresentasikan satu pelanggan, kemunculan identifier yang berulang diperlakukan sebagai duplikasi. Satu observasi dipertahankan dan kemunculan berikutnya dihapus.

pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]

# Mengatur kembali nomor baris
rownames(pelanggan) <- NULL

dim(pelanggan)
## [1] 28  6


Memeriksa Aturan Domain

Pada tahap pemeriksaan sebelumnya ditemukan nilai usia 150 dan 140 tahun. Karena atribut usia memiliki aturan domain, dilakukan pemeriksaan berdasarkan rentang usia yang dianggap wajar, misalnya digunakan aturan 15–100 tahun:

pelanggan[
  !is.na(pelanggan$usia) &
  (pelanggan$usia < 15 | pelanggan$usia > 100),
]
##    customer_id nama usia pendapatan  kota status
## 4         C004 Dodi  150    4.8e+06 Dumai  Aktif
## 25        C025 Aldi  140    3.5e+08  <NA>  Aktif
pelanggan$usia[
  !is.na(pelanggan$usia) &
  (pelanggan$usia < 15 | pelanggan$usia > 100)
] <- NA

Interpretasi: Nilai 140 dan 150 melanggar aturan domain, tetapi nilai sebenarnya tidak diketahui. Oleh karena itu, keduanya tidak digantikan dengan nilai estimasi tanpa dasar yang memadai.

Nilai yang melanggar aturan domain tidak langsung diganti dengan nilai tertentu karena tidak tersedia informasi yang dapat memastikan nilai sebenarnya. Oleh karena itu, nilai tersebut diubah menjadi NA dan selanjutnya ditangani bersama missing values.


Membuat Log Perubahan

log_perubahan <- data.frame(
  tahap = c(
    "Standardisasi",
    "Standardisasi",
    "Deduplikasi",
    "Deduplikasi",
    "Penanganan domain"
  ),
  
  atribut = c(
    "kota",
    "status",
    "customer_id",
    "customer_id",
    "usia"
  ),
  
  tindakan = c(
    "Menyeragamkan variasi PKU dan Pekanbaru menjadi Pekanbaru",
    "Menyeragamkan variasi Aktif dan Tidak Aktif menjadi dua kategori standar",
    "Menghapus kemunculan kedua C010",
    "Menghapus kemunculan kedua C015",
    "Mengubah usia 140 dan 150 menjadi NA karena melanggar aturan domain dan nilai sebenarnya tidak diketahui"
  ),
  
  stringsAsFactors = FALSE
)

log_perubahan
##               tahap     atribut
## 1     Standardisasi        kota
## 2     Standardisasi      status
## 3       Deduplikasi customer_id
## 4       Deduplikasi customer_id
## 5 Penanganan domain        usia
##                                                                                                   tindakan
## 1                                                Menyeragamkan variasi PKU dan Pekanbaru menjadi Pekanbaru
## 2                                 Menyeragamkan variasi Aktif dan Tidak Aktif menjadi dua kategori standar
## 3                                                                          Menghapus kemunculan kedua C010
## 4                                                                          Menghapus kemunculan kedua C015
## 5 Mengubah usia 140 dan 150 menjadi NA karena melanggar aturan domain dan nilai sebenarnya tidak diketahui


Bagian III — Penanganan Missing Values

Mengidentifikasi Lokasi Nilai Hilang

Setelah proses data cleaning, dilakukan pemeriksaan kembali untuk mengetahui jumlah dan lokasi missing values yang masih terdapat pada data.

colSums(is.na(pelanggan))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           4           5           2           0

Untuk melihat observasi yang masih memiliki setidaknya satu nilai hilang:

# Menampilkan baris yang memiliki sedikitnya satu missing value
pelanggan[!complete.cases(pelanggan), ]
##    customer_id  nama usia pendapatan      kota      status
## 2         C002  Budi   25         NA Pekanbaru       Aktif
## 4         C004  Dodi   NA    4.8e+06     Dumai       Aktif
## 6         C006  Fani   NA    5.1e+06     Dumai Tidak Aktif
## 11        C011  Kiki   28         NA      <NA>       Aktif
## 14        C014 Nanda   30         NA Pekanbaru       Aktif
## 20        C020   Uli   NA         NA Pekanbaru Tidak Aktif
## 25        C025  Aldi   NA    3.5e+08      <NA>       Aktif
## 27        C027  Caca   36         NA     Dumai Tidak Aktif

Interpretasi: Berdasarkan hasil pemeriksaan, missing values terdapat pada atribut usia, pendapatan, dan kota. Nilai hilang pada usia berasal dari nilai yang sebelumnya melanggar aturan domain, sedangkan nilai hilang pada pendapatan dan kota memang terdapat pada data awal.


Menilai Proporsi Missing Values

Sebelum menentukan strategi penanganan, persentase missing values perlu diperiksa untuk mengetahui seberapa besar bagian data yang terdampak.

round(colMeans(is.na(pelanggan)) * 100, 2)
## customer_id        nama        usia  pendapatan        kota      status 
##        0.00        0.00       14.29       17.86        7.14        0.00

Hasil ini digunakan sebagai pertimbangan dalam menentukan apakah penghapusan baris masih layak dilakukan atau diperlukan metode imputasi.


Strategi 1 — Menghapus Baris yang Memiliki Missing Values

Penghapusan baris digunakan sebagai salah satu strategi pembanding. Data asli tidak langsung diganti, sehingga dampak metode ini dapat dievaluasi terlebih dahulu.

pelanggan_complete <- pelanggan[complete.cases(pelanggan), ]

Jumlah baris sebelum dan sesudah penghapusan:

nrow(pelanggan)
## [1] 28
nrow(pelanggan_complete)
## [1] 20

Persentase baris yang akan hilang:

round(
  (1 - nrow(pelanggan_complete) / nrow(pelanggan)) * 100,
  2
)
## [1] 28.57

Interpretasi: Penghapusan baris menyebabkan sebagian observasi tidak dapat digunakan karena memiliki setidaknya satu missing value. Jika proporsi baris yang hilang cukup besar, metode ini dapat mengurangi jumlah data yang tersedia untuk analisis.


Strategi 2 — Imputasi Numerik dengan Median

Untuk atribut numerik usia dan pendapatan, dilakukan perbandingan antara nilai mean dan median sebelum menentukan metode imputasi.

Imputasi Pendapatan

mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
mean_pendapatan
## [1] 41552174
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan
## [1] 5100000

Berdasarkan pemeriksaan sebelumnya, pendapatan memiliki nilai ekstrem hingga Rp500.000.000. Kondisi tersebut dapat menyebabkan mean menjadi lebih besar dan kurang mewakili sebagian besar data. Oleh karena itu, median dipilih sebagai nilai imputasi karena lebih tahan terhadap pengaruh nilai ekstrem.

pelanggan$pendapatan_imputasi <- pelanggan$pendapatan

pelanggan$pendapatan_imputasi[
  is.na(pelanggan$pendapatan_imputasi)
] <- median_pendapatan

Imputasi Usia

Untuk usia, nilai 140 dan 150 sebelumnya telah ditangani sebagai missing values karena melanggar aturan domain. Selain itu, terdapat missing values pada data awal.

Median usia dihitung sebagai berikut:

median_usia <- median(pelanggan$usia, na.rm = TRUE)

median_usia
## [1] 27.5

Kemudian dilakukan imputasi:

pelanggan$usia_imputasi <- pelanggan$usia

pelanggan$usia_imputasi[
  is.na(pelanggan$usia_imputasi)
] <- median_usia
pelanggan[
  is.na(pelanggan$usia) |
  is.na(pelanggan$pendapatan),
  c(
    "customer_id",
    "usia",
    "usia_imputasi",
    "pendapatan",
    "pendapatan_imputasi"
  )
]
##    customer_id usia usia_imputasi pendapatan pendapatan_imputasi
## 2         C002   25          25.0         NA             5.1e+06
## 4         C004   NA          27.5    4.8e+06             4.8e+06
## 6         C006   NA          27.5    5.1e+06             5.1e+06
## 11        C011   28          28.0         NA             5.1e+06
## 14        C014   30          30.0         NA             5.1e+06
## 20        C020   NA          27.5         NA             5.1e+06
## 25        C025   NA          27.5    3.5e+08             3.5e+08
## 27        C027   36          36.0         NA             5.1e+06

Interpretasi: Missing values pada atribut numerik diisi menggunakan median agar jumlah observasi tetap dipertahankan. Median dipilih karena lebih stabil terhadap nilai ekstrem, terutama pada atribut pendapatan.


Strategi 3 — Imputasi Nilai Kategorik

Pada atribut kota, nilai yang hilang tidak langsung diisi menggunakan modus karena pengisian tersebut dapat membuat nilai yang tidak diketahui seolah-olah benar-benar berasal dari kategori tertentu.

Oleh karena itu, digunakan kategori eksplisit “Tidak diketahui”.

pelanggan$kota_imputasi <- pelanggan$kota

pelanggan$kota_imputasi[
  is.na(pelanggan$kota_imputasi)
] <- "Tidak diketahui"
table(pelanggan$kota_imputasi, useNA = "ifany")
## 
##           Dumai       Pekanbaru            Siak Tidak diketahui 
##               7              16               3               2

Interpretasi: Nilai kota yang hilang diberi kategori “Tidak diketahui” agar informasi mengenai ketidakpastian tetap dipertahankan dan tidak dianggap sebagai kategori kota tertentu.


Menambahkan Indikator Missing

Selain melakukan imputasi, informasi bahwa suatu nilai sebelumnya hilang dapat dipertahankan melalui variabel indikator.

Untuk pendapatan:

pelanggan$pendapatan_missing <- as.integer(
  is.na(pelanggan$pendapatan)
)

table(pelanggan$pendapatan_missing)
## 
##  0  1 
## 23  5

Untuk usia:

pelanggan$usia_missing <- as.integer(
  is.na(pelanggan$usia)
)

table(pelanggan$usia_missing)
## 
##  0  1 
## 24  4

Interpretasi: Variabel indikator menunjukkan observasi yang sebelumnya memiliki nilai hilang, sehingga informasi mengenai keberadaan missing value tetap dapat dipertahankan setelah proses imputasi.


Membandingkan Kondisi Sebelum dan Sesudah Imputasi

Karena pendapatan memiliki nilai ekstrem, distribusi sebelum dan sesudah imputasi perlu dibandingkan untuk melihat perubahan yang terjadi.

par(mfrow = c(1, 2))

hist(
  pelanggan$pendapatan,
  main = "Sebelum Imputasi",
  xlab = "Pendapatan",
  breaks = 8
)

hist(
  pelanggan$pendapatan_imputasi,
  main = "Sesudah Imputasi Median",
  xlab = "Pendapatan",
  breaks = 8
)

Kemudian kembalikan tampilan grafik:

par(mfrow = c(1, 1))

Imputasi median mengisi nilai yang hilang tanpa menambahkan nilai ekstrem baru ke dalam data. Namun, karena nilai yang hilang digantikan dengan satu nilai yang sama, variasi pada atribut tersebut dapat sedikit berkurang.


Bagian IV — Penanganan Outlier

Visualisasi dengan Boxplot

Setelah missing values ditangani, pemeriksaan dilanjutkan terhadap nilai ekstrem pada atribut numerik. Boxplot digunakan untuk memberikan gambaran awal mengenai penyebaran data dan mengidentifikasi observasi yang berada jauh dari sebagian besar nilai lainnya.

boxplot(
  pelanggan$pendapatan_imputasi,
  horizontal = TRUE,
  main = "Boxplot Pendapatan",
  xlab = "Pendapatan"
)

Interpretasi: Boxplot menunjukkan adanya beberapa nilai pendapatan yang berada jauh di atas sebagian besar observasi. Kondisi tersebut menjadi kandidat outlier yang perlu diperiksa lebih lanjut menggunakan batas IQR.


Menghitung Batas IQR

Untuk menentukan kandidat outlier, digunakan metode Interquartile Range (IQR). Nilai yang berada di bawah Q1 - 1.5 × IQR atau di atas Q3 + 1.5 × IQR ditandai sebagai kandidat outlier.

q1_pendapatan <- quantile(
  pelanggan$pendapatan_imputasi,
  0.25,
  na.rm = TRUE
)

q3_pendapatan <- quantile(
  pelanggan$pendapatan_imputasi,
  0.75,
  na.rm = TRUE
)

iqr_pendapatan <- IQR(
  pelanggan$pendapatan_imputasi,
  na.rm = TRUE
)

batas_bawah_pendapatan <- q1_pendapatan -
  1.5 * iqr_pendapatan

batas_atas_pendapatan <- q3_pendapatan +
  1.5 * iqr_pendapatan

c(
  Q1 = q1_pendapatan,
  Q3 = q3_pendapatan,
  IQR = iqr_pendapatan,
  batas_bawah = batas_bawah_pendapatan,
  batas_atas = batas_atas_pendapatan
)
##          Q1.25%          Q3.75%             IQR batas_bawah.25%  batas_atas.75% 
##         4800000         5225000          425000         4162500         5862500


Menandai Kandidat Outlier

Selanjutnya, setiap observasi dibandingkan dengan batas IQR yang telah diperoleh.

pelanggan$outlier_pendapatan <-
  pelanggan$pendapatan_imputasi < batas_bawah_pendapatan |
  pelanggan$pendapatan_imputasi > batas_atas_pendapatan

Untuk melihat observasi yang teridentifikasi sebagai kandidat outlier:

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "nama",
    "pendapatan_imputasi"
  )
]
##    customer_id   nama pendapatan_imputasi
## 7         C007 Gilang             5.0e+08
## 17        C017   Raka             6.0e+06
## 25        C025   Aldi             3.5e+08

Interpretasi: Hasil metode IQR menunjukkan observasi dengan pendapatan yang berada di atas batas atas sebagai kandidat outlier. Nilai tersebut kemudian perlu dievaluasi berdasarkan konteks data sebelum ditentukan apakah akan dipertahankan atau ditangani lebih lanjut.


Mengevaluasi Kandidat Outlier

Nilai ekstrem tidak langsung dianggap sebagai kesalahan. Pada data kamu, pendapatan yang sangat tinggi perlu diperiksa terlebih dahulu untuk menentukan apakah nilai tersebut merupakan kesalahan pencatatan atau memang mencerminkan kondisi pelanggan yang sebenarnya.

Beberapa kemungkinan yang dapat dipertimbangkan adalah:

Kesalahan input, jika nilai tidak sesuai dengan sumber data asli dan dapat diverifikasi. Observasi valid tetapi ekstrem, jika nilai tersebut memang mencerminkan pelanggan dengan pendapatan tinggi. Perbedaan karakteristik pelanggan, jika pelanggan dengan pendapatan sangat tinggi berasal dari kelompok atau segmen yang berbeda.

Karena tidak terdapat informasi tambahan yang dapat memastikan bahwa nilai ekstrem tersebut merupakan kesalahan input, nilai pendapatan ekstrem tidak langsung dihapus.


Membuat Versi Winsorized Sebagai Perbandingan

Untuk melihat bagaimana pembatasan nilai ekstrem dapat memengaruhi data, dibuat versi winsorized dari pendapatan. Nilai yang berada di luar batas IQR dibatasi pada nilai batas tersebut.

pelanggan$pendapatan_winsor <- pmin(
  pmax(
    pelanggan$pendapatan_imputasi,
    batas_bawah_pendapatan
  ),
  batas_atas_pendapatan
)

Kemudian dibandingkan nilai sebelum dan sesudah winsorization:

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "pendapatan_imputasi",
    "pendapatan_winsor"
  )
]
##    customer_id pendapatan_imputasi pendapatan_winsor
## 7         C007             5.0e+08           5862500
## 17        C017             6.0e+06           5862500
## 25        C025             3.5e+08           5862500

Interpretasi: Winsorization membatasi nilai pendapatan yang melewati batas IQR tanpa menghapus observasinya. Hasil ini dapat digunakan sebagai pembanding untuk melihat dampak penanganan nilai ekstrem terhadap data.

Catatan: Winsorization hanya digunakan sebagai alternatif pembanding dan tidak otomatis menjadi data akhir. Penggunaannya perlu disesuaikan dengan tujuan analisis dan karakteristik data.


Pemeriksaan Outlier pada Jumlah Transaksi

boxplot(
  transaksi$jumlah_transaksi,
  horizontal = TRUE,
  main = "Boxplot Jumlah Transaksi",
  xlab = "Jumlah Transaksi"
)

q1_transaksi <- quantile(
  transaksi$jumlah_transaksi,
  0.25
)

q3_transaksi <- quantile(
  transaksi$jumlah_transaksi,
  0.75
)

iqr_transaksi <- IQR(
  transaksi$jumlah_transaksi
)

batas_bawah_transaksi <- q1_transaksi -
  1.5 * iqr_transaksi

batas_atas_transaksi <- q3_transaksi +
  1.5 * iqr_transaksi

c(
  Q1 = q1_transaksi,
  Q3 = q3_transaksi,
  IQR = iqr_transaksi,
  batas_bawah = batas_bawah_transaksi,
  batas_atas = batas_atas_transaksi
)
##          Q1.25%          Q3.75%             IQR batas_bawah.25%  batas_atas.75% 
##           3.250           7.000           3.750          -2.375          12.625
transaksi$outlier_jumlah_transaksi <-
  transaksi$jumlah_transaksi < batas_bawah_transaksi |
  transaksi$jumlah_transaksi > batas_atas_transaksi

transaksi[
  transaksi$outlier_jumlah_transaksi,
  c(
    "cust_id",
    "jumlah_transaksi"
  )
]
##    cust_id jumlah_transaksi
## 7     C007               20
## 26    C027               25

Interpretasi: Kandidat outlier pada jumlah_transaksi dievaluasi terlebih dahulu. Karena tidak terdapat bukti bahwa nilai tersebut merupakan kesalahan pencatatan, nilai dipertahankan dalam dataset asli.

Pemeriksaan Outlier pada total_purchase

boxplot(
  transaksi$total_purchase,
  horizontal = TRUE,
  main = "Boxplot Total Pembelian",
  xlab = "Total Pembelian"
)

q1_purchase <- quantile(
  transaksi$total_purchase,
  0.25
)

q3_purchase <- quantile(
  transaksi$total_purchase,
  0.75
)

iqr_purchase <- IQR(
  transaksi$total_purchase
)

batas_bawah_purchase <- q1_purchase -
  1.5 * iqr_purchase

batas_atas_purchase <- q3_purchase +
  1.5 * iqr_purchase

c(
  Q1 = q1_purchase,
  Q3 = q3_purchase,
  IQR = iqr_purchase,
  batas_bawah = batas_bawah_purchase,
  batas_atas = batas_atas_purchase
)
##          Q1.25%          Q3.75%             IQR batas_bawah.25%  batas_atas.75% 
##         1012500         2700000         1687500        -1518750         5231250
transaksi$outlier_total_purchase <-
  transaksi$total_purchase < batas_bawah_purchase |
  transaksi$total_purchase > batas_atas_purchase

transaksi[
  transaksi$outlier_total_purchase,
  c(
    "cust_id",
    "total_purchase"
  )
]
##    cust_id total_purchase
## 7     C007        2.5e+07
## 24    C025        3.0e+07

Interpretasi: Nilai ekstrem pada total_purchase tidak langsung dihapus karena belum terdapat informasi yang menunjukkan bahwa nilai tersebut merupakan kesalahan. Nilai dipertahankan sebagai bagian dari data transaksi yang sebenarnya.


Bagian V — Transformasi Data

Setelah proses data cleaning dan penanganan missing value dilakukan, atribut numerik perlu ditransformasikan agar memiliki skala yang lebih sesuai untuk analisis. Pada data ini, transformasi diterapkan pada atribut numerik dari data pelanggan dan data transaksi dengan menggunakan tiga metode, yaitu normalisasi min–maks, z-score, dan decimal scaling. Hasil dari masing-masing metode kemudian dibandingkan untuk melihat perubahan skala data.

Normalisasi Min–Maks

Normalisasi min–maks digunakan untuk mengubah nilai numerik ke dalam rentang 0 hingga 1. Transformasi ini dilakukan menggunakan data usia dan pendapatan yang telah melalui tahap penanganan missing value.

Data Pelanggan

minmax <- function(x) {
  if (all(is.na(x))) return(rep(NA_real_, length(x)))
  
  rentang <- max(x, na.rm = TRUE) - min(x, na.rm = TRUE)
  
  if (rentang == 0) return(rep(0, length(x)))
  
  (x - min(x, na.rm = TRUE)) / rentang
}
pelanggan$usia_minmax <- minmax(pelanggan$usia_imputasi)

pelanggan$pendapatan_minmax <- minmax(
  pelanggan$pendapatan_imputasi
)
pelanggan[, c(
  "customer_id",
  "usia_imputasi",
  "usia_minmax",
  "pendapatan_imputasi",
  "pendapatan_minmax"
)]
##    customer_id usia_imputasi usia_minmax pendapatan_imputasi pendapatan_minmax
## 1         C001          21.0  0.00000000             4.5e+06      0.0000000000
## 2         C002          25.0  0.26666667             5.1e+06      0.0012108981
## 3         C003          23.0  0.13333333             5.2e+06      0.0014127144
## 4         C004          27.5  0.43333333             4.8e+06      0.0006054490
## 5         C005          27.0  0.40000000             4.9e+06      0.0008072654
## 6         C006          27.5  0.43333333             5.1e+06      0.0012108981
## 7         C007          31.0  0.66666667             5.0e+08      1.0000000000
## 8         C008          29.0  0.53333333             4.7e+06      0.0004036327
## 9         C009          22.0  0.06666667             4.6e+06      0.0002018163
## 10        C010          35.0  0.93333333             5.3e+06      0.0016145308
## 11        C011          28.0  0.46666667             5.1e+06      0.0012108981
## 12        C012          24.0  0.20000000             4.8e+06      0.0006054490
## 13        C013          26.0  0.33333333             5.5e+06      0.0020181635
## 14        C014          30.0  0.60000000             5.1e+06      0.0012108981
## 15        C015          32.0  0.73333333             5.1e+06      0.0012108981
## 16        C016          27.0  0.40000000             4.9e+06      0.0008072654
## 17        C017          34.0  0.86666667             6.0e+06      0.0030272452
## 18        C018          23.0  0.13333333             4.7e+06      0.0004036327
## 19        C019          29.0  0.53333333             5.2e+06      0.0014127144
## 20        C020          27.5  0.43333333             5.1e+06      0.0012108981
## 21        C021          31.0  0.66666667             5.4e+06      0.0018163471
## 22        C022          28.0  0.46666667             5.0e+06      0.0010090817
## 23        C023          25.0  0.26666667             5.6e+06      0.0022199798
## 24        C024          33.0  0.80000000             4.8e+06      0.0006054490
## 25        C025          27.5  0.43333333             3.5e+08      0.6972754793
## 26        C026          22.0  0.06666667             4.5e+06      0.0000000000
## 27        C027          36.0  1.00000000             5.1e+06      0.0012108981
## 28        C028          27.0  0.40000000             5.1e+06      0.0012108981

Interpretasi: Normalisasi min–maks mengubah usia dan pendapatan ke skala yang sama, yaitu 0–1, tanpa mengubah urutan relatif antarobservasi. Metode ini memudahkan perbandingan atribut yang memiliki satuan dan rentang nilai berbeda.

Data Transaksi

Karena jumlah_transaksi dan total_purchase tidak memiliki missing value, kedua atribut dapat langsung ditransformasikan.

transaksi$jumlah_transaksi_minmax <- minmax(
  transaksi$jumlah_transaksi
)

transaksi$total_purchase_minmax <- minmax(
  transaksi$total_purchase
)
transaksi[, c(
  "cust_id",
  "jumlah_transaksi",
  "jumlah_transaksi_minmax",
  "total_purchase",
  "total_purchase_minmax"
)]
##    cust_id jumlah_transaksi jumlah_transaksi_minmax total_purchase
## 1     C001                5              0.16666667        1.5e+06
## 2     C002                3              0.08333333        9.0e+05
## 3     C003                7              0.25000000        2.7e+06
## 4     C004                2              0.04166667        6.0e+05
## 5     C005                6              0.20833333        2.1e+06
## 6     C006                4              0.12500000        1.3e+06
## 7     C007               20              0.79166667        2.5e+07
## 8     C008                5              0.16666667        1.7e+06
## 9     C009                3              0.08333333        8.0e+05
## 10    C010                8              0.29166667        3.2e+06
## 11    C012                1              0.00000000        2.5e+05
## 12    C013                4              0.12500000        1.2e+06
## 13    C014                6              0.20833333        2.4e+06
## 14    C015                5              0.16666667        1.8e+06
## 15    C016                9              0.33333333        3.5e+06
## 16    C017                3              0.08333333        9.0e+05
## 17    C018                7              0.25000000        2.8e+06
## 18    C019                2              0.04166667        7.0e+05
## 19    C020                8              0.29166667        3.1e+06
## 20    C021                4              0.12500000        1.5e+06
## 21    C022                6              0.20833333        2.2e+06
## 22    C023                5              0.16666667        1.9e+06
## 23    C024               10              0.37500000        4.2e+06
## 24    C025                3              0.08333333        3.0e+07
## 25    C026                7              0.25000000        1.3e+06
## 26    C027               25              1.00000000        2.7e+06
## 27    C028                4              0.12500000        9.5e+05
## 28    C029                6              0.20833333        2.1e+06
## 29    C030                2              0.04166667        6.0e+05
## 30    C031                5              0.16666667        1.6e+06
##    total_purchase_minmax
## 1             0.04201681
## 2             0.02184874
## 3             0.08235294
## 4             0.01176471
## 5             0.06218487
## 6             0.03529412
## 7             0.83193277
## 8             0.04873950
## 9             0.01848739
## 10            0.09915966
## 11            0.00000000
## 12            0.03193277
## 13            0.07226891
## 14            0.05210084
## 15            0.10924370
## 16            0.02184874
## 17            0.08571429
## 18            0.01512605
## 19            0.09579832
## 20            0.04201681
## 21            0.06554622
## 22            0.05546218
## 23            0.13277311
## 24            1.00000000
## 25            0.03529412
## 26            0.08235294
## 27            0.02352941
## 28            0.06218487
## 29            0.01176471
## 30            0.04537815

Interpretasi: Normalisasi min–maks mengubah jumlah transaksi dan total pembelian ke rentang 0–1. Dengan demikian, kedua atribut transaksi dapat dibandingkan tanpa dipengaruhi oleh perbedaan satuan dan besar nilai awal.


Normalisasi Z-Score

Selain min–maks, dilakukan normalisasi z-score untuk menunjukkan posisi setiap nilai terhadap rata-rata atribut dalam satuan simpangan baku.

Data Pelanggan

pelanggan$usia_z <- as.numeric(
  scale(pelanggan$usia_imputasi)
)

pelanggan$pendapatan_z <- as.numeric(
  scale(pelanggan$pendapatan_imputasi)
)
hasil_zscore <- pelanggan[, c(
  "customer_id",
  "usia_z",
  "pendapatan_z"
)]

hasil_zscore$usia_z <- round(hasil_zscore$usia_z, 3)
hasil_zscore$pendapatan_z <- round(hasil_zscore$pendapatan_z, 3)

hasil_zscore
##    customer_id usia_z pendapatan_z
## 1         C001 -1.709       -0.273
## 2         C002 -0.702       -0.267
## 3         C003 -1.205       -0.266
## 4         C004 -0.072       -0.270
## 5         C005 -0.198       -0.269
## 6         C006 -0.072       -0.267
## 7         C007  0.809        4.151
## 8         C008  0.306       -0.271
## 9         C009 -1.457       -0.272
## 10        C010  1.817       -0.266
## 11        C011  0.054       -0.267
## 12        C012 -0.953       -0.270
## 13        C013 -0.450       -0.264
## 14        C014  0.558       -0.267
## 15        C015  1.061       -0.267
## 16        C016 -0.198       -0.269
## 17        C017  1.565       -0.259
## 18        C018 -1.205       -0.271
## 19        C019  0.306       -0.266
## 20        C020 -0.072       -0.267
## 21        C021  0.809       -0.265
## 22        C022  0.054       -0.268
## 23        C023 -0.702       -0.263
## 24        C024  1.313       -0.270
## 25        C025 -0.072        2.812
## 26        C026 -1.457       -0.273
## 27        C027  2.069       -0.267
## 28        C028 -0.198       -0.267

Interpretasi: Nilai z-score menunjukkan seberapa jauh suatu observasi berada dari rata-rata atributnya. Nilai positif menunjukkan posisi di atas rata-rata, sedangkan nilai negatif menunjukkan posisi di bawah rata-rata.

Data Transaksi

transaksi$jumlah_transaksi_z <- as.numeric(
  scale(transaksi$jumlah_transaksi)
)

transaksi$total_purchase_z <- as.numeric(
  scale(transaksi$total_purchase)
)
round(
  transaksi[, c(
    "jumlah_transaksi_z",
    "total_purchase_z"
  )],
  3
)
##    jumlah_transaksi_z total_purchase_z
## 1              -0.234           -0.305
## 2              -0.635           -0.395
## 3               0.167           -0.123
## 4              -0.835           -0.440
## 5              -0.033           -0.214
## 6              -0.434           -0.335
## 7               2.772            3.244
## 8              -0.234           -0.274
## 9              -0.635           -0.410
## 10              0.367           -0.048
## 11             -1.035           -0.493
## 12             -0.434           -0.350
## 13             -0.033           -0.169
## 14             -0.234           -0.259
## 15              0.568           -0.003
## 16             -0.635           -0.395
## 17              0.167           -0.108
## 18             -0.835           -0.425
## 19              0.367           -0.063
## 20             -0.434           -0.305
## 21             -0.033           -0.199
## 22             -0.234           -0.244
## 23              0.768            0.103
## 24             -0.635            3.999
## 25              0.167           -0.335
## 26              3.774           -0.123
## 27             -0.434           -0.388
## 28             -0.033           -0.214
## 29             -0.835           -0.440
## 30             -0.234           -0.289

Interpretasi: Hasil z-score menunjukkan posisi jumlah transaksi dan total pembelian relatif terhadap rata-rata masing-masing atribut. Nilai yang semakin positif menunjukkan nilai yang semakin tinggi dibandingkan rata-ratanya.


Decimal Scaling

Decimal scaling dilakukan dengan memindahkan posisi desimal berdasarkan besar nilai maksimum absolut sehingga skala data menjadi lebih kecil.

decimal_scale <- function(x) {
  maks <- max(abs(x), na.rm = TRUE)
  
  if (maks == 0) return(x)
  
  j <- ceiling(log10(maks + 1))
  
  x / (10 ^ j)
}

Data Pelanggan

pelanggan$usia_decimal <-
  decimal_scale(pelanggan$usia_imputasi)

pelanggan$pendapatan_decimal <-
  decimal_scale(pelanggan$pendapatan_imputasi)
pelanggan[, c(
  "customer_id",
  "usia_imputasi",
  "usia_decimal",
  "pendapatan_imputasi",
  "pendapatan_decimal"
)]
##    customer_id usia_imputasi usia_decimal pendapatan_imputasi
## 1         C001          21.0        0.210             4.5e+06
## 2         C002          25.0        0.250             5.1e+06
## 3         C003          23.0        0.230             5.2e+06
## 4         C004          27.5        0.275             4.8e+06
## 5         C005          27.0        0.270             4.9e+06
## 6         C006          27.5        0.275             5.1e+06
## 7         C007          31.0        0.310             5.0e+08
## 8         C008          29.0        0.290             4.7e+06
## 9         C009          22.0        0.220             4.6e+06
## 10        C010          35.0        0.350             5.3e+06
## 11        C011          28.0        0.280             5.1e+06
## 12        C012          24.0        0.240             4.8e+06
## 13        C013          26.0        0.260             5.5e+06
## 14        C014          30.0        0.300             5.1e+06
## 15        C015          32.0        0.320             5.1e+06
## 16        C016          27.0        0.270             4.9e+06
## 17        C017          34.0        0.340             6.0e+06
## 18        C018          23.0        0.230             4.7e+06
## 19        C019          29.0        0.290             5.2e+06
## 20        C020          27.5        0.275             5.1e+06
## 21        C021          31.0        0.310             5.4e+06
## 22        C022          28.0        0.280             5.0e+06
## 23        C023          25.0        0.250             5.6e+06
## 24        C024          33.0        0.330             4.8e+06
## 25        C025          27.5        0.275             3.5e+08
## 26        C026          22.0        0.220             4.5e+06
## 27        C027          36.0        0.360             5.1e+06
## 28        C028          27.0        0.270             5.1e+06
##    pendapatan_decimal
## 1              0.0045
## 2              0.0051
## 3              0.0052
## 4              0.0048
## 5              0.0049
## 6              0.0051
## 7              0.5000
## 8              0.0047
## 9              0.0046
## 10             0.0053
## 11             0.0051
## 12             0.0048
## 13             0.0055
## 14             0.0051
## 15             0.0051
## 16             0.0049
## 17             0.0060
## 18             0.0047
## 19             0.0052
## 20             0.0051
## 21             0.0054
## 22             0.0050
## 23             0.0056
## 24             0.0048
## 25             0.3500
## 26             0.0045
## 27             0.0051
## 28             0.0051

Interpretasi: Decimal scaling mengecilkan nilai usia dan pendapatan dengan membaginya menggunakan pangkat 10 tertentu, tetapi tetap mempertahankan perbandingan antarobservasi.

Data Transaksi

transaksi$jumlah_transaksi_decimal <-
  decimal_scale(transaksi$jumlah_transaksi)

transaksi$total_purchase_decimal <-
  decimal_scale(transaksi$total_purchase)
transaksi[, c(
  "cust_id",
  "jumlah_transaksi",
  "jumlah_transaksi_decimal",
  "total_purchase",
  "total_purchase_decimal"
)]
##    cust_id jumlah_transaksi jumlah_transaksi_decimal total_purchase
## 1     C001                5                     0.05        1.5e+06
## 2     C002                3                     0.03        9.0e+05
## 3     C003                7                     0.07        2.7e+06
## 4     C004                2                     0.02        6.0e+05
## 5     C005                6                     0.06        2.1e+06
## 6     C006                4                     0.04        1.3e+06
## 7     C007               20                     0.20        2.5e+07
## 8     C008                5                     0.05        1.7e+06
## 9     C009                3                     0.03        8.0e+05
## 10    C010                8                     0.08        3.2e+06
## 11    C012                1                     0.01        2.5e+05
## 12    C013                4                     0.04        1.2e+06
## 13    C014                6                     0.06        2.4e+06
## 14    C015                5                     0.05        1.8e+06
## 15    C016                9                     0.09        3.5e+06
## 16    C017                3                     0.03        9.0e+05
## 17    C018                7                     0.07        2.8e+06
## 18    C019                2                     0.02        7.0e+05
## 19    C020                8                     0.08        3.1e+06
## 20    C021                4                     0.04        1.5e+06
## 21    C022                6                     0.06        2.2e+06
## 22    C023                5                     0.05        1.9e+06
## 23    C024               10                     0.10        4.2e+06
## 24    C025                3                     0.03        3.0e+07
## 25    C026                7                     0.07        1.3e+06
## 26    C027               25                     0.25        2.7e+06
## 27    C028                4                     0.04        9.5e+05
## 28    C029                6                     0.06        2.1e+06
## 29    C030                2                     0.02        6.0e+05
## 30    C031                5                     0.05        1.6e+06
##    total_purchase_decimal
## 1                  0.0150
## 2                  0.0090
## 3                  0.0270
## 4                  0.0060
## 5                  0.0210
## 6                  0.0130
## 7                  0.2500
## 8                  0.0170
## 9                  0.0080
## 10                 0.0320
## 11                 0.0025
## 12                 0.0120
## 13                 0.0240
## 14                 0.0180
## 15                 0.0350
## 16                 0.0090
## 17                 0.0280
## 18                 0.0070
## 19                 0.0310
## 20                 0.0150
## 21                 0.0220
## 22                 0.0190
## 23                 0.0420
## 24                 0.3000
## 25                 0.0130
## 26                 0.0270
## 27                 0.0095
## 28                 0.0210
## 29                 0.0060
## 30                 0.0160

Interpretasi: Decimal scaling mengurangi besar angka pada atribut transaksi tanpa mengubah hubungan relatif antarobservasi. Metode ini menghasilkan nilai dengan skala yang lebih kecil sehingga lebih mudah digunakan dalam proses analisis tertentu.


Membandingkan Hasil Transformasi

Untuk melihat perbedaan skala yang dihasilkan oleh ketiga metode, hasil transformasi pendapatan dan total pembelian dapat dibandingkan.

Perbandingan Data Pelanggan

transformasi_pelanggan <- pelanggan[, c(
  "customer_id",
  "pendapatan_imputasi",
  "pendapatan_minmax",
  "pendapatan_z",
  "pendapatan_decimal"
)]

transformasi_pelanggan
##    customer_id pendapatan_imputasi pendapatan_minmax pendapatan_z
## 1         C001             4.5e+06      0.0000000000   -0.2726667
## 2         C002             5.1e+06      0.0012108981   -0.2673103
## 3         C003             5.2e+06      0.0014127144   -0.2664175
## 4         C004             4.8e+06      0.0006054490   -0.2699885
## 5         C005             4.9e+06      0.0008072654   -0.2690957
## 6         C006             5.1e+06      0.0012108981   -0.2673103
## 7         C007             5.0e+08      1.0000000000    4.1508335
## 8         C008             4.7e+06      0.0004036327   -0.2708812
## 9         C009             4.6e+06      0.0002018163   -0.2717739
## 10        C010             5.3e+06      0.0016145308   -0.2655248
## 11        C011             5.1e+06      0.0012108981   -0.2673103
## 12        C012             4.8e+06      0.0006054490   -0.2699885
## 13        C013             5.5e+06      0.0020181635   -0.2637393
## 14        C014             5.1e+06      0.0012108981   -0.2673103
## 15        C015             5.1e+06      0.0012108981   -0.2673103
## 16        C016             4.9e+06      0.0008072654   -0.2690957
## 17        C017             6.0e+06      0.0030272452   -0.2592756
## 18        C018             4.7e+06      0.0004036327   -0.2708812
## 19        C019             5.2e+06      0.0014127144   -0.2664175
## 20        C020             5.1e+06      0.0012108981   -0.2673103
## 21        C021             5.4e+06      0.0018163471   -0.2646321
## 22        C022             5.0e+06      0.0010090817   -0.2682030
## 23        C023             5.6e+06      0.0022199798   -0.2628466
## 24        C024             4.8e+06      0.0006054490   -0.2699885
## 25        C025             3.5e+08      0.6972754793    2.8117316
## 26        C026             4.5e+06      0.0000000000   -0.2726667
## 27        C027             5.1e+06      0.0012108981   -0.2673103
## 28        C028             5.1e+06      0.0012108981   -0.2673103
##    pendapatan_decimal
## 1              0.0045
## 2              0.0051
## 3              0.0052
## 4              0.0048
## 5              0.0049
## 6              0.0051
## 7              0.5000
## 8              0.0047
## 9              0.0046
## 10             0.0053
## 11             0.0051
## 12             0.0048
## 13             0.0055
## 14             0.0051
## 15             0.0051
## 16             0.0049
## 17             0.0060
## 18             0.0047
## 19             0.0052
## 20             0.0051
## 21             0.0054
## 22             0.0050
## 23             0.0056
## 24             0.0048
## 25             0.3500
## 26             0.0045
## 27             0.0051
## 28             0.0051

Perbandingan Data Transaksi

transformasi_transaksi <- transaksi[, c(
  "cust_id",
  "total_purchase",
  "total_purchase_minmax",
  "total_purchase_z",
  "total_purchase_decimal"
)]

transformasi_transaksi
##    cust_id total_purchase total_purchase_minmax total_purchase_z
## 1     C001        1.5e+06            0.04201681     -0.304546285
## 2     C002        9.0e+05            0.02184874     -0.395155097
## 3     C003        2.7e+06            0.08235294     -0.123328661
## 4     C004        6.0e+05            0.01176471     -0.440459502
## 5     C005        2.1e+06            0.06218487     -0.213937473
## 6     C006        1.3e+06            0.03529412     -0.334749222
## 7     C007        2.5e+07            0.83193277      3.244298850
## 8     C008        1.7e+06            0.04873950     -0.274343347
## 9     C009        8.0e+05            0.01848739     -0.410256565
## 10    C010        3.2e+06            0.09915966     -0.047821317
## 11    C012        2.5e+05            0.00000000     -0.493314643
## 12    C013        1.2e+06            0.03193277     -0.349850691
## 13    C014        2.4e+06            0.07226891     -0.168633067
## 14    C015        1.8e+06            0.05210084     -0.259241879
## 15    C016        3.5e+06            0.10924370     -0.002516911
## 16    C017        9.0e+05            0.02184874     -0.395155097
## 17    C018        2.8e+06            0.08571429     -0.108227192
## 18    C019        7.0e+05            0.01512605     -0.425358034
## 19    C020        3.1e+06            0.09579832     -0.062922786
## 20    C021        1.5e+06            0.04201681     -0.304546285
## 21    C022        2.2e+06            0.06554622     -0.198836004
## 22    C023        1.9e+06            0.05546218     -0.244140410
## 23    C024        4.2e+06            0.13277311      0.103193369
## 24    C025        3.0e+07            1.00000000      3.999372282
## 25    C026        1.3e+06            0.03529412     -0.334749222
## 26    C027        2.7e+06            0.08235294     -0.123328661
## 27    C028        9.5e+05            0.02352941     -0.387604362
## 28    C029        2.1e+06            0.06218487     -0.213937473
## 29    C030        6.0e+05            0.01176471     -0.440459502
## 30    C031        1.6e+06            0.04537815     -0.289444816
##    total_purchase_decimal
## 1                  0.0150
## 2                  0.0090
## 3                  0.0270
## 4                  0.0060
## 5                  0.0210
## 6                  0.0130
## 7                  0.2500
## 8                  0.0170
## 9                  0.0080
## 10                 0.0320
## 11                 0.0025
## 12                 0.0120
## 13                 0.0240
## 14                 0.0180
## 15                 0.0350
## 16                 0.0090
## 17                 0.0280
## 18                 0.0070
## 19                 0.0310
## 20                 0.0150
## 21                 0.0220
## 22                 0.0190
## 23                 0.0420
## 24                 0.3000
## 25                 0.0130
## 26                 0.0270
## 27                 0.0095
## 28                 0.0210
## 29                 0.0060
## 30                 0.0160

Interpretasi: Ketiga metode menghasilkan skala yang berbeda, tetapi informasi urutan nilai tetap dipertahankan. Pemilihan metode transformasi selanjutnya dapat disesuaikan dengan kebutuhan analisis yang akan dilakukan.


Dampak Outlier Terhadap Transformasi

Pada tahap sebelumnya ditemukan nilai ekstrem pada atribut pendapatan pelanggan dan beberapa nilai ekstrem pada atribut transaksi. Nilai tersebut tidak langsung dihapus karena belum terdapat bukti bahwa nilai tersebut merupakan kesalahan pencatatan.

Oleh karena itu, pengaruh outlier terhadap normalisasi min–maks dapat dibandingkan menggunakan data yang telah melalui winsorization.

Data Pelanggan

pelanggan$pendapatan_winsor_minmax <-
  minmax(pelanggan$pendapatan_winsor)
pelanggan[, c(
  "customer_id",
  "pendapatan_imputasi",
  "pendapatan_minmax",
  "pendapatan_winsor",
  "pendapatan_winsor_minmax"
)]
##    customer_id pendapatan_imputasi pendapatan_minmax pendapatan_winsor
## 1         C001             4.5e+06      0.0000000000           4500000
## 2         C002             5.1e+06      0.0012108981           5100000
## 3         C003             5.2e+06      0.0014127144           5200000
## 4         C004             4.8e+06      0.0006054490           4800000
## 5         C005             4.9e+06      0.0008072654           4900000
## 6         C006             5.1e+06      0.0012108981           5100000
## 7         C007             5.0e+08      1.0000000000           5862500
## 8         C008             4.7e+06      0.0004036327           4700000
## 9         C009             4.6e+06      0.0002018163           4600000
## 10        C010             5.3e+06      0.0016145308           5300000
## 11        C011             5.1e+06      0.0012108981           5100000
## 12        C012             4.8e+06      0.0006054490           4800000
## 13        C013             5.5e+06      0.0020181635           5500000
## 14        C014             5.1e+06      0.0012108981           5100000
## 15        C015             5.1e+06      0.0012108981           5100000
## 16        C016             4.9e+06      0.0008072654           4900000
## 17        C017             6.0e+06      0.0030272452           5862500
## 18        C018             4.7e+06      0.0004036327           4700000
## 19        C019             5.2e+06      0.0014127144           5200000
## 20        C020             5.1e+06      0.0012108981           5100000
## 21        C021             5.4e+06      0.0018163471           5400000
## 22        C022             5.0e+06      0.0010090817           5000000
## 23        C023             5.6e+06      0.0022199798           5600000
## 24        C024             4.8e+06      0.0006054490           4800000
## 25        C025             3.5e+08      0.6972754793           5862500
## 26        C026             4.5e+06      0.0000000000           4500000
## 27        C027             5.1e+06      0.0012108981           5100000
## 28        C028             5.1e+06      0.0012108981           5100000
##    pendapatan_winsor_minmax
## 1                 0.0000000
## 2                 0.4403670
## 3                 0.5137615
## 4                 0.2201835
## 5                 0.2935780
## 6                 0.4403670
## 7                 1.0000000
## 8                 0.1467890
## 9                 0.0733945
## 10                0.5871560
## 11                0.4403670
## 12                0.2201835
## 13                0.7339450
## 14                0.4403670
## 15                0.4403670
## 16                0.2935780
## 17                1.0000000
## 18                0.1467890
## 19                0.5137615
## 20                0.4403670
## 21                0.6605505
## 22                0.3669725
## 23                0.8073394
## 24                0.2201835
## 25                1.0000000
## 26                0.0000000
## 27                0.4403670
## 28                0.4403670
plot(
  pelanggan$pendapatan_minmax,
  pelanggan$pendapatan_winsor_minmax,
  pch = 19,
  xlab = "Min–Maks Pendapatan",
  ylab = "Min–Maks Setelah Winsorization",
  main = "Perbandingan Normalisasi Sebelum dan Sesudah Winsorization"
)

abline(0, 1, lty = 2)

Interpretasi: Perbandingan menunjukkan bahwa nilai ekstrem pada pendapatan dapat memengaruhi rentang hasil normalisasi min–maks. Winsorization dapat digunakan sebagai pembanding untuk melihat bagaimana pembatasan nilai ekstrem memengaruhi hasil transformasi.

Data Transaksi

Pada data transaksi, nilai ekstrem juga ditemukan pada jumlah_transaksi dan total_purchase. Namun, nilai tersebut tidak langsung diubah atau dihapus, karena nilai transaksi yang besar masih dapat merupakan transaksi yang valid.

Untuk melihat pengaruhnya terhadap normalisasi, dapat dibuat versi winsorized sebagai pembanding:

q1_jumlah <- quantile(transaksi$jumlah_transaksi, 0.25)
q3_jumlah <- quantile(transaksi$jumlah_transaksi, 0.75)
iqr_jumlah <- IQR(transaksi$jumlah_transaksi)

batas_atas_jumlah <- q3_jumlah + 1.5 * iqr_jumlah

transaksi$jumlah_transaksi_winsor <- pmin(
  transaksi$jumlah_transaksi,
  batas_atas_jumlah
)
q1_purchase <- quantile(transaksi$total_purchase, 0.25)
q3_purchase <- quantile(transaksi$total_purchase, 0.75)
iqr_purchase <- IQR(transaksi$total_purchase)

batas_atas_purchase <- q3_purchase + 1.5 * iqr_purchase

transaksi$total_purchase_winsor <- pmin(
  transaksi$total_purchase,
  batas_atas_purchase
)
transaksi$jumlah_transaksi_winsor_minmax <-
  minmax(transaksi$jumlah_transaksi_winsor)

transaksi$total_purchase_winsor_minmax <-
  minmax(transaksi$total_purchase_winsor)
transaksi[, c(
  "cust_id",
  "jumlah_transaksi",
  "jumlah_transaksi_minmax",
  "jumlah_transaksi_winsor",
  "jumlah_transaksi_winsor_minmax",
  "total_purchase",
  "total_purchase_minmax",
  "total_purchase_winsor",
  "total_purchase_winsor_minmax"
)]
##    cust_id jumlah_transaksi jumlah_transaksi_minmax jumlah_transaksi_winsor
## 1     C001                5              0.16666667                   5.000
## 2     C002                3              0.08333333                   3.000
## 3     C003                7              0.25000000                   7.000
## 4     C004                2              0.04166667                   2.000
## 5     C005                6              0.20833333                   6.000
## 6     C006                4              0.12500000                   4.000
## 7     C007               20              0.79166667                  12.625
## 8     C008                5              0.16666667                   5.000
## 9     C009                3              0.08333333                   3.000
## 10    C010                8              0.29166667                   8.000
## 11    C012                1              0.00000000                   1.000
## 12    C013                4              0.12500000                   4.000
## 13    C014                6              0.20833333                   6.000
## 14    C015                5              0.16666667                   5.000
## 15    C016                9              0.33333333                   9.000
## 16    C017                3              0.08333333                   3.000
## 17    C018                7              0.25000000                   7.000
## 18    C019                2              0.04166667                   2.000
## 19    C020                8              0.29166667                   8.000
## 20    C021                4              0.12500000                   4.000
## 21    C022                6              0.20833333                   6.000
## 22    C023                5              0.16666667                   5.000
## 23    C024               10              0.37500000                  10.000
## 24    C025                3              0.08333333                   3.000
## 25    C026                7              0.25000000                   7.000
## 26    C027               25              1.00000000                  12.625
## 27    C028                4              0.12500000                   4.000
## 28    C029                6              0.20833333                   6.000
## 29    C030                2              0.04166667                   2.000
## 30    C031                5              0.16666667                   5.000
##    jumlah_transaksi_winsor_minmax total_purchase total_purchase_minmax
## 1                      0.34408602        1.5e+06            0.04201681
## 2                      0.17204301        9.0e+05            0.02184874
## 3                      0.51612903        2.7e+06            0.08235294
## 4                      0.08602151        6.0e+05            0.01176471
## 5                      0.43010753        2.1e+06            0.06218487
## 6                      0.25806452        1.3e+06            0.03529412
## 7                      1.00000000        2.5e+07            0.83193277
## 8                      0.34408602        1.7e+06            0.04873950
## 9                      0.17204301        8.0e+05            0.01848739
## 10                     0.60215054        3.2e+06            0.09915966
## 11                     0.00000000        2.5e+05            0.00000000
## 12                     0.25806452        1.2e+06            0.03193277
## 13                     0.43010753        2.4e+06            0.07226891
## 14                     0.34408602        1.8e+06            0.05210084
## 15                     0.68817204        3.5e+06            0.10924370
## 16                     0.17204301        9.0e+05            0.02184874
## 17                     0.51612903        2.8e+06            0.08571429
## 18                     0.08602151        7.0e+05            0.01512605
## 19                     0.60215054        3.1e+06            0.09579832
## 20                     0.25806452        1.5e+06            0.04201681
## 21                     0.43010753        2.2e+06            0.06554622
## 22                     0.34408602        1.9e+06            0.05546218
## 23                     0.77419355        4.2e+06            0.13277311
## 24                     0.17204301        3.0e+07            1.00000000
## 25                     0.51612903        1.3e+06            0.03529412
## 26                     1.00000000        2.7e+06            0.08235294
## 27                     0.25806452        9.5e+05            0.02352941
## 28                     0.43010753        2.1e+06            0.06218487
## 29                     0.08602151        6.0e+05            0.01176471
## 30                     0.34408602        1.6e+06            0.04537815
##    total_purchase_winsor total_purchase_winsor_minmax
## 1                1500000                   0.25094103
## 2                 900000                   0.13048934
## 3                2700000                   0.49184442
## 4                 600000                   0.07026349
## 5                2100000                   0.37139272
## 6                1300000                   0.21079046
## 7                5231250                   1.00000000
## 8                1700000                   0.29109159
## 9                 800000                   0.11041405
## 10               3200000                   0.59222083
## 11                250000                   0.00000000
## 12               1200000                   0.19071518
## 13               2400000                   0.43161857
## 14               1800000                   0.31116688
## 15               3500000                   0.65244668
## 16                900000                   0.13048934
## 17               2800000                   0.51191970
## 18                700000                   0.09033877
## 19               3100000                   0.57214555
## 20               1500000                   0.25094103
## 21               2200000                   0.39146801
## 22               1900000                   0.33124216
## 23               4200000                   0.79297365
## 24               5231250                   1.00000000
## 25               1300000                   0.21079046
## 26               2700000                   0.49184442
## 27                950000                   0.14052698
## 28               2100000                   0.37139272
## 29                600000                   0.07026349
## 30               1600000                   0.27101631

Interpretasi: Perbandingan ini menunjukkan seberapa besar nilai ekstrem pada transaksi memengaruhi hasil normalisasi. Data winsorized hanya digunakan sebagai pembanding, sedangkan data transaksi asli tetap dipertahankan karena belum terdapat bukti bahwa nilai ekstrem tersebut merupakan kesalahan.

Untuk data transaksi, winsorization tidak harus digunakan sebagai data final. Tujuannya di sini adalah menunjukkan bahwa keputusan penanganan outlier perlu mempertimbangkan konteks data, bukan hanya berdasarkan hasil deteksi statistik.


Bagian VI — Integrasi Data

Setelah masing-masing sumber data diperiksa dan dibersihkan, tahap berikutnya adalah menggabungkan data pelanggan dengan data transaksi. Integrasi dilakukan berdasarkan identifier pelanggan agar informasi karakteristik pelanggan dapat dihubungkan dengan informasi transaksinya.

Memeriksa Kunci pada Kedua Sumber Data

Pada data pelanggan, identifier yang digunakan adalah customer_id, sedangkan pada data transaksi digunakan cust_id. Sebelum dilakukan penggabungan, kedua kolom tersebut perlu diperiksa untuk memastikan keunikan dan kesesuaian identitas pelanggan.

sum(duplicated(pelanggan$customer_id))
## [1] 0
sum(duplicated(transaksi$cust_id))
## [1] 0

Interpretasi: Hasil pemeriksaan menunjukkan bahwa customer_id pada data pelanggan telah dibuat unik setelah proses deduplikasi, sedangkan cust_id pada data transaksi juga tidak memiliki duplikasi. Kondisi ini memungkinkan kedua data dihubungkan berdasarkan identifier pelanggan.


Memeriksa Perbedaan Identifier

Karena nama identifier pada kedua sumber berbeda, dilakukan pemeriksaan terhadap ID yang hanya terdapat pada salah satu dataset.

setdiff(
  pelanggan$customer_id,
  transaksi$cust_id
)
## [1] "C011"
setdiff(
  transaksi$cust_id,
  pelanggan$customer_id
)
## [1] "C029" "C030" "C031"

Interpretasi: Hasil setdiff() menunjukkan adanya perbedaan cakupan pelanggan antara kedua sumber data. Beberapa pelanggan pada data pelanggan tidak memiliki catatan transaksi, sedangkan terdapat pula ID transaksi yang tidak memiliki pasangan pada data pelanggan.


Menyelaraskan Nama Identifier

Agar kedua sumber data memiliki nama kunci yang sama, cust_id pada data transaksi diubah menjadi customer_id.

names(transaksi)[
  names(transaksi) == "cust_id"
] <- "customer_id"
names(transaksi)
##  [1] "customer_id"                    "jumlah_transaksi"              
##  [3] "total_purchase"                 "outlier_jumlah_transaksi"      
##  [5] "outlier_total_purchase"         "jumlah_transaksi_minmax"       
##  [7] "total_purchase_minmax"          "jumlah_transaksi_z"            
##  [9] "total_purchase_z"               "jumlah_transaksi_decimal"      
## [11] "total_purchase_decimal"         "jumlah_transaksi_winsor"       
## [13] "total_purchase_winsor"          "jumlah_transaksi_winsor_minmax"
## [15] "total_purchase_winsor_minmax"

Interpretasi: Setelah nama identifier diseragamkan, kedua dataset memiliki kolom kunci dengan nama customer_id, sehingga proses integrasi dapat dilakukan dengan lebih mudah.


Mengintegrasikan Data Pelanggan dan Transaksi

Karena tujuan integrasi adalah mempertahankan seluruh pelanggan yang telah dibersihkan, digunakan left join melalui fungsi merge().

data_terintegrasi <- merge(
  pelanggan,
  transaksi,
  by = "customer_id",
  all.x = TRUE
)
data_terintegrasi[, c(
  "customer_id",
  "nama",
  "usia_imputasi",
  "pendapatan_imputasi",
  "kota_imputasi",
  "status",
  "jumlah_transaksi",
  "total_purchase"
)]
##    customer_id   nama usia_imputasi pendapatan_imputasi   kota_imputasi
## 1         C001    Ani          21.0             4.5e+06       Pekanbaru
## 2         C002   Budi          25.0             5.1e+06       Pekanbaru
## 3         C003  Citra          23.0             5.2e+06       Pekanbaru
## 4         C004   Dodi          27.5             4.8e+06           Dumai
## 5         C005    Eka          27.0             4.9e+06       Pekanbaru
## 6         C006   Fani          27.5             5.1e+06           Dumai
## 7         C007 Gilang          31.0             5.0e+08       Pekanbaru
## 8         C008   Hana          29.0             4.7e+06            Siak
## 9         C009  Indra          22.0             4.6e+06       Pekanbaru
## 10        C010   Joko          35.0             5.3e+06           Dumai
## 11        C011   Kiki          28.0             5.1e+06 Tidak diketahui
## 12        C012   Lina          24.0             4.8e+06       Pekanbaru
## 13        C013   Maya          26.0             5.5e+06       Pekanbaru
## 14        C014  Nanda          30.0             5.1e+06       Pekanbaru
## 15        C015    Oki          32.0             5.1e+06           Dumai
## 16        C016  Putri          27.0             4.9e+06            Siak
## 17        C017   Raka          34.0             6.0e+06       Pekanbaru
## 18        C018  Salsa          23.0             4.7e+06       Pekanbaru
## 19        C019    Tio          29.0             5.2e+06           Dumai
## 20        C020    Uli          27.5             5.1e+06       Pekanbaru
## 21        C021   Vina          31.0             5.4e+06       Pekanbaru
## 22        C022  Wawan          28.0             5.0e+06       Pekanbaru
## 23        C023   Yani          25.0             5.6e+06           Dumai
## 24        C024   Zaki          33.0             4.8e+06            Siak
## 25        C025   Aldi          27.5             3.5e+08 Tidak diketahui
## 26        C026  Bella          22.0             4.5e+06       Pekanbaru
## 27        C027   Caca          36.0             5.1e+06           Dumai
## 28        C028   Dian          27.0             5.1e+06       Pekanbaru
##         status jumlah_transaksi total_purchase
## 1        Aktif                5        1.5e+06
## 2        Aktif                3        9.0e+05
## 3        Aktif                7        2.7e+06
## 4        Aktif                2        6.0e+05
## 5  Tidak Aktif                6        2.1e+06
## 6  Tidak Aktif                4        1.3e+06
## 7        Aktif               20        2.5e+07
## 8        Aktif                5        1.7e+06
## 9        Aktif                3        8.0e+05
## 10 Tidak Aktif                8        3.2e+06
## 11       Aktif               NA             NA
## 12       Aktif                1        2.5e+05
## 13       Aktif                4        1.2e+06
## 14       Aktif                6        2.4e+06
## 15 Tidak Aktif                5        1.8e+06
## 16       Aktif                9        3.5e+06
## 17       Aktif                3        9.0e+05
## 18       Aktif                7        2.8e+06
## 19       Aktif                2        7.0e+05
## 20 Tidak Aktif                8        3.1e+06
## 21 Tidak Aktif                4        1.5e+06
## 22       Aktif                6        2.2e+06
## 23       Aktif                5        1.9e+06
## 24       Aktif               10        4.2e+06
## 25       Aktif                3        3.0e+07
## 26 Tidak Aktif                7        1.3e+06
## 27 Tidak Aktif               25        2.7e+06
## 28       Aktif                4        9.5e+05

Interpretasi: Data pelanggan menjadi tabel utama, sehingga seluruh pelanggan tetap dipertahankan meskipun tidak memiliki pasangan transaksi. Informasi transaksi akan terisi pada pelanggan yang memiliki customer_id yang sesuai.


Memvalidasi Hasil Integrasi

Setelah penggabungan dilakukan, hasilnya perlu diperiksa untuk memastikan tidak terjadi perubahan jumlah baris atau duplikasi identifier yang tidak diharapkan.

Membandingkan Jumlah Baris

c(
  sebelum = nrow(pelanggan),
  sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah 
##      28      28

Interpretasi: Jumlah baris setelah integrasi seharusnya tetap sama dengan jumlah pelanggan sebelum integrasi karena digunakan all.x = TRUE dan setiap customer_id pada data transaksi bersifat unik.

Memeriksa Kembali Keunikan Customer_id

sum(
  duplicated(data_terintegrasi$customer_id)
)
## [1] 0

Interpretasi: Jika hasilnya 0, berarti tidak terdapat customer_id yang berulang setelah integrasi sehingga hubungan satu pelanggan dengan satu baris data tetap terjaga.

Memeriksa Missing Value Akibat Integrasi

colSums(
  is.na(
    data_terintegrasi[, c(
      "jumlah_transaksi",
      "total_purchase"
    )]
  )
)
## jumlah_transaksi   total_purchase 
##                1                1

Interpretasi: NA pada atribut transaksi setelah integrasi menunjukkan bahwa terdapat pelanggan yang tidak memiliki pasangan data transaksi. Kondisi ini berbeda dari missing value pada data pelanggan karena muncul akibat ketidaksesuaian identifier antar sumber.

Mengidentifikasi Pelanggan Tanpa Transaksi

data_terintegrasi[
  is.na(data_terintegrasi$jumlah_transaksi),
  c("customer_id", "nama")
]
##    customer_id nama
## 11        C011 Kiki

Interpretasi: Hasil tersebut menunjukkan pelanggan yang tercatat dalam data pelanggan tetapi tidak memiliki pasangan pada data transaksi. Dalam dataset ini, kondisi tersebut perlu dipertahankan terlebih dahulu karena belum diketahui apakah pelanggan memang belum bertransaksi atau data transaksinya tidak tersedia.


Memeriksa Transaksi yang Tidak Memiliki Data Pelanggan

Karena pada pemeriksaan sebelumnya ditemukan C029, C030, dan C031 pada data transaksi tetapi tidak terdapat pada data pelanggan, kondisi tersebut juga perlu didokumentasikan.

transaksi[
  !transaksi$customer_id %in% pelanggan$customer_id,
  ]
##    customer_id jumlah_transaksi total_purchase outlier_jumlah_transaksi
## 28        C029                6        2100000                    FALSE
## 29        C030                2         600000                    FALSE
## 30        C031                5        1600000                    FALSE
##    outlier_total_purchase jumlah_transaksi_minmax total_purchase_minmax
## 28                  FALSE              0.20833333            0.06218487
## 29                  FALSE              0.04166667            0.01176471
## 30                  FALSE              0.16666667            0.04537815
##    jumlah_transaksi_z total_purchase_z jumlah_transaksi_decimal
## 28        -0.03339866       -0.2139375                     0.06
## 29        -0.83496647       -0.4404595                     0.02
## 30        -0.23379061       -0.2894448                     0.05
##    total_purchase_decimal jumlah_transaksi_winsor total_purchase_winsor
## 28                  0.021                       6               2100000
## 29                  0.006                       2                600000
## 30                  0.016                       5               1600000
##    jumlah_transaksi_winsor_minmax total_purchase_winsor_minmax
## 28                     0.43010753                   0.37139272
## 29                     0.08602151                   0.07026349
## 30                     0.34408602                   0.27101631

Interpretasi: Hasil tersebut menunjukkan adanya data transaksi yang tidak memiliki pasangan pada data pelanggan. Data tersebut tidak langsung dihapus karena perlu dipastikan terlebih dahulu apakah transaksi berasal dari pelanggan yang belum tercatat pada sumber data pelanggan atau terdapat kesalahan pada identifier.


Menentukan Penanganan NA pada Data Transaksi

Setelah data pelanggan dan data transaksi diintegrasikan, terdapat nilai NA pada atribut transaksi untuk pelanggan yang tidak memiliki pasangan data transaksi. Dalam dataset ini, kondisi tersebut diperlakukan sebagai tidak adanya catatan transaksi sehingga nilai NA diubah menjadi 0.

data_terintegrasi$jumlah_transaksi[
  is.na(data_terintegrasi$jumlah_transaksi)
] <- 0
data_terintegrasi$total_purchase[
  is.na(data_terintegrasi$total_purchase)
] <- 0

Pada tahap integrasi, nilai NA pada atribut transaksi diubah menjadi 0 untuk merepresentasikan pelanggan yang tidak memiliki catatan transaksi. Dengan demikian, atribut transaksi pada dataset akhir tidak lagi memiliki nilai NA.


Bagian VII — Dataset Akhir dan Evaluasi

Setelah seluruh tahapan data preprocessing dilakukan, data pelanggan dan transaksi yang telah dibersihkan kemudian digunakan untuk membentuk dataset akhir. Tahap ini bertujuan untuk memastikan bahwa hasil preprocessing telah sesuai dengan kebutuhan analisis dan tidak menimbulkan masalah baru pada struktur maupun kualitas data.

Menentukan Atribut pada Dataset Akhir

Dataset akhir memuat informasi utama pelanggan yang telah melalui proses preprocessing serta informasi transaksi yang berhasil dipadankan berdasarkan customer_id. Atribut hasil transformasi tetap dapat dipertahankan apabila diperlukan untuk analisis lanjutan.

data_final <- data_terintegrasi[, c(
  "customer_id",
  "nama",
  "usia_imputasi",
  "kota_imputasi",
  "status",
  "pendapatan_imputasi",
  "pendapatan_missing",
  "outlier_pendapatan",
  "usia_minmax",
  "pendapatan_minmax",
  "jumlah_transaksi",
  "total_purchase"
)]

names(data_final)[names(data_final) == "kota_imputasi"] <- "kota"
names(data_final)[names(data_final) == "usia_imputasi"] <- "usia"

data_final
##    customer_id   nama usia            kota      status pendapatan_imputasi
## 1         C001    Ani 21.0       Pekanbaru       Aktif             4.5e+06
## 2         C002   Budi 25.0       Pekanbaru       Aktif             5.1e+06
## 3         C003  Citra 23.0       Pekanbaru       Aktif             5.2e+06
## 4         C004   Dodi 27.5           Dumai       Aktif             4.8e+06
## 5         C005    Eka 27.0       Pekanbaru Tidak Aktif             4.9e+06
## 6         C006   Fani 27.5           Dumai Tidak Aktif             5.1e+06
## 7         C007 Gilang 31.0       Pekanbaru       Aktif             5.0e+08
## 8         C008   Hana 29.0            Siak       Aktif             4.7e+06
## 9         C009  Indra 22.0       Pekanbaru       Aktif             4.6e+06
## 10        C010   Joko 35.0           Dumai Tidak Aktif             5.3e+06
## 11        C011   Kiki 28.0 Tidak diketahui       Aktif             5.1e+06
## 12        C012   Lina 24.0       Pekanbaru       Aktif             4.8e+06
## 13        C013   Maya 26.0       Pekanbaru       Aktif             5.5e+06
## 14        C014  Nanda 30.0       Pekanbaru       Aktif             5.1e+06
## 15        C015    Oki 32.0           Dumai Tidak Aktif             5.1e+06
## 16        C016  Putri 27.0            Siak       Aktif             4.9e+06
## 17        C017   Raka 34.0       Pekanbaru       Aktif             6.0e+06
## 18        C018  Salsa 23.0       Pekanbaru       Aktif             4.7e+06
## 19        C019    Tio 29.0           Dumai       Aktif             5.2e+06
## 20        C020    Uli 27.5       Pekanbaru Tidak Aktif             5.1e+06
## 21        C021   Vina 31.0       Pekanbaru Tidak Aktif             5.4e+06
## 22        C022  Wawan 28.0       Pekanbaru       Aktif             5.0e+06
## 23        C023   Yani 25.0           Dumai       Aktif             5.6e+06
## 24        C024   Zaki 33.0            Siak       Aktif             4.8e+06
## 25        C025   Aldi 27.5 Tidak diketahui       Aktif             3.5e+08
## 26        C026  Bella 22.0       Pekanbaru Tidak Aktif             4.5e+06
## 27        C027   Caca 36.0           Dumai Tidak Aktif             5.1e+06
## 28        C028   Dian 27.0       Pekanbaru       Aktif             5.1e+06
##    pendapatan_missing outlier_pendapatan usia_minmax pendapatan_minmax
## 1                   0              FALSE  0.00000000      0.0000000000
## 2                   1              FALSE  0.26666667      0.0012108981
## 3                   0              FALSE  0.13333333      0.0014127144
## 4                   0              FALSE  0.43333333      0.0006054490
## 5                   0              FALSE  0.40000000      0.0008072654
## 6                   0              FALSE  0.43333333      0.0012108981
## 7                   0               TRUE  0.66666667      1.0000000000
## 8                   0              FALSE  0.53333333      0.0004036327
## 9                   0              FALSE  0.06666667      0.0002018163
## 10                  0              FALSE  0.93333333      0.0016145308
## 11                  1              FALSE  0.46666667      0.0012108981
## 12                  0              FALSE  0.20000000      0.0006054490
## 13                  0              FALSE  0.33333333      0.0020181635
## 14                  1              FALSE  0.60000000      0.0012108981
## 15                  0              FALSE  0.73333333      0.0012108981
## 16                  0              FALSE  0.40000000      0.0008072654
## 17                  0               TRUE  0.86666667      0.0030272452
## 18                  0              FALSE  0.13333333      0.0004036327
## 19                  0              FALSE  0.53333333      0.0014127144
## 20                  1              FALSE  0.43333333      0.0012108981
## 21                  0              FALSE  0.66666667      0.0018163471
## 22                  0              FALSE  0.46666667      0.0010090817
## 23                  0              FALSE  0.26666667      0.0022199798
## 24                  0              FALSE  0.80000000      0.0006054490
## 25                  0               TRUE  0.43333333      0.6972754793
## 26                  0              FALSE  0.06666667      0.0000000000
## 27                  1              FALSE  1.00000000      0.0012108981
## 28                  0              FALSE  0.40000000      0.0012108981
##    jumlah_transaksi total_purchase
## 1                 5        1.5e+06
## 2                 3        9.0e+05
## 3                 7        2.7e+06
## 4                 2        6.0e+05
## 5                 6        2.1e+06
## 6                 4        1.3e+06
## 7                20        2.5e+07
## 8                 5        1.7e+06
## 9                 3        8.0e+05
## 10                8        3.2e+06
## 11                0        0.0e+00
## 12                1        2.5e+05
## 13                4        1.2e+06
## 14                6        2.4e+06
## 15                5        1.8e+06
## 16                9        3.5e+06
## 17                3        9.0e+05
## 18                7        2.8e+06
## 19                2        7.0e+05
## 20                8        3.1e+06
## 21                4        1.5e+06
## 22                6        2.2e+06
## 23                5        1.9e+06
## 24               10        4.2e+06
## 25                3        3.0e+07
## 26                7        1.3e+06
## 27               25        2.7e+06
## 28                4        9.5e+05

Interpretasi: Dataset akhir berisi atribut pelanggan yang telah dibersihkan dan ditangani missing value-nya, serta informasi transaksi yang berhasil dipadankan. Variabel indikator seperti pendapatan_missing dan outlier_pendapatan tetap dipertahankan untuk menunjukkan informasi yang berkaitan dengan proses preprocessing sebelumnya.

jumlah_transaksi (final) dan total_purchase (final) hanya diisi 0 apabila sebelumnya telah ditetapkan bahwa pelanggan tanpa pasangan transaksi memang berarti belum melakukan transaksi. Jika makna NA belum dapat dipastikan, sebaiknya nilai tersebut tetap dipertahankan sebagai NA.


Melakukan Audit Akhir

Audit akhir dilakukan untuk memastikan kondisi dataset setelah seluruh tahapan preprocessing selesai. Pemeriksaan meliputi struktur atribut, missing values, dan keunikan customer_id.

audit_akhir <- audit_data(data_final)
audit_akhir
##                atribut      tipe jumlah_missing persen_missing jumlah_unik
## 1          customer_id character              0              0          28
## 2                 nama character              0              0          28
## 3                 usia   numeric              0              0          17
## 4                 kota character              0              0           4
## 5               status character              0              0           2
## 6  pendapatan_imputasi   numeric              0              0          15
## 7   pendapatan_missing   integer              0              0           2
## 8   outlier_pendapatan   logical              0              0           2
## 9          usia_minmax   numeric              0              0          17
## 10   pendapatan_minmax   numeric              0              0          15
## 11    jumlah_transaksi   numeric              0              0          13
## 12      total_purchase   numeric              0              0          24

Interpretasi: Audit akhir memberikan gambaran kondisi setiap atribut setelah preprocessing, termasuk tipe data, jumlah missing value, dan jumlah nilai unik. Hasil ini digunakan untuk memastikan bahwa perubahan yang dilakukan telah menghasilkan data dengan struktur yang lebih konsisten.

Memeriksa Duplikasi customer_id

sum(duplicated(data_final$customer_id))
## [1] 0

Interpretasi: Hasil pemeriksaan digunakan untuk memastikan bahwa setiap customer_id hanya muncul satu kali pada dataset akhir. Nilai 0 menunjukkan bahwa tidak terdapat lagi duplikasi identifier.

Memeriksa Missing Values

colSums(is.na(data_final))
##         customer_id                nama                usia                kota 
##                   0                   0                   0                   0 
##              status pendapatan_imputasi  pendapatan_missing  outlier_pendapatan 
##                   0                   0                   0                   0 
##         usia_minmax   pendapatan_minmax    jumlah_transaksi      total_purchase 
##                   0                   0                   0                   0

Interpretasi: Pemeriksaan ini menunjukkan apakah masih terdapat missing values setelah proses imputasi dan integrasi. Missing values yang masih tersisa perlu dibedakan antara nilai yang belum tersedia dan nilai yang sengaja dipertahankan karena maknanya belum dapat dipastikan.


Membandingkan Kondisi Sebelum dan Sesudah Preprocessing

Untuk mengevaluasi perubahan yang terjadi, kondisi data sebelum dan sesudah preprocessing dibandingkan berdasarkan beberapa indikator utama, yaitu jumlah baris, duplikasi customer_id, jumlah missing value, serta jumlah kategori pada atribut kota dan status.

perbandingan <- data.frame(
  indikator = c(
    "Jumlah baris",
    "Duplikasi customer_id",
    "Total missing value",
    "Kategori kota unik",
    "Kategori status unik"
  ),
  
  sebelum = c(
    nrow(pelanggan_raw),
    sum(duplicated(pelanggan_raw$customer_id)),
    sum(is.na(pelanggan_raw)),
    length(unique(na.omit(pelanggan_raw$kota))),
    length(unique(na.omit(pelanggan_raw$status)))
  ),
  
  sesudah = c(
    nrow(data_final),
    sum(duplicated(data_final$customer_id)),
    sum(is.na(data_final)),
    length(unique(na.omit(data_final$kota))),
    length(unique(data_final$status))
  )
)

perbandingan
##               indikator sebelum sesudah
## 1          Jumlah baris      30      28
## 2 Duplikasi customer_id       2       0
## 3   Total missing value       9       0
## 4    Kategori kota unik       9       4
## 5  Kategori status unik       9       2

Interpretasi: Perbandingan ini menunjukkan perubahan kualitas data dari kondisi awal hingga dataset akhir. Berkurangnya duplikasi dan kategori yang tidak konsisten menunjukkan bahwa proses cleaning berhasil memperbaiki struktur data, sedangkan perubahan jumlah missing values menunjukkan dampak dari strategi penanganan nilai hilang yang diterapkan.


Mengevaluasi Hasil Integrasi

Karena praktikum menggunakan dua sumber data, evaluasi akhir juga perlu memastikan bahwa proses integrasi tidak menyebabkan duplikasi pelanggan atau kehilangan pasangan data secara tidak teridentifikasi.

c(
  jumlah_pelanggan = nrow(pelanggan),
  jumlah_data_akhir = nrow(data_final),
  duplikasi_customer_id = sum(duplicated(data_final$customer_id))
)
##      jumlah_pelanggan     jumlah_data_akhir duplikasi_customer_id 
##                    28                    28                     0

Interpretasi: Jumlah baris pada dataset akhir dibandingkan dengan jumlah pelanggan setelah deduplikasi untuk memastikan bahwa proses left join tidak menggandakan data pelanggan. Tidak adanya duplikasi customer_id menunjukkan bahwa integrasi berlangsung sesuai struktur satu pelanggan untuk satu baris data.

Memeriksa Pelanggan yang Tidak Memiliki Transaksi

data_final[
  is.na(data_final$jumlah_transaksi_final),
  c("customer_id", "nama")
]
## [1] customer_id nama       
## <0 rows> (or 0-length row.names)

Interpretasi: Hasil ini menunjukkan pelanggan yang tidak memiliki pasangan pada data transaksi. Kondisi tersebut tidak langsung dianggap sebagai kesalahan karena dapat menunjukkan bahwa pelanggan belum melakukan transaksi atau data transaksinya tidak tersedia.

Memeriksa Transaksi yang Tidak Memiliki Pasangan Pelanggan

Karena sebelumnya terdapat C029, C030, dan C031 pada data transaksi, sedangkan identifier tersebut tidak terdapat pada data pelanggan, ketiganya perlu diperiksa secara terpisah.

setdiff(
  transaksi$customer_id,
  pelanggan$customer_id
)
## [1] "C029" "C030" "C031"

Interpretasi: Hasil pemeriksaan menunjukkan identifier transaksi yang tidak memiliki pasangan pada data pelanggan. Identifier tersebut tidak dapat dimasukkan ke dataset akhir melalui left join dari sisi pelanggan sehingga perlu dicatat sebagai data transaksi yang belum memiliki pasangan pelanggan.

Catatan: C011 juga termasuk kondisi penting pada data awal karena terdapat pada data pelanggan tetapi tidak terdapat pada data transaksi. Kondisi ini tidak perlu diperbaiki pada tahap cleaning, karena bukan kesalahan pada nilai atribut pelanggan. Ketidaksesuaian tersebut ditangani dan dijelaskan pada tahap integrasi data.


Menyimpan Dataset Hasil Preprocessing

Setelah dataset akhir dinyatakan sesuai, hasil preprocessing dapat disimpan untuk digunakan pada tahap analisis selanjutnya.

write.csv(
  data_final,
  "data_final_preprocessing.csv",
  row.names = FALSE
)
write.csv(
  log_perubahan,
  "log_perubahan_preprocessing.csv",
  row.names = FALSE
)


Ringkasan

Secara keseluruhan, proses data preprocessing pada praktikum ini dilakukan secara bertahap, mulai dari: 1. memeriksa struktur dan kondisi awal data pelanggan dan transaksi; 2. membersihkan data melalui standardisasi kategori dan penanganan duplikasi serta nilai yang tidak sesuai aturan; 3. menangani missing values berdasarkan karakteristik masing-masing atribut; 4. mendeteksi dan mengevaluasi nilai ekstrem pada data pelanggan dan transaksi; 5. melakukan transformasi atribut numerik menggunakan min–maks, z-score, dan decimal scaling; 6. mengintegrasikan data pelanggan dan transaksi berdasarkan customer_id dan memvalidasi hasil integrasinya.

Hasil akhir menunjukkan bahwa data telah menjadi lebih terstruktur, konsisten, dan lebih siap digunakan untuk analisis selanjutnya. Namun, setiap perubahan tetap perlu didasarkan pada kondisi data dan alasan yang jelas, karena data preprocessing bukan sekadar menjalankan kode, melainkan proses menentukan tindakan yang tepat terhadap setiap permasalahan data.