R version yang digunakan: R version 4.5.1 (2025-06-13 ucrt)

1. MEMBUAT DATA PELANGGAN

pelanggan_raw <- data.frame(
  customer_id = c("C001", "C002", "C003", "C004", "C005", "C006",
                  "C007", "C008", "C009", "C010", "C010", "C011"),
  nama = c("Ani", "Budi", "Citra", "Dodi", "Eka", "Fani",
           "Gilang", "Hana", "Indra", "Joko", "Joko", "Kiki"),
  usia = c(21, 25, 23, 150, 27, NA, 31, 29, 22, 35, 35, 28),
  pendapatan = c(4500000, NA, 5200000, 4800000, 4900000, 5100000,
                 500000000, 4700000, 4600000, 5300000, 5300000, NA),
  kota = c("Pekanbaru", " PKU", "PEKANBARU", "Dumai", "pekanbaru",
           "DUMAI", "Pekanbaru ", "Siak", "PKU", "Dumai", "Dumai", NA),
  status = c("Aktif", "aktif", "ACTIVE", "A", "Tidak Aktif", "nonaktif",
             "Aktif", "AKTIF", "A", "Tidak aktif", "Tidak aktif", "Aktif"),
  stringsAsFactors = FALSE
)

Sintaks data.frame() digunakan untuk membuat dataset pelanggan secara langsung di R. Dataset terdiri atas customer_id, nama, usia, pendapatan, kota, dan status pelanggan.

Beberapa nilai sengaja dibuat bermasalah untuk keperluan preprocessing, misalnya NA, usia 150, penulisan kota yang berbeda-beda, status dengan beberapa bentuk penulisan, dan customer_id yang muncul dua kali.

2. MEMBUAT DATA TRANSAKSI

