Preprocessing Data with RStudio
Preprocessing Data
Data yang diperoleh tidak selalu berada dalam kondisi yang langsung dapat digunakan untuk analisis. Pada praktik ini, data pelanggan sengaja dibuat memiliki beberapa permasalahan sehingga setiap tahap preprocessing dapat terlihat secara langsung.
Alurnya dibuat sederhana: lihat kondisi data → temukan masalah → lakukan perbaikan → periksa kembali hasilnya.
Langkah 1 - Membangun Dataset
Kita mulai dengan memasukkan data secara langsung menggunakan
data.frame(). Dengan cara ini, kondisi data awal dapat
dilihat tanpa perlu membaca file eksternal.
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
Ada beberapa kondisi yang sengaja terlihat pada data awal, misalnya
nilai NA, penulisan kota dan status yang belum seragam,
customer_id yang berulang, usia 150 tahun, serta pendapatan
500 juta yang jauh lebih tinggi daripada observasi lainnya.
Data transaksi juga memiliki cust_id yang tidak
seluruhnya memiliki pasangan pada data pelanggan.
## 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
Langkah 2 - Melihat Kondisi Data
Sebelum melakukan perubahan, kita lihat terlebih dahulu ukuran, nama variabel, struktur, dan beberapa observasi awal.
## [1] 12 6
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
## 'data.frame': 12 obs. of 6 variables:
## $ customer_id: chr "C001" "C002" "C003" "C004" ...
## $ nama : chr "Ani" "Budi" "Citra" "Dodi" ...
## $ usia : num 21 25 23 150 27 NA 31 29 22 35 ...
## $ pendapatan : num 4.5e+06 NA 5.2e+06 4.8e+06 4.9e+06 5.1e+06 5.0e+08 4.7e+06 4.6e+06 5.3e+06 ...
## $ kota : chr "Pekanbaru" " PKU" "PEKANBARU" "Dumai" ...
## $ status : chr "Aktif" "aktif" "ACTIVE" "A" ...
## customer_id nama usia pendapatan 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
Ringkasan numerik dan jumlah nilai yang hilang juga perlu diperiksa.
## 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
##
##
##
##
## 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
Dari pemeriksaan awal, terlihat bahwa usia,
pendapatan, dan kota memiliki nilai yang
hilang.
Kita juga dapat memeriksa apakah terdapat baris yang sama atau
customer_id yang berulang.
## [1] 1
## [1] 1
Kategori yang tercatat pada variabel kota dan
status juga diperiksa.
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
Untuk variabel numerik, kita lihat rentangnya.
## [1] 21 150
## [1] 4.5e+06 5.0e+08
Hasil pemeriksaan ini belum menjadi keputusan untuk mengubah data. Pemeriksaan awal hanya membantu menunjukkan bagian mana yang perlu diperhatikan lebih lanjut.
Ringkasan Kondisi Awal
Agar pemeriksaan lebih ringkas, kita dapat membuat fungsi audit sederhana.
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
Hasil audit digunakan sebagai gambaran awal, bukan sebagai keputusan otomatis untuk melakukan cleaning.
Langkah 3 - Membersihkan Data
Agar data mentah tetap dapat dibandingkan dengan hasil preprocessing, kita bekerja pada salinan data.
Membersihkan Spasi dan Kapitalisasi
Kita mulai dari masalah yang paling sederhana: spasi dan perbedaan penggunaan huruf.
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"
## [1] "a" "active" "aktif" "nonaktif" "tidak aktif"
Setelah huruf diseragamkan, kategori yang sebenarnya sama masih dapat memiliki nama berbeda. Karena itu, kita lanjutkan dengan standardisasi berdasarkan aturan domain.
Menyeragamkan Kategori
# 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"
Setelah standardisasi, kategori kota menjadi Dumai,
Pekanbaru, dan Siak, sedangkan status menjadi
Aktif dan Tidak Aktif.
Mendeteksi dan Menghapus Duplikasi
Kita tampilkan terlebih dahulu 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
Terlihat bahwa C010 tercatat dua kali dengan informasi
yang sama. Karena dalam dataset ini satu customer_id
mewakili satu pelanggan, kita mempertahankan kemunculan pertama.
pelanggan <- pelanggan[
!duplicated(pelanggan$customer_id),
]
rownames(pelanggan) <- NULL
dim(pelanggan)## [1] 11 6
Kita pastikan kembali bahwa tidak ada customer_id yang
berulang.
## [1] 0
Penghapusan duplikasi bergantung pada aturan data. Jika satu pelanggan memang boleh memiliki beberapa baris, maka penghapusan berdasarkan
customer_idtidak tepat.
Memeriksa Aturan Domain
Selanjutnya kita memeriksa nilai yang secara logis tidak sesuai.
Untuk usia, kita gunakan rentang 15–100 tahun sebagai aturan pemeriksaan.
## customer_id nama usia pendapatan kota status
## 4 C004 Dodi 150 4800000 Dumai Aktif
## NA <NA> <NA> NA NA <NA> <NA>
Nilai usia 150 pada C004 melanggar aturan tersebut.
Misalkan setelah memeriksa sumber asli, diketahui bahwa nilai yang benar
adalah 50.
Untuk pendapatan, kita dapat memeriksa apakah terdapat nilai negatif.
## customer_id nama usia pendapatan kota status
## NA <NA> <NA> NA NA <NA> <NA>
## NA.1 <NA> <NA> NA NA <NA> <NA>
Tidak ditemukan pendapatan negatif pada data ini.
Mencatat Perubahan
Supaya perubahan yang dilakukan tidak hanya tersimpan di dalam syntax, kita buat catatan singkat.
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
Langkah 4 - Menangani Missing Values
Setelah masalah penulisan, duplikasi, dan nilai domain diperiksa, kita kembali pada nilai yang hilang.
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
Kita lihat baris yang memiliki sedikitnya satu nilai hilang.
## 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
Terdapat beberapa pilihan penanganan. Kita lihat terlebih dahulu apa yang terjadi jika seluruh baris yang tidak lengkap dihapus.
Strategi 1 — Menghapus Baris
## [1] 11
## [1] 8
Persentase observasi yang hilang jika strategi ini digunakan:
## [1] 27.27
Pada data ini, penghapusan seluruh baris tidak lengkap menghilangkan sebagian observasi. Karena itu, kita bandingkan dengan pendekatan imputasi.
Strategi 2 — Imputasi Mean dan Median
Pendapatan memiliki satu nilai yang sangat ekstrem, yaitu 500 juta. Karena itu, mean dan median dibandingkan terlebih dahulu.
mean_pendapatan <- mean(
pelanggan$pendapatan,
na.rm = TRUE
)
median_pendapatan <- median(
pelanggan$pendapatan,
na.rm = TRUE
)
mean_pendapatan## [1] 59900000
## [1] 4900000
Mean jauh lebih besar daripada median karena dipengaruhi oleh nilai 500 juta. Untuk contoh ini, median lebih stabil sehingga digunakan sebagai nilai imputasi.
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
pelanggan$pendapatan_imputasi[
is.na(pelanggan$pendapatan_imputasi)
] <- median_pendapatanUntuk usia, kita juga menggunakan median.
median_usia <- median(
pelanggan$usia,
na.rm = TRUE
)
pelanggan$usia_imputasi <- pelanggan$usia
pelanggan$usia_imputasi[
is.na(pelanggan$usia_imputasi)
] <- median_usiaKita bandingkan nilai sebelum dan sesudah imputasi.
## customer_id usia usia_imputasi pendapatan pendapatan_imputasi
## 1 C001 21 21.0 4.5e+06 4.5e+06
## 2 C002 25 25.0 NA 4.9e+06
## 3 C003 23 23.0 5.2e+06 5.2e+06
## 4 C004 50 50.0 4.8e+06 4.8e+06
## 5 C005 27 27.0 4.9e+06 4.9e+06
## 6 C006 NA 27.5 5.1e+06 5.1e+06
## 7 C007 31 31.0 5.0e+08 5.0e+08
## 8 C008 29 29.0 4.7e+06 4.7e+06
## 9 C009 22 22.0 4.6e+06 4.6e+06
## 10 C010 35 35.0 5.3e+06 5.3e+06
## 11 C011 28 28.0 NA 4.9e+06
Strategi 3 — Imputasi Nilai Kategorik
Untuk kota, kita tidak langsung mengganti nilai yang
hilang dengan modus. Kita gunakan kategori eksplisit
Tidak diketahui agar informasi bahwa nilai awalnya tidak
tersedia 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
Selain mengisi nilai yang hilang, kita dapat menyimpan informasi bahwa suatu nilai awalnya memang missing.
pelanggan$pendapatan_missing <- as.integer(
is.na(pelanggan$pendapatan)
)
table(pelanggan$pendapatan_missing)##
## 0 1
## 9 2
Nilai 1 menunjukkan bahwa pendapatan awalnya hilang,
sedangkan 0 menunjukkan bahwa pendapatan tersedia sejak
awal.
Membandingkan Sebelum dan Sesudah Imputasi
Kita dapat melihat perubahan distribusi pendapatan 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 = "lightgreen",
breaks = 8
)Perbandingan ini membantu melihat apakah bentuk distribusi berubah secara berlebihan setelah imputasi.
Langkah 5 - Menangani Outlier
Setelah missing value ditangani, kita melihat nilai yang sangat jauh dari sebagian besar observasi.
Visualisasi dengan Boxplot
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot Pendapatan Setelah Imputasi",
xlab = "Pendapatan"
)Terlihat adanya satu nilai yang jauh lebih tinggi daripada sebagian besar pendapatan lainnya.
Menghitung Batas IQR
Q1 <- quantile(
pelanggan$pendapatan_imputasi,
0.25,
na.rm = TRUE
)
Q3 <- quantile(
pelanggan$pendapatan_imputasi,
0.75,
na.rm = TRUE
)
IQR_pendapatan <- Q3 - Q1
batas_bawah <- Q1 - 1.5 * IQR_pendapatan
batas_atas <- Q3 + 1.5 * IQR_pendapatan
c(
batas_bawah = batas_bawah,
batas_atas = batas_atas
)## batas_bawah.25% batas_atas.75%
## 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 5e+08
Pada data ini, C007 menjadi kandidat outlier karena
memiliki pendapatan 500 juta.
Mengevaluasi Tindakan
Nilai ekstrem tidak otomatis berarti salah. Ada beberapa kemungkinan: nilai tersebut bisa merupakan kesalahan input, observasi valid tetapi ekstrem, atau berasal dari populasi yang berbeda.
Pada contoh ini, nilai 500 juta dipertahankan. Untuk melihat dampak penanganan outlier, kita buat versi winsorized sebagai pembanding.
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 5e+08 5750000
Nilai ekstrem tidak dihapus dari data asli; versi winsorized hanya digunakan untuk melihat bagaimana perubahan tersebut memengaruhi transformasi berikutnya.
Langkah 6 - Transformasi Data
Setelah data diperiksa, kita dapat mengubah skala variabel numerik jika diperlukan.
Normalisasi Min–Maks
Normalisasi min–maks mengubah nilai ke rentang 0 sampai 1.
\[ 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 4.5e+06 0.0000000000
## 2 C002 25 0.13793103 4.9e+06 0.0008072654
## 3 C003 23 0.06896552 5.2e+06 0.0014127144
## 4 C004 50 1.00000000 4.8e+06 0.0006054490
## 5 C005 27 0.20689655 4.9e+06 0.0008072654
## 6 C006 NA 0.22413793 5.1e+06 0.0012108981
## 7 C007 31 0.34482759 5.0e+08 1.0000000000
## 8 C008 29 0.27586207 4.7e+06 0.0004036327
## 9 C009 22 0.03448276 4.6e+06 0.0002018163
## 10 C010 35 0.48275862 5.3e+06 0.0016145308
## 11 C011 28 0.24137931 4.9e+06 0.0008072654
Terlihat bahwa nilai pendapatan 500 juta menjadi 1, sementara sebagian besar pendapatan lainnya berada sangat dekat dengan 0. Kondisi ini menunjukkan bahwa outlier dapat memengaruhi hasil normalisasi.
Normalisasi Z-Score
Z-score menggunakan rata-rata dan simpangan baku.
\[ z = \frac{x-\bar{x}}{s} \]
pelanggan$usia_z <- as.numeric(
scale(pelanggan$usia_imputasi)
)
pelanggan$pendapatan_z <- as.numeric(
scale(pelanggan$pendapatan_imputasi)
)
round(
pelanggan[
,
c("usia_z", "pendapatan_z")
],
3
)## 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
Agar perbedaannya lebih mudah dilihat, kita letakkan beberapa hasil transformasi dalam satu tabel.
transformasi <- pelanggan[
,
c(
"customer_id",
"pendapatan_imputasi",
"pendapatan_minmax",
"pendapatan_z",
"pendapatan_decimal"
)
]
transformasi## customer_id pendapatan_imputasi pendapatan_minmax pendapatan_z
## 1 C001 4.5e+06 0.0000000000 -0.3041235
## 2 C002 4.9e+06 0.0008072654 -0.3014440
## 3 C003 5.2e+06 0.0014127144 -0.2994343
## 4 C004 4.8e+06 0.0006054490 -0.3021138
## 5 C005 4.9e+06 0.0008072654 -0.3014440
## 6 C006 5.1e+06 0.0012108981 -0.3001042
## 7 C007 5.0e+08 1.0000000000 3.0151095
## 8 C008 4.7e+06 0.0004036327 -0.3027837
## 9 C009 4.6e+06 0.0002018163 -0.3034536
## 10 C010 5.3e+06 0.0016145308 -0.2987645
## 11 C011 4.9e+06 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
Tidak ada satu metode transformasi yang selalu paling baik. Pemilihan metode bergantung pada karakteristik data dan tujuan analisis.
Dampak Outlier terhadap Normalisasi
Untuk melihat dampaknya secara langsung, kita bandingkan hasil min–maks data asli dengan data yang telah di-winsorize.
pelanggan$pendapatan_winsor_minmax <-
minmax(pelanggan$pendapatan_winsor)
plot(
pelanggan$pendapatan_minmax,
pelanggan$pendapatan_winsor_minmax,
pch = 19,
col = "navy",
xlab = "Min–maks Data Asli",
ylab = "Min–maks Data Winsorized",
main = "Dampak Penanganan Outlier terhadap Normalisasi"
)
abline(
0, 1,
col = "red",
lty = 2
)Outlier dapat membuat sebagian besar nilai min–maks berada pada rentang yang sangat sempit. Karena itu, pemeriksaan outlier perlu dilakukan sebelum menentukan transformasi yang akan digunakan.
Langkah 7 - Integrasi Data
Data yang digunakan dalam analisis tidak selalu berasal dari satu tabel. Pada contoh ini, informasi pelanggan dan transaksi berada pada dua sumber yang berbeda.
Memeriksa Kunci pada Kedua Sumber
Pertama, kita pastikan tidak terdapat identifier yang berulang.
## [1] 0
## [1] 0
Selanjutnya, kita lihat identifier yang hanya muncul pada salah satu sumber.
## [1] "C011"
## [1] "C012"
Hasilnya menunjukkan bahwa C011 terdapat pada data
pelanggan tetapi tidak memiliki transaksi, sedangkan C012
terdapat pada data transaksi tetapi tidak memiliki pasangan pada data
pelanggan.
Menyelaraskan Nama Identifier
Nama identifier pada kedua sumber belum sama:
customer_id pada data pelanggan dan cust_id
pada data transaksi.
Kita samakan terlebih dahulu.
Menggabungkan Data
Kita gunakan merge() dengan all.x = TRUE
sehingga seluruh pelanggan tetap dipertahankan.
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 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
C011 tetap muncul meskipun tidak memiliki transaksi.
Nilai transaksi untuk pelanggan tersebut menjadi NA.
Memvalidasi Hasil Integrasi
Kita periksa apakah jumlah baris berubah secara tidak wajar.
## sebelum sesudah
## 11 11
Kemudian kita pastikan customer_id tetap unik.
## [1] 0
Kita juga periksa missing value yang muncul pada atribut transaksi.
## jumlah_transaksi total_purchase
## 1 1
Pelanggan yang tidak memiliki pasangan transaksi dapat ditampilkan dengan:
## customer_id nama
## 11 C011 Kiki
NA pada transaksi tidak otomatis boleh diganti menjadi
0. Nilai tersebut bisa berarti pelanggan memang belum pernah
bertransaksi, tetapi bisa juga berarti data transaksi tidak tersedia.
Penggantian menjadi 0 hanya dilakukan jika definisinya sudah
dipastikan.
Sebagai contoh, jika sudah dikonfirmasi bahwa NA memang
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
data_terintegrasi[
,
c(
"customer_id",
"nama",
"jumlah_transaksi_final",
"total_purchase_final"
)
]## customer_id nama jumlah_transaksi_final total_purchase_final
## 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
Langkah 8 - Pemeriksaan Akhir
Setelah seluruh proses dilakukan, kita kembali memeriksa dataset yang dihasilkan.
## 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 9.09 11
## 4 pendapatan numeric 2 18.18 10
## 5 kota character 1 9.09 4
## 6 status character 0 0.00 2
## 7 pendapatan_imputasi numeric 0 0.00 9
## 8 usia_imputasi numeric 0 0.00 11
## 9 kota_imputasi character 0 0.00 4
## 10 pendapatan_missing integer 0 0.00 2
## 11 outlier_pendapatan logical 0 0.00 2
## 12 pendapatan_winsor numeric 0 0.00 9
## 13 usia_minmax numeric 0 0.00 11
## 14 pendapatan_minmax numeric 0 0.00 9
## 15 usia_z numeric 0 0.00 11
## 16 pendapatan_z numeric 0 0.00 9
## 17 pendapatan_decimal numeric 0 0.00 9
## 18 pendapatan_winsor_minmax numeric 0 0.00 9
## 19 jumlah_transaksi numeric 1 9.09 9
## 20 total_purchase numeric 1 9.09 11
## 21 jumlah_transaksi_final numeric 0 0.00 9
## 22 total_purchase_final numeric 0 0.00 11
Kita juga melihat kondisi duplikasi dan missing value.
## [1] 0
## customer_id nama usia
## 0 0 1
## pendapatan kota status
## 2 1 0
## pendapatan_imputasi usia_imputasi kota_imputasi
## 0 0 0
## pendapatan_missing outlier_pendapatan pendapatan_winsor
## 0 0 0
## usia_minmax pendapatan_minmax usia_z
## 0 0 0
## pendapatan_z pendapatan_decimal pendapatan_winsor_minmax
## 0 0 0
## jumlah_transaksi total_purchase jumlah_transaksi_final
## 1 1 0
## total_purchase_final
## 0
Ringkasan Sebelum dan Sesudah
Agar perubahan lebih mudah dilihat, kita buat ringkasan sederhana.
data.frame(
pemeriksaan = c(
"Jumlah baris pelanggan",
"Duplicate customer_id",
"Missing usia",
"Missing pendapatan",
"Missing kota"
),
sebelum = c(
nrow(pelanggan_raw),
sum(duplicated(pelanggan_raw$customer_id)),
sum(is.na(pelanggan_raw$usia)),
sum(is.na(pelanggan_raw$pendapatan)),
sum(is.na(pelanggan_raw$kota))
),
sesudah = c(
nrow(pelanggan),
sum(duplicated(pelanggan$customer_id)),
sum(is.na(pelanggan$usia_imputasi)),
sum(is.na(pelanggan$pendapatan_imputasi)),
sum(is.na(pelanggan$kota_imputasi))
)
)## pemeriksaan sebelum sesudah
## 1 Jumlah baris pelanggan 12 11
## 2 Duplicate customer_id 1 0
## 3 Missing usia 1 0
## 4 Missing pendapatan 2 0
## 5 Missing kota 1 0
Perbandingan tersebut menunjukkan bahwa kondisi data berubah setelah
melalui proses preprocessing. Perubahan tidak hanya dilihat dari jumlah
NA, tetapi juga dari konsistensi kategori, duplikasi, nilai
domain, dan hasil transformasi.
Data Setelah Preprocessing
Berikut adalah data pelanggan setelah melalui tahapan pemeriksaan dan pembersihan. Beberapa kolom tambahan dipertahankan agar proses yang dilakukan tetap dapat dilacak.
## 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 pendapatan_winsor_minmax
## 1 0.0045 0.00
## 2 0.0049 0.32
## 3 0.0052 0.56
## 4 0.0048 0.24
## 5 0.0049 0.32
## 6 0.0051 0.48
## 7 0.5000 1.00
## 8 0.0047 0.16
## 9 0.0046 0.08
## 10 0.0053 0.64
## 11 0.0049 0.32
Untuk kebutuhan analisis berikutnya, data terintegrasi dapat dilihat sebagai berikut.
## 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 pendapatan_winsor_minmax jumlah_transaksi total_purchase
## 1 0.0045 0.00 5 1.5e+06
## 2 0.0049 0.32 3 9.0e+05
## 3 0.0052 0.56 7 2.7e+06
## 4 0.0048 0.24 2 6.0e+05
## 5 0.0049 0.32 6 2.1e+06
## 6 0.0051 0.48 4 1.3e+06
## 7 0.5000 1.00 20 2.5e+07
## 8 0.0047 0.16 5 1.7e+06
## 9 0.0046 0.08 3 8.0e+05
## 10 0.0053 0.64 8 3.2e+06
## 11 0.0049 0.32 NA NA
## jumlah_transaksi_final total_purchase_final
## 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
Data yang semula memiliki beberapa masalah kini telah melalui proses pemeriksaan, pembersihan, penanganan missing value, pemeriksaan outlier, transformasi, dan integrasi.
Dengan demikian, dataset menjadi lebih siap untuk digunakan pada tahap analisis selanjutnya.
Penutup
Proses preprocessing telah dilakukan secara bertahap mulai dari pemeriksaan kondisi awal data, penanganan duplikasi dan ketidakkonsistenan kategori, pemeriksaan nilai yang tidak wajar, penanganan missing value, hingga deteksi outlier dan transformasi data. Selain itu, data pelanggan dan data transaksi juga diintegrasikan menggunakan customer_id.
Setelah melalui tahapan tersebut, data menjadi lebih terstruktur dan siap digunakan untuk tahap analisis selanjutnya. Proses ini juga menunjukkan bahwa preprocessing bukan sekadar menghapus data yang dianggap bermasalah, tetapi perlu dilakukan melalui pemeriksaan dan pertimbangan terhadap karakteristik masing-masing variabel.