1 Pendahuluan

Data pelanggan dan data transaksi dapat dimanfaatkan untuk memperoleh gambaran mengenai karakteristik pelanggan serta pola aktivitas pembelian. Namun, sebelum data digunakan untuk analisis, kondisi dan kualitas data perlu diperiksa terlebih dahulu karena data yang tersedia dapat mengandung berbagai permasalahan.

Dalam pengolahan ini digunakan dua dataset, yaitu dataset pelanggan dan dataset transaksi. Dataset pelanggan memuat informasi terkait identitas pelanggan, usia, pendapatan, kota, serta status pelanggan. Sementara itu, dataset transaksi memuat informasi mengenai jumlah transaksi dan total pembelian yang dilakukan.

Dari kedua dataset tersebut ditemukan beberapa kondisi yang perlu diperhatikan, antara lain nilai yang belum tersedia, perbedaan format penulisan kategori, adanya identifier pelanggan yang berulang, nilai numerik yang berada di luar rentang yang semestinya, serta nilai pendapatan yang memiliki jarak sangat jauh dari observasi lainnya. Selain itu, terdapat perbedaan penamaan identifier pada kedua dataset.

Untuk mengatasi kondisi tersebut, dilakukan beberapa tahapan preprocessing agar data menjadi lebih terstruktur dan sesuai untuk analisis lanjutan. Proses yang dilakukan mencakup pemeriksaan data awal, pembersihan data, penanganan missing value, identifikasi outlier, transformasi variabel, penggabungan dataset, hingga pemeriksaan terhadap hasil akhir.

2 Skenario Pengolahan Data

Kasus yang digunakan dalam pengolahan ini merupakan kasus perusahaan e-commerce yang hendak memperoleh informasi lebih lanjut mengenai pelanggan yang dimilikinya. Data yang tersedia berasal dari dua sumber, yaitu data pelanggan dan data transaksi.

Data pelanggan digunakan untuk memberikan informasi mengenai karakteristik setiap pelanggan, sedangkan data transaksi memberikan gambaran mengenai aktivitas pembelian. Kedua dataset tersebut selanjutnya dikaitkan berdasarkan identifier pelanggan.

Preprocessing dalam kasus ini tidak hanya bertujuan untuk menghilangkan data yang bermasalah. Setiap tindakan terhadap data perlu didasarkan pada alasan yang dapat dipertanggungjawabkan sehingga perubahan yang dilakukan tetap dapat ditelusuri. Dengan pendekatan tersebut, dataset yang dihasilkan diharapkan memiliki kualitas dan konsistensi yang lebih baik untuk analisis selanjutnya.

3 Persiapan RStudio

3.1 Memeriksa lingkungan kerja

Pemeriksaan lingkungan kerja dilakukan untuk mengetahui lokasi direktori yang sedang digunakan serta versi R yang tersedia pada saat pengolahan data.

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

4 Membangun Dataset Praktikum

Dataset pelanggan dan transaksi disusun dengan kondisi tertentu yang sengaja digunakan untuk menggambarkan beberapa permasalahan yang umum ditemukan dalam proses pengolahan data. Kondisi tersebut selanjutnya akan ditangani melalui tahapan preprocessing.

4.1 Data Pelanggan dan Transaksi

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
)

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
)

pelanggan_raw
transaksi_raw

Pada kondisi awal, dataset pelanggan memiliki 12 baris dengan 6 variabel. Beberapa nilai belum tersedia pada variabel usia, pendapatan, dan kota. Selain itu, identifier C010 muncul lebih dari satu kali. Dataset transaksi sendiri terdiri atas 11 baris dan menggunakan identifier dengan nama cust_id.

5 Bagian I — Pemeriksaan Data Awal

5.1 Memahami struktur data

Tahap pemeriksaan awal dilakukan untuk memperoleh informasi mengenai dimensi data, nama variabel, struktur tipe data, contoh observasi, serta ringkasan statistik. Informasi tersebut diperlukan agar karakteristik dataset dapat dipahami sebelum proses perubahan dilakukan.