transaksi_raw <- data.frame(
  cust_id = c("C001", "C002", "C003", "C004", "C005", "C006",
              "C007", "C008", "C009", "C010", "C012"),
  jumlah_transaksi = c(5, 3, 7, 2, 6, 4, 20, 5, 3, 8, 1),
  total_purchase = c(1500000, 900000, 2700000, 600000, 2100000, 1300000,
                     25000000, 1700000, 800000, 3200000, 250000),
  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

Dataset kedua berisi informasi transaksi pelanggan. Identifier pada data ini bernama cust_id, bukan customer_id. Perbedaan nama identifier tersebut digunakan untuk menunjukkan bahwa sebelum dua dataset digabungkan, nama kunci perlu diselaraskan terlebih dahulu.

3. MELIHAT UKURAN DATASET

dim(pelanggan_raw)
## [1] 12  6

Fungsi dim() digunakan untuk mengetahui jumlah baris dan kolom dataset. Pada data awal terdapat 12 baris dan 6 kolom.

4. MELIHAT NAMA KOLOM

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

Fungsi names() digunakan untuk melihat nama setiap atribut dalam dataset.

5. MELIHAT STRUKTUR DATA

str(pelanggan_raw)
## 'data.frame':    12 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" ...

Fungsi str() digunakan untuk melihat struktur dataset, termasuk nama variabel, tipe data, dan contoh nilai.

6. MELIHAT DATA AWAL

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

Fungsi head() menampilkan beberapa baris pertama dataset agar bentuk data dapat dilihat secara cepat.

7. MELIHAT RINGKASAN DATA

summary(pelanggan_raw)
##  customer_id            nama                usia          pendapatan       
##  Length:12          Length:12          Min.   : 21.00   Min.   :  4500000  
##  Class :character   Class :character   1st Qu.: 24.00   1st Qu.:  4725000  
##  Mode  :character   Mode  :character   Median : 28.00   Median :  5000000  
##                                        Mean   : 38.73   Mean   : 54440000  
##                                        3rd Qu.: 33.00   3rd Qu.:  5275000  
##                                        Max.   :150.00   Max.   :500000000  
##                                        NA's   :1        NA's   :2          
##      kota              status         
##  Length:12          Length:12         
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Fungsi summary() memberikan ringkasan setiap variabel. Untuk variabel numerik, R menampilkan statistik seperti minimum, median, mean, dan maksimum.

8. MEMERIKSA MISSING VALUE

colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0

is.na() digunakan untuk mendeteksi nilai kosong atau NA, sedangkan colSums() menghitung jumlah NA pada setiap kolom.

9. MENGHITUNG PERSENTASE MISSING VALUE

round(colMeans(is.na(pelanggan_raw)) * 100, 2)
## customer_id        nama        usia  pendapatan        kota      status 
##        0.00        0.00        8.33       16.67        8.33        0.00

Sintaks ini menghitung persentase data yang hilang pada setiap kolom. round() digunakan agar hasil ditampilkan sampai dua angka desimal.

10. MEMERIKSA DUPLIKASI CUSTOMER_ID

sum(duplicated(pelanggan_raw$customer_id))
## [1] 1

duplicated() digunakan untuk mencari identifier yang muncul lebih dari satu kali. sum() kemudian menghitung jumlah duplikasi tersebut.

11. MELIHAT DATA YANG DUPLIKAT

pelanggan_raw[
  duplicated(pelanggan_raw$customer_id) |
    duplicated(pelanggan_raw$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

Sintaks ini digunakan untuk menampilkan seluruh baris yang memiliki customer_id duplikat. Pada dataset ini terdapat C010 yang muncul dua kali.

12. MEMBUAT DATA KERJA

pelanggan <- pelanggan_raw

Dataset asli tidak langsung diubah. Dataset pelanggan_raw disalin menjadi pelanggan sebagai data kerja agar data awal tetap tersedia sebagai pembanding.

13. MEMBERSIHKAN SPASI

pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)

Fungsi trimws() digunakan untuk menghapus spasi yang tidak diperlukan di awal atau akhir teks.

14. MENYERAGAMKAN HURUF

pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)

unique(pelanggan$kota)
## [1] "pekanbaru" "pku"       "dumai"     "siak"      NA
unique(pelanggan$status)
## [1] "aktif"       "active"      "a"           "tidak aktif" "nonaktif"

Fungsi tolower() mengubah seluruh huruf menjadi huruf kecil sehingga perbedaan kapitalisasi dapat diseragamkan. unique() digunakan untuk melihat kategori yang tersedia.

15. MENYERAGAMKAN KATEGORI KOTA

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

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

Variasi penulisan kota yang memiliki makna sama diseragamkan menjadi satu kategori agar tidak dianggap sebagai kategori yang berbeda.

16. MENYERAGAMKAN STATUS

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

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

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

Variasi penulisan status diseragamkan. Misalnya aktif, active, dan a menjadi Aktif, sedangkan tidak aktif dan nonaktif menjadi Tidak Aktif.

17. MENGHAPUS DUPLIKASI

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

rownames(pelanggan) <- NULL

pelanggan
##    customer_id   nama usia pendapatan      kota      status
## 1         C001    Ani   21    4.5e+06 Pekanbaru       Aktif
## 2         C002   Budi   25         NA Pekanbaru       Aktif
## 3         C003  Citra   23    5.2e+06 Pekanbaru       Aktif
## 4         C004   Dodi  150    4.8e+06     Dumai       Aktif
## 5         C005    Eka   27    4.9e+06 Pekanbaru Tidak Aktif
## 6         C006   Fani   NA    5.1e+06     Dumai Tidak Aktif
## 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 Pekanbaru       Aktif
## 10        C010   Joko   35    5.3e+06     Dumai Tidak Aktif
## 11        C011   Kiki   28         NA      <NA>       Aktif

Baris duplikat dihapus berdasarkan customer_id. Kemunculan kedua C010 dihapus. rownames() digunakan untuk mengatur kembali nomor baris.

18. MEMERIKSA USIA TIDAK VALID

pelanggan[
  pelanggan$usia < 15 |
    pelanggan$usia > 100,
]
##    customer_id nama usia pendapatan  kota status
## 4         C004 Dodi  150    4800000 Dumai  Aktif
## NA        <NA> <NA>   NA         NA  <NA>   <NA>

Sintaks ini mencari nilai usia yang berada di luar rentang yang dianggap wajar. Pada data terdapat usia 150 untuk C004.

19. MENGOREKSI USIA

pelanggan$usia[
  pelanggan$customer_id == "C004"
] <- 50

Nilai usia 150 pada C004 dikoreksi menjadi 50 sesuai skenario data yang diberikan.

20. MENCARI BARIS YANG MEMILIKI MISSING VALUE

pelanggan[
  !complete.cases(pelanggan),
]
##    customer_id nama usia pendapatan      kota      status
## 2         C002 Budi   25         NA Pekanbaru       Aktif
## 6         C006 Fani   NA    5100000     Dumai Tidak Aktif
## 11        C011 Kiki   28         NA      <NA>       Aktif

complete.cases() digunakan untuk menemukan baris yang lengkap. Tanda ! membalik hasilnya sehingga baris yang memiliki minimal satu NA dapat ditampilkan.

21. MEMBANDINGKAN PENGHAPUSAN BARIS MISSING

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

nrow(pelanggan)
## [1] 11
nrow(pelanggan_complete)
## [1] 8

Sintaks ini membuat dataset pembanding dengan menghapus baris yang memiliki missing value. Jumlah baris sebelum dan sesudah penghapusan dibandingkan untuk melihat banyaknya data yang hilang.

22. MENGHITUNG PERSENTASE DATA YANG HILANG

persen_hilang <- (
  1 - nrow(pelanggan_complete) /
    nrow(pelanggan)
) * 100

persen_hilang
## [1] 27.27273

Sintaks ini menghitung persentase baris yang hilang jika strategi penghapusan baris digunakan.

23. MENGHITUNG MEDIAN USIA

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

median_usia
## [1] 27.5

Median digunakan sebagai nilai pengganti untuk usia yang kosong. na.rm = TRUE digunakan agar NA tidak ikut dalam perhitungan median.

24. IMPUTASI MISSING PADA USIA

pelanggan$usia_imputasi <- pelanggan$usia

pelanggan$usia_imputasi[
  is.na(pelanggan$usia_imputasi)
] <- median_usia

pelanggan[
  , c("customer_id", "usia", "usia_imputasi")
]
##    customer_id usia usia_imputasi
## 1         C001   21          21.0
## 2         C002   25          25.0
## 3         C003   23          23.0
## 4         C004   50          50.0
## 5         C005   27          27.0
## 6         C006   NA          27.5
## 7         C007   31          31.0
## 8         C008   29          29.0
## 9         C009   22          22.0
## 10        C010   35          35.0
## 11        C011   28          28.0

Kolom usia disalin menjadi usia_imputasi, kemudian nilai NA diganti menggunakan median usia.

25. MEMBANDINGKAN MEAN DAN MEDIAN PENDAPATAN

mean_pendapatan <- mean(
  pelanggan$pendapatan,
  na.rm = TRUE
)

median_pendapatan <- median(
  pelanggan$pendapatan,
  na.rm = TRUE
)

mean_pendapatan
## [1] 59900000
median_pendapatan
## [1] 4900000

Perbandingan mean dan median dilakukan karena terdapat nilai ekstrem 500000000. Median lebih tahan terhadap pengaruh nilai ekstrem sehingga digunakan untuk imputasi pendapatan.

26. IMPUTASI PENDAPATAN

pelanggan$pendapatan_imputasi <- pelanggan$pendapatan

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

pelanggan[
  , c(
    "customer_id",
    "pendapatan",
    "pendapatan_imputasi"
  )
]
##    customer_id pendapatan pendapatan_imputasi
## 1         C001    4.5e+06             4.5e+06
## 2         C002         NA             4.9e+06
## 3         C003    5.2e+06             5.2e+06
## 4         C004    4.8e+06             4.8e+06
## 5         C005    4.9e+06             4.9e+06
## 6         C006    5.1e+06             5.1e+06
## 7         C007    5.0e+08             5.0e+08
## 8         C008    4.7e+06             4.7e+06
## 9         C009    4.6e+06             4.6e+06
## 10        C010    5.3e+06             5.3e+06
## 11        C011         NA             4.9e+06

Nilai pendapatan yang NA diganti menggunakan median pendapatan.

27. IMPUTASI KOTA

pelanggan$kota_imputasi <- pelanggan$kota

pelanggan$kota_imputasi[
  is.na(pelanggan$kota_imputasi)
] <- "Tidak diketahui"

pelanggan[
  , c("customer_id", "kota", "kota_imputasi")
]
##    customer_id      kota   kota_imputasi
## 1         C001 Pekanbaru       Pekanbaru
## 2         C002 Pekanbaru       Pekanbaru
## 3         C003 Pekanbaru       Pekanbaru
## 4         C004     Dumai           Dumai
## 5         C005 Pekanbaru       Pekanbaru
## 6         C006     Dumai           Dumai
## 7         C007 Pekanbaru       Pekanbaru
## 8         C008      Siak            Siak
## 9         C009 Pekanbaru       Pekanbaru
## 10        C010     Dumai           Dumai
## 11        C011      <NA> Tidak diketahui

Nilai kota yang kosong diberi kategori Tidak diketahui agar informasi bahwa data tidak tersedia tetap dipertahankan.

28. MEMBUAT INDIKATOR MISSING

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

table(pelanggan$pendapatan_missing)
## 
## 0 1 
## 9 2

Variabel pendapatan_missing menjadi penanda keberadaan missing value. Nilai 1 berarti pendapatan awalnya kosong dan 0 berarti tersedia.

29. BOXPLOT UNTUK OUTLIER

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

Boxplot digunakan untuk melihat penyebaran data dan menemukan kandidat outlier secara visual.

30. MENGHITUNG IQR

Q1 <- quantile(
  pelanggan$pendapatan_imputasi,
  0.25
)

Q3 <- quantile(
  pelanggan$pendapatan_imputasi,
  0.75
)

IQR_pendapatan <- IQR(
  pelanggan$pendapatan_imputasi
)

batas_bawah <- Q1 - 1.5 * IQR_pendapatan
batas_atas <- Q3 + 1.5 * IQR_pendapatan

Q1
##     25% 
## 4750000
Q3
##     75% 
## 5150000
IQR_pendapatan
## [1] 4e+05
batas_bawah
##     25% 
## 4150000
batas_atas
##     75% 
## 5750000

IQR dihitung dengan rumus IQR = Q3 - Q1. Kandidat outlier ditentukan menggunakan batas bawah Q1 - 1,5 × IQR dan batas atas Q3 + 1,5 × IQR.

31. MENANDAI OUTLIER

pelanggan$outlier_pendapatan <-
  pelanggan$pendapatan_imputasi < batas_bawah |
  pelanggan$pendapatan_imputasi > batas_atas

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "nama",
    "pendapatan_imputasi"
  )
]
##   customer_id   nama pendapatan_imputasi
## 7        C007 Gilang               5e+08

