Tahukah kamu apa itu Data Mining? Data mining adalah proses pengumpulan dan pengolaan data berkuran besar untuk menemukan pola, tren, atau informasi tersembunyi yang berguna. Dengan kata lain, data mining seperti detektif untuk melihat makna tersembunyi yang ada dalam data kita. Dalam prosesnya, terdapat tahapan yang dikenal sebagai data preprocessing yang bertugas untuk mengolah data mentah kita menjadi data yang layak dianalisis.
Setelah menyelesaikan praktikum ini, kita diharapkan mampu:
Catatan: Prinsip utama adalah jangan membersihkan data hanya karena sebuah nilai terlihat aneh. Perhatikan dahulu alasan, aturan, dan pertimbangan yang jelas meskipun data tersebut tampak seperti outlier.
Bayangkan sebuah perusahaan e-commerce akan melakukan analisis pelanggan yang bersumber dari:
Data ini belum dapat langsung digunakan karena mengandung nilai hilang, inkonsistensi kategori, duplikasi, nilai ekstrem, dan perbedaan nama identifier antarsumber. Disinilah peran kita untuk menyiapkan data tersebut hingga menjadi satu dataset analisis yang bersih dan siap pakai.
Sebelum mulai menulis kode, siapkan dahulu proyeknya:
File → New Project → New Directory → New Project.praktikum_preprocessing.Knit kapan saja untuk melihat hasilnya
dalam bentuk HTML.Periksa terlebih dahulu lokasi di mana file akan tersimpan dan versi R yang sedang digunakan.
## [1] "C:/Users/HP/Downloads"
## [1] "R version 4.4.2 (2024-10-31 ucrt)"
Catatan: Tahap ini hanya menggunakan fungsi dasar R (base R) sehingga dapat langsung dijalankan tanpa memasang paket tambahan apa pun.
Berikut disajikan dataset yang dapat dipraktikkan. Perhatikan apa saja masalah yang terkandung di dalam data seperti usia yang tidak masuk akal, pendapatan yang hilang, penulisan kota yang berantakan, inkonsistensi status, hingga duplikasi data.
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_rawBerdasarkan hasil yang ada, dapat dilihat bahwa pada baris ke-10 dan
ke-11 pada pelanggan_raw tampak identik, usia 150 tahun
pada baris ke-4 tentu tidak wajar, dan penulisan kota “Pekanbaru” muncul
dalam berbagai bentuk. Kesalahan seperti ini kerap terjadi dan perlu
segera ditangani sebelum memulai analisis.
Langkah paling awal sebelum mengubah apa pun adalah mengenali dahulu struktur, dimensi, tipe atribut, dan beberapa observasi awal dari data yang kita miliki.
## [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 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" ...
## 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
##
##
##
##
Berdasarkan paparan singkat yang ada, diperoleh bahwa nilai maksimum usia mencapai 150, dan nilai maksimum pendapatan melonjak jauh dari nilai-nilai lainnya.
Han, Kamber, dan Pei menjelaskan enam dimensi kualitas data, yaitu accuracy, completeness, consistency, timeliness, believability, dan interpretability. Tidak semua dimensi tersebut dapat dihitung hanya dari isi tabel. Misalnya, accuracy memerlukan pembanding terhadap kondisi sebenarnya, sedangkan timeliness membutuhkan informasi waktu pemutakhiran data.
Karena itu, pemeriksaan awal berikut kita fokuskan pada masalah yang memang bisa dideteksi langsung dari dataset.
## 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] 4.5e+06 5.0e+08
Catatan: Perhatikan bagaimana atribut
kotasebenarnya hanya berisi empat kota, tetapi tercatat dalam sembilan variasi penulisan yang berbeda. Begitu pula atributstatusyang seharusnya hanya dua kategori.
Untuk memudahkan dalam penggunaan berulang, jadikan satu fungsi untuk mengefisiensi pengerjaan.
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_awalCatatan: Hasil audit ini bukanlah keputusan cleaning itu sendiri. Audit hanya memberi sinyal atribut mana yang perlu diperiksa lebih lanjut, keputusan penanganannya tetap perlu dipertimbangkan satu per satu.
Sebelum mengubah apa pun, kita pertahankan dahulu data mentahnya dan
bekerja di atas salinan. Dengan begitu, pelanggan_raw tetap
bisa dijadikan pembanding di akhir praktikum nanti.
Salah satu penyebab paling umum kategori tampak berbeda padahal
maknanya sama adalah spasi tersembunyi di awal atau akhir teks, serta
perbedaan huruf besar-kecil. trimws() menangani yang
pertama, tolower() menangani yang kedua.
# Menghapus spasi di awal dan akhir
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)
# Menyeragamkan huruf menjadi kecil untuk mempermudah pencocokan
pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)
sort(unique(pelanggan$kota))## [1] "dumai" "pekanbaru" "pku" "siak"
## [1] "a" "active" "aktif" "nonaktif" "tidak aktif"
Variasi penulisan sudah berkurang banyak, tetapi belum sepenuhnya seragam. “pku” dan “pekanbaru” misalnya, secara makna adalah kota yang sama namun masih tercatat sebagai dua kategori berbeda.
Tahap ini disebut standardisasi kategori dan dilakukan berdasarkan aturan domain. Dalam praktiknya, aturan ini idealnya dikonfirmasi terlebih dulu melalui kamus data atau langsung ke pemilik data, bukan ditebak langsung.
# Standardisasi 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$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"
## [1] "Aktif" "Tidak Aktif"
Sekarang atribut kota sudah rapi menjadi tiga kategori,
dan status menjadi dua kategori sebagaimana seharusnya.
Selanjutnya kita periksa baris mana saja yang
customer_id-nya berulang.
# Menampilkan seluruh baris dengan customer_id yang berulang
pelanggan[duplicated(pelanggan$customer_id) |
duplicated(pelanggan$customer_id, fromLast = TRUE), ]# Mempertahankan kemunculan pertama untuk customer_id yang identik
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL
dim(pelanggan)## [1] 11 6
Catatan: Menghapus duplikasi seperti ini hanya aman dilakukan jika setiap
customer_idmemang seharusnya mewakili satu pelanggan. Jika dalam konteks data lain satu pelanggan boleh memiliki banyak baris (misalnya baris per transaksi), tindakan menghapus duplikasi justru keliru dan bisa menghilangkan informasi penting.
Aturan domain adalah batasan nilai yang masuk akal berdasarkan pengetahuan tentang data itu sendiri. Usia pelanggan misalnya, secara wajar berada pada rentang tertentu.
# Kandidat usia tidak valid berdasarkan aturan bisnis 15-100 tahun
pelanggan[pelanggan$usia < 15 | pelanggan$usia > 100, ]Nilai usia 150 pada baris customer_id “C004” jelas
melanggar aturan domain yang kita tetapkan. Misalkan setelah ditelusuri
ke formulir pendaftaran asli, ternyata nilai yang benar adalah 50.
Setiap perubahan yang kita lakukan terhadap data sebaiknya dicatat. Log ini berguna sebagai dokumentasi, sekaligus memudahkan siapa pun untuk menelusuri kembali alasan di balik setiap keputusan.
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_perubahanSebelum memilih strategi penanganan, kita perlu tahu dulu persis di mana saja nilai yang hilang berada.
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
Ada tiga baris yang tidak lengkap, masing-masing kehilangan atribut yang berbeda, yaitu: pendapatan, usia, dan kota.
Strategi paling sederhana adalah membuang seluruh baris yang tidak
lengkap. Kode berikut hanya mendemonstrasikan strategi tersebut secara
terpisah; objek pelanggan yang sedang kita kerjakan tidak
langsung ditimpa.
## [1] 11
## [1] 8
## [1] 27.27
Catatan: Menghapus baris berarti kehilangan sekitar seperempat data hanya karena tiga atribut yang berbeda-beda kebetulan hilang pada baris yang berlainan. Untuk dataset sekecil ini, kehilangan sebanyak itu tentu sangat disayangkan. Strategi ini lebih masuk akal ketika proporsi baris tidak lengkap kecil dan datanya berlimpah.
Untuk atribut numerik, nilai yang hilang bisa diisi dengan ukuran pemusatan seperti mean atau median. Sebelum memilih, ada baiknya membandingkan keduanya terlebih dahulu, terutama jika distribusi datanya miring.
mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)
mean_pendapatan## [1] 59900000
## [1] 4900000
Selisih keduanya sangat jauh. Ini karena satu nilai pendapatan yang sangat ekstrem (500 juta) menarik nilai mean jauh ke atas yang menjadi salah satu kelemahan penggunaan mean juga, sementara median relatif tidak terpengaruh. Untuk kasus seperti ini, median adalah pilihan yang lebih stabil.
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
pelanggan[, c("customer_id", "usia", "usia_imputasi",
"pendapatan", "pendapatan_imputasi")]Untuk atribut kategorik seperti kota, mengisi nilai hilang dengan modus bukan selalu pilihan yang tepat, sebab cara ini berpotensi menyembunyikan fakta bahwa data tersebut sebenarnya tidak diketahui. Pada kesempatan ini kita memilih menggunakan kategori eksplisit “Tidak diketahui” agar ketidakpastian tersebut tetap terlihat, bukan tersamarkan.
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
Setelah nilai hilang diisi, jejak bahwa nilai tersebut awalnya hilang bisa saja hilang juga dari catatan kita. Indikator biner berikut menjaga informasi tersebut tetap tersedia untuk analisis lanjutan.
pelanggan$pendapatan_missing <- as.integer(is.na(pelanggan$pendapatan))
table(pelanggan$pendapatan_missing)##
## 0 1
## 9 2
Mengisi nilai yang hilang bukan tindakan tanpa konsekuensi. Mari kita lihat bagaimana bentuk distribusi pendapatan berubah setelah proses imputasi.
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)Catatan: Imputasi dapat mengubah bentuk distribusi dan mengecilkan variasi data. Oleh karena itu, proses evaluasi tidak boleh dianggap selesai hanya karena seluruh nilai
NAsudah terisi.
Cara paling cepat untuk melihat keberadaan nilai ekstrem adalah dengan menggunakan boxplot.
boxplot(pelanggan$pendapatan_imputasi, horizontal = TRUE,
col = "lightblue", main = "Boxplot Pendapatan",
xlab = "Pendapatan")Satu titik tampak jauh terpisah dari titik-titik lainnya, menandakan adanya outlier yang perlu diperiksa lebih lanjut.
Salah satu cara menentukan batas outlier secara kuantitatif adalah dengan aturan IQR (interquartile range): nilai di luar 1.5 kali IQR dari kuartil pertama dan ketiga dianggap sebagai kandidat outlier.
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
pelanggan$outlier_pendapatan <- pelanggan$pendapatan_imputasi < batas_bawah |
pelanggan$pendapatan_imputasi > batas_atas
pelanggan[pelanggan$outlier_pendapatan,
c("customer_id", "nama", "pendapatan_imputasi")]Nilai 500 juta pada pelanggan “Gilang” tidak serta-merta kita hapus. Setidaknya ada tiga kemungkinan yang perlu dipertimbangkan terlebih dahulu:
Pada kesempatan ini, nilai tersebut kita pertahankan apa adanya, dan sebagai pembanding kita buat versi winsorized, yaitu nilai ekstrem yang dibatasi hingga batas atas IQR.
pelanggan$pendapatan_winsor <- pmin(
pmax(pelanggan$pendapatan_imputasi, batas_bawah),
batas_atas
)
pelanggan[pelanggan$outlier_pendapatan,
c("customer_id", "pendapatan_imputasi", "pendapatan_winsor")]Catatan: winsorizing Di sini hanya sebagai demonstrasi tambahan. Terapkan metode ini hanya jika sesuai dengan tujuan analisis, dan jangan lupa mendokumentasikan bahwa nilai ekstrem telah dibatasi.
Normalisasi min-maks memetakan nilai ke dalam rentang [0, 1] 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")]Z-score mengukur seberapa jauh sebuah nilai dari rata-rata, dalam satuan simpangan baku.
\[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)Perhatikan nilai z-score pendapatan pada baris ketujuh yang jauh melampaui nilai lainnya, sekali lagi menegaskan keberadaan outlier pada atribut tersebut.
Decimal scaling menggeser titik desimal berdasarkan nilai absolut terbesar dalam data, sehingga seluruh nilai berada dalam rentang -1 sampai 1.
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
Ketiga metode transformasi di atas sama-sama valid, namun memberikan tampilan angka yang cukup berbeda. Mari kita bandingkan hasilnya berdampingan.
transformasi <- pelanggan[, c("customer_id", "pendapatan_imputasi",
"pendapatan_minmax", "pendapatan_z",
"pendapatan_decimal")]
transformasiMari kita lihat bagaimana keberadaan outlier memengaruhi hasil normalisasi min-maks, dengan membandingkan versi data asli dan versi yang sudah di-winsorize.
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)Catatan: Outlier dapat menekan sebagian besar nilai min-maks ke rentang yang sangat sempit, seperti terlihat pada titik-titik yang berhimpitan di sudut kiri bawah grafik. Inilah alasan mengapa deteksi outlier perlu dilakukan sebelum memilih metode transformasi yang tepat.
Sebelum menggabungkan dua sumber data, kita pastikan dahulu bahwa kunci penggabungannya (identifier) unik dan cocok satu sama lain.
## [1] 0
## [1] 0
## [1] "C011"
## [1] "C012"
Hasil setdiff() di atas menunjukkan bahwa ada pelanggan
yang belum memiliki catatan transaksi, dan sebaliknya ada catatan
transaksi yang tidak memiliki pasangan data pelanggan.
Kedua tabel menggunakan nama kolom identifier yang berbeda, yaitu
customer_id dan cust_id. Sebelum digabung,
keduanya perlu diselaraskan terlebih dahulu.
Kita menggunakan left join agar seluruh data pelanggan tetap dipertahankan, meskipun ada yang belum memiliki transaksi.
data_terintegrasi <- merge(pelanggan, transaksi, by = "customer_id", all.x = TRUE)
data_terintegrasi[, c("customer_id", "nama", "jumlah_transaksi", "total_purchase")]Parameter all.x = TRUE inilah yang memastikan seluruh
pelanggan dipertahankan, meskipun tidak memiliki pasangan transaksi.
Setelah penggabungan, validasi selalu diperlukan untuk memastikan tidak ada yang berubah secara tidak wajar.
# Apakah jumlah baris berubah secara tidak wajar?
c(sebelum = nrow(pelanggan), sesudah = nrow(data_terintegrasi))## sebelum sesudah
## 11 11
## [1] 0
# Missing value baru akibat tidak ditemukan pasangan transaksi
colSums(is.na(data_terintegrasi[, c("jumlah_transaksi", "total_purchase")]))## jumlah_transaksi total_purchase
## 1 1
# Pelanggan tanpa pasangan transaksi
data_terintegrasi[is.na(data_terintegrasi$jumlah_transaksi),
c("customer_id", "nama")]Ini adalah salah satu keputusan yang sering diambil terlalu cepat.
Nilai NA pada transaksi bisa berarti dua hal yang sangat
berbeda:
Nilai NA hanya boleh diubah menjadi 0 apabila makna
sesungguhnya sudah dikonfirmasi terlebih dahulu.
# Contoh jika telah dikonfirmasi bahwa NA berarti belum pernah bertransaksi
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)] <- 0Setelah melalui seluruh tahapan, saatnya menyusun dataset akhir dengan memilih atribut yang relevan untuk dipakai pada analisis selanjutnya.
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_finalFungsi audit_data() yang kita buat di awal praktikum
kini terpakai kembali untuk memeriksa kondisi dataset akhir.
## [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
Dengan demikian, mari kita bandingkan langsung beberapa indikator kualitas data antara kondisi awal dan kondisi akhir.
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))
)
)
perbandinganTabel ini merangkum dengan jelas seberapa jauh kualitas data telah membaik seperti duplikasi hilang, missing value tuntas tertangani, dan kategori kota maupun status menjadi jauh lebih konsisten.
Langkah terakhir adalah menyimpan dataset akhir beserta log perubahannya, agar dapat dipakai kembali pada percobaan berikutnya atau dibagikan kepada pihak lain.
Sepanjang materi ini, alur yang telah kita lalui adalah:
Catatan: Preprocessing pada dasarnya adalah proses pengambilan keputusan. Kode R hanya menjalankan keputusan tersebut; kualitas hasil akhirnya tetap bergantung pada pemahaman kita terhadap data, tujuan analisis, dan sejauh mana setiap perubahan didokumentasikan dengan baik.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.