dim(pelanggan_raw)
## [1] 12  6
names(pelanggan_raw)
## [1] "customer_id" "nama"        "usia"        "pendapatan"  "kota"       
## [6] "status"
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" ...
head(pelanggan_raw)
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  
##                                       
##                                       
##                                       
## 

Dari pemeriksaan struktur dapat diketahui bahwa dataset pelanggan memiliki 12 observasi dan 6 variabel. Variabel customer_id, nama, kota, dan status bertipe karakter, sedangkan usia dan pendapatan termasuk variabel numerik.

5.2 Mengukur kualitas awal

Selanjutnya dilakukan pengecekan kualitas untuk mengidentifikasi masalah yang terdapat pada dataset. Pemeriksaan diarahkan pada keberadaan missing value, data yang berulang, variasi kategori, dan rentang nilai pada variabel numerik.

colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0
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
sum(duplicated(pelanggan_raw))
## [1] 1
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1
sort(unique(pelanggan_raw$kota))
## [1] " PKU"       "Dumai"      "DUMAI"      "pekanbaru"  "Pekanbaru" 
## [6] "PEKANBARU"  "Pekanbaru " "PKU"        "Siak"
sort(unique(pelanggan_raw$status))
## [1] "A"           "ACTIVE"      "aktif"       "Aktif"       "AKTIF"      
## [6] "nonaktif"    "Tidak aktif" "Tidak Aktif"
range(pelanggan_raw$usia, na.rm = TRUE)
## [1]  21 150
range(pelanggan_raw$pendapatan, na.rm = TRUE)
## [1] 4.5e+06 5.0e+08

Hasil pengecekan memperlihatkan satu nilai hilang pada usia, dua nilai hilang pada pendapatan, dan satu nilai hilang pada kota. Selain itu, terdapat satu customer_id yang muncul kembali, yaitu C010. Pada variabel kota dan status juga ditemukan beberapa bentuk penulisan yang berbeda walaupun merujuk pada kategori yang sama.

Pada variabel usia terdapat nilai maksimum 150 tahun. Sementara itu, pendapatan memiliki nilai maksimum 500 juta. Kedua nilai tersebut selanjutnya perlu ditinjau agar dapat ditentukan apakah merupakan kesalahan data atau kondisi yang memang valid.

5.3 Membuat fungsi ringkasan kualitas

Agar proses audit dapat dilakukan secara lebih praktis, dibuat sebuah fungsi yang merangkum informasi penting dari setiap variabel, meliputi tipe data, jumlah dan persentase missing value, serta banyaknya nilai unik.

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

Audit awal memberikan gambaran mengenai kualitas dataset sebelum dilakukan tindakan preprocessing. Informasi tersebut kemudian digunakan sebagai pertimbangan dalam menentukan langkah perbaikan yang diperlukan.

6 Bagian II — Implementasi Data Cleaning

6.1 Membuat salinan kerja

Dataset asli tidak langsung dimodifikasi. Sebagai gantinya, dibuat salinan kerja sehingga kondisi data sebelum dan sesudah preprocessing masih dapat dibandingkan.

pelanggan <- pelanggan_raw

6.2 Membersihkan spasi dan kapitalisasi

Beberapa nilai pada variabel kota memiliki spasi yang tidak diperlukan, sementara penulisan kategori menggunakan kombinasi huruf besar dan huruf kecil. Jika kondisi ini dibiarkan, satu kategori dapat terbaca sebagai beberapa kategori berbeda.

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

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

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

Setelah dilakukan penghapusan spasi tambahan dan penyamaan kapitalisasi, variasi penulisan kategori menjadi lebih mudah dikenali dan diseragamkan pada tahap berikutnya.

6.3 Menyeragamkan kategori

Nilai pada variabel kota dan status selanjutnya dipetakan ke kategori yang telah ditentukan agar setiap kategori memiliki satu bentuk penulisan.

pelanggan$kota[pelanggan$kota %in%
                 c("pku", "pekanbaru")] <- "Pekanbaru"