Kolom outlier_pendapatan memberikan tanda TRUE untuk nilai yang berada di luar batas IQR dan FALSE untuk nilai lainnya.

32. WINSORIZING

pelanggan$pendapatan_winsor <- pmin(
  pmax(
    pelanggan$pendapatan_imputasi,
    batas_bawah
  ),
  batas_atas
)

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "pendapatan_imputasi",
    "pendapatan_winsor"
  )
]
##   customer_id pendapatan_imputasi pendapatan_winsor
## 7        C007               5e+08           5750000

Winsorizing menangani nilai ekstrem dengan membatasi nilai pada batas bawah dan batas atas tanpa menghapus baris data.

33. MEMBUAT FUNGSI MIN-MAX

minmax <- function(x) {
  (x - min(x, na.rm = TRUE)) /
    (max(x, na.rm = TRUE) -
       min(x, na.rm = TRUE))
}

Fungsi ini digunakan untuk melakukan normalisasi Min-Max. Hasil transformasi berada pada rentang 0 sampai 1.

34. NORMALISASI MIN-MAX

pelanggan$usia_minmax <- minmax(
  pelanggan$usia_imputasi
)

pelanggan$pendapatan_minmax <- minmax(
  pelanggan$pendapatan_imputasi
)

pelanggan[
  , c(
    "customer_id",
    "usia_minmax",
    "pendapatan_minmax"
  )
]
##    customer_id usia_minmax pendapatan_minmax
## 1         C001  0.00000000      0.0000000000
## 2         C002  0.13793103      0.0008072654
## 3         C003  0.06896552      0.0014127144
## 4         C004  1.00000000      0.0006054490
## 5         C005  0.20689655      0.0008072654
## 6         C006  0.22413793      0.0012108981
## 7         C007  0.34482759      1.0000000000
## 8         C008  0.27586207      0.0004036327
## 9         C009  0.03448276      0.0002018163
## 10        C010  0.48275862      0.0016145308
## 11        C011  0.24137931      0.0008072654

