Data Preprocessing Menggunakan RStudio
Identitas Praktikum
| Komponen | Keterangan |
|---|---|
| Mata kuliah | Data Mining |
| Pertemuan | 03 |
| Topik | Data Preprocessing |
| Software | R dan RStudio |
| Alokasi waktu | 2 SKS / 100 menit |
| Acuan utama | Han, Kamber, dan Pei (2012), Bab 3 |
Capaian Praktikum
Setelah mengikuti seluruh rangkaian praktikum, mahasiswa diharapkan memiliki kemampuan untuk:
- menjelaskan kondisi data yang menyebabkan preprocessing diperlukan;
- mengidentifikasi masalah kualitas data menggunakan R;
- menerapkan data cleaning secara sistematis;
- memilih dan menerapkan strategi penanganan missing values;
- mendeteksi serta mengevaluasi outlier;
- melakukan transformasi atribut numerik; dan
- mengintegrasikan dua sumber data serta memvalidasi hasilnya.
Skenario Praktikum
Sebuah perusahaan e-commerce ingin melakukan analisis terhadap pelanggan dengan memanfaatkan data yang berasal dari dua sumber berikut:
- data pelanggan, yang berisi informasi identitas, usia, kota, pendapatan, serta status pelanggan;
- data transaksi, yang memuat informasi mengenai jumlah transaksi dan total nilai pembelian.
Data tersebut belum siap digunakan secara langsung karena masih terdapat beberapa permasalahan, seperti nilai hilang, penulisan kategori yang tidak seragam, data duplikat, nilai yang sangat ekstrem, serta perbedaan penamaan identifier. Tugas pada praktikum ini adalah menyiapkan dan memperbaiki data hingga terbentuk satu dataset yang layak digunakan untuk analisis.
Prinsip utama: suatu nilai tidak boleh langsung diubah atau dihapus hanya karena tampak tidak biasa. Setiap tindakan preprocessing harus didasarkan pada alasan yang jelas, aturan yang dapat dipertanggungjawabkan, serta dokumentasi perubahan.
Persiapan RStudio
Membuat proyek
- Buka RStudio.
- Pilih File → New Project → New Directory → New Project.
- Beri nama proyek, misalnya
praktikum_preprocessing. - Simpan file ini di dalam folder proyek.
- Klik Knit untuk menghasilkan HTML.
Membangun Dataset Praktikum
Jalankan kode berikut untuk membentuk dataset praktikum. Data sengaja dibuat mengandung berbagai permasalahan agar setiap tahapan preprocessing dapat dipelajari dan diterapkan.
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## 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
## cust_id jumlah_transaksi total_purchase
## 1 C001 5 1500000
## 2 C002 3 900000
## 3 C003 7 2700000
## 4 C004 2 600000
## 5 C005 6 2100000
## 6 C006 4 1300000
## 7 C007 20 25000000
## 8 C008 5 1700000
## 9 C009 3 800000
## 10 C010 8 3200000
## 11 C012 1 250000
Bagian I — Konsep Data Preprocessing
Memahami struktur data
Sebelum data diperbaiki atau diubah, terlebih dahulu perlu dilakukan pemeriksaan terhadap ukuran dataset, struktur data, tipe setiap atribut, serta beberapa baris awal sebagai gambaran kondisi data.
## [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
##
##
##
##
Mengukur kualitas awal
Menurut Han, Kamber, dan Pei, kualitas data dapat ditinjau melalui enam dimensi, yaitu accuracy, completeness, consistency, timeliness, believability, dan interpretability. Namun, tidak seluruh dimensi tersebut dapat dinilai hanya berdasarkan tabel yang tersedia. Sebagai contoh, accuracy memerlukan data pembanding yang merepresentasikan kondisi sebenarnya, sedangkan timeliness membutuhkan informasi mengenai waktu pembaruan data.
Pemeriksaan awal pada bagian ini difokuskan pada berbagai masalah kualitas yang masih dapat dikenali secara 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] 4500000 500000000
Membuat fungsi ringkasan kualitas
audit_data <- function(data) {
data.frame(
atribut = names(data),
tipe = sapply(data, function(x) class(x)[1]),
jumlah_missing = sapply(data, function(x) sum(is.na(x))),
persen_missing = round(sapply(data, function(x) mean(is.na(x)) * 100), 2),
jumlah_unik = sapply(data, function(x) length(unique(x))),
row.names = NULL
)
}
audit_awal <- audit_data(pelanggan_raw)
audit_awal## atribut tipe jumlah_missing persen_missing jumlah_unik
## 1 customer_id character 0 0.00 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 tidak langsung menentukan tindakan pembersihan data. Audit berfungsi sebagai petunjuk untuk menunjukkan atribut mana yang perlu dianalisis lebih lanjut.
Bagian II — Implementasi Data Cleaning
Membuat salinan kerja
Data mentah sebaiknya tetap disimpan agar seluruh perubahan yang dilakukan selama preprocessing dapat ditelusuri kembali.
Membersihkan spasi dan kapitalisasi
# Menghapus spasi di awal dan akhir
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)
# Menyeragamkan huruf menjadi kecil untuk proses 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"
Menyeragamkan kategori
Penyeragaman kategori dilakukan berdasarkan aturan yang sesuai dengan domain data. Dalam penerapan nyata, aturan tersebut sebaiknya diverifikasi melalui kamus data atau dikonfirmasi kepada pihak yang memahami dan memiliki data.
# 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"
Mendeteksi dan menghapus duplikasi
# Menampilkan seluruh 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
# Mempertahankan kemunculan pertama untuk customer_id yang identik
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL
dim(pelanggan)## [1] 11 6
Catatan: penghapusan data duplikat hanya tepat dilakukan apabila setiap customer_id memang dirancang untuk mewakili satu pelanggan. Jika satu pelanggan secara valid dapat memiliki lebih dari satu baris data, maka penghapusan tersebut justru dapat menyebabkan hilangnya informasi.
Memeriksa aturan domain
# Kandidat usia tidak valid berdasarkan aturan bisnis 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>
Nilai usia 150 tidak sesuai dengan aturan yang ditetapkan pada domain data. Misalnya, setelah dilakukan pengecekan terhadap formulir sumber, diketahui bahwa nilai yang seharusnya tercatat adalah 50.
Membuat log perubahan
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## tahap atribut
## 1 Standardisasi kota
## 2 Standardisasi status
## 3 Deduplikasi customer_id
## 4 Koreksi domain usia
## tindakan
## 1 PKU dan variasi kapital menjadi Pekanbaru
## 2 ACTIVE/A menjadi Aktif; nonaktif menjadi Tidak Aktif
## 3 Menghapus kemunculan kedua C010
## 4 Usia C004 dikoreksi dari 150 menjadi 50 berdasarkan sumber asli
Bagian III — Penanganan Missing Values
Mengidentifikasi lokasi nilai hilang
## 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
Strategi 1: menghapus baris
Kode berikut digunakan hanya untuk memperlihatkan penerapan strategi penghapusan baris. Objek pelanggan yang digunakan dalam analisis utama tidak langsung diganti.
## [1] 11
## [1] 8
Pertanyaan untuk dipertimbangkan: seberapa besar proporsi data yang akan hilang apabila semua baris yang tidak lengkap langsung dihapus?
## [1] 27.27
Strategi 2: imputasi mean dan median
Variabel pendapatan mengandung nilai yang sangat ekstrem. Oleh karena itu, nilai mean dan median perlu dibandingkan terlebih dahulu sebelum menentukan metode imputasi yang paling sesuai.
mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)
mean_pendapatan## [1] 59900000
## [1] 4900000
Pada contoh ini, keberadaan nilai pendapatan sebesar 500 juta membuat distribusi data menjadi sangat menceng. Dalam kondisi tersebut, median menjadi ukuran yang lebih stabil untuk digunakan sebagai dasar imputasi.
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")]## 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
Strategi 3: imputasi nilai kategorik
Nilai kota yang hilang tidak selalu tepat jika langsung digantikan dengan kategori yang paling sering muncul. Dalam praktikum ini digunakan kategori eksplisit Tidak diketahui agar informasi mengenai ketidakpastian data tetap terlihat.
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 missing
Variabel indikator digunakan untuk mempertahankan informasi bahwa suatu nilai pada awalnya memang tidak tersedia.
pelanggan$pendapatan_missing <- as.integer(is.na(pelanggan$pendapatan))
table(pelanggan$pendapatan_missing)##
## 0 1
## 9 2
Membandingkan distribusi sebelum dan sesudah 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)Proses imputasi berpotensi mengubah bentuk distribusi data dan mengurangi tingkat variasinya. Oleh sebab itu, pemeriksaan kualitas data tidak cukup dilakukan hanya dengan memastikan seluruh nilai NA telah terisi.
Bagian IV — Penanganan Outlier
Visualisasi dengan boxplot
boxplot(pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot Pendapatan",
xlab = "Pendapatan")Menghitung batas 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
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")]## customer_id nama pendapatan_imputasi
## 7 C007 Gilang 500000000
Mengevaluasi tindakan
Nilai sebesar 500 juta tidak serta-merta harus dihapus. Sebelum mengambil tindakan, terdapat tiga kemungkinan yang perlu dipertimbangkan:
- Kesalahan input: nilai dapat diperbaiki apabila nilai yang benar dapat diverifikasi dari sumber yang dapat dipercaya.
- Observasi valid tetapi ekstrem: nilai dapat dipertahankan atau dianalisis menggunakan metode yang lebih robust terhadap nilai ekstrem.
- Populasi berbeda: data dapat dipisahkan ke dalam kelompok yang berbeda apabila, misalnya, pelanggan korporasi tercampur dengan pelanggan ritel.
Dalam modul ini, nilai ekstrem tetap dipertahankan. Selain itu, dibuat pula variabel versi winsorized semata-mata untuk melihat perbedaan hasil dan dampak dari metode tersebut.
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
Winsorizing pada bagian ini hanya digunakan sebagai ilustrasi tambahan. Metode tersebut sebaiknya diterapkan apabila benar-benar relevan dengan tujuan analisis, dan setiap pembatasan terhadap nilai ekstrem harus dicatat dengan jelas.
Bagian V — Transformasi Data
Normalisasi min–maks
Rumus untuk rentang \([0,1]\) adalah
\[ 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
Normalisasi z-score
\[ 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
Decimal scaling
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
Membandingkan metode 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 4500000 0.0000000000 -0.3041235
## 2 C002 4900000 0.0008072654 -0.3014440
## 3 C003 5200000 0.0014127144 -0.2994343
## 4 C004 4800000 0.0006054490 -0.3021138
## 5 C005 4900000 0.0008072654 -0.3014440
## 6 C006 5100000 0.0012108981 -0.3001042
## 7 C007 500000000 1.0000000000 3.0151095
## 8 C008 4700000 0.0004036327 -0.3027837
## 9 C009 4600000 0.0002018163 -0.3034536
## 10 C010 5300000 0.0016145308 -0.2987645
## 11 C011 4900000 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
Dampak outlier terhadap normalisasi
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: keberadaan outlier dapat menyebabkan sebagian besar nilai hasil normalisasi min–maks terkonsentrasi pada rentang yang sangat sempit. Karena itu, outlier perlu diperiksa terlebih dahulu sebelum menentukan metode transformasi yang akan digunakan.
Bagian VI — Integrasi Data
Memeriksa kunci pada kedua sumber
## [1] 0
## [1] 0
## [1] "C011"
## [1] "C012"
Hasil dari setdiff() digunakan untuk menunjukkan pelanggan yang belum memiliki data transaksi serta data transaksi yang tidak menemukan pasangan pada data pelanggan.
Menyelaraskan nama identifier
Melakukan left join dengan merge()
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
Pengaturan all.x = TRUE memastikan seluruh data pelanggan tetap dipertahankan, termasuk pelanggan yang tidak memiliki pasangan data transaksi.
Memvalidasi hasil integrasi
# 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")]## customer_id nama
## 11 C011 Kiki
Mengisi nol atau mempertahankan NA?
Nilai NA pada informasi transaksi dapat memiliki dua interpretasi yang berbeda:
- pelanggan memang belum pernah melakukan transaksi; atau
- data transaksi pelanggan tidak tersedia atau proses pemadanan data tidak berhasil.
Nilai NA hanya dapat diubah menjadi 0 apabila makna dari nilai tersebut telah dipastikan dan dikonfirmasi.
# 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)
] <- 0Dataset Akhir dan Evaluasi
Memilih atribut 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"
)]
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
Audit akhir
## 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
Membandingkan kondisi sebelum dan sesudah
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
Pertanyaan Refleksi
- Apakah dataset tanpa missing value selalu lebih berkualitas?
- Mengapa outlier tidak boleh otomatis dihapus?
- Bagaimana preprocessing dapat menimbulkan bias?
- Mengapa parameter imputasi dan transformasi seharusnya dihitung dari data pelatihan?
- Apa risiko integrasi data jika identifier tidak unik?
Ringkasan
Secara umum, tahapan preprocessing yang telah dilakukan dapat dirangkum sebagai berikut:
- memeriksa struktur data serta menilai kondisi kualitas data pada tahap awal;
- membersihkan ketidakkonsistenan kategori, menangani duplikasi, dan memeriksa pelanggaran terhadap aturan domain;
- menangani missing values dengan tetap mempertahankan informasi mengenai ketidakpastian yang ada;
- mengidentifikasi dan mengevaluasi keberadaan outlier;
- melakukan transformasi pada atribut numerik; serta
- menggabungkan beberapa sumber data dan melakukan validasi terhadap hasil integrasinya.
Data preprocessing pada dasarnya merupakan rangkaian proses pengambilan keputusan. Kode R berperan sebagai alat untuk menerapkan keputusan tersebut, sedangkan kualitas hasil akhirnya tetap ditentukan oleh pemahaman terhadap data, kesesuaian dengan tujuan analisis, serta kelengkapan dokumentasi setiap perubahan.
Daftar Pustaka
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.