pelanggan$kota[pelanggan$kota == "dumai"] <- "Dumai"
pelanggan$kota[pelanggan$kota == "siak"] <- "Siak"

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

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

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

Setelah standardisasi dilakukan, variabel kota memiliki tiga kategori, yakni Pekanbaru, Dumai, dan Siak. Pada variabel status tersisa dua kategori, yaitu Aktif dan Tidak Aktif.

6.4 Mendeteksi dan menghapus duplikasi

Identifier pelanggan seharusnya bersifat unik karena satu identifier merepresentasikan satu pelanggan. Oleh sebab itu, kemunculan customer_id yang sama perlu diperiksa sebelum data digunakan lebih lanjut.

pelanggan[
  duplicated(pelanggan$customer_id) |
    duplicated(pelanggan$customer_id, fromLast = TRUE),
]

Pemeriksaan menemukan identifier C010 sebanyak dua kali dengan informasi yang sama. Karena identifier tersebut harus bersifat unik, salah satu baris duplikat kemudian dikeluarkan dari dataset.

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

rownames(pelanggan) <- NULL

dim(pelanggan)
## [1] 11  6

Setelah duplikasi dihilangkan, dataset pelanggan terdiri atas 11 baris.

6.5 Memeriksa aturan domain

Selain melihat struktur dan kelengkapan data, nilai numerik juga perlu dibandingkan dengan aturan domain yang relevan. Untuk variabel usia, digunakan batas usia 15 hingga 100 tahun.

pelanggan[
  pelanggan$usia < 15 |
    pelanggan$usia > 100,
]

Hasil pemeriksaan menemukan usia 150 tahun pada C004. Nilai tersebut berada di luar rentang yang ditetapkan sehingga dianggap tidak sesuai. Berdasarkan informasi sumber asli pada skenario, nilai tersebut diperbaiki menjadi 50.

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

Pada variabel pendapatan dilakukan pemeriksaan tambahan untuk memastikan tidak terdapat nilai negatif.

pelanggan[
  pelanggan$pendapatan < 0,
]

Tidak terdapat observasi dengan pendapatan negatif. Dengan demikian, tidak diperlukan perubahan pada nilai pendapatan berdasarkan aturan tersebut.

6.6 Membuat log perubahan

Agar seluruh tindakan preprocessing dapat ditelusuri, setiap perubahan penting dicatat dalam sebuah tabel log.

log_perubahan <- data.frame(
  tahap = c(
    "Standardisasi",
    "Standardisasi",
    "Deduplikasi",
    "Koreksi domain"
  ),
  atribut = c(
    "kota",
    "status",
    "customer_id",
    "usia"
  ),
  tindakan = c(
    "PKU dan variasi kapital menjadi Pekanbaru",
    "ACTIVE/A menjadi Aktif; nonaktif menjadi Tidak Aktif",
    "Menghapus kemunculan kedua C010",
    "Usia C004 dikoreksi dari 150 menjadi 50 berdasarkan sumber asli"
  ),
  stringsAsFactors = FALSE
)

log_perubahan

7 Bagian III — Penanganan Missing Values

7.1 Mengidentifikasi lokasi nilai hilang

Setelah proses cleaning selesai, pemeriksaan missing value dilakukan kembali. Tujuannya adalah mengetahui nilai yang masih kosong serta menentukan metode penanganan yang sesuai.

colSums(is.na(pelanggan))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0
pelanggan[
  !complete.cases(pelanggan),
]

Nilai yang masih kosong terdapat pada variabel usia, pendapatan, dan kota. Kondisi tersebut kemudian dipertimbangkan dalam menentukan strategi penanganan missing value.

7.2 Strategi 1: menghapus baris

Salah satu pendekatan yang dapat digunakan adalah menghilangkan seluruh observasi yang memiliki nilai kosong. Untuk mengetahui dampaknya, pendekatan tersebut dicoba terlebih dahulu sebagai simulasi.

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

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

