Data Preprocessing Menggunakan RStudio
Apa Itu Data Preprocessing?
Data preprocessing merupakan proses yang saya lakukan untuk menyiapkan data sebelum memasuki tahap analisis. Data yang diperoleh belum tentu dapat langsung digunakan karena masih mungkin terdapat nilai yang hilang, data yang tercatat berulang, penulisan kategori yang berbeda, atau nilai yang tidak sesuai.
Melalui proses ini, saya memeriksa kondisi data dan melakukan penyesuaian yang diperlukan agar data menjadi lebih terstruktur dan konsisten. Tahap ini membantu saya memahami kondisi dataset sebelum melanjutkan ke proses analisis.
Latar Belakang
Saya menggunakan proses data preprocessing sebagai tahap
awal untuk menyiapkan data pelanggan sebelum digunakan dalam analisis.
Dataset yang saya gunakan masih memiliki beberapa kondisi yang perlu
diperhatikan, seperti nilai yang hilang, penulisan kategori yang belum
konsisten, customer_id yang berulang, nilai usia yang tidak
sesuai, serta nilai pendapatan yang jauh lebih besar dibandingkan data
lainnya.
Saya tidak langsung melakukan perubahan terhadap data tersebut. Saya terlebih dahulu memeriksa setiap kondisi untuk mengetahui masalah yang terdapat dalam dataset dan menentukan cara penanganan yang sesuai.
Proses yang saya lakukan dimulai dengan memeriksa struktur data, melakukan audit, menyeragamkan kategori, menangani data duplikat, memperbaiki nilai yang tidak sesuai, menangani missing value, mengidentifikasi outlier, melakukan transformasi, dan mengintegrasikan data pelanggan dengan data transaksi.
Menyiapkan Lingkungan Kerja
Saya memulai pengolahan dengan memeriksa direktori kerja dan versi R yang digunakan.
getwd()
## [1] "D:/SEMESTER 5/Data Mining"
R.version.string
## [1] "R version 4.4.2 (2024-10-31 ucrt)"
Menyiapkan Data Awal
Saya menggunakan dua dataset, yaitu data pelanggan dan data transaksi. Kedua dataset ini digunakan untuk menunjukkan proses pemeriksaan, pembersihan, transformasi, hingga integrasi 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_raw
## customer_id nama usia pendapatan kota status
## 1 C001 Ani 21 4.5e+06 Pekanbaru Aktif
## 2 C002 Budi 25 NA PKU aktif
## 3 C003 Citra 23 5.2e+06 PEKANBARU ACTIVE
## 4 C004 Dodi 150 4.8e+06 Dumai A
## 5 C005 Eka 27 4.9e+06 pekanbaru Tidak Aktif
## 6 C006 Fani NA 5.1e+06 DUMAI nonaktif
## 7 C007 Gilang 31 5.0e+08 Pekanbaru Aktif
## 8 C008 Hana 29 4.7e+06 Siak AKTIF
## 9 C009 Indra 22 4.6e+06 PKU A
## 10 C010 Joko 35 5.3e+06 Dumai Tidak aktif
## 11 C010 Joko 35 5.3e+06 Dumai Tidak aktif
## 12 C011 Kiki 28 NA <NA> Aktif
Output menampilkan dataset pelanggan awal yang terdiri atas 12 baris
dan 6 variabel. Dari tampilan awal terlihat beberapa kondisi yang perlu
diperiksa lebih lanjut, yaitu adanya NA, perbedaan penulisan kota dan
status, customer_id yang berulang, usia 150 tahun, serta
pendapatan sebesar 500.000.000 yang terlihat jauh lebih besar
dibandingkan nilai lainnya.
transaksi_raw
## cust_id jumlah_transaksi total_purchase
## 1 C001 5 1.5e+06
## 2 C002 3 9.0e+05
## 3 C003 7 2.7e+06
## 4 C004 2 6.0e+05
## 5 C005 6 2.1e+06
## 6 C006 4 1.3e+06
## 7 C007 20 2.5e+07
## 8 C008 5 1.7e+06
## 9 C009 3 8.0e+05
## 10 C010 8 3.2e+06
## 11 C012 1 2.5e+05
Output menunjukkan data transaksi yang terdiri atas 11 pelanggan.
Dataset transaksi menggunakan cust_id sebagai identifier
pelanggan sehingga nantinya perlu diseragamkan menjadi
customer_id sebelum proses integrasi.
Mengenali Kondisi Awal Data
Sebelum melakukan perubahan, saya memeriksa ukuran dataset, nama variabel, struktur data, beberapa observasi awal, dan ringkasan statistik.
dim(pelanggan_raw)
## [1] 12 6
Output menunjukkan jumlah baris dan kolom dataset. Dataset pelanggan awal memiliki 12 observasi dan 6 variabel. Informasi ini menjadi dasar untuk membandingkan ukuran data sebelum dan sesudah proses preprocessing.
names(pelanggan_raw)
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
Output menunjukkan nama variabel yang terdapat dalam dataset, yaitu
customer_id, nama, usia,
pendapatan, kota, dan status.
Nama variabel tersebut digunakan sebagai acuan dalam proses pembersihan
dan transformasi.
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" ...
Output str() digunakan untuk melihat struktur dataset,
tipe data setiap variabel, serta beberapa nilai awal. Pemeriksaan ini
membantu memastikan bahwa variabel numerik seperti usia dan pendapatan
tersimpan sebagai numerik, sedangkan variabel kategori tersimpan sebagai
karakter.
head(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
Output menampilkan enam observasi pertama dari dataset. Pemeriksaan ini membantu saya melihat data secara langsung dan menemukan pola atau nilai yang perlu diperiksa lebih lanjut.
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
##
##
##
##
Output summary() memberikan ringkasan statistik untuk
variabel numerik dan distribusi kategori untuk variabel karakter.
Ringkasan ini membantu mengidentifikasi nilai minimum, maksimum, median,
rata-rata, serta kemungkinan nilai yang tidak wajar.
Melakukan Audit Data
Saya memeriksa jumlah missing value, data duplikat, identitas pelanggan yang berulang, variasi kategori, dan rentang nilai numerik.
colSums(is.na(pelanggan_raw))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
Output menunjukkan jumlah NA pada setiap variabel. Nilai NA menunjukkan bahwa terdapat informasi yang belum tersedia pada dataset. Informasi ini digunakan sebagai dasar untuk menentukan metode penanganan missing value.
Persentase missing value pada setiap variabel dihitung dengan rumus:
\[ \text{Persentase Missing} = \frac{\text{Jumlah Missing}} {\text{Jumlah Observasi}} \times 100\% \]
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
Output menunjukkan persentase nilai hilang pada setiap variabel. Variabel dengan persentase missing value lebih besar memerlukan perhatian lebih dalam menentukan metode penanganannya.
Saya memeriksa jumlah baris yang benar-benar sama.
sum(duplicated(pelanggan_raw))
## [1] 1
Output menunjukkan jumlah baris yang sepenuhnya identik. Jika
hasilnya nol, tidak terdapat baris yang seluruh nilainya sama. Kondisi
tersebut belum menjamin bahwa identifier pelanggan tidak berulang
sehingga pemeriksaan customer_id tetap diperlukan.
Saya juga memeriksa apakah terdapat customer_id yang
berulang.
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1
Output menunjukkan jumlah customer_id yang muncul
kembali setelah kemunculan pertamanya. Pada data ini terdapat satu
customer_id yang berulang, yaitu C010. Kondisi tersebut
perlu diperiksa karena identifier pelanggan seharusnya unik.
Variasi kategori pada kota diperiksa menggunakan:
sort(unique(pelanggan_raw$kota))
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
Output menunjukkan bahwa kota yang sama ditulis dengan beberapa bentuk, seperti Pekanbaru, PKU, PEKANBARU, pekanbaru, Pekanbaru , dan PKU. Perbedaan tersebut merupakan masalah konsistensi kategori sehingga perlu dilakukan standardisasi.
Variasi kategori pada status diperiksa menggunakan:
sort(unique(pelanggan_raw$status))
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
Output menunjukkan beberapa bentuk penulisan status yang memiliki makna sama, seperti Aktif, aktif, ACTIVE, dan A. Kondisi tersebut perlu diseragamkan agar setiap kategori memiliki satu bentuk penulisan yang konsisten.
Rentang usia diperiksa dengan:
range(
pelanggan_raw$usia,
na.rm = TRUE
)
## [1] 21 150
Output menunjukkan rentang usia minimum hingga maksimum. Nilai maksimum sebesar 150 menunjukkan adanya nilai yang tidak wajar untuk usia pelanggan sehingga perlu diperiksa lebih lanjut.
Rentang pendapatan diperiksa dengan:
range(
pelanggan_raw$pendapatan,
na.rm = TRUE
)
## [1] 4.5e+06 5.0e+08
Output menunjukkan bahwa terdapat rentang pendapatan yang sangat lebar. Nilai 500.000.000 jauh lebih besar dibandingkan sebagian besar pendapatan lainnya sehingga perlu diperiksa sebagai kemungkinan outlier.
Agar hasil pemeriksaan lebih terstruktur, saya membuat fungsi
audit_data.
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
Tabel audit memberikan gambaran kondisi setiap variabel secara lebih terstruktur, meliputi tipe data, jumlah missing value, persentase missing value, dan jumlah nilai unik. Tabel ini menjadi dasar untuk menentukan tindakan preprocessing yang diperlukan pada setiap atribut.
Membersihkan dan Menyeragamkan Data
Sebelum melakukan perubahan, saya terlebih dahulu memeriksa kondisi setiap variabel. Saya tidak langsung menghapus atau mengubah suatu nilai hanya karena terlihat berbeda dari data lainnya.
Prinsip Utama
Jangan membersihkan data hanya karena sebuah nilai terlihat aneh. Setiap perubahan harus memiliki alasan, aturan, dan catatan.
Agar data awal tetap dapat dijadikan pembanding, saya membuat objek baru untuk proses pembersihan.
pelanggan <- pelanggan_raw
Saya menemukan perbedaan penulisan pada variabel kota dan status. Saya menghapus spasi yang tidak diperlukan dan menyeragamkan huruf terlebih dahulu.
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"
Output menunjukkan kategori yang telah dibersihkan dari spasi berlebih dan diubah menjadi huruf kecil. Langkah ini mempermudah proses pemetaan kategori yang memiliki arti sama.
sort(unique(pelanggan$status))
## [1] "a" "active" "aktif" "nonaktif" "tidak aktif"
Output menunjukkan bentuk kategori status setelah penyeragaman huruf. Beberapa kategori masih memiliki bentuk berbeda, tetapi selanjutnya akan digabungkan berdasarkan makna yang sama.
Setelah itu, saya menyeragamkan kategori yang memiliki makna yang sama.
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"
Kategori kota telah diseragamkan sehingga variasi penulisan Pekanbaru menjadi satu kategori, yaitu Pekanbaru. Dumai dan Siak juga menggunakan format penulisan yang konsisten.
sort(unique(pelanggan$status))
## [1] "Aktif" "Tidak Aktif"
Kategori status telah disederhanakan menjadi dua kategori utama, yaitu Aktif dan Tidak Aktif. Penyeragaman ini membuat kategori lebih konsisten untuk analisis berikutnya.
Memeriksa Customer ID yang Berulang
Saya memeriksa seluruh baris yang memiliki customer_id
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
Output menampilkan seluruh baris yang memiliki
customer_id berulang. Pada data ini, C010 muncul dua kali
dengan informasi pelanggan yang sama. Karena satu pelanggan seharusnya
hanya memiliki satu identitas, salah satu baris perlu dihilangkan.
Setelah diperiksa, saya mempertahankan kemunculan pertama dari setiap identitas pelanggan.
pelanggan <- pelanggan[
!duplicated(pelanggan$customer_id),
]
rownames(pelanggan) <- NULL
dim(pelanggan)
## [1] 11 6
Jumlah baris berkurang dari 12 menjadi 11 karena satu baris duplikat
C010 telah dihapus. Setiap customer_id sekarang hanya
muncul satu kali.
Memeriksa Nilai yang Tidak Sesuai
Saya memeriksa apakah terdapat nilai usia yang berada di luar rentang 15 hingga 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>
Output menunjukkan pelanggan C004 memiliki usia 150 tahun. Nilai tersebut tidak sesuai dengan rentang usia pelanggan yang digunakan sehingga perlu dilakukan pemeriksaan dan koreksi berdasarkan informasi yang tersedia.
Saya juga memeriksa kemungkinan adanya pendapatan bernilai negatif.
pelanggan[
pelanggan$pendapatan < 0,
]
## customer_id nama usia pendapatan kota status
## NA <NA> <NA> NA NA <NA> <NA>
## NA.1 <NA> <NA> NA NA <NA> <NA>
Jika output tidak menampilkan baris, berarti tidak terdapat pendapatan negatif. Dengan demikian, tidak ditemukan kesalahan domain berupa pendapatan yang bernilai kurang dari nol.
Setelah dilakukan pemeriksaan, nilai usia pelanggan C004 dikoreksi.
pelanggan$usia[
pelanggan$customer_id == "C004"
] <- 50
Nilai usia C004 diubah dari 150 menjadi 50 berdasarkan sumber informasi asli. Koreksi dilakukan karena nilai 150 dianggap sebagai kesalahan pencatatan, bukan kondisi nyata.
Mencatat Perubahan Data
Setiap perubahan yang saya lakukan dicatat agar proses preprocessing tetap dapat ditelusuri.
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
Tabel log_perubahan menunjukkan tindakan yang dilakukan
selama preprocessing. Pencatatan ini membuat setiap perubahan dapat
ditelusuri kembali sehingga proses pengolahan data menjadi lebih
transparan dan dapat dipertanggungjawabkan.
Memeriksa Missing Value
Setelah pembersihan awal, saya kembali memeriksa jumlah nilai yang hilang.
colSums(is.na(pelanggan))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
Output menunjukkan jumlah NA yang masih terdapat pada setiap variabel setelah standardisasi dan deduplikasi. Nilai yang masih hilang akan ditangani pada tahap berikutnya menggunakan metode yang sesuai.
Saya juga menampilkan observasi yang memiliki setidaknya satu nilai yang hilang.
pelanggan[
!complete.cases(pelanggan),
]
## 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
Output menunjukkan baris yang masih memiliki minimal satu nilai kosong. Pemeriksaan ini membantu mengetahui pelanggan mana yang terdampak missing value dan variabel apa yang perlu ditangani.
Saya membandingkan jumlah data sebelum dan sesudah penghapusan seluruh baris yang memiliki missing value.
pelanggan_complete <- pelanggan[
complete.cases(pelanggan),
]
nrow(pelanggan)
## [1] 11
Output menunjukkan jumlah baris dataset setelah deduplikasi tetapi sebelum penghapusan missing value. Jumlah tersebut digunakan sebagai pembanding untuk melihat dampak apabila seluruh baris dengan NA dihapus.
nrow(pelanggan_complete)
## [1] 8
Output menunjukkan jumlah observasi yang lengkap tanpa NA. Perbedaan jumlah observasi dengan dataset sebelumnya menunjukkan berapa banyak baris yang akan hilang jika digunakan metode complete case deletion.
Persentase data yang hilang akibat penghapusan baris dihitung dengan rumus:
\[ \text{Persentase Data Terhapus} = \left( 1 - \frac{n_{\text{complete}}} {n_{\text{awal}}} \right) \times 100\% \]
round(
(
1 -
nrow(pelanggan_complete) /
nrow(pelanggan)
) * 100,
2
)
## [1] 27.27
Output menunjukkan persentase observasi yang akan hilang jika seluruh baris dengan missing value dihapus. Karena penghapusan baris dapat mengurangi jumlah informasi yang tersedia, saya memilih pendekatan imputasi untuk mempertahankan observasi yang masih dapat digunakan.
Mengisi Missing Value
Saya menghitung nilai mean dan median dari pendapatan sebagai dasar untuk menentukan metode imputasi.
Rumus mean adalah:
\[ \bar{x} = \frac{\sum_{i=1}^{n}x_i}{n} \]
mean_pendapatan <- mean(
pelanggan$pendapatan,
na.rm = TRUE
)
median_pendapatan <- median(
pelanggan$pendapatan,
na.rm = TRUE
)
mean_pendapatan
## [1] 59900000
Output menunjukkan rata-rata pendapatan setelah nilai NA diabaikan. Nilai ini digunakan sebagai dasar perbandingan dengan median. Keberadaan nilai pendapatan yang sangat besar dapat membuat rata-rata menjadi kurang representatif terhadap sebagian besar pelanggan.
median_pendapatan
## [1] 4900000
Output menunjukkan median pendapatan. Median lebih tahan terhadap nilai ekstrem dibandingkan rata-rata sehingga dipilih sebagai dasar imputasi pendapatan pada dataset ini.
Saya menggunakan median untuk mengisi nilai pendapatan yang hilang dan median usia untuk mengisi usia yang kosong.
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
Nilai pendapatan yang hilang telah digantikan menggunakan median pendapatan, sedangkan usia yang hilang digantikan menggunakan median usia. Pendekatan ini mempertahankan jumlah observasi tanpa menambahkan nilai ekstrem secara berlebihan.
Saya menggunakan kategori “Tidak diketahui” untuk nilai kota yang kosong.
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
Output menunjukkan jumlah pelanggan pada setiap kategori kota setelah nilai kosong diganti menjadi Tidak diketahui. Pendekatan ini digunakan karena tidak terdapat informasi yang cukup untuk menentukan kota sebenarnya dari pelanggan tersebut.
Saya juga membuat indikator untuk menunjukkan data pendapatan yang sebelumnya memiliki nilai hilang.
pelanggan$pendapatan_missing <-
as.integer(
is.na(pelanggan$pendapatan)
)
table(pelanggan$pendapatan_missing)
##
## 0 1
## 9 2
Nilai 0 menunjukkan pendapatan awal tidak hilang, sedangkan nilai 1 menunjukkan pendapatan awal memiliki NA. Variabel indikator ini berguna untuk mempertahankan informasi mengenai observasi yang sebelumnya mengalami missing value.
Melihat Perubahan Setelah Imputasi
Saya membandingkan distribusi pendapatan sebelum dan sesudah imputasi menggunakan histogram.
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
)
par(mfrow = c(1, 1))
Histogram sebelah kiri menunjukkan distribusi pendapatan sebelum imputasi, sedangkan histogram sebelah kanan menunjukkan distribusi setelah nilai hilang diganti dengan median. Imputasi menambah nilai pada posisi median sehingga jumlah observasi bertambah tanpa memasukkan nilai yang terlalu ekstrem.
Mengidentifikasi Outlier
Saya menggunakan boxplot untuk melihat kemungkinan adanya nilai pendapatan yang ekstrem.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Boxplot digunakan untuk melihat penyebaran pendapatan dan mendeteksi nilai yang berada jauh dari kelompok data utama. Nilai yang berada di luar batas whisker merupakan kandidat outlier dan perlu diperiksa lebih lanjut.
Selanjutnya, saya menggunakan metode IQR untuk menentukan batas bawah dan batas atas.
Rumus Interquartile Range adalah:
\[ IQR = Q_3 - Q_1 \]
\[ Batas\ Bawah = Q_1 - 1.5(IQR) \]
\[ Batas\ Atas = Q_3 + 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
Output menunjukkan nilai kuartil pertama, kuartil ketiga, IQR, batas bawah, dan batas atas. Nilai pendapatan yang berada di bawah batas bawah atau di atas batas atas dikategorikan sebagai kandidat outlier berdasarkan aturan IQR.
Data yang berada di luar batas tersebut kemudian saya tandai.
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 5e+08
Output menampilkan pelanggan yang memiliki pendapatan di luar batas
IQR. Nilai tersebut ditandai sebagai TRUE pada variabel
outlier_pendapatan. Penandaan dilakukan terlebih dahulu
sebelum menentukan tindakan terhadap nilai ekstrem.
Nilai ekstrem tidak langsung saya hapus. Saya menggunakan winsorization untuk membatasi nilai tersebut.
pelanggan$pendapatan_winsor <- pmin(
pmax(
pelanggan$pendapatan_imputasi,
batas_bawah
),
batas_atas
)
Winsorization membatasi nilai yang berada di luar batas IQR tanpa menghapus observasinya. Dengan demikian, jumlah pelanggan tetap dipertahankan tetapi pengaruh nilai ekstrem terhadap analisis dapat dikurangi.
Melakukan Transformasi Data
Saya melakukan normalisasi Min–Maks untuk menyesuaikan nilai ke dalam skala yang sama.
Rumus normalisasi Min–Maks adalah:
\[ x' = \frac{x-x_{\min}} {x_{\max}-x_{\min}} \]
Hasil transformasi berada pada rentang 0 sampai 1.
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)
Nilai usia_minmax dan pendapatan_minmax
telah berada pada skala 0 sampai 1. Nilai yang mendekati 0 menunjukkan
posisi relatif rendah terhadap nilai minimum, sedangkan nilai yang
mendekati 1 menunjukkan posisi relatif tinggi terhadap nilai
maksimum.
Saya juga melakukan standardisasi menggunakan Z-Score.
Rumus Z-Score adalah:
\[ z = \frac{x-\bar{x}} {s} \]
dengan x sebagai nilai pengamatan, x̄ sebagai rata-rata, dan s sebagai standar deviasi.
pelanggan$usia_z <-
as.numeric(
scale(pelanggan$usia_imputasi)
)
pelanggan$pendapatan_z <-
as.numeric(
scale(pelanggan$pendapatan_imputasi)
)
Z-Score menunjukkan posisi suatu pengamatan terhadap rata-rata dalam satuan standar deviasi. Nilai positif menunjukkan pengamatan berada di atas rata-rata, sedangkan nilai negatif menunjukkan pengamatan berada di bawah rata-rata.
Transformasi decimal scaling digunakan untuk memperkecil skala nilai berdasarkan pangkat sepuluh.
Rumus decimal scaling adalah:
\[ x' = \frac{x}{10^j} \]
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
)
Transformasi decimal scaling menghasilkan nilai pendapatan dengan skala yang lebih kecil. Transformasi ini tidak mengubah urutan relatif antarobservasi, tetapi membuat angka lebih mudah digunakan pada beberapa metode analisis.
Menyiapkan Data untuk Integrasi
Setelah proses pembersihan, imputasi, dan transformasi selesai, saya menyiapkan dataset pelanggan dan transaksi untuk digabungkan. Sebelum integrasi dilakukan, saya memastikan bahwa identifier pada kedua dataset memiliki nama yang sama dan tidak terdapat duplikasi pada identifier pelanggan.
transaksi <- transaksi_raw
names(transaksi)[
names(transaksi) == "cust_id"
] <- "customer_id"
sum(
duplicated(
pelanggan$customer_id
)
)
## [1] 0
Output bernilai 0 menunjukkan bahwa setiap customer_id
pada data pelanggan sudah unik. Kondisi ini penting karena satu
pelanggan seharusnya hanya memiliki satu identitas pada dataset
pelanggan.
sum(
duplicated(
transaksi$customer_id
)
)
## [1] 0
Output bernilai 0 menunjukkan bahwa tidak terdapat
customer_id yang berulang pada dataset transaksi.
Identifier pada kedua dataset sudah memenuhi syarat untuk digunakan
sebagai variabel penghubung.
Saya memeriksa identifier yang hanya terdapat pada salah satu dataset.
id_tanpa_transaksi <- setdiff(
pelanggan$customer_id,
transaksi$customer_id
)
id_tanpa_pelanggan <- setdiff(
transaksi$customer_id,
pelanggan$customer_id
)
id_tanpa_transaksi
## [1] "C011"
Output menunjukkan pelanggan yang terdapat pada dataset pelanggan tetapi tidak memiliki pasangan pada dataset transaksi. Kondisi tersebut tidak langsung dianggap sebagai kesalahan karena pelanggan dapat saja belum melakukan transaksi.
id_tanpa_pelanggan
## [1] "C012"
Output menunjukkan identifier transaksi yang tidak ditemukan pada dataset pelanggan. Jika terdapat nilai, data tersebut perlu diperiksa karena menunjukkan adanya transaksi yang belum memiliki informasi pelanggan.
Mengintegrasikan Data Pelanggan dan Transaksi
Saya menggabungkan kedua dataset menggunakan
customer_id. Saya menggunakan merge() dengan
all.x = TRUE agar seluruh pelanggan tetap dipertahankan,
termasuk pelanggan yang belum memiliki transaksi.
data_terintegrasi <- merge(
pelanggan,
transaksi,
by = "customer_id",
all.x = TRUE
)
data_terintegrasi
## customer_id nama usia pendapatan kota status pendapatan_imputasi
## 1 C001 Ani 21 4.5e+06 Pekanbaru Aktif 4.5e+06
## 2 C002 Budi 25 NA Pekanbaru Aktif 4.9e+06
## 3 C003 Citra 23 5.2e+06 Pekanbaru Aktif 5.2e+06
## 4 C004 Dodi 50 4.8e+06 Dumai Aktif 4.8e+06
## 5 C005 Eka 27 4.9e+06 Pekanbaru Tidak Aktif 4.9e+06
## 6 C006 Fani NA 5.1e+06 Dumai Tidak Aktif 5.1e+06
## 7 C007 Gilang 31 5.0e+08 Pekanbaru Aktif 5.0e+08
## 8 C008 Hana 29 4.7e+06 Siak Aktif 4.7e+06
## 9 C009 Indra 22 4.6e+06 Pekanbaru Aktif 4.6e+06
## 10 C010 Joko 35 5.3e+06 Dumai Tidak Aktif 5.3e+06
## 11 C011 Kiki 28 NA <NA> Aktif 4.9e+06
## usia_imputasi kota_imputasi pendapatan_missing outlier_pendapatan
## 1 21.0 Pekanbaru 0 FALSE
## 2 25.0 Pekanbaru 1 FALSE
## 3 23.0 Pekanbaru 0 FALSE
## 4 50.0 Dumai 0 FALSE
## 5 27.0 Pekanbaru 0 FALSE
## 6 27.5 Dumai 0 FALSE
## 7 31.0 Pekanbaru 0 TRUE
## 8 29.0 Siak 0 FALSE
## 9 22.0 Pekanbaru 0 FALSE
## 10 35.0 Dumai 0 FALSE
## 11 28.0 Tidak diketahui 1 FALSE
## pendapatan_winsor usia_minmax pendapatan_minmax usia_z pendapatan_z
## 1 4500000 0.00000000 0.0000000000 -0.984199667 -0.3041235
## 2 4900000 0.13793103 0.0008072654 -0.489287834 -0.3014440
## 3 5200000 0.06896552 0.0014127144 -0.736743750 -0.2994343
## 4 4800000 1.00000000 0.0006054490 2.603911118 -0.3021138
## 5 4900000 0.20689655 0.0008072654 -0.241831918 -0.3014440
## 6 5100000 0.22413793 0.0012108981 -0.179967939 -0.3001042
## 7 5750000 0.34482759 1.0000000000 0.253079914 3.0151095
## 8 4700000 0.27586207 0.0004036327 0.005623998 -0.3027837
## 9 4600000 0.03448276 0.0002018163 -0.860471709 -0.3034536
## 10 5300000 0.48275862 0.0016145308 0.747991747 -0.2987645
## 11 4900000 0.24137931 0.0008072654 -0.118103960 -0.3014440
## pendapatan_decimal jumlah_transaksi total_purchase
## 1 0.0045 5 1.5e+06
## 2 0.0049 3 9.0e+05
## 3 0.0052 7 2.7e+06
## 4 0.0048 2 6.0e+05
## 5 0.0049 6 2.1e+06
## 6 0.0051 4 1.3e+06
## 7 0.5000 20 2.5e+07
## 8 0.0047 5 1.7e+06
## 9 0.0046 3 8.0e+05
## 10 0.0053 8 3.2e+06
## 11 0.0049 NA NA
Output menunjukkan data pelanggan yang telah digabungkan dengan
informasi transaksi berdasarkan customer_id. Seluruh
pelanggan dari dataset pelanggan tetap dipertahankan. Pelanggan yang
tidak memiliki pasangan transaksi akan memiliki nilai NA pada variabel
transaksi.
Saya memeriksa nilai kosong yang muncul pada variabel transaksi setelah proses integrasi.
colSums(
is.na(
data_terintegrasi[
,
c(
"jumlah_transaksi",
"total_purchase"
)
]
)
)
## jumlah_transaksi total_purchase
## 1 1
Nilai NA pada jumlah_transaksi dan
total_purchase menunjukkan pelanggan yang tidak memiliki
data transaksi. Berdasarkan konteks dataset, kondisi tersebut
diinterpretasikan sebagai pelanggan yang belum melakukan transaksi
sehingga nilai tersebut akan diganti menjadi 0.
Saya mengganti NA pada variabel transaksi menjadi 0.
data_terintegrasi$jumlah_transaksi[
is.na(
data_terintegrasi$jumlah_transaksi
)
] <- 0
data_terintegrasi$total_purchase[
is.na(
data_terintegrasi$total_purchase
)
] <- 0
Nilai NA pada variabel transaksi telah diganti menjadi 0. Dengan demikian, pelanggan yang tidak memiliki transaksi tetap dipertahankan dalam dataset dan dapat dibedakan dari pelanggan yang memiliki transaksi.
Menyusun Dataset Akhir
Setelah integrasi selesai, saya memilih variabel yang diperlukan untuk membentuk dataset akhir. Variabel hasil transformasi yang tidak digunakan dalam analisis akhir tidak dimasukkan kembali agar struktur data tetap sederhana.
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",
"total_purchase"
)
]
names(data_final)[
names(data_final) == "usia_imputasi"
] <- "usia"
names(data_final)[
names(data_final) == "kota_imputasi"
] <- "kota"
data_final
## customer_id nama usia kota status pendapatan_imputasi
## 1 C001 Ani 21.0 Pekanbaru Aktif 4.5e+06
## 2 C002 Budi 25.0 Pekanbaru Aktif 4.9e+06
## 3 C003 Citra 23.0 Pekanbaru Aktif 5.2e+06
## 4 C004 Dodi 50.0 Dumai Aktif 4.8e+06
## 5 C005 Eka 27.0 Pekanbaru Tidak Aktif 4.9e+06
## 6 C006 Fani 27.5 Dumai Tidak Aktif 5.1e+06
## 7 C007 Gilang 31.0 Pekanbaru Aktif 5.0e+08
## 8 C008 Hana 29.0 Siak Aktif 4.7e+06
## 9 C009 Indra 22.0 Pekanbaru Aktif 4.6e+06
## 10 C010 Joko 35.0 Dumai Tidak Aktif 5.3e+06
## 11 C011 Kiki 28.0 Tidak diketahui Aktif 4.9e+06
## 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 total_purchase
## 1 5 1.5e+06
## 2 3 9.0e+05
## 3 7 2.7e+06
## 4 2 6.0e+05
## 5 6 2.1e+06
## 6 4 1.3e+06
## 7 20 2.5e+07
## 8 5 1.7e+06
## 9 3 8.0e+05
## 10 8 3.2e+06
## 11 0 0.0e+00
Output menunjukkan dataset akhir yang telah melalui seluruh tahapan preprocessing dan integrasi. Dataset ini memuat identitas pelanggan, informasi demografi, pendapatan hasil imputasi, indikator missing value, indikator outlier, hasil normalisasi, serta informasi transaksi.
Validasi Dataset Akhir
Setelah dataset akhir terbentuk, saya melakukan satu tahap validasi untuk memastikan hasil preprocessing sudah sesuai. Pemeriksaan ini menjadi validasi akhir sehingga tidak perlu mengulang audit yang sama pada setiap tahap.
validasi_akhir <- data.frame(
pemeriksaan = c(
"Jumlah baris",
"Jumlah kolom",
"Duplikasi customer_id",
"Total missing value",
"Kategori kota",
"Kategori status"
),
hasil = c(
nrow(data_final),
ncol(data_final),
sum(
duplicated(
data_final$customer_id
)
),
sum(
is.na(data_final)
),
length(
unique(data_final$kota)
),
length(
unique(data_final$status)
)
)
)
validasi_akhir
## pemeriksaan hasil
## 1 Jumlah baris 11
## 2 Jumlah kolom 12
## 3 Duplikasi customer_id 0
## 4 Total missing value 0
## 5 Kategori kota 4
## 6 Kategori status 2
Tabel validasi akhir memberikan gambaran kondisi dataset setelah
seluruh proses selesai. Jumlah duplikasi customer_id
digunakan untuk memastikan identifier tetap unik, sedangkan jumlah
missing value digunakan untuk memastikan tidak terdapat nilai kosong
pada dataset akhir. Jumlah kategori kota dan status digunakan untuk
memastikan standardisasi kategori telah berhasil dilakukan.
Saya memastikan kembali bahwa seluruh customer_id pada
dataset akhir bersifat unik.
all(
!duplicated(
data_final$customer_id
)
)
## [1] TRUE
Output TRUE menunjukkan bahwa seluruh
customer_id pada dataset akhir bersifat unik. Kondisi
tersebut menunjukkan bahwa tidak terdapat duplikasi identifier setelah
proses integrasi.
Saya juga memastikan bahwa dataset akhir tidak memiliki missing value.
all(
!is.na(data_final)
)
## [1] TRUE
Output TRUE menunjukkan bahwa dataset akhir tidak
memiliki nilai NA. Missing value yang sebelumnya terdapat pada data
pelanggan telah ditangani melalui imputasi, sedangkan NA yang muncul
akibat tidak adanya transaksi telah diubah menjadi 0 berdasarkan konteks
data.
Membandingkan Kondisi Data Sebelum dan Sesudah Preprocessing
Untuk melihat perubahan yang terjadi secara keseluruhan, saya membuat tabel perbandingan antara data awal dan data akhir.
perbandingan <- data.frame(
indikator = c(
"Jumlah baris",
"Jumlah kolom",
"Duplikasi customer_id",
"Total missing value",
"Kategori kota unik",
"Kategori status unik"
),
sebelum = c(
nrow(pelanggan_raw),
ncol(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),
ncol(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 Jumlah kolom 6 12
## 3 Duplikasi customer_id 1 0
## 4 Total missing value 4 0
## 5 Kategori kota unik 10 4
## 6 Kategori status unik 8 2
Tabel perbandingan menunjukkan perubahan kondisi dataset dari sebelum hingga sesudah preprocessing. Jumlah baris dapat berubah karena adanya duplikasi yang dihapus, sedangkan jumlah missing value berkurang setelah dilakukan imputasi dan penanganan NA akibat integrasi. Jumlah kategori kota dan status juga menjadi lebih konsisten setelah dilakukan standardisasi.
Menyimpan Hasil Preprocessing
Setelah seluruh proses preprocessing dan validasi selesai, saya menyimpan dataset akhir dan catatan perubahan ke dalam file CSV.
write.csv(
data_final,
"data_pelanggan_setelah_preprocessing.csv",
row.names = FALSE
)
write.csv(
log_perubahan,
"log_perubahan_preprocessing.csv",
row.names = FALSE
)
Dataset akhir disimpan sebagai
data_pelanggan_setelah_preprocessing.csv, sedangkan catatan
perubahan disimpan sebagai log_perubahan_preprocessing.csv.
Kedua file tersebut dapat digunakan kembali untuk analisis berikutnya
dan dokumentasi proses preprocessing.
Saya memeriksa lokasi penyimpanan kedua file tersebut.
file.exists(
"data_pelanggan_setelah_preprocessing.csv"
)
## [1] TRUE
file.exists(
"log_perubahan_preprocessing.csv"
)
## [1] TRUE
Output TRUE menunjukkan bahwa file telah berhasil dibuat
pada direktori kerja RStudio. Dataset akhir dan log perubahan dapat
digunakan sebagai hasil akhir dari proses preprocessing.
Pertanyaan Refleksi
1. Apakah dataset tanpa missing value selalu lebih berkualitas?
Tidak. Dataset tanpa missing value belum tentu lebih berkualitas karena kualitas data tidak hanya ditentukan oleh ada atau tidaknya nilai yang hilang. Data juga perlu diperiksa dari aspek konsistensi, ketepatan, keunikan, validitas, dan kesesuaian dengan konteks analisis. Pengisian missing value dengan metode yang tidak tepat bahkan dapat menghasilkan data yang terlihat lengkap tetapi tidak menggambarkan kondisi sebenarnya.
Pada proses yang saya lakukan, nilai yang hilang tidak langsung dihapus. Saya terlebih dahulu memeriksa jumlah dan posisi missing value, kemudian menentukan metode penanganan yang sesuai. Pendekatan tersebut membantu menjaga informasi yang masih dapat digunakan dari dataset.
2. Mengapa outlier tidak boleh otomatis dihapus?
Outlier tidak boleh langsung dihapus karena nilai tersebut belum tentu merupakan kesalahan pencatatan. Nilai ekstrem dapat menunjukkan kondisi yang memang benar-benar terjadi pada objek pengamatan. Penghapusan tanpa pemeriksaan dapat menyebabkan hilangnya informasi penting dan mengubah karakteristik data.
Pada dataset yang saya gunakan, nilai pendapatan yang sangat besar ditandai sebagai outlier menggunakan metode IQR. Saya tidak langsung menghapus nilai tersebut, tetapi melakukan pemeriksaan dan menggunakan winsorization untuk membatasi pengaruh nilai ekstrem. Tindakan tersebut dilakukan agar informasi pengamatan tetap dipertahankan sekaligus mengurangi pengaruh ekstrem terhadap analisis.
3. Bagaimana preprocessing dapat menimbulkan bias?
Preprocessing dapat menimbulkan bias apabila metode yang digunakan untuk membersihkan atau mengubah data tidak sesuai dengan karakteristik data. Contohnya adalah mengisi seluruh nilai yang hilang dengan rata-rata, menghapus semua outlier, atau menyeragamkan kategori tanpa mempertimbangkan makna sebenarnya dari setiap kategori. Perlakuan tersebut dapat mengubah distribusi data dan menghasilkan kesimpulan yang berbeda dari kondisi sebenarnya.
Bias juga dapat muncul apabila keputusan preprocessing hanya
didasarkan pada kemudahan pengolahan. Oleh karena itu, setiap perubahan
data perlu memiliki alasan yang jelas dan dicatat. Pada proses yang saya
lakukan, perubahan seperti standardisasi kategori, koreksi usia,
imputasi, dan penanganan outlier dicatat dalam
log_perubahan agar prosesnya dapat ditelusuri.
4. Mengapa parameter imputasi dan transformasi seharusnya dihitung dari data pelatihan?
Parameter imputasi dan transformasi seharusnya dihitung dari data pelatihan agar informasi dari data pengujian tidak masuk ke dalam proses pembentukan model. Jika rata-rata, median, nilai minimum, nilai maksimum, atau standar deviasi dihitung menggunakan seluruh data sebelum pemisahan data pelatihan dan pengujian, informasi dari data pengujian dapat memengaruhi proses analisis.
Kondisi tersebut disebut data leakage dan dapat menyebabkan performa model terlihat lebih baik daripada kemampuan sebenarnya. Oleh karena itu, parameter seperti median untuk imputasi, nilai minimum dan maksimum untuk normalisasi, serta rata-rata dan standar deviasi untuk standardisasi sebaiknya dihitung hanya menggunakan data pelatihan. Parameter yang telah diperoleh kemudian digunakan untuk mentransformasi data pelatihan maupun data pengujian dengan aturan yang sama.
5. Apa risiko integrasi data jika identifier tidak unik?
Identifier yang tidak unik dapat menyebabkan kesalahan dalam proses integrasi karena satu objek dapat dicocokkan dengan lebih dari satu baris data. Kondisi tersebut dapat menghasilkan baris yang berlipat ganda, menyebabkan jumlah transaksi atau nilai lainnya terhitung lebih dari sekali, serta menghasilkan informasi yang tidak sesuai.
Pada proses yang saya lakukan, customer_id diperiksa
terlebih dahulu sebelum data pelanggan dan transaksi digabungkan. Saya
memastikan bahwa identifier pelanggan tidak memiliki duplikasi sehingga
proses integrasi dapat dilakukan dengan lebih aman. Pemeriksaan seperti
duplicated() dan setdiff() membantu saya
mengetahui apakah terdapat identifier yang berulang atau tidak memiliki
pasangan pada dataset lainnya.
Kesimpulan
Melalui proses data preprocessing ini, saya memulai pengolahan dengan memeriksa kondisi awal data pelanggan dan data transaksi. Pemeriksaan tersebut membantu saya menemukan beberapa permasalahan, seperti nilai yang hilang, kategori yang belum konsisten, identitas pelanggan yang berulang, nilai usia yang tidak sesuai, dan nilai pendapatan yang ekstrem.
Saya kemudian melakukan pembersihan dan standardisasi data, menangani
missing value, mengidentifikasi outlier, melakukan
transformasi, serta mengintegrasikan data pelanggan dengan data
transaksi. Setiap perubahan dilakukan berdasarkan hasil pemeriksaan
terhadap kondisi data dan dicatat dalam log_perubahan.
Tahap integrasi dilakukan setelah kedua dataset memiliki identifier
yang konsisten dan tidak memiliki duplikasi. Seluruh pelanggan tetap
dipertahankan melalui penggunaan all.x = TRUE, sedangkan
nilai NA pada informasi transaksi diubah menjadi 0 karena menunjukkan
tidak adanya transaksi.
Hasil akhirnya adalah data_final, yaitu dataset yang
telah melalui proses pemeriksaan, pembersihan, imputasi, penanganan
outlier, transformasi, dan integrasi. Dataset tersebut kemudian
divalidasi untuk memastikan identifier unik dan tidak terdapat
missing value sehingga dapat digunakan pada tahap analisis
selanjutnya.
Daftar Pustaka
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.