praktikum_preprocessing..Rmd ini di dalam folder proyek.Penjelasan langkah: sebelum mulai mengolah data, kita perlu memastikan RStudio bekerja pada folder proyek yang benar. Langkah ini penting agar file hasil preprocessing, gambar, dan laporan tersimpan pada lokasi yang konsisten.
Kode getwd() digunakan untuk melihat lokasi folder kerja
aktif.
getwd()
## [1] "C:/SEMESTER 5/Data Mining"
Selanjutnya, R.version.string digunakan untuk mengetahui
versi R yang sedang digunakan. Informasi ini berguna ketika hasil
praktikum perlu direproduksi pada komputer lain.
R.version.string
## [1] "R version 4.4.2 (2024-10-31 ucrt)"
Modul ini menggunakan fungsi dasar R sehingga tidak membutuhkan paket tambahan untuk menjalankan proses utama.
Dataset berikut sengaja dibuat tidak sempurna agar setiap tahap preprocessing dapat dipraktikkan secara langsung.
Penjelasan langkah: data pelanggan menjadi sumber utama yang akan dibersihkan. Dataset dibuat secara sengaja memiliki beberapa masalah kualitas, sehingga kita dapat melihat bagaimana setiap teknik preprocessing bekerja.
Kode berikut membuat data pelanggan dalam bentuk
data.frame.
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
)
pelanggan_raw
Penjelasan langkah: data transaksi merupakan sumber
kedua. Data ini nantinya akan digabungkan dengan data pelanggan
menggunakan customer_id. Nama kolom cust_id
sengaja dibuat berbeda agar proses penyelarasan identifier juga dapat
dipraktikkan.
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
)
transaksi_raw
Sebelum memperbaiki data, kondisi awal perlu dipahami terlebih dahulu. Pemeriksaan ini membantu menentukan masalah apa saja yang benar-benar terdapat pada dataset.
Penjelasan langkah: pemeriksaan struktur dilakukan sebelum data diubah. Tujuannya adalah mengetahui ukuran dataset, nama atribut, tipe data, beberapa baris awal, serta ringkasan statistik. Dengan begitu, masalah dapat dikenali sebelum tindakan cleaning dilakukan.
Pertama, dim() digunakan untuk mengetahui jumlah baris
dan kolom.
dim(pelanggan_raw)
## [1] 12 6
Berikutnya, names() menampilkan seluruh nama atribut
yang tersedia.
names(pelanggan_raw)
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
Fungsi str() membantu melihat struktur dan tipe
masing-masing variabel.
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" ...
Untuk melihat contoh isi data, digunakan head().
head(pelanggan_raw)
Terakhir, summary() memberikan ringkasan statistik untuk
membantu menemukan nilai yang tidak wajar.
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
##
##
##
##
Penjelasan langkah: NA menunjukkan
nilai yang tidak tersedia. Kita tidak cukup hanya mengetahui ada atau
tidaknya NA; persentasenya juga perlu dihitung agar dapat
menilai seberapa besar masalah tersebut pada setiap atribut.
colSums(is.na(...)) menghitung jumlah nilai hilang pada
setiap kolom.
colSums(is.na(pelanggan_raw))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
Kode berikut mengubah jumlah tersebut menjadi persentase sehingga lebih mudah dibandingkan antaratribut.
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
Penjelasan langkah: duplikasi dapat menyebabkan satu
pelanggan dihitung lebih dari satu kali. Karena customer_id
berfungsi sebagai identifier, pemeriksaan dilakukan pada seluruh baris
sekaligus pada kolom identifier tersebut.
Kode pertama menghitung baris yang benar-benar duplikat.
sum(duplicated(pelanggan_raw))
## [1] 1
Kode kedua memeriksa apakah ada customer_id yang muncul
lebih dari sekali.
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1
Penjelasan langkah: kategori yang sama dapat ditulis dengan variasi huruf besar-kecil, spasi, atau singkatan. Jika tidak diseragamkan, satu kategori dapat terbaca sebagai beberapa kategori berbeda saat analisis.
unique() digunakan untuk melihat seluruh kategori kota
yang muncul.
sort(unique(pelanggan_raw$kota))
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
Hal yang sama dilakukan pada variabel status pelanggan.
sort(unique(pelanggan_raw$status))
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
Penjelasan langkah: rentang nilai membantu menemukan angka yang secara logika perlu diperiksa. Contohnya, usia yang terlalu tinggi untuk konteks pelanggan dapat menjadi indikasi kesalahan pencatatan atau outlier.
Rentang usia diperiksa terlebih dahulu.
range(pelanggan_raw$usia, na.rm = TRUE)
## [1] 21 150
Kemudian rentang pendapatan diperiksa untuk melihat apakah terdapat nilai yang jauh berbeda dari observasi lainnya.
range(pelanggan_raw$pendapatan, na.rm = TRUE)
## [1] 4.5e+06 5.0e+08
Penjelasan langkah: daripada melakukan pemeriksaan
satu per satu berulang kali, kita membuat fungsi
audit_data(). Fungsi ini merangkum tipe data, jumlah
NA, persentase NA, dan jumlah nilai unik untuk
setiap atribut. Ringkasan ini akan berguna lagi pada tahap audit
akhir.
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
Pada tahap ini dilakukan perbaikan terhadap kategori, duplikasi, dan nilai yang tidak sesuai dengan aturan domain. Dataset asli tetap disimpan agar perubahan dapat dilacak.
pelanggan <- pelanggan_raw
Penjelasan langkah: sebelum kategori diseragamkan,
spasi yang tidak diperlukan dihapus dengan trimws().
Setelah itu, seluruh teks diubah menjadi huruf kecil menggunakan
tolower() agar variasi kapitalisasi tidak dianggap sebagai
kategori yang 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"
Setelah standardisasi awal, kategori diperiksa kembali.
sort(unique(pelanggan$status))
## [1] "a" "active" "aktif" "nonaktif" "tidak aktif"
Penjelasan langkah: setelah spasi dan kapitalisasi dibersihkan, beberapa nilai masih memiliki makna yang sama tetapi bentuk penulisannya berbeda. Nilai-nilai tersebut dipetakan ke label standar agar analisis kategori menjadi konsisten.
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"
Pemeriksaan ulang dilakukan untuk memastikan kategori kota sudah menjadi lebih konsisten.
sort(unique(pelanggan$status))
## [1] "Aktif" "Tidak Aktif"
Penjelasan langkah: sebelum menghapus duplikasi,
baris yang memiliki customer_id berulang ditampilkan
terlebih dahulu. Ini penting agar kita dapat mengetahui data mana yang
akan terdampak.
pelanggan[
duplicated(pelanggan$customer_id) |
duplicated(pelanggan$customer_id, fromLast = TRUE),
]
Setelah duplikasi diperiksa, hanya satu baris untuk setiap
customer_id yang dipertahankan.
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL
dim(pelanggan)
## [1] 11 6
Penjelasan langkah: pemeriksaan domain digunakan untuk melihat apakah nilai masih masuk akal menurut aturan konteks data. Untuk contoh ini, usia di luar 15–100 tahun dianggap sebagai kandidat masalah. Kandidat tersebut diperiksa terlebih dahulu, bukan langsung dihapus.
pelanggan[pelanggan$usia < 15 | pelanggan$usia > 100, ]
Pendapatan negatif juga diperiksa sebagai kandidat nilai yang tidak valid.
pelanggan[pelanggan$pendapatan < 0, ]
Pada dataset praktikum, usia C004 bernilai 150. Nilai tersebut dikoreksi menjadi 50 berdasarkan sumber asli yang menjadi acuan praktikum.
pelanggan$usia[pelanggan$customer_id == "C004"] <- 50
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
Nilai hilang perlu diidentifikasi sebelum menentukan tindakan. Tidak semua missing value harus dihapus; pilihan metode bergantung pada jenis atribut dan tujuan analisis.
colSums(is.na(pelanggan))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
pelanggan[!complete.cases(pelanggan), ]
Penjelasan langkah: salah satu cara menangani
NA adalah mempertahankan hanya baris yang lengkap. Di sini
metode tersebut digunakan sebagai pembanding, bukan sebagai keputusan
otomatis.
pelanggan_complete <- pelanggan[complete.cases(pelanggan), ]
nrow(pelanggan)
## [1] 11
Kode berikut menunjukkan jumlah baris yang benar-benar lengkap.
nrow(pelanggan_complete)
## [1] 8
Persentase baris yang hilang akibat penghapusan juga dihitung.
round((1 - nrow(pelanggan_complete) / nrow(pelanggan)) * 100, 2)
## [1] 27.27
Pendekatan ini sederhana, tetapi dapat mengurangi jumlah observasi. Karena itu, penghapusan baris sebaiknya tidak dilakukan otomatis.
Penjelasan langkah: jika penghapusan baris terlalu banyak mengurangi data, nilai hilang dapat diisi menggunakan nilai statistik tertentu. Mean dan median dibandingkan terlebih dahulu. Median dipilih pada contoh ini karena lebih tahan terhadap pengaruh nilai ekstrem.
mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)
mean_pendapatan
## [1] 59900000
median_pendapatan
## [1] 4900000
Pada data praktikum, median digunakan sebagai dasar imputasi.
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")]
Penjelasan langkah: atribut kategorik tidak dapat
diimputasi menggunakan mean atau median. Untuk kota yang tidak
diketahui, digunakan label Tidak diketahui sehingga
informasi mengenai ketidaktersediaan 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
Penjelasan langkah: selain mengisi NA,
kita dapat menyimpan indikator yang menunjukkan apakah sebuah nilai pada
awalnya hilang. Variabel indikator ini berguna apabila kondisi
missingness ingin tetap dipertimbangkan dalam analisis.
pelanggan$pendapatan_missing <- as.integer(is.na(pelanggan$pendapatan))
table(pelanggan$pendapatan_missing)
##
## 0 1
## 9 2
Penjelasan langkah: imputasi dapat mengubah distribusi data. Karena itu, histogram sebelum dan sesudah imputasi dibandingkan untuk melihat perubahan pola 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 = "navy",
breaks = 8
)
par(mfrow = c(1, 1))
Outlier adalah observasi yang berada jauh dari pola umum data. Nilai seperti ini tidak langsung dianggap salah karena bisa saja merupakan kejadian yang memang terjadi.
Penjelasan langkah: boxplot digunakan untuk melihat persebaran data dan kandidat outlier. Titik yang berada jauh dari pola utama perlu diperiksa lebih lanjut, tetapi belum tentu merupakan kesalahan.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue4",
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Penjelasan langkah: metode IQR menggunakan kuartil pertama (Q1), kuartil ketiga (Q3), dan jarak antarkuartil (IQR). Batas kandidat outlier ditentukan dari Q1 − 1,5×IQR dan Q3 + 1,5×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
Penjelasan langkah: setelah batas IQR tersedia, setiap nilai pendapatan diberi penanda apakah berada di luar batas tersebut. Penanda ini membantu proses evaluasi tanpa langsung menghapus observasi.
pelanggan$outlier_pendapatan <-
pelanggan$pendapatan_imputasi < batas_bawah |
pelanggan$pendapatan_imputasi > batas_atas
pelanggan[
pelanggan$outlier_pendapatan,
c("customer_id", "nama", "pendapatan_imputasi")
]
Penjelasan langkah: outlier tidak langsung dihapus. Salah satu alternatif yang dapat diperagakan adalah winsorization, yaitu membatasi nilai ekstrem pada batas bawah dan 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")
]
Pada praktik nyata, keputusan untuk mempertahankan, mengoreksi, mentransformasi, atau menghapus outlier harus didasarkan pada konteks data. Nilai ekstrem tidak seharusnya dihapus hanya karena berbeda jauh dari observasi lain.
Transformasi digunakan untuk mengubah skala atribut numerik sehingga lebih sesuai untuk kebutuhan analisis atau algoritma tertentu.
Penjelasan langkah: normalisasi min–maks mengubah nilai numerik ke skala 0 sampai 1. Metode ini berguna ketika atribut memiliki rentang yang sangat berbeda dan analisis membutuhkan skala yang lebih seragam.
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"
)]
Penjelasan langkah: z-score mengubah nilai berdasarkan jaraknya dari rata-rata dalam satuan simpangan baku. Nilai positif menunjukkan posisi di atas rata-rata, sedangkan nilai negatif menunjukkan posisi di bawah rata-rata.
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)
Penjelasan langkah: decimal scaling mengecilkan skala data dengan membaginya menggunakan pangkat 10 tertentu. Tujuannya adalah mengurangi besarnya angka tanpa mengubah urutan relatif observasi.
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
Penjelasan langkah: hasil dari beberapa metode transformasi ditampilkan berdampingan agar perbedaan skala dapat dibandingkan secara langsung.
transformasi <- pelanggan[, c(
"customer_id",
"pendapatan_imputasi",
"pendapatan_minmax",
"pendapatan_z",
"pendapatan_decimal"
)]
transformasi
Penjelasan langkah: pada tahap ini kita membandingkan normalisasi berdasarkan data sebelum dan sesudah winsorization. Tujuannya adalah melihat bagaimana nilai ekstrem memengaruhi skala sebagian besar observasi.
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)
Grafik membantu melihat bahwa keberadaan nilai ekstrem dapat membuat sebagian besar observasi terkonsentrasi pada rentang yang sangat sempit setelah normalisasi. Karena itu, pemeriksaan outlier sebaiknya dilakukan sebelum memilih metode transformasi.
Setelah data pelanggan dibersihkan, data tersebut dapat digabungkan dengan data transaksi. Sebelum join, kecocokan identifier perlu diperiksa.
Penjelasan langkah: sebelum dua dataset digabungkan,
identifier harus diperiksa terlebih dahulu. Kita perlu memastikan
customer_id tidak berulang dan mengetahui identifier yang
hanya muncul pada salah satu sumber.
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"
Hasil setdiff() menunjukkan identifier yang hanya muncul
pada salah satu sumber data. Informasi ini penting untuk mengetahui
apakah ada pelanggan tanpa transaksi atau transaksi tanpa data
pelanggan.
Penjelasan langkah: kedua sumber harus memiliki nama
kolom kunci yang sama agar proses penggabungan lebih mudah.
cust_id pada data transaksi diselaraskan menjadi
customer_id.
transaksi <- transaksi_raw
names(transaksi)[names(transaksi) == "cust_id"] <- "customer_id"
merge()Penjelasan langkah: merge() digunakan
untuk menggabungkan data pelanggan dengan data transaksi berdasarkan
customer_id. Opsi all.x = TRUE memastikan
seluruh pelanggan dari dataset utama tetap dipertahankan.
data_terintegrasi <- merge(
pelanggan,
transaksi,
by = "customer_id",
all.x = TRUE
)
data_terintegrasi[, c(
"customer_id",
"nama",
"jumlah_transaksi",
"total_purchase"
)]
Penggunaan all.x = TRUE membuat seluruh pelanggan dari
tabel utama tetap dipertahankan meskipun tidak ditemukan pasangan
transaksi.
Penjelasan langkah: setelah penggabungan, hasil
tidak boleh langsung dianggap benar. Jumlah baris, keunikan identifier,
dan NA pada kolom transaksi perlu diperiksa kembali untuk
memastikan integrasi tidak menghasilkan duplikasi atau kehilangan data
yang tidak diinginkan.
Pertama, periksa apakah jumlah baris berubah secara tidak wajar.
c(
sebelum = nrow(pelanggan),
sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah
## 11 11
Selanjutnya, pastikan customer_id tetap unik.
sum(duplicated(data_terintegrasi$customer_id))
## [1] 0
Periksa pula missing value yang muncul pada atribut transaksi.
colSums(
is.na(
data_terintegrasi[, c("jumlah_transaksi", "total_purchase")]
)
)
## jumlah_transaksi total_purchase
## 1 1
Cari pelanggan yang belum memiliki pasangan transaksi.
data_terintegrasi[
is.na(data_terintegrasi$jumlah_transaksi),
c("customer_id", "nama")
]
Nilai NA pada data transaksi tidak otomatis berarti nol.
Nilai tersebut dapat berarti pelanggan memang belum bertransaksi, atau
data transaksi tidak tersedia. Oleh sebab itu, penggantian menjadi nol
hanya dilakukan apabila definisinya sudah dikonfirmasi.
# Contoh: NA dianggap 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)
] <- 0
Penjelasan langkah: setelah seluruh proses selesai, hanya atribut yang dibutuhkan untuk dataset analisis akhir yang dipertahankan. Kolom hasil imputasi dan transformasi dipilih agar dataset akhir siap digunakan untuk tahap analisis berikutnya.
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
Penjelasan langkah: audit akhir dilakukan dengan prinsip yang sama seperti audit awal. Hasilnya dibandingkan untuk memastikan proses preprocessing benar-benar memperbaiki kondisi data sesuai tujuan.
audit_akhir <- audit_data(data_final)
audit_akhir
Pastikan tidak ada customer_id yang masih berulang.
sum(duplicated(data_final$customer_id))
## [1] 0
Periksa kembali seluruh missing value.
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
Penjelasan langkah: tabel perbandingan menjadi ringkasan perubahan kualitas data. Dengan membandingkan kondisi sebelum dan sesudah, kita dapat melihat apakah duplikasi dan missing value berkurang serta apakah kategori sudah lebih konsisten.
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
Tabel perbandingan digunakan sebagai pemeriksaan akhir untuk melihat perubahan kualitas data. Indikator seperti jumlah baris, duplikasi, missing value, serta jumlah kategori membantu memastikan bahwa proses preprocessing menghasilkan perubahan yang memang diharapkan.
Penjelasan langkah: dataset akhir dan log perubahan disimpan ke file CSV. Menyimpan keduanya penting agar hasil preprocessing dapat digunakan kembali dan setiap perubahan tetap terdokumentasi.
write.csv(
data_final,
"data_pelanggan_setelah_preprocessing.csv",
row.names = FALSE
)
write.csv(
log_perubahan,
"log_perubahan_preprocessing.csv",
row.names = FALSE
)
Alur preprocessing yang dilakukan dalam praktikum ini adalah:
Kesimpulan: preprocessing merupakan rangkaian keputusan untuk meningkatkan kesiapan data sebelum analisis. R menjalankan langkah yang telah ditentukan, tetapi kualitas hasil akhirnya tetap bergantung pada pemahaman terhadap konteks data, tujuan analisis, serta dokumentasi setiap perubahan.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.