Penghapusan observasi yang tidak lengkap menyebabkan jumlah data berkurang cukup besar. Karena kehilangan observasi cukup berarti, metode penghapusan baris tidak dipilih sebagai solusi utama pada pengolahan ini.

7.3 Strategi 2: imputasi mean dan median

Pada pendapatan terdapat nilai 500 juta yang jauh lebih tinggi dibandingkan sebagian besar observasi. Kondisi tersebut berpotensi menyebabkan rata-rata menjadi kurang representatif, sehingga nilai mean dibandingkan dengan median terlebih dahulu.

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

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

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

Perbedaan antara mean dan median memperlihatkan adanya pengaruh kuat dari nilai ekstrem terhadap rata-rata pendapatan. Oleh karena itu, median dipilih sebagai dasar imputasi untuk nilai pendapatan yang hilang.

pelanggan$pendapatan_imputasi <-
  pelanggan$pendapatan

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

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

pelanggan$usia_imputasi <-
  pelanggan$usia

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

pelanggan[
  ,
  c(
    "customer_id",
    "usia",
    "usia_imputasi",
    "pendapatan",
    "pendapatan_imputasi"
  )
]

Variabel hasil imputasi dibuat terpisah sehingga nilai asli tetap dapat dilihat. Dengan demikian, proses perubahan data dapat dibandingkan dengan kondisi sebelum imputasi.

7.4 Strategi 3: imputasi nilai kategorik

Untuk nilai kota yang tidak tersedia, pengisian dengan modus tidak langsung digunakan. Hal ini dilakukan agar informasi mengenai ketidakpastian lokasi pelanggan tetap terlihat dalam dataset.

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 
##               3               6               1               1

7.5 Menambahkan indikator missing

Selain mengisi nilai yang kosong, informasi bahwa suatu nilai awalnya missing juga dipertahankan melalui variabel indikator. Cara ini memungkinkan kondisi awal data tetap diketahui setelah imputasi.

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

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

7.6 Membandingkan distribusi sebelum dan sesudah imputasi

Untuk melihat pengaruh imputasi terhadap pola data, distribusi pendapatan sebelum dan sesudah pengisian nilai kosong dibandingkan secara visual.

par(mfrow = c(1, 2))

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

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

par(mfrow = c(1, 1))

Pengisian missing value membuat data menjadi lebih lengkap, tetapi perubahan tersebut dapat memengaruhi bentuk distribusi dan tingkat variasi. Karena itu, metode imputasi yang dipilih perlu dicatat sebagai bagian dari proses preprocessing.

8 Bagian IV — Penanganan Outlier

8.1 Visualisasi dengan boxplot

Keberadaan nilai ekstrem pada pendapatan diamati terlebih dahulu melalui boxplot agar perbedaan antara nilai umum dan nilai yang jauh dari observasi lainnya dapat terlihat.

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

Dari boxplot terlihat adanya satu observasi dengan pendapatan yang jauh lebih tinggi dibandingkan nilai lainnya.

8.2 Menghitung batas IQR

Untuk menentukan kandidat outlier secara lebih terukur digunakan pendekatan Interquartile Range (IQR).

q1 <- quantile(
  pelanggan$pendapatan_imputasi,
  0.25
)

q3 <- quantile(
  pelanggan$pendapatan_imputasi,
  0.75
)

iqr <- IQR(
  pelanggan$pendapatan_imputasi
)

batas_bawah <- q1 -
  1.5 * iqr

batas_atas <- q3 +
  1.5 * iqr

c(
  Q1 = q1,
  Q3 = q3,
  IQR = iqr,
  batas_bawah = batas_bawah,
  batas_atas = batas_atas
)
##          Q1.25%          Q3.75%             IQR batas_bawah.25%  batas_atas.75% 
##         4750000         5150000          400000         4150000         5750000

Hasil perhitungan menghasilkan batas bawah sebesar 4.150.000 dan batas atas sebesar 5.750.000. Observasi yang berada di luar interval tersebut selanjutnya dikategorikan sebagai kandidat outlier.