Variabel usia dan pendapatan ditransformasikan menggunakan Min-Max agar berada pada skala yang sama.

35. NORMALISASI Z-SCORE

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

pelanggan$pendapatan_z <- as.numeric(
  scale(pelanggan$pendapatan_imputasi)
)

pelanggan[
  , c(
    "customer_id",
    "usia_z",
    "pendapatan_z"
  )
]
##    customer_id       usia_z pendapatan_z
## 1         C001 -0.984199667   -0.3041235
## 2         C002 -0.489287834   -0.3014440
## 3         C003 -0.736743750   -0.2994343
## 4         C004  2.603911118   -0.3021138
## 5         C005 -0.241831918   -0.3014440
## 6         C006 -0.179967939   -0.3001042
## 7         C007  0.253079914    3.0151095
## 8         C008  0.005623998   -0.3027837
## 9         C009 -0.860471709   -0.3034536
## 10        C010  0.747991747   -0.2987645
## 11        C011 -0.118103960   -0.3014440

Fungsi scale() digunakan untuk standardisasi z-score. Hasilnya menunjukkan posisi nilai terhadap rata-rata dalam satuan simpangan baku.

36. DECIMAL SCALING

decimal_scale <- function(x) {
  nilai_maks <- max(
    abs(x),
    na.rm = TRUE
  )

  j <- ceiling(
    log10(nilai_maks + 1)
  )

  x / (10^j)
}

Fungsi ini melakukan decimal scaling, yaitu mengecilkan skala data dengan membagi nilai menggunakan pangkat sepuluh.

37. MENERAPKAN DECIMAL SCALING

pelanggan$pendapatan_decimal <-
  decimal_scale(
    pelanggan$pendapatan_imputasi
  )

pelanggan[
  , c(
    "customer_id",
    "pendapatan_imputasi",
    "pendapatan_decimal"
  )
]
##    customer_id pendapatan_imputasi pendapatan_decimal
## 1         C001             4.5e+06             0.0045
## 2         C002             4.9e+06             0.0049
## 3         C003             5.2e+06             0.0052
## 4         C004             4.8e+06             0.0048
## 5         C005             4.9e+06             0.0049
## 6         C006             5.1e+06             0.0051
## 7         C007             5.0e+08             0.5000
## 8         C008             4.7e+06             0.0047
## 9         C009             4.6e+06             0.0046
## 10        C010             5.3e+06             0.0053
## 11        C011             4.9e+06             0.0049

Pendapatan yang telah melalui preprocessing ditransformasikan menggunakan decimal scaling dan disimpan pada kolom baru.

38. MEMBANDINGKAN HASIL TRANSFORMASI

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

transformasi
##    customer_id pendapatan_imputasi pendapatan_minmax pendapatan_z
## 1         C001             4.5e+06      0.0000000000   -0.3041235
## 2         C002             4.9e+06      0.0008072654   -0.3014440
## 3         C003             5.2e+06      0.0014127144   -0.2994343
## 4         C004             4.8e+06      0.0006054490   -0.3021138
## 5         C005             4.9e+06      0.0008072654   -0.3014440
## 6         C006             5.1e+06      0.0012108981   -0.3001042
## 7         C007             5.0e+08      1.0000000000    3.0151095
## 8         C008             4.7e+06      0.0004036327   -0.3027837
## 9         C009             4.6e+06      0.0002018163   -0.3034536
## 10        C010             5.3e+06      0.0016145308   -0.2987645
## 11        C011             4.9e+06      0.0008072654   -0.3014440
##    pendapatan_decimal
## 1              0.0045
## 2              0.0049
## 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.0049

