Data pelanggan dan transaksi merupakan jenis data yang dapat digunakan untuk memahami karakteristik pelanggan serta aktivitas pembelian yang terjadi. Sebelum digunakan dalam proses analisis, data perlu diperiksa dan dipersiapkan terlebih dahulu karena data yang diperoleh belum tentu memiliki kualitas yang baik.
Pada pengolahan ini digunakan dua sumber data, yaitu data pelanggan dan data transaksi. Data pelanggan berisi informasi mengenai identitas pelanggan, usia, pendapatan, kota, dan status pelanggan. Sementara itu, data transaksi berisi jumlah transaksi dan total pembelian yang dilakukan oleh pelanggan.
Kedua dataset tersebut memiliki beberapa permasalahan yang perlu ditangani, seperti adanya nilai yang hilang, ketidakkonsistenan dalam penulisan kategori, data pelanggan yang tercatat lebih dari satu kali, nilai numerik yang tidak sesuai dengan aturan data, serta adanya nilai pendapatan yang sangat ekstrem. Selain itu, terdapat perbedaan nama identifier antara data pelanggan dan data transaksi.
Oleh karena itu, dilakukan serangkaian proses preprocessing untuk mempersiapkan data sebelum digunakan dalam analisis. Tahapan tersebut meliputi pemeriksaan kondisi awal data, data cleaning, penanganan missing value, deteksi outlier, transformasi data, integrasi dua sumber data, dan evaluasi dataset akhir.
Pengolahan data dilakukan pada kasus perusahaan e-commerce yang ingin melakukan analisis terhadap pelanggan. Data yang tersedia berasal dari dua sumber yang berbeda, yaitu data pelanggan dan data transaksi.
Data pelanggan digunakan untuk menggambarkan karakteristik pelanggan, sedangkan data transaksi digunakan untuk menggambarkan aktivitas pembelian. Kedua sumber data tersebut kemudian dihubungkan menggunakan identifier pelanggan.
Tujuan utama preprocessing pada kasus ini bukan hanya menghilangkan data yang dianggap bermasalah, tetapi memastikan bahwa setiap perubahan yang dilakukan memiliki alasan yang jelas dan dapat dijelaskan. Dengan demikian, hasil preprocessing dapat digunakan sebagai dataset yang lebih konsisten untuk analisis berikutnya.
Setelah itu di RStudio 1.Buka file praktikum_preprocessing.Rmd. 2.Pastikan semua kode dan penjelasan ada dalam file yang sama. 3.Klik Knit, kemudian Knit to HTML. 4.Kalau HTML berhasil dibuat, klik Publish pada hasil HTML. 5.Pilih RPubs. 6.Login akun RPubs jika diminta. 7.Isi judul, misalnya: 8.Preprocessing Data Pelanggan dan Transaksi 9.Klik Publish.
getwd()
## [1] "C:/Users/LenovoX390/Downloads"
R.version.string
## [1] "R version 4.5.0 (2025-04-11 ucrt)"
Data pelanggan dan transaksi yang digunakan dalam pengolahan ini dibuat dalam bentuk dataset yang memiliki beberapa kondisi yang perlu ditangani melalui preprocessing.
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
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
Data awal menunjukkan bahwa data pelanggan terdiri atas 12 baris dan
6 variabel. Pada data tersebut terdapat nilai kosong pada usia,
pendapatan, dan kota. Selain itu, terdapat customer_id yang
tercatat dua kali, yaitu C010. Data transaksi memiliki 11 baris dan
menggunakan nama identifier cust_id.
Pemeriksaan awal dilakukan untuk mengetahui ukuran dataset, nama variabel, tipe data, beberapa observasi pertama, dan ringkasan statistik. Tahap ini membantu mengetahui kondisi data sebelum dilakukan perubahan.
dim(pelanggan_raw)
## [1] 12 6
names(pelanggan_raw)
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
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" ...
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
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
##
##
##
##
Hasil pemeriksaan menunjukkan bahwa data pelanggan memiliki 12
observasi dan 6 variabel. Variabel customer_id,
nama, kota, dan status merupakan
data karakter, sedangkan usia dan pendapatan
merupakan variabel numerik.
Pemeriksaan kualitas dilakukan untuk mengetahui permasalahan yang dapat ditemukan secara langsung dari dataset. Fokus pemeriksaan meliputi missing value, duplikasi, konsistensi kategori, serta rentang variabel numerik.
colSums(is.na(pelanggan_raw))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
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
sum(duplicated(pelanggan_raw))
## [1] 1
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1
sort(unique(pelanggan_raw$kota))
## [1] " PKU" "Dumai" "DUMAI" "pekanbaru" "Pekanbaru"
## [6] "PEKANBARU" "Pekanbaru " "PKU" "Siak"
sort(unique(pelanggan_raw$status))
## [1] "A" "ACTIVE" "aktif" "Aktif" "AKTIF"
## [6] "nonaktif" "Tidak aktif" "Tidak Aktif"
range(pelanggan_raw$usia, na.rm = TRUE)
## [1] 21 150
range(pelanggan_raw$pendapatan, na.rm = TRUE)
## [1] 4.5e+06 5.0e+08
Berdasarkan pemeriksaan tersebut, terdapat satu missing value pada
usia, dua missing value pada pendapatan, dan satu missing value pada
kota. Selain itu, ditemukan satu customer_id yang berulang.
Kategori kota dan status juga memiliki beberapa bentuk penulisan yang
berbeda, meskipun beberapa di antaranya sebenarnya menunjukkan kategori
yang sama.
Rentang usia menunjukkan adanya nilai 150 tahun, sedangkan pendapatan memiliki nilai maksimum sebesar 500 juta. Kedua kondisi tersebut perlu diperiksa lebih lanjut sebelum data digunakan.
Untuk mempermudah pemeriksaan, dibuat fungsi yang memberikan ringkasan tipe data, jumlah missing value, persentase missing value, dan jumlah kategori unik.
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
Audit awal digunakan sebagai gambaran kondisi data sebelum dilakukan perubahan. Hasil audit selanjutnya menjadi dasar untuk menentukan bagian data yang perlu diperbaiki.
Data mentah tetap dipertahankan agar perubahan yang dilakukan selama preprocessing dapat dilacak dan dibandingkan dengan kondisi awal.
pelanggan <- pelanggan_raw
Pada data pelanggan terdapat spasi tambahan pada beberapa nilai kota serta perbedaan penggunaan huruf besar dan kecil. Kondisi tersebut dapat menyebabkan kategori yang sebenarnya sama terbaca sebagai kategori 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"
sort(unique(pelanggan$status))
## [1] "a" "active" "aktif" "nonaktif" "tidak aktif"
Setelah spasi dan kapitalisasi diperbaiki, kategori menjadi lebih mudah untuk diseragamkan karena bentuk penulisannya sudah berada pada format yang sama.
Kategori kota dan status kemudian disesuaikan berdasarkan aturan yang digunakan pada data.
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"
sort(unique(pelanggan$status))
## [1] "Aktif" "Tidak Aktif"
Standardisasi menghasilkan tiga kategori kota, yaitu Pekanbaru, Dumai, dan Siak. Variabel status juga telah memiliki dua kategori, yaitu Aktif dan Tidak Aktif.
Duplikasi customer_id perlu diperiksa karena dalam data
pelanggan satu identifier seharusnya mewakili satu pelanggan.
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
Pada data ditemukan C010 yang tercatat sebanyak dua kali dengan
informasi yang sama. Karena customer_id digunakan sebagai
identitas unik pelanggan, salah satu kemunculan tersebut dihapus.
pelanggan <- pelanggan[
!duplicated(pelanggan$customer_id),
]
rownames(pelanggan) <- NULL
dim(pelanggan)
## [1] 11 6
Setelah proses deduplikasi, jumlah pelanggan menjadi 11.
Nilai numerik juga diperiksa berdasarkan aturan yang sesuai dengan karakteristik data. Untuk usia digunakan rentang 15 sampai 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>
Pemeriksaan menunjukkan adanya usia 150 tahun pada C004. Nilai tersebut tidak sesuai dengan aturan usia yang digunakan. Berdasarkan informasisumber asli pada skenario data, nilai tersebut dikoreksi menjadi 50.
pelanggan$usia[
pelanggan$customer_id == "C004"
] <- 50
Pendapatan juga diperiksa untuk memastikan tidak terdapat nilai 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>
Tidak ditemukan pendapatan bernilai negatif sehingga tidak diperlukan koreksi pada bagian tersebut.
Setiap perubahan yang dilakukan dicatat agar proses preprocessing dapat ditelusuri kembali.
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
Setelah cleaning, missing value diperiksa kembali untuk mengetahui bagian data yang masih belum lengkap.
colSums(is.na(pelanggan))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
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
Missing value masih terdapat pada usia, pendapatan, dan kota. Data yang memiliki missing value kemudian menjadi bahan pertimbangan dalam pemilihan metode penanganan.
Sebagai perbandingan, dilakukan simulasi apabila seluruh baris yang memiliki missing value dihapus.
pelanggan_complete <- pelanggan[
complete.cases(pelanggan),
]
nrow(pelanggan)
## [1] 11
nrow(pelanggan_complete)
## [1] 8
round(
(1 - nrow(pelanggan_complete) /
nrow(pelanggan)) * 100,
2
)
## [1] 27.27
Penghapusan seluruh baris yang tidak lengkap akan mengurangi jumlah data secara cukup besar. Oleh karena itu, pada kasus ini pendekatan tersebut tidak langsung digunakan sebagai hasil akhir.
Pendapatan memiliki satu nilai yang sangat ekstrem, yaitu 500 juta. Karena nilai ekstrem dapat memengaruhi nilai rata-rata, 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
median_pendapatan
## [1] 4900000
Perbedaan yang besar antara mean dan median menunjukkan bahwa distribusi pendapatan sangat dipengaruhi oleh nilai ekstrem. Oleh karena itu, median digunakan untuk mengisi missing value pendapatan.
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"
)
]
## 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
Dengan cara tersebut, data asli tetap disimpan dan nilai hasil imputasi tersedia pada variabel baru.
Missing value pada kota tidak langsung diganti menggunakan modus
karena hal tersebut dapat membuat ketidakpastian data menjadi tidak
terlihat. Sebagai gantinya digunakan kategori eksplisit
Tidak diketahui.
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
Untuk mempertahankan informasi bahwa suatu nilai pendapatan awalnya merupakan missing value, dibuat variabel indikator.
pelanggan$pendapatan_missing <-
as.integer(
is.na(pelanggan$pendapatan)
)
table(
pelanggan$pendapatan_missing
)
##
## 0 1
## 9 2
Perbandingan visual dilakukan untuk melihat perubahan distribusi pendapatan akibat imputasi.
par(mfrow = c(1, 2))
hist(
pelanggan$pendapatan,
main = "Sebelum Imputasi",
xlab = "Pendapatan",
col = "skyblue",
breaks = 8
)
hist(
pelanggan$pendapatan_imputasi,
main = "Sesudah Imputasi Median",
xlab = "Pendapatan",
col = "darkred",
breaks = 8
)
par(mfrow = c(1, 1))
Imputasi membuat data menjadi lengkap, tetapi proses tersebut juga dapat mengubah distribusi dan mengurangi variasi data. Oleh sebab itu, keputusan imputasi perlu tetap dicatat dan dipertimbangkan ketika melakukan analisis berikutnya.
Outlier pada pendapatan diperiksa secara visual menggunakan boxplot.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Boxplot menunjukkan adanya satu nilai pendapatan yang jauh lebih tinggi dibandingkan observasi lainnya.
Deteksi outlier dilakukan menggunakan metode Interquartile Range (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
Berdasarkan perhitungan IQR, batas bawah adalah 4.150.000 dan batas atas adalah 5.750.000. Nilai pendapatan yang berada di luar batas tersebut dianggap sebagai 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
Hasil pemeriksaan menunjukkan bahwa pendapatan C007 sebesar 500.000.000 merupakan kandidat outlier.
Nilai ekstrem tidak langsung dihapus karena nilai tersebut belum tentu merupakan kesalahan. Nilai 500 juta dapat merupakan data yang benar, misalnya pelanggan dengan karakteristik pendapatan yang berbeda. Karena tidak terdapat bukti bahwa nilai tersebut merupakan kesalahan input, nilai asli dipertahankan.
Sebagai pembanding, dibuat versi winsorized dengan membatasi nilai tersebut pada batas atas.
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
Winsorizing pada tahap ini digunakan sebagai alternatif untuk melihat dampak penanganan outlier dan bukan sebagai pengganti otomatis terhadap nilai asli.
Transformasi digunakan untuk mengubah skala data numerik agar lebih sesuai dengan kebutuhan analisis.
Normalisasi min–maks menggunakan rumus:
\[ 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
Hasil normalisasi berada pada rentang 0 sampai 1. Pada pendapatan, keberadaan nilai ekstrem menyebabkan sebagian besar nilai lainnya berada sangat dekat dengan nol.
Z-score digunakan untuk mengubah data berdasarkan 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 dilakukan dengan membagi nilai menggunakan pangkat sepuluh tertentu.
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
Ketiga hasil transformasi pendapatan dibandingkan untuk melihat perbedaan skala yang dihasilkan.
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
Perbedaan hasil transformasi menunjukkan bahwa setiap metode memiliki cara berbeda dalam mengubah skala data. Pemilihan metode transformasi selanjutnya perlu disesuaikan dengan tujuan analisis.
Untuk melihat pengaruh outlier, dilakukan normalisasi terhadap data pendapatan yang telah di-winsorize.
pelanggan$pendapatan_winsor_minmax <-
minmax(
pelanggan$pendapatan_winsor
)
plot(
pelanggan$pendapatan_minmax,
pelanggan$pendapatan_winsor_minmax,
pch = 19,
col = "black",
xlab = "Min–maks Data Asli",
ylab = "Min–maks Data Winsorized",
main = "Dampak Penanganan Outlier terhadap Normalisasi"
)
abline(
0,
1,
col = "pink",
lty = 2
)
Hasil perbandingan menunjukkan bahwa outlier dapat membuat sebagian besar observasi berada pada rentang min–maks yang sangat sempit. Kondisi tersebut menjadi salah satu alasan mengapa outlier perlu diperiksa sebelum memilih metode transformasi.
Sebelum kedua dataset digabungkan, identifier diperiksa untuk memastikan tidak terdapat duplikasi dan untuk mengetahui perbedaan pelanggan antara kedua sumber data.
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 pemeriksaan menunjukkan bahwa C011 terdapat pada data pelanggan tetapi tidak memiliki pasangan pada data transaksi. Sebaliknya, C012 terdapat pada data transaksi tetapi tidak ditemukan pada data pelanggan.
Data transaksi menggunakan nama cust_id, sedangkan data
pelanggan menggunakan customer_id. Nama identifier
disesuaikan agar kedua data dapat digabungkan.
transaksi <- transaksi_raw
names(transaksi)[
names(transaksi) == "cust_id"
] <- "customer_id"
Penggabungan dilakukan dengan mempertahankan seluruh data pelanggan. Dengan demikian, pelanggan yang tidak memiliki transaksi tetap terdapat dalam dataset hasil integrasi.
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
Setelah penggabungan dilakukan, hasil integrasi diperiksa kembali.
c(
sebelum = nrow(pelanggan),
sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah
## 11 11
sum(
duplicated(
data_terintegrasi$customer_id
)
)
## [1] 0
colSums(
is.na(
data_terintegrasi[
,
c(
"jumlah_transaksi",
"total_purchase"
)
]
)
)
## jumlah_transaksi total_purchase
## 1 1
data_terintegrasi[
is.na(
data_terintegrasi$jumlah_transaksi
),
c(
"customer_id",
"nama"
)
]
## customer_id nama
## 11 C011 Kiki
Hasil integrasi mempertahankan 11 pelanggan. C011 memiliki nilai missing pada informasi transaksi karena tidak ditemukan pasangan transaksi.
Nilai NA pada transaksi perlu dipahami terlebih dahulu.
Nilai tersebut dapat berarti pelanggan memang belum pernah bertransaksi
atau data transaksi pelanggan tidak tersedia. Oleh karena itu, perubahan
NA menjadi nol hanya dilakukan apabila telah diketahui
bahwa NA memang berarti tidak ada transaksi.
Pada pengolahan ini digunakan asumsi bahwa C011 belum pernah melakukan transaksi, sehingga nilai transaksi dapat direpresentasikan sebagai nol.
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
Setelah seluruh proses preprocessing selesai, dipilih atribut yang dibutuhkan untuk membentuk dataset akhir.
data_final <- data_terintegrasi[
,
c(
"customer_id",
"nama",
"usia_imputasi",
"kota_imputasi",
"status",
"pendapatan_imputasi",
"pendapatan_missing",
"outlier_pendapatan",
"usia_minmax",
"pendapatan_minmax",
"jumlah_transaksi_final",
"total_purchase_final"
)
]
names(data_final)[
names(data_final) == "kota_imputasi"
] <- "kota"
names(data_final)[
names(data_final) == "usia_imputasi"
] <- "usia"
data_final
## customer_id nama usia kota status pendapatan_imputasi
## 1 C001 Ani 21.0 Pekanbaru Aktif 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_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
Dataset akhir berisi informasi pelanggan yang telah dibersihkan dan ditambah dengan informasi transaksi. Variabel hasil imputasi dan transformasi juga dipertahankan untuk kebutuhan analisis selanjutnya.
Audit akhir dilakukan untuk memastikan tidak terdapat missing value yang tidak ditangani dan tidak terdapat duplikasi identifier.
audit_akhir <- audit_data(
data_final
)
audit_akhir
## atribut tipe jumlah_missing persen_missing jumlah_unik
## 1 customer_id character 0 0 11
## 2 nama character 0 0 11
## 3 usia numeric 0 0 11
## 4 kota character 0 0 4
## 5 status character 0 0 2
## 6 pendapatan_imputasi numeric 0 0 9
## 7 pendapatan_missing integer 0 0 2
## 8 outlier_pendapatan logical 0 0 2
## 9 usia_minmax numeric 0 0 11
## 10 pendapatan_minmax numeric 0 0 9
## 11 jumlah_transaksi_final numeric 0 0 9
## 12 total_purchase_final numeric 0 0 11
sum(
duplicated(
data_final$customer_id
)
)
## [1] 0
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
Hasil audit menunjukkan bahwa dataset akhir tidak memiliki missing
value dan tidak terdapat duplikasi customer_id. Dengan
demikian, dataset telah berada pada kondisi yang lebih konsisten
dibandingkan dataset awal.
Perbandingan dilakukan untuk melihat perubahan kualitas data setelah preprocessing.
perbandingan <- data.frame(
indikator = c(
"Jumlah baris",
"Duplikasi customer_id",
"Total missing value",
"Kategori kota unik",
"Kategori status unik"
),
sebelum = c(
nrow(pelanggan_raw),
sum(
duplicated(
pelanggan_raw$customer_id
)
),
sum(
is.na(pelanggan_raw)
),
length(
unique(
pelanggan_raw$kota
)
),
length(
unique(
pelanggan_raw$status
)
)
),
sesudah = c(
nrow(data_final),
sum(
duplicated(
data_final$customer_id
)
),
sum(
is.na(data_final)
),
length(
unique(
data_final$kota
)
),
length(
unique(
data_final$status
)
)
)
)
perbandingan
## indikator sebelum sesudah
## 1 Jumlah baris 12 11
## 2 Duplikasi customer_id 1 0
## 3 Total missing value 4 0
## 4 Kategori kota unik 10 4
## 5 Kategori status unik 8 2
Perbandingan menunjukkan bahwa jumlah baris berkurang dari 12 menjadi
11 karena satu data duplikat dihapus. Duplikasi customer_id
berkurang dari satu menjadi nol. Total missing value juga berhasil
ditangani, sedangkan kategori kota dan status menjadi lebih sederhana
dan konsisten.
Dataset akhir dan catatan perubahan disimpan agar dapat digunakan kembali pada tahap analisis selanjutnya.
write.csv(
data_final,
"data_pelanggan_setelah_preprocessing.csv",
row.names = FALSE
)
write.csv(
log_perubahan,
"log_perubahan_preprocessing.csv",
row.names = FALSE
)
Berdasarkan seluruh tahapan preprocessing, kondisi awal data
pelanggan dan transaksi menunjukkan beberapa permasalahan kualitas data.
Pada data pelanggan terdapat missing value pada usia, pendapatan, dan
kota. Selain itu, ditemukan satu customer_id yang tercatat
dua kali serta beberapa variasi penulisan pada kategori kota dan
status.
Tahap cleaning dilakukan dengan membersihkan spasi dan menyeragamkan
kapitalisasi sebelum kategori distandardisasi. Variasi seperti
PKU, PEKANBARU, dan pekanbaru
disatukan menjadi kategori Pekanbaru. Begitu pula dengan
variasi status seperti ACTIVE, A, dan
aktif yang diseragamkan menjadi Aktif. Data
C010 yang tercatat dua kali kemudian diperiksa dan salah satu
kemunculannya dihapus.
Pemeriksaan aturan domain menemukan usia 150 tahun pada C004. Nilai tersebut tidak sesuai dengan rentang usia yang digunakan sehingga dikoreksi menjadi 50 berdasarkan informasi sumber asli. Pemeriksaan pendapatan tidak menemukan nilai negatif, tetapi ditemukan nilai pendapatan sebesar 500 juta yang sangat berbeda dari observasi lainnya.
Missing value pada pendapatan dan usia ditangani menggunakan median.
Pemilihan median dilakukan karena nilai pendapatan ekstrem menyebabkan
perbedaan yang cukup besar antara mean dan median. Untuk kota yang tidak
diketahui digunakan kategori Tidak diketahui agar
ketidakpastian data tidak disembunyikan.
Pendapatan kemudian diperiksa menggunakan metode IQR. Hasilnya menunjukkan bahwa pendapatan C007 sebesar 500 juta merupakan kandidat outlier. Nilai tersebut tidak langsung dihapus karena belum terdapat bukti bahwa nilai tersebut merupakan kesalahan. Sebagai alternatif, dilakukan winsorizing untuk melihat bagaimana pembatasan nilai ekstrem memengaruhi hasil transformasi.
Tahap transformasi dilakukan menggunakan Min-Max, Z-Score, dan Decimal Scaling. Hasil transformasi menunjukkan bahwa keberadaan outlier dapat mempengaruhi skala data, terutama pada normalisasi Min-Max. Oleh karena itu, pemeriksaan outlier menjadi bagian penting sebelum menentukan transformasi yang digunakan.
Pada tahap integrasi, data pelanggan dan transaksi digabungkan
menggunakan customer_id. Hasil pemeriksaan menunjukkan
bahwa C011 tidak memiliki pasangan transaksi, sedangkan C012 terdapat
pada data transaksi tetapi tidak terdapat pada data pelanggan. Karena
tujuan penggabungan adalah mempertahankan seluruh pelanggan, digunakan
merge() dengan all.x = TRUE.
Setelah seluruh proses dilakukan, dataset akhir tidak lagi memiliki
missing value dan duplikasi customer_id. Kategori kota dan
status juga menjadi lebih konsisten. Hasil tersebut menunjukkan bahwa
proses preprocessing berhasil menghasilkan dataset yang lebih
terstruktur dan siap digunakan untuk tahap analisis berikutnya.
Data pelanggan dan transaksi yang digunakan dalam pengolahan ini memiliki beberapa permasalahan yang perlu diperbaiki sebelum dilakukan analisis. Permasalahan tersebut meliputi missing value, ketidakkonsistenan kategori, duplikasi identifier, nilai usia yang tidak sesuai, dan nilai pendapatan yang ekstrem.
Proses preprocessing dilakukan melalui beberapa tahap, yaitu
pemeriksaan data awal, cleaning, penanganan missing value, deteksi
outlier, transformasi, dan integrasi data. Missing value numerik
ditangani menggunakan median, sedangkan missing value kategorik pada
kota diberi kategori Tidak diketahui. Duplikasi C010
dihapus dan nilai usia C004 dikoreksi berdasarkan aturan domain.
Deteksi menggunakan metode IQR menunjukkan bahwa pendapatan C007 merupakan kandidat outlier. Nilai tersebut dipertahankan karena belum dapat dibuktikan sebagai kesalahan, sementara versi winsorized dibuat sebagai pembanding. Data numerik kemudian ditransformasikan menggunakan Min-Max, Z-Score, dan Decimal Scaling.
Pada tahap akhir, data pelanggan dan transaksi berhasil
diintegrasikan menggunakan customer_id. Dataset hasil
preprocessing memiliki struktur yang lebih konsisten, tidak memiliki
missing value, dan tidak memiliki duplikasi customer_id.
Dengan demikian, dataset tersebut dapat digunakan sebagai dasar untuk
analisis pelanggan dan transaksi pada tahap selanjutnya.
Alur preprocessing yang dilakukan dalam pengolahan data ini terdiri atas pemeriksaan kondisi awal, pembersihan data, penanganan missing value, pemeriksaan outlier, transformasi variabel numerik, integrasi data pelanggan dan transaksi, serta evaluasi dataset akhir.
Preprocessing merupakan proses pengambilan keputusan. Kode R hanya menjalankan keputusan tersebut; kualitas hasil tetap bergantung pada pemahaman data, tujuan analisis, dan dokumentasi perubahan.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Bab 3: Data Preprocessing.