8.3 Menandai kandidat outlier

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

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "nama",
    "pendapatan_imputasi"
  )
]

Berdasarkan hasil deteksi, C007 dengan pendapatan sebesar 500.000.000 teridentifikasi sebagai kandidat outlier.

8.4 Mengevaluasi tindakan terhadap outlier

Nilai yang terdeteksi sebagai outlier tidak serta-merta dianggap sebagai kesalahan dan dihapus. Nilai 500 juta masih mungkin mencerminkan kondisi pelanggan yang sebenarnya, misalnya pelanggan dengan tingkat pendapatan yang memang lebih tinggi. Karena tidak ditemukan bukti yang menunjukkan bahwa nilai tersebut merupakan kesalahan pencatatan, nilai aslinya tetap dipertahankan.

Sebagai metode pembanding, dibuat pula data hasil winsorizing dengan membatasi nilai ekstrem pada batas atas yang telah dihitung.

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

pelanggan[
  pelanggan$outlier_pendapatan,
  c(
    "customer_id",
    "pendapatan_imputasi",
    "pendapatan_winsor"
  )
]

Winsorizing digunakan untuk memberikan gambaran mengenai perubahan data apabila nilai ekstrem dibatasi. Metode tersebut tidak digunakan untuk menggantikan nilai asli secara langsung.

9 Bagian V — Transformasi Data

Transformasi dilakukan untuk menyesuaikan skala variabel numerik agar dapat digunakan secara lebih sesuai dalam analisis tertentu. Beberapa metode transformasi dibandingkan untuk melihat karakteristik hasil yang diperoleh.

9.1 Normalisasi min–maks

Normalisasi min–maks mengubah nilai ke dalam skala antara 0 dan 1 dengan menggunakan rumus berikut:

\[ x' = \frac{x-\min(x)}{\max(x)-\min(x)} \]

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",
    "usia_minmax",
    "pendapatan_imputasi",
    "pendapatan_minmax"
  )
]

Hasil normalisasi menghasilkan nilai dalam interval 0 hingga 1. Namun, pada variabel pendapatan, nilai ekstrem menyebabkan sebagian besar observasi memiliki nilai normalisasi yang sangat kecil.

9.2 Normalisasi z-score

Z-score mengubah data berdasarkan posisi setiap observasi terhadap rata-rata dan simpangan bakunya.

\[ z = \frac{x-\bar{x}}{s} \]

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

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

round(
  pelanggan[
    ,
    c(
      "usia_z",
      "pendapatan_z"
    )
  ],
  3
)

9.3 Decimal scaling

Decimal scaling merupakan teknik transformasi yang mengurangi skala nilai dengan membaginya menggunakan suatu pangkat sepuluh.

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

  if (maks == 0) {
    return(x)
  }

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

  x / (10 ^ j)
}

pelanggan$pendapatan_decimal <-
  decimal_scale(
    pelanggan$pendapatan_imputasi
  )

range(
  pelanggan$pendapatan_decimal,
  na.rm = TRUE
)
## [1] 0.0045 0.5000

9.4 Membandingkan metode transformasi

Hasil dari ketiga teknik transformasi kemudian disajikan secara bersamaan agar perubahan skala yang dihasilkan masing-masing metode dapat dibandingkan.

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

transformasi

Ketiga metode menghasilkan skala yang berbeda karena masing-masing memiliki mekanisme transformasi yang berbeda. Oleh karena itu, penggunaan metode tertentu sebaiknya disesuaikan dengan kebutuhan dan tujuan analisis yang akan dilakukan.

9.5 Dampak outlier terhadap normalisasi

Pengaruh nilai ekstrem terhadap hasil normalisasi diperiksa dengan membandingkan data pendapatan asli dengan data yang telah melalui proses winsorizing.

pelanggan$pendapatan_winsor_minmax <-
  minmax(
    pelanggan$pendapatan_winsor
  )

plot(
  pelanggan$pendapatan_minmax,
  pelanggan$pendapatan_winsor_minmax,
  pch = 19,
  col = "navy",
  xlab = "Min–maks Data Asli",
  ylab = "Min–maks Data Winsorized",
  main = "Dampak Penanganan Outlier terhadap Normalisasi"
)