Tabel ini digunakan untuk membandingkan nilai pendapatan sebelum transformasi dengan hasil Min-Max, z-score, dan decimal scaling.

39. MENGECEK DAMPAK OUTLIER

pelanggan$pendapatan_winsor_minmax <-
  minmax(
    pelanggan$pendapatan_winsor
  )

plot(
  pelanggan$pendapatan_minmax,
  pelanggan$pendapatan_winsor_minmax,
  pch = 19,
  xlab = "Min-Max Data Asli",
  ylab = "Min-Max Data Winsorized",
  main = "Dampak Outlier terhadap Normalisasi"
)

abline(
  0,
  1,
  lty = 2
)

Grafik digunakan untuk membandingkan hasil Min-Max sebelum dan sesudah winsorizing sehingga dampak nilai ekstrem terhadap transformasi dapat dilihat.

40. MEMERIKSA IDENTIFIER SEBELUM INTEGRASI

setdiff(
  pelanggan$customer_id,
  transaksi_raw$cust_id
)
## [1] "C011"
setdiff(
  transaksi_raw$cust_id,
  pelanggan$customer_id
)
## [1] "C012"

setdiff() digunakan untuk mencari identifier yang hanya terdapat pada salah satu dataset. Dengan demikian dapat diketahui pelanggan yang tidak mempunyai pasangan transaksi dan transaksi yang tidak mempunyai pasangan data pelanggan.

41. MENYAMAKAN NAMA IDENTIFIER

transaksi <- transaksi_raw

names(transaksi)[
  names(transaksi) == "cust_id"
] <- "customer_id"

names(transaksi)
## [1] "customer_id"      "jumlah_transaksi" "total_purchase"

cust_id pada data transaksi diubah menjadi customer_id agar kedua dataset memiliki nama kunci yang sama.

42. MENGGABUNGKAN DATA

data_terintegrasi <- merge(
  pelanggan,
  transaksi,
  by = "customer_id",
  all.x = TRUE
)

data_terintegrasi
##    customer_id   nama usia pendapatan      kota      status usia_imputasi
## 1         C001    Ani   21    4.5e+06 Pekanbaru       Aktif          21.0
## 2         C002   Budi   25         NA Pekanbaru       Aktif          25.0
## 3         C003  Citra   23    5.2e+06 Pekanbaru       Aktif          23.0
## 4         C004   Dodi   50    4.8e+06     Dumai       Aktif          50.0
## 5         C005    Eka   27    4.9e+06 Pekanbaru Tidak Aktif          27.0
## 6         C006   Fani   NA    5.1e+06     Dumai Tidak Aktif          27.5
## 7         C007 Gilang   31    5.0e+08 Pekanbaru       Aktif          31.0
## 8         C008   Hana   29    4.7e+06      Siak       Aktif          29.0
## 9         C009  Indra   22    4.6e+06 Pekanbaru       Aktif          22.0
## 10        C010   Joko   35    5.3e+06     Dumai Tidak Aktif          35.0
## 11        C011   Kiki   28         NA      <NA>       Aktif          28.0
##    pendapatan_imputasi   kota_imputasi pendapatan_missing outlier_pendapatan
## 1              4.5e+06       Pekanbaru                  0              FALSE
## 2              4.9e+06       Pekanbaru                  1              FALSE
## 3              5.2e+06       Pekanbaru                  0              FALSE
## 4              4.8e+06           Dumai                  0              FALSE
## 5              4.9e+06       Pekanbaru                  0              FALSE
## 6              5.1e+06           Dumai                  0              FALSE
## 7              5.0e+08       Pekanbaru                  0               TRUE
## 8              4.7e+06            Siak                  0              FALSE
## 9              4.6e+06       Pekanbaru                  0              FALSE
## 10             5.3e+06           Dumai                  0              FALSE
## 11             4.9e+06 Tidak diketahui                  1              FALSE
##    pendapatan_winsor usia_minmax pendapatan_minmax       usia_z pendapatan_z
## 1            4500000  0.00000000      0.0000000000 -0.984199667   -0.3041235
## 2            4900000  0.13793103      0.0008072654 -0.489287834   -0.3014440
## 3            5200000  0.06896552      0.0014127144 -0.736743750   -0.2994343
## 4            4800000  1.00000000      0.0006054490  2.603911118   -0.3021138
## 5            4900000  0.20689655      0.0008072654 -0.241831918   -0.3014440
## 6            5100000  0.22413793      0.0012108981 -0.179967939   -0.3001042
## 7            5750000  0.34482759      1.0000000000  0.253079914    3.0151095
## 8            4700000  0.27586207      0.0004036327  0.005623998   -0.3027837
## 9            4600000  0.03448276      0.0002018163 -0.860471709   -0.3034536
## 10           5300000  0.48275862      0.0016145308  0.747991747   -0.2987645
## 11           4900000  0.24137931      0.0008072654 -0.118103960   -0.3014440
##    pendapatan_decimal pendapatan_winsor_minmax jumlah_transaksi total_purchase
## 1              0.0045                     0.00                5        1.5e+06
## 2              0.0049                     0.32                3        9.0e+05
## 3              0.0052                     0.56                7        2.7e+06
## 4              0.0048                     0.24                2        6.0e+05
## 5              0.0049                     0.32                6        2.1e+06
## 6              0.0051                     0.48                4        1.3e+06
## 7              0.5000                     1.00               20        2.5e+07
## 8              0.0047                     0.16                5        1.7e+06
## 9              0.0046                     0.08                3        8.0e+05
## 10             0.0053                     0.64                8        3.2e+06
## 11             0.0049                     0.32               NA             NA

