Kualitas data menjadi fondasi utama dalam setiap proses analisis data. Data yang diperoleh dari berbagai sumber sering kali memiliki beragam masalah seperti ketidakkonsistenan format, nilai yang hilang, duplikasi, serta nilai ekstrem yang dapat mengganggu hasil akhir. Oleh karena itu, diperlukan tahapan preprocessing untuk membersihkan dan menyiapkan data sebelum dilakukan analisis atau pemodelan lebih lanjut.
Praktikum ini disusun dengan tujuan agar mahasiswa mampu:
Sebuah perusahaan e-commerce ingin melakukan analisis terhadap profil pelanggannya. Data tersedia dalam dua sumber terpisah:
Kedua dataset tersebut masih dalam kondisi “mentah” dan mengandung berbagai masalah sehingga perlu melalui serangkaian tahap pembersihan sebelum dapat digunakan untuk analisis.
Langkah awal yang dilakukan adalah membuat proyek baru di RStudio agar pekerjaan lebih terstruktur dan terdokumentasi:
praktikum_preprocessing.Dataset berikut sengaja dibuat dengan berbagai masalah kualitas data agar seluruh tahap preprocessing dapat dipraktikkan secara menyeluruh.
# Data pelanggan dengan berbagai masalah kualitas
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
)
# 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
)
# Tampilkan data pelanggan
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
## 7 C007 Gilang 31 500000000 Pekanbaru Aktif
## 8 C008 Hana 29 4700000 Siak AKTIF
## 9 C009 Indra 22 4600000 PKU A
## 10 C010 Joko 35 5300000 Dumai Tidak aktif
## 11 C010 Joko 35 5300000 Dumai Tidak aktif
## 12 C011 Kiki 28 NA <NA> Aktif
Sebelum melakukan perubahan, penting untuk memahami struktur, dimensi, tipe data, dan beberapa observasi awal dari dataset.
## [1] 12 6
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
## '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 4500000 NA 5200000 4800000 4900000 5100000 500000000 4700000 4600000 5300000 ...
## $ kota : chr "Pekanbaru" " PKU" "PEKANBARU" "Dumai" ...
## $ status : chr "Aktif" "aktif" "ACTIVE" "A" ...
## 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
## 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
##
##
##
##
Dalam literatur data mining, dikenal enam dimensi kualitas data: akurasi, kelengkapan, konsistensi, ketepatan waktu, kredibilitas, dan interpretabilitas. Tidak semua dimensi dapat dihitung hanya dari isi tabel.
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
## customer_id nama usia pendapatan kota status
## 0.00 0.00 8.33 16.67 8.33 0.00
## [1] 1
## [1] 1
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
## [1] 21 150
## [1] 4500000 500000000
Untuk memudahkan pemeriksaan kualitas data, dibuat fungsi yang dapat digunakan berulang kali.
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 11
## 2 nama character 0 0.00 11
## 3 usia numeric 1 8.33 11
## 4 pendapatan numeric 2 16.67 10
## 5 kota character 1 8.33 10
## 6 status character 0 0.00 8
Interpretasi: Hasil audit berfungsi sebagai sinyal awal, bukan keputusan final. Setiap indikasi masalah perlu diverifikasi lebih lanjut sebelum dilakukan tindakan.
Data mentah dipertahankan agar setiap perubahan dapat dilacak dan dievaluasi.
# Hapus spasi di awal dan akhir teks
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)
# Ubah semua huruf menjadi kecil untuk memudahkan pencocokan
pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)
# Lihat kategori kota setelah pembersihan awal
sort(unique(pelanggan$kota))## [1] "dumai" "pekanbaru" "pku" "siak"
# Standardisasi nama kota
pelanggan$kota[pelanggan$kota %in% c("pku", "pekanbaru")] <- "Pekanbaru"
pelanggan$kota[pelanggan$kota == "dumai"] <- "Dumai"
pelanggan$kota[pelanggan$kota == "siak"] <- "Siak"
# Standardisasi status pelanggan
pelanggan$status[pelanggan$status %in% c("aktif", "active", "a")] <- "Aktif"
pelanggan$status[pelanggan$status %in% c("tidak aktif", "nonaktif")] <- "Tidak Aktif"
# Periksa hasil standardisasi
sort(unique(pelanggan$kota))## [1] "Dumai" "Pekanbaru" "Siak"
## [1] "Aktif" "Tidak Aktif"
# Tampilkan baris dengan customer_id yang berulang
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
# Hapus duplikat, pertahankan kemunculan pertama
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL
dim(pelanggan)## [1] 11 6
# Cek usia yang tidak masuk akal (di luar rentang 15-100 tahun)
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>
## customer_id nama usia pendapatan kota status
## NA <NA> <NA> NA NA <NA> <NA>
## NA.1 <NA> <NA> NA NA <NA> <NA>
Setiap tindakan yang dilakukan dicatat untuk keperluan audit dan reproduktifitas.
log_perubahan <- data.frame(
tahap = c("Standardisasi", "Standardisasi", "Deduplikasi", "Koreksi domain"),
atribut = c("kota", "status", "customer_id", "usia"),
tindakan = c(
"PKU dan variasi kapital distandardisasi menjadi Pekanbaru",
"ACTIVE/A distandardisasi menjadi Aktif; nonaktif menjadi Tidak Aktif",
"Menghapus kemunculan kedua customer_id C010",
"Usia C004 dikoreksi dari 150 menjadi 50 berdasarkan verifikasi sumber"
),
stringsAsFactors = FALSE
)
log_perubahan## tahap atribut
## 1 Standardisasi kota
## 2 Standardisasi status
## 3 Deduplikasi customer_id
## 4 Koreksi domain usia
## tindakan
## 1 PKU dan variasi kapital distandardisasi menjadi Pekanbaru
## 2 ACTIVE/A distandardisasi menjadi Aktif; nonaktif menjadi Tidak Aktif
## 3 Menghapus kemunculan kedua customer_id C010
## 4 Usia C004 dikoreksi dari 150 menjadi 50 berdasarkan verifikasi sumber
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
## 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
Pendekatan ini hanya didemonstrasikan, tidak langsung diterapkan.
## [1] 11
## [1] 8
# Persentase data yang hilang jika baris tidak lengkap dihapus
round((1 - nrow(pelanggan_complete) / nrow(pelanggan)) * 100, 2)## [1] 27.27
Pendapatan memiliki nilai ekstrem yang memengaruhi mean. Median lebih stabil sebagai ukuran pemusatan.
mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)
# Tampilkan perbandingan mean dan median
data.frame(
ukuran = c("Mean", "Median"),
nilai = c(mean_pendapatan, median_pendapatan)
)## ukuran nilai
## 1 Mean 59900000
## 2 Median 4900000
# Imputasi pendapatan dengan median
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
pelanggan$pendapatan_imputasi[is.na(pelanggan$pendapatan_imputasi)] <-
median_pendapatan
# Imputasi usia dengan median
median_usia <- median(pelanggan$usia, na.rm = TRUE)
pelanggan$usia_imputasi <- pelanggan$usia
pelanggan$usia_imputasi[is.na(pelanggan$usia_imputasi)] <- median_usia
# Bandingkan hasil imputasi
pelanggan[, c("customer_id", "usia", "usia_imputasi",
"pendapatan", "pendapatan_imputasi")]## customer_id usia usia_imputasi pendapatan pendapatan_imputasi
## 1 C001 21 21.0 4500000 4500000
## 2 C002 25 25.0 NA 4900000
## 3 C003 23 23.0 5200000 5200000
## 4 C004 50 50.0 4800000 4800000
## 5 C005 27 27.0 4900000 4900000
## 6 C006 NA 27.5 5100000 5100000
## 7 C007 31 31.0 500000000 500000000
## 8 C008 29 29.0 4700000 4700000
## 9 C009 22 22.0 4600000 4600000
## 10 C010 35 35.0 5300000 5300000
## 11 C011 28 28.0 NA 4900000
Untuk data kategorik, nilai hilang diisi dengan kategori eksplisit untuk menjaga transparansi.
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
Menambahkan indikator untuk mempertahankan informasi bahwa suatu nilai awalnya hilang.
pelanggan$pendapatan_missing <- as.integer(is.na(pelanggan$pendapatan))
table(pelanggan$pendapatan_missing)##
## 0 1
## 9 2
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)Perhatian: Imputasi dapat mengubah distribusi dan mengecilkan variasi data. Evaluasi tidak berhenti setelah semua nilai hilang terisi.
boxplot(pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot Pendapatan",
xlab = "Pendapatan")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
data.frame(
keterangan = c("Q1", "Q3", "IQR", "Batas Bawah", "Batas Atas"),
nilai = c(q1, q3, iqr, batas_bawah, batas_atas)
)## keterangan nilai
## 1 Q1 4750000
## 2 Q3 5150000
## 3 IQR 400000
## 4 Batas Bawah 4150000
## 5 Batas Atas 5750000
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 500000000
Outlier tidak otomatis dihapus. Beberapa kemungkinan yang perlu dipertimbangkan:
Sebagai demonstrasi tambahan, dibuat versi winsorized untuk membandingkan dampak metode.
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 500000000 5750000
Rumus: \(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")]## customer_id usia usia_minmax pendapatan_imputasi pendapatan_minmax
## 1 C001 21 0.00000000 4500000 0.0000000000
## 2 C002 25 0.13793103 4900000 0.0008072654
## 3 C003 23 0.06896552 5200000 0.0014127144
## 4 C004 50 1.00000000 4800000 0.0006054490
## 5 C005 27 0.20689655 4900000 0.0008072654
## 6 C006 NA 0.22413793 5100000 0.0012108981
## 7 C007 31 0.34482759 500000000 1.0000000000
## 8 C008 29 0.27586207 4700000 0.0004036327
## 9 C009 22 0.03448276 4600000 0.0002018163
## 10 C010 35 0.48275862 5300000 0.0016145308
## 11 C011 28 0.24137931 4900000 0.0008072654
Rumus: \(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)## usia_z pendapatan_z
## 1 -0.984 -0.304
## 2 -0.489 -0.301
## 3 -0.737 -0.299
## 4 2.604 -0.302
## 5 -0.242 -0.301
## 6 -0.180 -0.300
## 7 0.253 3.015
## 8 0.006 -0.303
## 9 -0.860 -0.303
## 10 0.748 -0.299
## 11 -0.118 -0.301
Rumus: \(x' = \frac{x}{10^j}\) dengan \(j = \lceil \log_{10}(\max|x| + 1) \rceil\)
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
# Format angka agar tidak pakai notasi ilmiah
transformasi <- pelanggan[, c(
"customer_id", "pendapatan_imputasi", "pendapatan_minmax",
"pendapatan_z", "pendapatan_decimal"
)]
transformasi$pendapatan_imputasi <- format(
transformasi$pendapatan_imputasi,
big.mark = ".",
scientific = FALSE
)
transformasi## customer_id pendapatan_imputasi pendapatan_minmax pendapatan_z
## 1 C001 4.500.000 0.0000000000 -0.3041235
## 2 C002 4.900.000 0.0008072654 -0.3014440
## 3 C003 5.200.000 0.0014127144 -0.2994343
## 4 C004 4.800.000 0.0006054490 -0.3021138
## 5 C005 4.900.000 0.0008072654 -0.3014440
## 6 C006 5.100.000 0.0012108981 -0.3001042
## 7 C007 500.000.000 1.0000000000 3.0151095
## 8 C008 4.700.000 0.0004036327 -0.3027837
## 9 C009 4.600.000 0.0002018163 -0.3034536
## 10 C010 5.300.000 0.0016145308 -0.2987645
## 11 C011 4.900.000 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
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)Interpretasi: Outlier dapat menekan sebagian besar nilai min-maks ke rentang yang sangat sempit. Inilah alasan deteksi outlier perlu dilakukan sebelum memilih transformasi.
## [1] 0
## [1] 0
## [1] "C011"
## [1] "C012"
data_terintegrasi <- merge(
pelanggan,
transaksi,
by = "customer_id",
all.x = TRUE
)
data_terintegrasi[, c(
"customer_id", "nama", "jumlah_transaksi", "total_purchase"
)]## customer_id nama jumlah_transaksi total_purchase
## 1 C001 Ani 5 1500000
## 2 C002 Budi 3 900000
## 3 C003 Citra 7 2700000
## 4 C004 Dodi 2 600000
## 5 C005 Eka 6 2100000
## 6 C006 Fani 4 1300000
## 7 C007 Gilang 20 25000000
## 8 C008 Hana 5 1700000
## 9 C009 Indra 3 800000
## 10 C010 Joko 8 3200000
## 11 C011 Kiki NA NA
## sebelum sesudah
## 11 11
## [1] 0
# Missing value baru akibat tidak ada pasangan transaksi
colSums(is.na(data_terintegrasi[, c("jumlah_transaksi", "total_purchase")]))## jumlah_transaksi total_purchase
## 1 1
# Pelanggan tanpa transaksi
data_terintegrasi[is.na(data_terintegrasi$jumlah_transaksi),
c("customer_id", "nama")]## customer_id nama
## 11 C011 Kiki
# Hanya dilakukan jika telah dikonfirmasi bahwa NA berarti belum pernah transaksi
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)
] <- 0data_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## customer_id nama usia kota status pendapatan_imputasi
## 1 C001 Ani 21.0 Pekanbaru Aktif 4500000
## 2 C002 Budi 25.0 Pekanbaru Aktif 4900000
## 3 C003 Citra 23.0 Pekanbaru Aktif 5200000
## 4 C004 Dodi 50.0 Dumai Aktif 4800000
## 5 C005 Eka 27.0 Pekanbaru Tidak Aktif 4900000
## 6 C006 Fani 27.5 Dumai Tidak Aktif 5100000
## 7 C007 Gilang 31.0 Pekanbaru Aktif 500000000
## 8 C008 Hana 29.0 Siak Aktif 4700000
## 9 C009 Indra 22.0 Pekanbaru Aktif 4600000
## 10 C010 Joko 35.0 Dumai Tidak Aktif 5300000
## 11 C011 Kiki 28.0 Tidak diketahui Aktif 4900000
## 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 1500000
## 2 3 900000
## 3 7 2700000
## 4 2 600000
## 5 6 2100000
## 6 4 1300000
## 7 20 25000000
## 8 5 1700000
## 9 3 800000
## 10 8 3200000
## 11 0 0
## atribut tipe jumlah_missing persen_missing jumlah_unik
## 1 customer_id character 0 0 11
## 2 nama character 0 0 11
## 3 usia numeric 0 0 11
## 4 kota character 0 0 4
## 5 status character 0 0 2
## 6 pendapatan_imputasi numeric 0 0 9
## 7 pendapatan_missing integer 0 0 2
## 8 outlier_pendapatan logical 0 0 2
## 9 usia_minmax numeric 0 0 11
## 10 pendapatan_minmax numeric 0 0 9
## 11 jumlah_transaksi_final numeric 0 0 9
## 12 total_purchase_final numeric 0 0 11
## [1] 0
## 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
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## indikator sebelum sesudah
## 1 Jumlah baris 12 11
## 2 Duplikasi customer_id 1 0
## 3 Total missing value 4 0
## 4 Kategori kota unik 10 4
## 5 Kategori status unik 8 2
Apakah dataset tanpa nilai hilang selalu lebih
berkualitas?
Tidak selalu. Menghapus atau mengisi nilai hilang tanpa pemahaman
konteks dapat menghilangkan informasi penting atau menimbulkan
bias.
Mengapa outlier tidak boleh otomatis
dihapus?
Outlier bisa merupakan kesalahan input, tetapi juga bisa mencerminkan
fenomena nyata yang penting untuk analisis.
Bagaimana preprocessing dapat menimbulkan
bias?
Setiap keputusan dalam preprocessing (metode imputasi, transformasi,
penghapusan data) dapat mempengaruhi hasil analisis akhir.
Mengapa parameter imputasi dan transformasi seharusnya
dihitung dari data pelatihan?
Untuk menghindari kebocoran data (data leakage) yang dapat
menyebabkan overestimasi kinerja model.
Apa risiko integrasi data jika identifier tidak
unik?
Dapat menyebabkan penggandaan baris (cartesian product) dan
hasil analisis yang tidak akurat.
Alur preprocessing yang telah dilakukan mencakup:
Preprocessing adalah proses pengambilan keputusan. Kode R hanya menjalankan keputusan tersebut; kualitas hasil tetap bergantung pada pemahaman data, tujuan analisis, dan dokumentasi perubahan.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.