abline(
  0,
  1,
  col = "red",
  lty = 2
)

Perbandingan tersebut memperlihatkan bahwa keberadaan nilai ekstrem dapat menyebabkan rentang hasil normalisasi menjadi sangat sempit bagi sebagian besar observasi. Hal ini menunjukkan pentingnya mengevaluasi outlier sebelum menentukan teknik transformasi.

10 Bagian VI — Integrasi Data

10.1 Memeriksa kunci pada kedua sumber

Sebelum proses penggabungan, identifier pada kedua dataset diperiksa. Langkah ini bertujuan memastikan kunci tidak mengalami duplikasi serta mengetahui apakah terdapat pelanggan yang hanya muncul pada salah satu sumber data.

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

Pemeriksaan menunjukkan bahwa C011 terdapat pada dataset pelanggan, tetapi tidak ditemukan pada dataset transaksi. Sebaliknya, identifier C012 hanya ditemukan pada data transaksi dan tidak terdapat dalam data pelanggan.

10.2 Menyelaraskan nama identifier

Kedua dataset menggunakan nama kolom identifier yang berbeda. Dataset pelanggan menggunakan customer_id, sedangkan dataset transaksi menggunakan cust_id. Agar dapat digunakan sebagai kunci penggabungan, nama tersebut disamakan.

transaksi <- transaksi_raw

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

10.3 Melakukan left join dengan merge()

Penggabungan dilakukan dengan mempertahankan seluruh observasi dari dataset pelanggan. Dengan cara ini, pelanggan yang belum memiliki data transaksi tetap dapat muncul pada dataset hasil penggabungan.

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

data_terintegrasi[
  ,
  c(
    "customer_id",
    "nama",
    "jumlah_transaksi",
    "total_purchase"
  )
]

10.4 Memvalidasi hasil integrasi

Setelah kedua sumber data digabungkan, dilakukan validasi untuk memastikan jumlah observasi, keunikan identifier, serta kondisi missing value pada variabel transaksi.

c(
  sebelum = nrow(pelanggan),
  sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah 
##      11      11
sum(
  duplicated(
    data_terintegrasi$customer_id
  )
)
## [1] 0
colSums(
  is.na(
    data_terintegrasi[
      ,
      c(
        "jumlah_transaksi",
        "total_purchase"
      )
    ]
  )
)
## jumlah_transaksi   total_purchase 
##                1                1
data_terintegrasi[
  is.na(
    data_terintegrasi$jumlah_transaksi
  ),
  c(
    "customer_id",
    "nama"
  )
]

Hasil penggabungan tetap menghasilkan 11 pelanggan. C011 tidak memiliki pasangan pada data transaksi sehingga nilai transaksi untuk pelanggan tersebut menjadi missing.

10.5 Mengisi nol atau mempertahankan NA?

Nilai NA pada variabel transaksi tidak dapat langsung dianggap sebagai nol. Nilai tersebut perlu ditafsirkan terlebih dahulu karena dapat menunjukkan tidak adanya transaksi maupun ketidaktersediaan informasi.

Dalam skenario ini digunakan asumsi bahwa C011 memang belum melakukan transaksi. Oleh sebab itu, nilai NA pada variabel transaksi untuk pelanggan tersebut diubah menjadi nol.

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

11 Dataset Akhir dan Evaluasi

11.1 Memilih atribut akhir

Setelah tahapan preprocessing selesai, variabel yang diperlukan untuk membentuk dataset akhir dipilih dari hasil integrasi.

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"
  )
]

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

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

data_final

Dataset akhir merupakan gabungan informasi pelanggan dan transaksi yang telah melalui proses pembersihan. Beberapa variabel tambahan hasil imputasi, indikator missing, deteksi outlier, dan transformasi juga disertakan untuk mendukung analisis berikutnya.

11.2 Audit akhir