merge() digunakan untuk menggabungkan data pelanggan dan transaksi berdasarkan customer_id. all.x = TRUE memastikan seluruh pelanggan tetap dipertahankan walaupun tidak memiliki transaksi.

43. MEMVALIDASI HASIL INTEGRASI

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

Jumlah baris sebelum dan sesudah integrasi dibandingkan. Selain itu, duplikasi customer_id diperiksa untuk memastikan penggabungan tidak menghasilkan data yang tidak sesuai.

44. MENGECEK MISSING PADA DATA TRANSAKSI

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

Kode ini menghitung jumlah missing value pada variabel transaksi setelah penggabungan. NA dapat muncul ketika pelanggan tidak memiliki pasangan transaksi.

45. MELIHAT PELANGGAN TANPA TRANSAKSI

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

Sintaks ini menampilkan pelanggan yang tidak mempunyai data transaksi setelah proses integrasi.

46. MENGUBAH NA TRANSAKSI MENJADI 0

data_terintegrasi$jumlah_transaksi_final <-
  data_terintegrasi$jumlah_transaksi

data_terintegrasi$total_purchase_final <-
  data_terintegrasi$total_purchase

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

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

NA pada transaksi diubah menjadi 0 jika NA memang memiliki arti bahwa pelanggan tidak melakukan transaksi. Jika arti NA belum diketahui, sebaiknya nilai tersebut tidak langsung diubah menjadi nol.

47. MEMBUAT DATASET AKHIR

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_final",
    "total_purchase_final"
  )
]

data_final
##    customer_id   nama usia_imputasi   kota_imputasi      status
## 1         C001    Ani          21.0       Pekanbaru       Aktif
## 2         C002   Budi          25.0       Pekanbaru       Aktif
## 3         C003  Citra          23.0       Pekanbaru       Aktif
## 4         C004   Dodi          50.0           Dumai       Aktif
## 5         C005    Eka          27.0       Pekanbaru Tidak Aktif
## 6         C006   Fani          27.5           Dumai Tidak Aktif
## 7         C007 Gilang          31.0       Pekanbaru       Aktif
## 8         C008   Hana          29.0            Siak       Aktif
## 9         C009  Indra          22.0       Pekanbaru       Aktif
## 10        C010   Joko          35.0           Dumai Tidak Aktif
## 11        C011   Kiki          28.0 Tidak diketahui       Aktif
##    pendapatan_imputasi pendapatan_missing outlier_pendapatan usia_minmax
## 1              4.5e+06                  0              FALSE  0.00000000
## 2              4.9e+06                  1              FALSE  0.13793103
## 3              5.2e+06                  0              FALSE  0.06896552
## 4              4.8e+06                  0              FALSE  1.00000000
## 5              4.9e+06                  0              FALSE  0.20689655
## 6              5.1e+06                  0              FALSE  0.22413793
## 7              5.0e+08                  0               TRUE  0.34482759
## 8              4.7e+06                  0              FALSE  0.27586207
## 9              4.6e+06                  0              FALSE  0.03448276
## 10             5.3e+06                  0              FALSE  0.48275862
## 11             4.9e+06                  1              FALSE  0.24137931
##    pendapatan_minmax jumlah_transaksi_final total_purchase_final
## 1       0.0000000000                      5              1.5e+06
## 2       0.0008072654                      3              9.0e+05
## 3       0.0014127144                      7              2.7e+06
## 4       0.0006054490                      2              6.0e+05
## 5       0.0008072654                      6              2.1e+06
## 6       0.0012108981                      4              1.3e+06
## 7       1.0000000000                     20              2.5e+07
## 8       0.0004036327                      5              1.7e+06
## 9       0.0002018163                      3              8.0e+05
## 10      0.0016145308                      8              3.2e+06
## 11      0.0008072654                      0              0.0e+00

Dataset akhir berisi identitas pelanggan, data yang telah dibersihkan, indikator missing, penanda outlier, hasil transformasi, dan informasi transaksi.

