R version yang digunakan: R version 4.5.1 (2025-06-13 ucrt)
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
)
Sintaks data.frame() digunakan untuk membuat dataset
pelanggan secara langsung di R. Dataset terdiri atas
customer_id, nama, usia,
pendapatan, kota, dan status
pelanggan.
Beberapa nilai sengaja dibuat bermasalah untuk keperluan
preprocessing, misalnya NA, usia 150, penulisan kota yang
berbeda-beda, status dengan beberapa bentuk penulisan, dan
customer_id yang muncul dua kali.
transaksi_raw <- data.frame(
cust_id = c("C001", "C002", "C003", "C004", "C005", "C006",
"C007", "C008", "C009", "C010", "C012"),
jumlah_transaksi = c(5, 3, 7, 2, 6, 4, 20, 5, 3, 8, 1),
total_purchase = c(1500000, 900000, 2700000, 600000, 2100000, 1300000,
25000000, 1700000, 800000, 3200000, 250000),
stringsAsFactors = FALSE
)
transaksi_raw
## 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
Dataset kedua berisi informasi transaksi pelanggan. Identifier pada
data ini bernama cust_id, bukan customer_id.
Perbedaan nama identifier tersebut digunakan untuk menunjukkan bahwa
sebelum dua dataset digabungkan, nama kunci perlu diselaraskan terlebih
dahulu.
dim(pelanggan_raw)
## [1] 12 6
Fungsi dim() digunakan untuk mengetahui jumlah baris dan
kolom dataset. Pada data awal terdapat 12 baris dan 6 kolom.
names(pelanggan_raw)
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
Fungsi names() digunakan untuk melihat nama setiap
atribut dalam dataset.
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" ...
Fungsi str() digunakan untuk melihat struktur dataset,
termasuk nama variabel, tipe data, dan contoh nilai.
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
Fungsi head() menampilkan beberapa baris pertama dataset
agar bentuk data dapat dilihat secara cepat.
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
##
##
##
##
Fungsi summary() memberikan ringkasan setiap variabel.
Untuk variabel numerik, R menampilkan statistik seperti minimum, median,
mean, dan maksimum.
colSums(is.na(pelanggan_raw))
## customer_id nama usia pendapatan kota status
## 0 0 1 2 1 0
is.na() digunakan untuk mendeteksi nilai kosong atau
NA, sedangkan colSums() menghitung jumlah
NA pada setiap kolom.
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
Sintaks ini menghitung persentase data yang hilang pada setiap kolom.
round() digunakan agar hasil ditampilkan sampai dua angka
desimal.
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1
duplicated() digunakan untuk mencari identifier yang
muncul lebih dari satu kali. sum() kemudian menghitung
jumlah duplikasi tersebut.
pelanggan_raw[
duplicated(pelanggan_raw$customer_id) |
duplicated(pelanggan_raw$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
Sintaks ini digunakan untuk menampilkan seluruh baris yang memiliki
customer_id duplikat. Pada dataset ini terdapat
C010 yang muncul dua kali.
pelanggan <- pelanggan_raw
Dataset asli tidak langsung diubah. Dataset
pelanggan_raw disalin menjadi pelanggan
sebagai data kerja agar data awal tetap tersedia sebagai pembanding.
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)
Fungsi trimws() digunakan untuk menghapus spasi yang
tidak diperlukan di awal atau akhir teks.
pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)
unique(pelanggan$kota)
## [1] "pekanbaru" "pku" "dumai" "siak" NA
unique(pelanggan$status)
## [1] "aktif" "active" "a" "tidak aktif" "nonaktif"
Fungsi tolower() mengubah seluruh huruf menjadi huruf
kecil sehingga perbedaan kapitalisasi dapat diseragamkan.
unique() digunakan untuk melihat kategori yang
tersedia.
pelanggan$kota[pelanggan$kota %in% c("pku", "pekanbaru")] <- "Pekanbaru"
pelanggan$kota[pelanggan$kota == "dumai"] <- "Dumai"
pelanggan$kota[pelanggan$kota == "siak"] <- "Siak"
unique(pelanggan$kota)
## [1] "Pekanbaru" "Dumai" "Siak" NA
Variasi penulisan kota yang memiliki makna sama diseragamkan menjadi satu kategori agar tidak dianggap sebagai kategori yang berbeda.
pelanggan$status[
pelanggan$status %in% c("aktif", "active", "a")
] <- "Aktif"
pelanggan$status[
pelanggan$status %in% c("tidak aktif", "nonaktif")
] <- "Tidak Aktif"
unique(pelanggan$status)
## [1] "Aktif" "Tidak Aktif"
Variasi penulisan status diseragamkan. Misalnya aktif,
active, dan a menjadi Aktif,
sedangkan tidak aktif dan nonaktif menjadi
Tidak Aktif.
pelanggan <- pelanggan[
!duplicated(pelanggan$customer_id),
]
rownames(pelanggan) <- NULL
pelanggan
## customer_id nama usia pendapatan kota status
## 1 C001 Ani 21 4.5e+06 Pekanbaru Aktif
## 2 C002 Budi 25 NA Pekanbaru Aktif
## 3 C003 Citra 23 5.2e+06 Pekanbaru Aktif
## 4 C004 Dodi 150 4.8e+06 Dumai Aktif
## 5 C005 Eka 27 4.9e+06 Pekanbaru Tidak Aktif
## 6 C006 Fani NA 5.1e+06 Dumai Tidak Aktif
## 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 Pekanbaru Aktif
## 10 C010 Joko 35 5.3e+06 Dumai Tidak Aktif
## 11 C011 Kiki 28 NA <NA> Aktif
Baris duplikat dihapus berdasarkan customer_id.
Kemunculan kedua C010 dihapus. rownames()
digunakan untuk mengatur kembali nomor baris.
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>
Sintaks ini mencari nilai usia yang berada di luar rentang yang
dianggap wajar. Pada data terdapat usia 150 untuk C004.
pelanggan$usia[
pelanggan$customer_id == "C004"
] <- 50
Nilai usia 150 pada C004 dikoreksi menjadi 50 sesuai
skenario data yang diberikan.
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
complete.cases() digunakan untuk menemukan baris yang
lengkap. Tanda ! membalik hasilnya sehingga baris yang
memiliki minimal satu NA dapat ditampilkan.
pelanggan_complete <- pelanggan[
complete.cases(pelanggan),
]
nrow(pelanggan)
## [1] 11
nrow(pelanggan_complete)
## [1] 8
Sintaks ini membuat dataset pembanding dengan menghapus baris yang memiliki missing value. Jumlah baris sebelum dan sesudah penghapusan dibandingkan untuk melihat banyaknya data yang hilang.
persen_hilang <- (
1 - nrow(pelanggan_complete) /
nrow(pelanggan)
) * 100
persen_hilang
## [1] 27.27273
Sintaks ini menghitung persentase baris yang hilang jika strategi penghapusan baris digunakan.
median_usia <- median(
pelanggan$usia,
na.rm = TRUE
)
median_usia
## [1] 27.5
Median digunakan sebagai nilai pengganti untuk usia yang kosong.
na.rm = TRUE digunakan agar NA tidak ikut
dalam perhitungan median.
pelanggan$usia_imputasi <- pelanggan$usia
pelanggan$usia_imputasi[
is.na(pelanggan$usia_imputasi)
] <- median_usia
pelanggan[
, c("customer_id", "usia", "usia_imputasi")
]
## customer_id usia usia_imputasi
## 1 C001 21 21.0
## 2 C002 25 25.0
## 3 C003 23 23.0
## 4 C004 50 50.0
## 5 C005 27 27.0
## 6 C006 NA 27.5
## 7 C007 31 31.0
## 8 C008 29 29.0
## 9 C009 22 22.0
## 10 C010 35 35.0
## 11 C011 28 28.0
Kolom usia disalin menjadi usia_imputasi,
kemudian nilai NA diganti menggunakan median usia.
mean_pendapatan <- mean(
pelanggan$pendapatan,
na.rm = TRUE
)
median_pendapatan <- median(
pelanggan$pendapatan,
na.rm = TRUE
)
mean_pendapatan
## [1] 59900000
median_pendapatan
## [1] 4900000
Perbandingan mean dan median dilakukan karena terdapat nilai ekstrem
500000000. Median lebih tahan terhadap pengaruh nilai
ekstrem sehingga digunakan untuk imputasi pendapatan.
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
pelanggan$pendapatan_imputasi[
is.na(pelanggan$pendapatan_imputasi)
] <- median_pendapatan
pelanggan[
, c(
"customer_id",
"pendapatan",
"pendapatan_imputasi"
)
]
## customer_id pendapatan pendapatan_imputasi
## 1 C001 4.5e+06 4.5e+06
## 2 C002 NA 4.9e+06
## 3 C003 5.2e+06 5.2e+06
## 4 C004 4.8e+06 4.8e+06
## 5 C005 4.9e+06 4.9e+06
## 6 C006 5.1e+06 5.1e+06
## 7 C007 5.0e+08 5.0e+08
## 8 C008 4.7e+06 4.7e+06
## 9 C009 4.6e+06 4.6e+06
## 10 C010 5.3e+06 5.3e+06
## 11 C011 NA 4.9e+06
Nilai pendapatan yang NA diganti menggunakan median
pendapatan.
pelanggan$kota_imputasi <- pelanggan$kota
pelanggan$kota_imputasi[
is.na(pelanggan$kota_imputasi)
] <- "Tidak diketahui"
pelanggan[
, c("customer_id", "kota", "kota_imputasi")
]
## customer_id kota kota_imputasi
## 1 C001 Pekanbaru Pekanbaru
## 2 C002 Pekanbaru Pekanbaru
## 3 C003 Pekanbaru Pekanbaru
## 4 C004 Dumai Dumai
## 5 C005 Pekanbaru Pekanbaru
## 6 C006 Dumai Dumai
## 7 C007 Pekanbaru Pekanbaru
## 8 C008 Siak Siak
## 9 C009 Pekanbaru Pekanbaru
## 10 C010 Dumai Dumai
## 11 C011 <NA> Tidak diketahui
Nilai kota yang kosong diberi kategori Tidak diketahui
agar informasi bahwa data tidak tersedia tetap dipertahankan.
pelanggan$pendapatan_missing <- as.integer(
is.na(pelanggan$pendapatan)
)
table(pelanggan$pendapatan_missing)
##
## 0 1
## 9 2
Variabel pendapatan_missing menjadi penanda keberadaan
missing value. Nilai 1 berarti pendapatan awalnya kosong dan 0 berarti
tersedia.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Boxplot digunakan untuk melihat penyebaran data dan menemukan kandidat outlier secara visual.
Q1 <- quantile(
pelanggan$pendapatan_imputasi,
0.25
)
Q3 <- quantile(
pelanggan$pendapatan_imputasi,
0.75
)
IQR_pendapatan <- IQR(
pelanggan$pendapatan_imputasi
)
batas_bawah <- Q1 - 1.5 * IQR_pendapatan
batas_atas <- Q3 + 1.5 * IQR_pendapatan
Q1
## 25%
## 4750000
Q3
## 75%
## 5150000
IQR_pendapatan
## [1] 4e+05
batas_bawah
## 25%
## 4150000
batas_atas
## 75%
## 5750000
IQR dihitung dengan rumus IQR = Q3 - Q1. Kandidat
outlier ditentukan menggunakan batas bawah Q1 - 1,5 × IQR
dan batas atas Q3 + 1,5 × IQR.
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
Kolom outlier_pendapatan memberikan tanda
TRUE untuk nilai yang berada di luar batas IQR dan
FALSE untuk nilai lainnya.
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 menangani nilai ekstrem dengan membatasi nilai pada batas bawah dan batas atas tanpa menghapus baris data.
minmax <- function(x) {
(x - min(x, na.rm = TRUE)) /
(max(x, na.rm = TRUE) -
min(x, na.rm = TRUE))
}
Fungsi ini digunakan untuk melakukan normalisasi Min-Max. Hasil transformasi berada pada rentang 0 sampai 1.
pelanggan$usia_minmax <- minmax(
pelanggan$usia_imputasi
)
pelanggan$pendapatan_minmax <- minmax(
pelanggan$pendapatan_imputasi
)
pelanggan[
, c(
"customer_id",
"usia_minmax",
"pendapatan_minmax"
)
]
## customer_id usia_minmax pendapatan_minmax
## 1 C001 0.00000000 0.0000000000
## 2 C002 0.13793103 0.0008072654
## 3 C003 0.06896552 0.0014127144
## 4 C004 1.00000000 0.0006054490
## 5 C005 0.20689655 0.0008072654
## 6 C006 0.22413793 0.0012108981
## 7 C007 0.34482759 1.0000000000
## 8 C008 0.27586207 0.0004036327
## 9 C009 0.03448276 0.0002018163
## 10 C010 0.48275862 0.0016145308
## 11 C011 0.24137931 0.0008072654
Variabel usia dan pendapatan ditransformasikan menggunakan Min-Max agar berada pada skala yang sama.
pelanggan$usia_z <- as.numeric(
scale(pelanggan$usia_imputasi)
)
pelanggan$pendapatan_z <- as.numeric(
scale(pelanggan$pendapatan_imputasi)
)
pelanggan[
, c(
"customer_id",
"usia_z",
"pendapatan_z"
)
]
## customer_id usia_z pendapatan_z
## 1 C001 -0.984199667 -0.3041235
## 2 C002 -0.489287834 -0.3014440
## 3 C003 -0.736743750 -0.2994343
## 4 C004 2.603911118 -0.3021138
## 5 C005 -0.241831918 -0.3014440
## 6 C006 -0.179967939 -0.3001042
## 7 C007 0.253079914 3.0151095
## 8 C008 0.005623998 -0.3027837
## 9 C009 -0.860471709 -0.3034536
## 10 C010 0.747991747 -0.2987645
## 11 C011 -0.118103960 -0.3014440
Fungsi scale() digunakan untuk standardisasi z-score.
Hasilnya menunjukkan posisi nilai terhadap rata-rata dalam satuan
simpangan baku.
decimal_scale <- function(x) {
nilai_maks <- max(
abs(x),
na.rm = TRUE
)
j <- ceiling(
log10(nilai_maks + 1)
)
x / (10^j)
}
Fungsi ini melakukan decimal scaling, yaitu mengecilkan skala data dengan membagi nilai menggunakan pangkat sepuluh.
pelanggan$pendapatan_decimal <-
decimal_scale(
pelanggan$pendapatan_imputasi
)
pelanggan[
, c(
"customer_id",
"pendapatan_imputasi",
"pendapatan_decimal"
)
]
## customer_id pendapatan_imputasi pendapatan_decimal
## 1 C001 4.5e+06 0.0045
## 2 C002 4.9e+06 0.0049
## 3 C003 5.2e+06 0.0052
## 4 C004 4.8e+06 0.0048
## 5 C005 4.9e+06 0.0049
## 6 C006 5.1e+06 0.0051
## 7 C007 5.0e+08 0.5000
## 8 C008 4.7e+06 0.0047
## 9 C009 4.6e+06 0.0046
## 10 C010 5.3e+06 0.0053
## 11 C011 4.9e+06 0.0049
Pendapatan yang telah melalui preprocessing ditransformasikan menggunakan decimal scaling dan disimpan pada kolom baru.
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
Tabel ini digunakan untuk membandingkan nilai pendapatan sebelum transformasi dengan hasil Min-Max, z-score, dan decimal scaling.
pelanggan$pendapatan_winsor_minmax <-
minmax(
pelanggan$pendapatan_winsor
)
plot(
pelanggan$pendapatan_minmax,
pelanggan$pendapatan_winsor_minmax,
pch = 19,
xlab = "Min-Max Data Asli",
ylab = "Min-Max Data Winsorized",
main = "Dampak Outlier terhadap Normalisasi"
)
abline(
0,
1,
lty = 2
)
Grafik digunakan untuk membandingkan hasil Min-Max sebelum dan sesudah winsorizing sehingga dampak nilai ekstrem terhadap transformasi dapat dilihat.
setdiff(
pelanggan$customer_id,
transaksi_raw$cust_id
)
## [1] "C011"
setdiff(
transaksi_raw$cust_id,
pelanggan$customer_id
)
## [1] "C012"
setdiff() digunakan untuk mencari identifier yang hanya
terdapat pada salah satu dataset. Dengan demikian dapat diketahui
pelanggan yang tidak mempunyai pasangan transaksi dan transaksi yang
tidak mempunyai pasangan data pelanggan.
transaksi <- transaksi_raw
names(transaksi)[
names(transaksi) == "cust_id"
] <- "customer_id"
names(transaksi)
## [1] "customer_id" "jumlah_transaksi" "total_purchase"
cust_id pada data transaksi diubah menjadi
customer_id agar kedua dataset memiliki nama kunci yang
sama.
data_terintegrasi <- merge(
pelanggan,
transaksi,
by = "customer_id",
all.x = TRUE
)
data_terintegrasi
## customer_id nama usia pendapatan kota status usia_imputasi
## 1 C001 Ani 21 4.5e+06 Pekanbaru Aktif 21.0
## 2 C002 Budi 25 NA Pekanbaru Aktif 25.0
## 3 C003 Citra 23 5.2e+06 Pekanbaru Aktif 23.0
## 4 C004 Dodi 50 4.8e+06 Dumai Aktif 50.0
## 5 C005 Eka 27 4.9e+06 Pekanbaru Tidak Aktif 27.0
## 6 C006 Fani NA 5.1e+06 Dumai Tidak Aktif 27.5
## 7 C007 Gilang 31 5.0e+08 Pekanbaru Aktif 31.0
## 8 C008 Hana 29 4.7e+06 Siak Aktif 29.0
## 9 C009 Indra 22 4.6e+06 Pekanbaru Aktif 22.0
## 10 C010 Joko 35 5.3e+06 Dumai Tidak Aktif 35.0
## 11 C011 Kiki 28 NA <NA> Aktif 28.0
## pendapatan_imputasi kota_imputasi pendapatan_missing outlier_pendapatan
## 1 4.5e+06 Pekanbaru 0 FALSE
## 2 4.9e+06 Pekanbaru 1 FALSE
## 3 5.2e+06 Pekanbaru 0 FALSE
## 4 4.8e+06 Dumai 0 FALSE
## 5 4.9e+06 Pekanbaru 0 FALSE
## 6 5.1e+06 Dumai 0 FALSE
## 7 5.0e+08 Pekanbaru 0 TRUE
## 8 4.7e+06 Siak 0 FALSE
## 9 4.6e+06 Pekanbaru 0 FALSE
## 10 5.3e+06 Dumai 0 FALSE
## 11 4.9e+06 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
merge() digunakan untuk menggabungkan data pelanggan dan
transaksi berdasarkan customer_id.
all.x = TRUE memastikan seluruh pelanggan tetap
dipertahankan walaupun tidak memiliki transaksi.
nrow(pelanggan)
## [1] 11
nrow(data_terintegrasi)
## [1] 11
sum(
duplicated(
data_terintegrasi$customer_id
)
)
## [1] 0
Jumlah baris sebelum dan sesudah integrasi dibandingkan. Selain itu,
duplikasi customer_id diperiksa untuk memastikan
penggabungan tidak menghasilkan data yang tidak sesuai.
colSums(
is.na(
data_terintegrasi[
, c(
"jumlah_transaksi",
"total_purchase"
)
]
)
)
## jumlah_transaksi total_purchase
## 1 1
Kode ini menghitung jumlah missing value pada variabel transaksi
setelah penggabungan. NA dapat muncul ketika pelanggan
tidak memiliki pasangan transaksi.
data_terintegrasi[
is.na(
data_terintegrasi$jumlah_transaksi
),
c(
"customer_id",
"nama"
)
]
## customer_id nama
## 11 C011 Kiki
Sintaks ini menampilkan pelanggan yang tidak mempunyai data transaksi setelah proses integrasi.
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
NA pada transaksi diubah menjadi 0 jika NA memang memiliki arti bahwa pelanggan tidak melakukan transaksi. Jika arti NA belum diketahui, sebaiknya nilai tersebut tidak langsung diubah menjadi nol.
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"
)
]
data_final
## customer_id nama usia_imputasi kota_imputasi status
## 1 C001 Ani 21.0 Pekanbaru Aktif
## 2 C002 Budi 25.0 Pekanbaru Aktif
## 3 C003 Citra 23.0 Pekanbaru Aktif
## 4 C004 Dodi 50.0 Dumai Aktif
## 5 C005 Eka 27.0 Pekanbaru Tidak Aktif
## 6 C006 Fani 27.5 Dumai Tidak Aktif
## 7 C007 Gilang 31.0 Pekanbaru Aktif
## 8 C008 Hana 29.0 Siak Aktif
## 9 C009 Indra 22.0 Pekanbaru Aktif
## 10 C010 Joko 35.0 Dumai Tidak Aktif
## 11 C011 Kiki 28.0 Tidak diketahui Aktif
## pendapatan_imputasi pendapatan_missing outlier_pendapatan usia_minmax
## 1 4.5e+06 0 FALSE 0.00000000
## 2 4.9e+06 1 FALSE 0.13793103
## 3 5.2e+06 0 FALSE 0.06896552
## 4 4.8e+06 0 FALSE 1.00000000
## 5 4.9e+06 0 FALSE 0.20689655
## 6 5.1e+06 0 FALSE 0.22413793
## 7 5.0e+08 0 TRUE 0.34482759
## 8 4.7e+06 0 FALSE 0.27586207
## 9 4.6e+06 0 FALSE 0.03448276
## 10 5.3e+06 0 FALSE 0.48275862
## 11 4.9e+06 1 FALSE 0.24137931
## pendapatan_minmax jumlah_transaksi_final total_purchase_final
## 1 0.0000000000 5 1.5e+06
## 2 0.0008072654 3 9.0e+05
## 3 0.0014127144 7 2.7e+06
## 4 0.0006054490 2 6.0e+05
## 5 0.0008072654 6 2.1e+06
## 6 0.0012108981 4 1.3e+06
## 7 1.0000000000 20 2.5e+07
## 8 0.0004036327 5 1.7e+06
## 9 0.0002018163 3 8.0e+05
## 10 0.0016145308 8 3.2e+06
## 11 0.0008072654 0 0.0e+00
Dataset akhir berisi identitas pelanggan, data yang telah dibersihkan, indikator missing, penanda outlier, hasil transformasi, dan informasi transaksi.
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_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
Nama kolom hasil preprocessing disederhanakan agar dataset akhir lebih mudah dibaca dan digunakan.
nrow(data_final)
## [1] 11
ncol(data_final)
## [1] 12
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
sum(
duplicated(
data_final$customer_id
)
)
## [1] 0
str(data_final)
## 'data.frame': 11 obs. of 12 variables:
## $ customer_id : chr "C001" "C002" "C003" "C004" ...
## $ nama : chr "Ani" "Budi" "Citra" "Dodi" ...
## $ usia : num 21 25 23 50 27 27.5 31 29 22 35 ...
## $ kota : chr "Pekanbaru" "Pekanbaru" "Pekanbaru" "Dumai" ...
## $ status : chr "Aktif" "Aktif" "Aktif" "Aktif" ...
## $ pendapatan_imputasi : num 4.5e+06 4.9e+06 5.2e+06 4.8e+06 4.9e+06 5.1e+06 5.0e+08 4.7e+06 4.6e+06 5.3e+06 ...
## $ pendapatan_missing : int 0 1 0 0 0 0 0 0 0 0 ...
## $ outlier_pendapatan : logi FALSE FALSE FALSE FALSE FALSE FALSE ...
## $ usia_minmax : num 0 0.138 0.069 1 0.207 ...
## $ pendapatan_minmax : num 0 0.000807 0.001413 0.000605 0.000807 ...
## $ jumlah_transaksi_final: num 5 3 7 2 6 4 20 5 3 8 ...
## $ total_purchase_final : num 1.5e+06 9.0e+05 2.7e+06 6.0e+05 2.1e+06 1.3e+06 2.5e+07 1.7e+06 8.0e+05 3.2e+06 ...
summary(data_final)
## customer_id nama usia kota
## Length:11 Length:11 Min. :21.00 Length:11
## Class :character Class :character 1st Qu.:24.00 Class :character
## Mode :character Mode :character Median :27.50 Mode :character
## Mean :28.95
## 3rd Qu.:30.00
## Max. :50.00
## status pendapatan_imputasi pendapatan_missing outlier_pendapatan
## Length:11 Min. :4.50e+06 Min. :0.0000 Mode :logical
## Class :character 1st Qu.:4.75e+06 1st Qu.:0.0000 FALSE:10
## Mode :character Median :4.90e+06 Median :0.0000 TRUE :1
## Mean :4.99e+07 Mean :0.1818
## 3rd Qu.:5.15e+06 3rd Qu.:0.0000
## Max. :5.00e+08 Max. :1.0000
## usia_minmax pendapatan_minmax jumlah_transaksi_final
## Min. :0.0000 Min. :0.0000000 Min. : 0.000
## 1st Qu.:0.1034 1st Qu.:0.0005045 1st Qu.: 3.000
## Median :0.2241 Median :0.0008073 Median : 5.000
## Mean :0.2743 Mean :0.0916246 Mean : 5.727
## 3rd Qu.:0.3103 3rd Qu.:0.0013118 3rd Qu.: 6.500
## Max. :1.0000 Max. :1.0000000 Max. :20.000
## total_purchase_final
## Min. : 0
## 1st Qu.: 850000
## Median : 1500000
## Mean : 3618182
## 3rd Qu.: 2400000
## Max. :25000000
Audit akhir digunakan untuk memastikan hasil preprocessing sudah sesuai. Pemeriksaan meliputi jumlah baris, jumlah kolom, missing value, duplikasi identifier, struktur data, dan ringkasan statistik.
write.csv(
data_final,
"data_pelanggan_setelah_preprocessing.csv",
row.names = FALSE
)
getwd()
## [1] "C:/Users/arimb/Downloads"
write.csv() digunakan untuk menyimpan dataset akhir
dalam format CSV. getwd() digunakan untuk mengetahui folder
tempat file disimpan.
Tidak selalu. Menghapus seluruh baris yang memiliki missing value memang dapat menghasilkan dataset yang terlihat lengkap, tetapi jumlah data yang tersedia dapat berkurang. Pada dataset pelanggan, beberapa nilai seperti usia, pendapatan, dan kota memiliki missing value. Jika semua baris tersebut langsung dihapus, informasi pelanggan yang masih berguna dapat ikut hilang. Oleh karena itu, penanganan missing value harus disesuaikan dengan kondisi data dan tujuan analisis. Pada data ini, imputasi median digunakan untuk variabel numerik seperti usia dan pendapatan, sedangkan kota yang kosong diberi kategori “Tidak diketahui”.
Outlier tidak selalu merupakan kesalahan data. Nilai yang sangat berbeda dari data lainnya dapat menunjukkan kondisi yang benar-benar terjadi. Pada dataset ini terdapat pendapatan Rp500.000.000 pada pelanggan C007 yang jauh lebih besar dibandingkan pelanggan lainnya. Nilai tersebut merupakan kandidat outlier berdasarkan metode IQR, tetapi tidak langsung dihapus karena masih mungkin merupakan nilai yang benar. Oleh karena itu, outlier perlu diperiksa terlebih dahulu berdasarkan konteks data sebelum menentukan apakah akan dipertahankan, diperbaiki, atau ditangani menggunakan metode seperti winsorizing.
Preprocessing dapat menimbulkan bias apabila keputusan yang digunakan untuk membersihkan atau mengubah data tidak sesuai dengan kondisi sebenarnya. Contohnya, jika semua missing value langsung dihapus, kelompok pelanggan tertentu dapat menjadi kurang terwakili. Begitu juga dengan imputasi menggunakan satu nilai yang sama untuk banyak data dapat mengurangi variasi sebenarnya dalam dataset. Pada data ini, pemilihan median untuk pendapatan dilakukan karena terdapat nilai ekstrem yang dapat memengaruhi mean. Setiap keputusan preprocessing harus memiliki alasan yang jelas dan dicatat agar tidak menghasilkan kesimpulan yang menyesatkan.
Parameter seperti median, mean, standar deviasi, nilai minimum, dan maksimum sebaiknya dihitung dari data pelatihan agar informasi dari data pengujian tidak ikut memengaruhi proses preprocessing. Jika parameter dihitung menggunakan seluruh dataset, informasi dari data pengujian dapat masuk ke proses pembentukan model sehingga menyebabkan data leakage. Dengan menggunakan parameter dari data pelatihan saja, proses analisis menjadi lebih adil dan hasil evaluasi model dapat menggambarkan kemampuan model pada data yang belum pernah digunakan sebelumnya.
Identifier yang tidak unik dapat menyebabkan data bergabung secara
tidak tepat dan menghasilkan jumlah baris yang lebih banyak dari
seharusnya. Pada dataset pelanggan terdapat customer_id
yang muncul lebih dari satu kali, yaitu C010. Jika duplikasi tersebut
tidak diperiksa sebelum integrasi, satu pelanggan dapat memiliki
beberapa pasangan data transaksi sehingga hasil penggabungan menjadi
tidak akurat. Oleh karena itu, identifier perlu diperiksa keunikannya
terlebih dahulu sebelum digunakan sebagai kunci untuk menggabungkan dua
dataset.
Alur preprocessing yang telah dilakukan pada data pelanggan dan transaksi adalah:
cust_id menjadi customer_id.Preprocessing pada dataset ini menunjukkan bahwa pembersihan data bukan hanya menjalankan kode R, tetapi juga membutuhkan pertimbangan terhadap kondisi dan makna data. Nilai seperti usia 150, pendapatan 500.000.000, kategori kota yang berbeda penulisan, serta missing value harus diperiksa berdasarkan konteks sebelum dilakukan perubahan. Setiap keputusan preprocessing dapat memengaruhi hasil analisis, sehingga perubahan data perlu dilakukan secara hati-hati, memiliki alasan yang jelas, dan didokumentasikan.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann. Chapter 3: Data Preprocessing.