Pemeriksaan terakhir dilakukan untuk memastikan hasil preprocessing telah memenuhi kondisi yang diharapkan, khususnya berkaitan dengan missing value dan duplikasi identifier.

audit_akhir <- audit_data(
  data_final
)

audit_akhir
sum(
  duplicated(
    data_final$customer_id
  )
)
## [1] 0
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

Hasil audit akhir menunjukkan bahwa seluruh missing value telah ditangani dan tidak terdapat lagi identifier pelanggan yang duplikat. Dengan demikian, kondisi dataset akhir menjadi lebih teratur dibandingkan kondisi sebelum preprocessing.

11.3 Membandingkan kondisi sebelum dan sesudah

Untuk melihat perubahan kualitas secara keseluruhan, kondisi dataset sebelum preprocessing dibandingkan dengan kondisi dataset setelah seluruh tahapan selesai.

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(
        pelanggan_raw$kota
      )
    ),
    length(
      unique(
        pelanggan_raw$status
      )
    )
  ),
  sesudah = c(
    nrow(data_final),
    sum(
      duplicated(
        data_final$customer_id
      )
    ),
    sum(
      is.na(data_final)
    ),
    length(
      unique(
        data_final$kota
      )
    ),
    length(
      unique(
        data_final$status
      )
    )
  )
)

perbandingan

Dari perbandingan tersebut terlihat bahwa jumlah observasi berubah dari 12 menjadi 11 akibat penghapusan satu data duplikat. Jumlah duplikasi identifier berhasil menjadi nol dan missing value telah ditangani. Selain itu, kategori pada variabel kota dan status menjadi lebih seragam.

11.4 Menyimpan hasil

Hasil akhir preprocessing beserta catatan perubahan disimpan ke dalam file agar dapat digunakan kembali tanpa perlu mengulangi seluruh proses dari awal.

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

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

12 Hasil dan Pembahasan

Berdasarkan rangkaian pemeriksaan yang telah dilakukan, dataset pelanggan dan transaksi pada awalnya masih mengandung beberapa masalah kualitas. Permasalahan tersebut meliputi missing value, perbedaan format kategori, identifier yang berulang, nilai usia yang berada di luar batas wajar, serta nilai pendapatan yang sangat tinggi.

Pada tahap data cleaning, spasi tambahan terlebih dahulu dihilangkan dan penggunaan huruf besar maupun kecil diseragamkan. Selanjutnya, kategori kota dan status dipetakan ke bentuk yang konsisten. Variasi PKU, PEKANBARU, dan pekanbaru disatukan menjadi Pekanbaru, sedangkan variasi ACTIVE, A, dan aktif dikelompokkan menjadi Aktif. Identifier C010 yang muncul dua kali juga ditangani dengan menghapus salah satu baris yang memiliki informasi sama.

Pemeriksaan berdasarkan aturan domain menemukan nilai usia 150 tahun pada C004. Karena nilai tersebut berada di luar rentang usia yang telah ditentukan, nilainya diperbaiki menjadi 50 dengan mengacu pada informasi sumber asli. Sementara itu, tidak ditemukan pendapatan negatif. Namun, nilai pendapatan 500 juta pada C007 terlihat jauh berbeda dibandingkan observasi lainnya sehingga perlu dianalisis lebih lanjut.

Untuk missing value numerik, digunakan median sebagai metode imputasi. Pemilihan tersebut didasarkan pada adanya nilai pendapatan ekstrem yang dapat memengaruhi nilai mean. Pada variabel kota, nilai yang tidak tersedia diberi kategori Tidak diketahui sehingga informasi mengenai ketidakpastian tetap terlihat.

Deteksi outlier menggunakan pendekatan IQR menghasilkan C007 dengan pendapatan 500 juta sebagai kandidat outlier. Meskipun demikian, nilai tersebut tidak langsung dihapus karena belum ada dasar yang cukup untuk menyatakan bahwa data tersebut merupakan kesalahan. Sebagai alternatif, winsorizing dilakukan untuk mengetahui bagaimana pembatasan nilai ekstrem memengaruhi skala data.