48. MERAPIKAN NAMA KOLOM

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

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

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             4.9e+06
## 3         C003  Citra 23.0       Pekanbaru       Aktif             5.2e+06
## 4         C004   Dodi 50.0           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             4.9e+06
##    pendapatan_missing outlier_pendapatan usia_minmax pendapatan_minmax
## 1                   0              FALSE  0.00000000      0.0000000000
## 2                   1              FALSE  0.13793103      0.0008072654
## 3                   0              FALSE  0.06896552      0.0014127144
## 4                   0              FALSE  1.00000000      0.0006054490
## 5                   0              FALSE  0.20689655      0.0008072654
## 6                   0              FALSE  0.22413793      0.0012108981
## 7                   0               TRUE  0.34482759      1.0000000000
## 8                   0              FALSE  0.27586207      0.0004036327
## 9                   0              FALSE  0.03448276      0.0002018163
## 10                  0              FALSE  0.48275862      0.0016145308
## 11                  1              FALSE  0.24137931      0.0008072654
##    jumlah_transaksi_final total_purchase_final
## 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

Nama kolom hasil preprocessing disederhanakan agar dataset akhir lebih mudah dibaca dan digunakan.

49. AUDIT DATASET AKHIR

nrow(data_final)
## [1] 11
ncol(data_final)
## [1] 12
colSums(is.na(data_final))
##            customer_id                   nama                   usia 
##                      0                      0                      0 
##                   kota                 status    pendapatan_imputasi 
##                      0                      0                      0 
##     pendapatan_missing     outlier_pendapatan            usia_minmax 
##                      0                      0                      0 
##      pendapatan_minmax jumlah_transaksi_final   total_purchase_final 
##                      0                      0                      0
sum(
  duplicated(
    data_final$customer_id
  )
)
## [1] 0
str(data_final)
## 'data.frame':    11 obs. of  12 variables:
##  $ customer_id           : chr  "C001" "C002" "C003" "C004" ...
##  $ nama                  : chr  "Ani" "Budi" "Citra" "Dodi" ...
##  $ usia                  : num  21 25 23 50 27 27.5 31 29 22 35 ...
##  $ kota                  : chr  "Pekanbaru" "Pekanbaru" "Pekanbaru" "Dumai" ...
##  $ status                : chr  "Aktif" "Aktif" "Aktif" "Aktif" ...
##  $ pendapatan_imputasi   : num  4.5e+06 4.9e+06 5.2e+06 4.8e+06 4.9e+06 5.1e+06 5.0e+08 4.7e+06 4.6e+06 5.3e+06 ...
##  $ pendapatan_missing    : int  0 1 0 0 0 0 0 0 0 0 ...
##  $ outlier_pendapatan    : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ usia_minmax           : num  0 0.138 0.069 1 0.207 ...
##  $ pendapatan_minmax     : num  0 0.000807 0.001413 0.000605 0.000807 ...
##  $ jumlah_transaksi_final: num  5 3 7 2 6 4 20 5 3 8 ...
##  $ total_purchase_final  : 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 ...
summary(data_final)
##  customer_id            nama                usia           kota          
##  Length:11          Length:11          Min.   :21.00   Length:11         
##  Class :character   Class :character   1st Qu.:24.00   Class :character  
##  Mode  :character   Mode  :character   Median :27.50   Mode  :character  
##                                        Mean   :28.95                     
##                                        3rd Qu.:30.00                     
##                                        Max.   :50.00                     
##     status          pendapatan_imputasi pendapatan_missing outlier_pendapatan
##  Length:11          Min.   :4.50e+06    Min.   :0.0000     Mode :logical     
##  Class :character   1st Qu.:4.75e+06    1st Qu.:0.0000     FALSE:10          
##  Mode  :character   Median :4.90e+06    Median :0.0000     TRUE :1           
##                     Mean   :4.99e+07    Mean   :0.1818                       
##                     3rd Qu.:5.15e+06    3rd Qu.:0.0000                       
##                     Max.   :5.00e+08    Max.   :1.0000                       
##   usia_minmax     pendapatan_minmax   jumlah_transaksi_final
##  Min.   :0.0000   Min.   :0.0000000   Min.   : 0.000        
##  1st Qu.:0.1034   1st Qu.:0.0005045   1st Qu.: 3.000        
##  Median :0.2241   Median :0.0008073   Median : 5.000        
##  Mean   :0.2743   Mean   :0.0916246   Mean   : 5.727        
##  3rd Qu.:0.3103   3rd Qu.:0.0013118   3rd Qu.: 6.500        
##  Max.   :1.0000   Max.   :1.0000000   Max.   :20.000        
##  total_purchase_final
##  Min.   :       0    
##  1st Qu.:  850000    
##  Median : 1500000    
##  Mean   : 3618182    
##  3rd Qu.: 2400000    
##  Max.   :25000000

Audit akhir digunakan untuk memastikan hasil preprocessing sudah sesuai. Pemeriksaan meliputi jumlah baris, jumlah kolom, missing value, duplikasi identifier, struktur data, dan ringkasan statistik.

50. MENYIMPAN DATASET

write.csv(
  data_final,
  "data_pelanggan_setelah_preprocessing.csv",
  row.names = FALSE
)

getwd()
## [1] "C:/Users/arimb/Downloads"

write.csv() digunakan untuk menyimpan dataset akhir dalam format CSV. getwd() digunakan untuk mengetahui folder tempat file disimpan.

Pertanyaan Refleksi

1. Apakah dataset tanpa missing value selalu lebih berkualitas?

