Pendahuluan
Melanjutkan dari pembelajaran pada pertemuan ke-3. Praktikum data mining dengan skenario dari sebuah kasus. Dimana, sebuah perusahaan e-commerce akan melakukan analisis pelanggan. terdapat dua sumber data, yaitu:
- data pelanggan, yang memuat identitas, usia, kota, pendapatan, dan status pelanggan;
pelanggan_raw <- data.frame(
customer_id = c("C001", "C002", "C003", "C004", "C005", "C006","C007", "C008", "C009", "C010", "C010", "C011"),
nama = c("Ani", "Budi", "Citra", "Dodi", "Eka", "Fani", "Gilang", "Hana", "Indra", "Joko", "Joko", "Kiki"),
usia = c(21, 25, 23, 150, 27, NA, 31, 29, 22, 35, 35, 28),
pendapatan = c(4500000, NA, 5200000, 4800000, 4900000, 5100000,500000000, 4700000, 4600000, 5300000, 5300000, NA),
kota = c("Pekanbaru", " PKU", "PEKANBARU", "Dumai", "pekanbaru", "DUMAI", "Pekanbaru ", "Siak", "PKU", "Dumai", "Dumai", NA),
status = c("Aktif", "aktif", "ACTIVE", "A", "Tidak Aktif", "nonaktif", "Aktif", "AKTIF", "A", "Tidak aktif", "Tidak aktif", "Aktif"),
stringAsFactors = FALSE
)
pelanggan_raw## customer_id nama usia pendapatan kota status stringAsFactors
## 1 C001 Ani 21 4.5e+06 Pekanbaru Aktif FALSE
## 2 C002 Budi 25 NA PKU aktif FALSE
## 3 C003 Citra 23 5.2e+06 PEKANBARU ACTIVE FALSE
## 4 C004 Dodi 150 4.8e+06 Dumai A FALSE
## 5 C005 Eka 27 4.9e+06 pekanbaru Tidak Aktif FALSE
## 6 C006 Fani NA 5.1e+06 DUMAI nonaktif FALSE
## 7 C007 Gilang 31 5.0e+08 Pekanbaru Aktif FALSE
## 8 C008 Hana 29 4.7e+06 Siak AKTIF FALSE
## 9 C009 Indra 22 4.6e+06 PKU A FALSE
## 10 C010 Joko 35 5.3e+06 Dumai Tidak aktif FALSE
## 11 C010 Joko 35 5.3e+06 Dumai Tidak aktif FALSE
## 12 C011 Kiki 28 NA <NA> Aktif FALSE
- data transaksi, yang memuat jumlah transaksi dan total pembelian.
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),
stringAsFactors = FALSE
)
transaksi_raw## cust_id jumlah_transaksi total_purchase stringAsFactors
## 1 C001 5 1.5e+06 FALSE
## 2 C002 3 9.0e+05 FALSE
## 3 C003 7 2.7e+06 FALSE
## 4 C004 2 6.0e+05 FALSE
## 5 C005 6 2.1e+06 FALSE
## 6 C006 4 1.3e+06 FALSE
## 7 C007 20 2.5e+07 FALSE
## 8 C008 5 1.7e+06 FALSE
## 9 C009 3 8.0e+05 FALSE
## 10 C010 8 3.2e+06 FALSE
## 11 C012 1 2.5e+05 FALSE
Datanya belum di integrasikan dan beberapa mengandung nilai hilang, kategori yang kurang konsisten, duplikasi, outlier, hingga perbedaan nama identifier.
Tujuan dari praktikum ini ialah untuk melakukan penyiapan dan pembersihan data sehingga hasil data akhir yang telah di integrasikan akan menjadi data bersih yang siap di analisis sehingga dapat memberikan insight yang bermakna.
Isi
Pada tahapan ini kita akan mencoba melakukan persiapan, pembersihan, transformasi, dan integrasi dari data mentah yang kita punya. Untuk nantinya dapat menghasilkan data bersih yang siap untuk di analisis.
Persiapan
Kita lihat terlebih dahulu tentang keadaaan datanya.
## 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 stringAsFactors
## Length:12 Length:12 Mode :logical
## Class :character Class :character FALSE:12
## Mode :character Mode :character
##
##
##
##
Pada tabel 1. pelanggan_raw, terdapat beberapa informasi spesifik seperti ukuran tabel 12x6, kemudian hanya kolom usia dan pendapatan yang numerik (sisanya character). Terlihat juga ada outlier pada kolom usia dan pendapatan. Dimana di kolom usia ada nilai 150 yang belum di pastikan apakah itu tahun atau bulan. dan di kolom pendapatan ada pendapatan yang sebesar 500 juta, yang bisa jadi kebetulan atau kesalahan input angka 0 yang berlebihan.
## cust_id jumlah_transaksi total_purchase stringAsFactors
## Length:11 Min. : 1.000 Min. : 250000 Mode :logical
## Class :character 1st Qu.: 3.000 1st Qu.: 850000 FALSE:11
## Mode :character Median : 5.000 Median : 1500000
## Mean : 5.818 Mean : 3640909
## 3rd Qu.: 6.500 3rd Qu.: 2400000
## Max. :20.000 Max. :25000000
Pada tabel 2. Ukuran tabel nya ialah 11x3, dimana hal ini terdapat perbedaan ukuran terhadap tabel 1. yang bisa jadi terdapat duplikat di tabel 1 (data pelanggan). Kemudian ada juga total pembelian yang sampai 25 juta. Hal ini perlu di pastikan kembali apakah jumlah pembelian dengan harga nya itu sesuai.
Selanjutnya ialah kita akan mengecek missing value dan duplikat.
## customer_id nama usia pendapatan kota
## 0 0 1 2 1
## status stringAsFactors
## 0 0
Pada tabel 1. terdapat nilai hilang pada kolom usia, pendapatan, dan kota.
## cust_id jumlah_transaksi total_purchase stringAsFactors
## 0 0 0 0
Pada tabel 2. Tidak terdapat nilai hilang.
Untuk pengecekkan duplikat, perlu di pastikan kolom identifier nya. Dimana disini ada customer_id dan cust_id.
## [1] 1
Terlihat ada 1 baris data duplikat pada tabel 1 (data pelanggan).
## [1] 0
Tidak terdapat baris data duplikat pada tabel 2 (data transaksi).
Tak lupa juga kita mengecek kategori pada kolom yang berisi character. Karena untuk keseragaman label nantinya.
Yang pertama ialah pada tabel 1 (data pelanggan).
## [1] "Ani" "Budi" "Citra" "Dodi" "Eka" "Fani" "Gilang" "Hana"
## [9] "Indra" "Joko" "Kiki"
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
Dari hasil pengecekkan ini terlihat beberapa ketidaksamaan/ketidakseragaman pada baris data, sehingga hal ini akan menimbulkan masalah karena tidak terdefinisi dengan baik (case-sensitif label). Dan pada bagian nama, kita hanya mengecek penulisannya saja apakah nantinya hanya kata depannya saja yang besar atau lainnya.
Untuk di tabel 2 (data transaksi). Tidak ada kolom yang harus kita pastikan untuk keseragamannya, sehingga tahap pembersihan data dapat di laksanakan.
Pembersihan Data
Pada tahapan pembersihan data ini, kita akan melakukan pengerjaan dengan mulai dari menghapus duplikasi, missing value, menyeragamkan kategori/label, dan memeriksa atuan domain/logika di dalamnya.
sebelum di mulai pembersihan, akan lebih baik membuat salinan kerja atau salinan dataframe baru agar apabila terjadi kesalahan kita tidak kehilangan dataset aslinya.
Pada tahapan persiapan kita sudah mendapatkan indikasi duplikat yang ada pada tabel 1 (data pelanggan).
## customer_id nama usia pendapatan kota status stringAsFactors
## 10 C010 Joko 35 5300000 Dumai Tidak aktif FALSE
## 11 C010 Joko 35 5300000 Dumai Tidak aktif FALSE
Ternyata pada kustomer C010 terduplikasi sehingga hal ini bisa kita hapuskan salah satunya saja. Karena kolom customer_id merupakan identifier yang sifatnya itu unik (didalamnya tidak boleh sama). Sehingga logikanya satu transaksi memiliki 1 customer_id yang unik.
# Kita pertahankan kemunculan pertama untuk yang identik
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL
# Tampilkan data
pelanggan$customer_id## [1] "C001" "C002" "C003" "C004" "C005" "C006" "C007" "C008" "C009" "C010"
## [11] "C011"
setelah data duplikat ditangani, langkah selanjutnya ialah menangani nilai hilang atau Na. Dimana kita akan menggunakan imputasi median untuk kolom usia dan pendapatan (karena terdapat outlier). kemudian mendifinisikan label “Tidak diketahui” untuk nilai hilang pada kolom character seperti kota.
# Imputasi median untuk usia dan pendapatan
median_pendapatan <- median(pelanggan$pendapatan, na.rm = T)
median_usia <- median(pelanggan$usia, na.rm = T) # Definisikan nilai urut
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
pelanggan$usia_imputasi <- pelanggan$usia # Buat kolom baru di sebelahnya
pelanggan$pendapatan_imputasi[is.na(pelanggan$pendapatan_imputasi)] <- median_pendapatan
pelanggan$usia_imputasi[is.na(pelanggan$usia_imputasi)] <- median_usia
# Imputasi nilai kategorik dengan "Tidak diketahui"
pelanggan$kota_imputasi <- pelanggan$kota
pelanggan$kota_imputasi[is.na(pelanggan$kota_imputasi)] <- "Tidak diketahui"
# Tampilkan tabel
pelanggan[, c("customer_id", "usia", "usia_imputasi", "pendapatan", "pendapatan_imputasi", "kota", "kota_imputasi")]## customer_id usia usia_imputasi pendapatan pendapatan_imputasi kota
## 1 C001 21 21.0 4.5e+06 4.5e+06 Pekanbaru
## 2 C002 25 25.0 NA 4.9e+06 PKU
## 3 C003 23 23.0 5.2e+06 5.2e+06 PEKANBARU
## 4 C004 150 150.0 4.8e+06 4.8e+06 Dumai
## 5 C005 27 27.0 4.9e+06 4.9e+06 pekanbaru
## 6 C006 NA 27.5 5.1e+06 5.1e+06 DUMAI
## 7 C007 31 31.0 5.0e+08 5.0e+08 Pekanbaru
## 8 C008 29 29.0 4.7e+06 4.7e+06 Siak
## 9 C009 22 22.0 4.6e+06 4.6e+06 PKU
## 10 C010 35 35.0 5.3e+06 5.3e+06 Dumai
## 11 C011 28 28.0 NA 4.9e+06 <NA>
## kota_imputasi
## 1 Pekanbaru
## 2 PKU
## 3 PEKANBARU
## 4 Dumai
## 5 pekanbaru
## 6 DUMAI
## 7 Pekanbaru
## 8 Siak
## 9 PKU
## 10 Dumai
## 11 Tidak diketahui
Missing value berhasil di tangani. alasan kenapa tidak menghapus baris saja itu karena kalo menghapus baris akan kehilangan lumayan banyak data. Dengan total observasi yang hanya 11 ini pastinya kehilangan 2 data saja akan mengurangi informasi yang akan di dapat nantinya.
langkah selanjutnya ialah menyeragamkan kategori/label pada kolom kota_imputasi dan status.
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru"
## [5] "Pekanbaru" "PEKANBARU" "Pekanbaru " "PKU"
## [9] "Siak" "Tidak diketahui"
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
# Dictionary/standarisasi untuk menyeragamkan kategori text
pelanggan$kota_imputasi[pelanggan$kota_imputasi %in% c("PKU", "pekanbaru", "PEKANBARU")] <- "Pekanbaru"
pelanggan$kota_imputasi[pelanggan$kota_imputasi == "DUMAI"] <- "Dumai"
pelanggan$status[pelanggan$status %in% c("aktif", "AKTIF", "A", "ACTIVE")] <- "Aktif"
pelanggan$status[pelanggan$status %in% c("Tidak aktif", "nonaktif")] <- "Tidak Aktif"
# Tampilkan
pelanggan[, c("customer_id", "kota_imputasi", "status")]## customer_id kota_imputasi status
## 1 C001 Pekanbaru Aktif
## 2 C002 PKU Aktif
## 3 C003 Pekanbaru Aktif
## 4 C004 Dumai Aktif
## 5 C005 Pekanbaru Tidak Aktif
## 6 C006 Dumai Tidak Aktif
## 7 C007 Pekanbaru Aktif
## 8 C008 Siak Aktif
## 9 C009 Pekanbaru Aktif
## 10 C010 Dumai Tidak Aktif
## 11 C011 Tidak diketahui Aktif
Terdapat kejanggalan dimana kata PKU masih belum terseragamkan seperti yang lain. hal ini bisa jadi karena ada spasi tersembunyi.
# Menghapus spasi di awal dan di akhir
pelanggan$kota_imputasi <- trimws(pelanggan$kota_imputasi)
# kategorisasi kembali
pelanggan$kota_imputasi[pelanggan$kota_imputasi == "PKU"] <- "Pekanbaru"
pelanggan$kota_imputasi## [1] "Pekanbaru" "Pekanbaru" "Pekanbaru" "Dumai"
## [5] "Pekanbaru" "Dumai" "Pekanbaru" "Siak"
## [9] "Pekanbaru" "Dumai" "Tidak diketahui"
Sekarang data telah terkategorisasi dengan baik. langkah selanjutnya ialah kita akan mengecek aturan domain/logikanya sehingga apakah benar dengan adanya outlier di dalamnya itu menjadi masalah atau justru kebetulan.
Untuk usia yang 150 itu, misal saat dipastikan kembali ternyata usia sebenernya ialah 50 tahun maka kita perlu menggantinya menjadi angka sebenarnya.
Kemudian kita cek juga bagian pendapatan yang nilainya sungguh fantastis sebesar 500 juta. Hal ini sungguh wajar dan sifatnya kebetulan. Karena bisa jadi memang orang tersebut memiliki usaha yang banyak. Dan lagi umurnya 31 tahun. Untuk tabel 2 (transaksi). Nilai-nilainya sudah dipastikan tidak ada masalah seperti nilai tak wajar. Jadi kita bisa lanjutkan ke tahapan selanjutnya.
Transformasi Data
Dikarenakan ada baris data outlier dari pendapatan. Oleh karena itu, metode robust scaler akan lebih baik dibanding metode normalisasi yang biasanya.
robust_scaler <- function(x) {
if (all(is.na(x))) return(rep(NA_real_, length(x)))
iqr_val <- IQR(x, na.rm = TRUE)
if (iqr_val == 0) return(rep(0, length(x)))
(x - median(x, na.rm = TRUE)) / iqr_val
}# Terapkan di tabel pelanggan
pelanggan$usia_robust <- robust_scaler(pelanggan$usia_imputasi)
pelanggan$pendapatan_robust <- robust_scaler(pelanggan$pendapatan_imputasi)
head(pelanggan[, c("customer_id", "usia_imputasi", "usia_robust", "pendapatan_imputasi", "pendapatan_robust")])## customer_id usia_imputasi usia_robust pendapatan_imputasi pendapatan_robust
## 1 C001 21.0 -1.08333333 4500000 -1.00
## 2 C002 25.0 -0.41666667 4900000 0.00
## 3 C003 23.0 -0.75000000 5200000 0.75
## 4 C004 50.0 3.75000000 4800000 -0.25
## 5 C005 27.0 -0.08333333 4900000 0.00
## 6 C006 27.5 0.00000000 5100000 0.50
# Terapkan di tabel transaksi
transaksi$jumlah_transaksi_robust <- robust_scaler(transaksi$jumlah_transaksi)
transaksi$total_purchase_robust <- robust_scaler(transaksi$total_purchase)
head(transaksi[, c("cust_id", "jumlah_transaksi", "jumlah_transaksi_robust", "total_purchase", "total_purchase_robust")])## cust_id jumlah_transaksi jumlah_transaksi_robust total_purchase
## 1 C001 5 0.0000000 1500000
## 2 C002 3 -0.5714286 900000
## 3 C003 7 0.5714286 2700000
## 4 C004 2 -0.8571429 600000
## 5 C005 6 0.2857143 2100000
## 6 C006 4 -0.2857143 1300000
## total_purchase_robust
## 1 0.0000000
## 2 -0.3870968
## 3 0.7741935
## 4 -0.5806452
## 5 0.3870968
## 6 -0.1290323
Nilai robust scaller yang 0 mengindikasikan bahwa nilai aslinya sama persis dengan nilai median nya. Sehingga hal itu menunjukkan nilai robust scaller berada tepat di titik tengah (median).
Untuk melihat bagaimana kinerja dari robust scaler ini kita akan melihat titik-titiknya.
plot(pelanggan$pendapatan_imputasi,
pelanggan$pendapatan_robust,
pch = 19,
)
abline(0, 1, col = "red", lty = 2)Interpretasinya, outliernya berhasil di tekan dengan nilai robust scaler dimana median dan iqr dari data berperan penting dalam menyeimbangkannya. Hal ini menjadi bukti bahwa teknik transformasi perlu memperhatikan outlier yang ada di datasetnya juga. Alasan mengapa tabel 2 (transaksi) juga diikutkan transformasi ialah karena nantinya kedua tabel ini akan digabungkan sehingga untuk tahap analisis lanjutan perlu menggunakan jarak data yang memiliki pola. Apabila nanti teknik transformasinya di bedakan, akibatnya model analisis kita akan berantakan.
Integrasi Data
Pada tahap ini kita akan mencoba menggabungkan tabel 1 (pelanggan) dan tabel 2 (transaksi) menjadi satu tabel utuh dengan kunci identifiernya. untuk itu kita perlu menyamakan kolom identifier nya dulu.
Selanjutnya ialah kita akan menggabungkannya merge().
data_terintegrasi <- merge(pelanggan, transaksi,
by = "customer_id",
all.x = T)
# Tampilkan
data_terintegrasi[, c("customer_id", "nama", "jumlah_transaksi", "total_purchase")]## customer_id nama jumlah_transaksi total_purchase
## 1 C001 Ani 5 1.5e+06
## 2 C002 Budi 3 9.0e+05
## 3 C003 Citra 7 2.7e+06
## 4 C004 Dodi 2 6.0e+05
## 5 C005 Eka 6 2.1e+06
## 6 C006 Fani 4 1.3e+06
## 7 C007 Gilang 20 2.5e+07
## 8 C008 Hana 5 1.7e+06
## 9 C009 Indra 3 8.0e+05
## 10 C010 Joko 8 3.2e+06
## 11 C011 Kiki NA NA
Nah, ada hal menarik. Dimana customer C011 yaitu Kiki memiliki jumlah transaksi dan total purchase yang NA. Hal ini cukup wajar dimana bisa jadi si Kiki memang belum ada melakukan transaksi. Oleh karena itu, kita akan mengisinya dengan nilai 0 saja.
data_terintegrasi$jumlah_transaksi[
is.na(data_terintegrasi$jumlah_transaksi)] <- 0
data_terintegrasi$total_purchase[
is.na(data_terintegrasi$total_purchase)] <- 0
# Tampilkan
data_terintegrasi[, c("customer_id", "nama", "jumlah_transaksi", "total_purchase")]## customer_id nama jumlah_transaksi total_purchase
## 1 C001 Ani 5 1.5e+06
## 2 C002 Budi 3 9.0e+05
## 3 C003 Citra 7 2.7e+06
## 4 C004 Dodi 2 6.0e+05
## 5 C005 Eka 6 2.1e+06
## 6 C006 Fani 4 1.3e+06
## 7 C007 Gilang 20 2.5e+07
## 8 C008 Hana 5 1.7e+06
## 9 C009 Indra 3 8.0e+05
## 10 C010 Joko 8 3.2e+06
## 11 C011 Kiki 0 0.0e+00
Evaluasi
Pada tahap ini kita akan menyimpan dataset yang sudah bersih dan siap diolah. pada tahap transformasi sebelumnya, tabel 2 (transaksi) juga ikut di normalisasikan dengan robust scaler. Kita tetep menggunakan atribut yang di normalisasikan dengan baris C011 dihapuskan saja sehingga tersisa baris bersih dengan pelanggan dan transaksi.
Kesimpulan
Rangkaian proses preprocessing data ini berhasil membersihkan dan menyatukan data mentah pelanggan serta transaksi menjadi satu dataset yang terstandarisasi, utuh, dan bebas dari anomali. Melalui penanganan duplikasi baris, koreksi domain rule, imputasi nilai hilang secara kontekstual (median untuk numerik dan pelabelan untuk kategorik), serta penyeragaman tipografi teks, kualitas data dapat ditingkatkan tanpa kehilangan esensi informasi penting. Penerapan Robust Scaler secara konsisten pada kedua tabel terbukti efektif meredam dampak nilai ekstrem (outlier) pada atribut pendapatan dan transaksi, sehingga hasil akhir penggabungan data siap digunakan secara optimal dan andal untuk pemodelan data mining atau analisis analitik lanjutan.