Transformasi numerik kemudian dilakukan menggunakan Min-Max, Z-Score, dan Decimal Scaling. Ketiga metode menghasilkan bentuk skala yang berbeda. Khusus pada Min-Max, keberadaan nilai pendapatan ekstrem menyebabkan sebagian besar observasi memiliki nilai yang sangat dekat dengan nol. Hal tersebut memperlihatkan bahwa kondisi outlier perlu dipertimbangkan sebelum menentukan teknik transformasi.

Pada proses integrasi, kedua dataset disatukan berdasarkan customer_id setelah nama identifier diselaraskan. Pemeriksaan identifier menunjukkan bahwa C011 hanya terdapat pada data pelanggan, sedangkan C012 hanya terdapat pada data transaksi. Karena fokus integrasi adalah mempertahankan seluruh pelanggan, penggabungan dilakukan menggunakan merge() dengan all.x = TRUE.

Setelah seluruh tahapan preprocessing diterapkan, dataset akhir tidak lagi memiliki missing value dan duplikasi customer_id. Kategori kota dan status juga telah memiliki bentuk penulisan yang lebih konsisten. Dengan demikian, hasil pengolahan memberikan dataset yang lebih siap untuk digunakan pada proses analisis selanjutnya.

13 Kesimpulan

Dataset pelanggan dan transaksi pada kondisi awal masih memiliki sejumlah masalah kualitas data, seperti nilai yang hilang, ketidakteraturan penulisan kategori, duplikasi identifier, nilai usia yang berada di luar aturan domain, serta nilai pendapatan yang sangat ekstrem.

Perbaikan dilakukan secara bertahap melalui pemeriksaan kondisi awal, data cleaning, penanganan missing value, identifikasi outlier, transformasi variabel, dan integrasi data. Missing value numerik ditangani menggunakan median, sedangkan nilai kota yang kosong diberi kategori Tidak diketahui. Duplikasi C010 dihilangkan dan nilai usia C004 diperbaiki dari 150 menjadi 50 sesuai informasi sumber.

Berdasarkan metode IQR, pendapatan C007 sebesar 500 juta teridentifikasi sebagai kandidat outlier. Nilai tersebut tetap dipertahankan karena tidak ditemukan bukti bahwa data merupakan kesalahan pencatatan. Sebagai bahan evaluasi, dibuat pula versi winsorized. Selanjutnya, variabel numerik ditransformasikan menggunakan Min-Max, Z-Score, dan Decimal Scaling untuk melihat perbedaan skala yang dihasilkan.

Pada tahap integrasi, data pelanggan dan transaksi berhasil digabungkan menggunakan customer_id. Dataset akhir memiliki struktur yang lebih konsisten, seluruh missing value telah ditangani, dan tidak terdapat duplikasi identifier. Oleh karena itu, dataset hasil preprocessing dapat digunakan sebagai dasar untuk analisis pelanggan dan transaksi pada tahap berikutnya.

14 Ringkasan

Tahapan preprocessing pada data ini dimulai dari pemeriksaan kondisi awal, dilanjutkan dengan pembersihan data, penanganan missing value, pemeriksaan outlier, transformasi variabel numerik, penggabungan data, dan evaluasi dataset akhir.

Hasil pengolahan menunjukkan bahwa preprocessing tidak selalu berarti menghapus seluruh nilai yang dianggap tidak biasa. Setiap perubahan perlu disesuaikan dengan karakteristik data dan tujuan analisis. Melalui pendekatan tersebut, proses cleaning, imputasi, evaluasi outlier, transformasi, serta integrasi dapat dilakukan dengan tetap mempertahankan informasi penting dari data awal.

15 Daftar Pustaka

Batini, C., & Scannapieco, M. (2016). Data and Information Quality: Dimensions, Principles and Techniques. Springer.

Kuhn, M., & Johnson, K. (2013). Applied Predictive Modeling. Springer.

Wickham, H., & Grolemund, G. (2017). R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. O’Reilly Media.