Tidak selalu. Menghapus seluruh baris yang memiliki missing value memang dapat menghasilkan dataset yang terlihat lengkap, tetapi jumlah data yang tersedia dapat berkurang. Pada dataset pelanggan, beberapa nilai seperti usia, pendapatan, dan kota memiliki missing value. Jika semua baris tersebut langsung dihapus, informasi pelanggan yang masih berguna dapat ikut hilang. Oleh karena itu, penanganan missing value harus disesuaikan dengan kondisi data dan tujuan analisis. Pada data ini, imputasi median digunakan untuk variabel numerik seperti usia dan pendapatan, sedangkan kota yang kosong diberi kategori “Tidak diketahui”.

2. Mengapa outlier tidak boleh otomatis dihapus?

Outlier tidak selalu merupakan kesalahan data. Nilai yang sangat berbeda dari data lainnya dapat menunjukkan kondisi yang benar-benar terjadi. Pada dataset ini terdapat pendapatan Rp500.000.000 pada pelanggan C007 yang jauh lebih besar dibandingkan pelanggan lainnya. Nilai tersebut merupakan kandidat outlier berdasarkan metode IQR, tetapi tidak langsung dihapus karena masih mungkin merupakan nilai yang benar. Oleh karena itu, outlier perlu diperiksa terlebih dahulu berdasarkan konteks data sebelum menentukan apakah akan dipertahankan, diperbaiki, atau ditangani menggunakan metode seperti winsorizing.

3. Bagaimana preprocessing dapat menimbulkan bias?

Preprocessing dapat menimbulkan bias apabila keputusan yang digunakan untuk membersihkan atau mengubah data tidak sesuai dengan kondisi sebenarnya. Contohnya, jika semua missing value langsung dihapus, kelompok pelanggan tertentu dapat menjadi kurang terwakili. Begitu juga dengan imputasi menggunakan satu nilai yang sama untuk banyak data dapat mengurangi variasi sebenarnya dalam dataset. Pada data ini, pemilihan median untuk pendapatan dilakukan karena terdapat nilai ekstrem yang dapat memengaruhi mean. Setiap keputusan preprocessing harus memiliki alasan yang jelas dan dicatat agar tidak menghasilkan kesimpulan yang menyesatkan.

4. Mengapa parameter imputasi dan transformasi seharusnya dihitung dari data pelatihan?

Parameter seperti median, mean, standar deviasi, nilai minimum, dan maksimum sebaiknya dihitung dari data pelatihan agar informasi dari data pengujian tidak ikut memengaruhi proses preprocessing. Jika parameter dihitung menggunakan seluruh dataset, informasi dari data pengujian dapat masuk ke proses pembentukan model sehingga menyebabkan data leakage. Dengan menggunakan parameter dari data pelatihan saja, proses analisis menjadi lebih adil dan hasil evaluasi model dapat menggambarkan kemampuan model pada data yang belum pernah digunakan sebelumnya.

5. Apa risiko integrasi data jika identifier tidak unik?

Identifier yang tidak unik dapat menyebabkan data bergabung secara tidak tepat dan menghasilkan jumlah baris yang lebih banyak dari seharusnya. Pada dataset pelanggan terdapat customer_id yang muncul lebih dari satu kali, yaitu C010. Jika duplikasi tersebut tidak diperiksa sebelum integrasi, satu pelanggan dapat memiliki beberapa pasangan data transaksi sehingga hasil penggabungan menjadi tidak akurat. Oleh karena itu, identifier perlu diperiksa keunikannya terlebih dahulu sebelum digunakan sebagai kunci untuk menggabungkan dua dataset.

Ringkasan

Alur preprocessing yang telah dilakukan pada data pelanggan dan transaksi adalah:

  1. Memahami struktur dan kualitas awal data, termasuk ukuran, struktur, missing value, dan duplikasi.
  2. Membersihkan data, yaitu menghapus spasi, menyeragamkan kategori kota dan status, menghapus duplikasi, serta memperbaiki nilai usia yang tidak sesuai.
  3. Menangani missing values dengan membandingkan strategi penghapusan baris dan imputasi.
  4. Mendeteksi dan mengevaluasi outlier menggunakan boxplot dan metode IQR.
  5. Melakukan transformasi numerik menggunakan Min-Max, z-score, dan decimal scaling.
  6. Mengintegrasikan data pelanggan dan transaksi dengan menyamakan identifier cust_id menjadi customer_id.
  7. Memvalidasi hasil integrasi dengan memeriksa jumlah baris, duplikasi identifier, dan missing value.
  8. Menyimpan dataset hasil preprocessing dalam bentuk CSV.

Preprocessing pada dataset ini menunjukkan bahwa pembersihan data bukan hanya menjalankan kode R, tetapi juga membutuhkan pertimbangan terhadap kondisi dan makna data. Nilai seperti usia 150, pendapatan 500.000.000, kategori kota yang berbeda penulisan, serta missing value harus diperiksa berdasarkan konteks sebelum dilakukan perubahan. Setiap keputusan preprocessing dapat memengaruhi hasil analisis, sehingga perubahan data perlu dilakukan secara hati-hati, memiliki alasan yang jelas, dan didokumentasikan.

Daftar Pustaka

Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.