• RSTUDIO
Data yang baik tidak hanya membutuhkan jumlah yang cukup, tetapi juga kualitas yang baik. Melalui proses preprocessing, data diperiksa, dibersihkan, diolah, dan divalidasi agar siap digunakan untuk analisis.
Data merupakan salah satu dasar penting dalam proses pengambilan keputusan.Sebelum melakukan analisis lebih lanjut, diperlukan proses data preprocessing.Data preprocessing merupakan serangkaian proses untuk mempersiapkan data mentah agar menjadi lebih bersih, konsisten, dan siap digunakan dalam analisis. Proses ini tidak hanya bertujuan untuk menghapus data yang bermasalah, tetapi juga untuk memahami kondisi data, menentukan tindakan yang tepat, serta memastikan bahwa perubahan yang dilakukan tetap dapat dipertanggungjawabkan.
Pada praktik ini digunakan data pelanggan dan data transaksi sebagai
contoh. Dataset sengaja dibuat mengandung beberapa permasalahan agar
proses preprocessing dapat dipelajari secara langsung. Masalah tersebut
meliputi missing value, duplikasi customer_id,
variasi penulisan kota dan status, usia yang tidak masuk akal, serta
pendapatan yang sangat ekstrem.
Proses preprocessing dilakukan secara bertahap mulai dari audit data, cleaning, penanganan missing value, evaluasi outlier, transformasi, integrasi data, hingga validasi akhir. Dengan pendekatan ini, perubahan terhadap data dapat dilakukan secara sistematis dan dapat ditelusuri.
Data yang baik bukan hanya data yang banyak, tetapi data yang memiliki kualitas yang cukup baik untuk menjawab tujuan analisis.
R.version.string
[1] "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
)
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 <- 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
Data pelanggan terdiri dari 12 baris dan 6 atribut. Data transaksi terdiri dari 11 baris.
Beberapa masalah memang sengaja dimasukkan ke dalam dataset, misalnya
missing value, duplikasi C010, variasi penulisan kota dan
status, usia 150 tahun, serta pendapatan 500 juta.
Sebelum melakukan perubahan, periksa struktur, dimensi, tipe atribut, dan beberapa observasi awal.
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
Dari pemeriksaan awal, terlihat bahwa dataset mempunyai:
customer_id yang berulang;Enam dimensi kualitas data yang dibahas Han, Kamber, dan Pei meliputi accuracy, completeness, consistency, timeliness, believability, dan interpretability.
Tidak semua dimensi dapat dihitung hanya dari tabel.
Pemeriksaan berikut berfokus pada masalah yang dapat dideteksi dari dataset.
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$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
Audit awal memberikan gambaran mengenai kualitas dataset sebelum dilakukan perubahan.
Tahap ini penting karena keputusan preprocessing sebaiknya didasarkan pada kondisi data yang benar-benar ditemukan.
Data mentah dipertahankan sebagai referensi. Semua perubahan
dilakukan pada objek pelanggan.
pelanggan <- pelanggan_raw
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"
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"
Kategori yang sebelumnya ditulis dengan berbagai bentuk telah diseragamkan menjadi kategori yang lebih konsisten.
Contohnya:
PKU, Pekanbaru, PEKANBARU →
Pekanbaru
aktif, ACTIVE, A →
Aktif
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
pelanggan <-
pelanggan[
!duplicated(
pelanggan$customer_id
),
]
rownames(
pelanggan
) <- NULL
dim(
pelanggan
)
[1] 11 6
Menghapus duplikasi aman apabila setiap customer_id
memang merepresentasikan satu pelanggan.
Jika satu pelanggan boleh memiliki banyak baris transaksi, duplikasi tidak boleh dihapus dengan cara ini.
Aturan bisnis yang digunakan adalah usia 15–100 tahun.
pelanggan[
!is.na(pelanggan$usia) &
(
pelanggan$usia < 15 |
pelanggan$usia > 100
),
]
customer_id nama usia pendapatan kota status
4 C004 Dodi 150 4800000 Dumai Aktif
pelanggan[
!is.na(pelanggan$pendapatan) &
pelanggan$pendapatan < 0,
]
[1] customer_id nama usia pendapatan kota status
<0 rows> (or 0-length row.names)
Nilai usia 150 pada C004 merupakan nilai yang tidak masuk akal untuk aturan domain yang digunakan. Pada contoh ini, nilai dikoreksi menjadi 50 berdasarkan sumber asli.
pelanggan$usia[
pelanggan$customer_id == "C004"
] <- 50
pelanggan[
pelanggan$customer_id == "C004",
]
customer_id nama usia pendapatan kota status
4 C004 Dodi 50 4800000 Dumai Aktif
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
Log membuat proses preprocessing lebih transparan.
Dengan adanya log, kita dapat mengetahui:
apa yang diubah → atribut yang diubah → tindakan yang dilakukan.
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
Kode berikut hanya mendemonstrasikan strategi. Objek
pelanggan tidak langsung diganti.
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
Menghapus seluruh baris yang mempunyai missing value dapat mengurangi jumlah observasi secara cukup besar.
Karena itu, keputusan ini perlu mempertimbangkan jumlah data yang hilang dan pentingnya variabel tersebut.
Pendapatan memiliki nilai ekstrem sehingga mean dan median perlu dibandingkan terlebih dahulu.
mean_pendapatan <-
mean(
pelanggan$pendapatan,
na.rm = TRUE
)
mean_pendapatan
[1] 59900000
median_pendapatan <-
median(
pelanggan$pendapatan,
na.rm = TRUE
)
median_pendapatan
[1] 4900000
Karena terdapat nilai pendapatan sebesar 500 juta, mean menjadi jauh lebih besar daripada median.
Median lebih stabil terhadap pengaruh nilai ekstrem sehingga digunakan sebagai contoh strategi imputasi pada data ini.
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
Nilai kota yang hilang tidak langsung diisi dengan modus.
Pada contoh ini digunakan kategori eksplisit “Tidak diketahui” agar informasi ketidakpastian 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
Indikator missing mempertahankan informasi bahwa suatu nilai awalnya hilang.
pelanggan$pendapatan_missing <-
as.integer(
is.na(
pelanggan$pendapatan
)
)
table(
pelanggan$pendapatan_missing
)
0 1
9 2
par(
mfrow = c(1, 2)
)
hist(
pelanggan$pendapatan,
main = "Sebelum Imputasi",
xlab = "Pendapatan",
col = "#A7C4BC",
breaks = 8
)
hist(
pelanggan$pendapatan_imputasi,
main = "Sesudah Imputasi Median",
xlab = "Pendapatan",
col = "#52796F",
breaks = 8
)
par(
mfrow = c(1, 1)
)
Imputasi dapat mengubah distribusi data dan mengurangi variasi.
Karena itu, evaluasi tidak berhenti hanya karena seluruh
NA sudah tertangani.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
col = "#A7C4BC",
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Satu nilai pendapatan jauh lebih besar dibandingkan sebagian besar observasi lainnya.
Nilai tersebut menjadi kandidat outlier yang perlu dievaluasi.
q1 <-
quantile(
pelanggan$pendapatan_imputasi,
0.25,
na.rm = TRUE
)
q3 <-
quantile(
pelanggan$pendapatan_imputasi,
0.75,
na.rm = TRUE
)
iqr <-
IQR(
pelanggan$pendapatan_imputasi,
na.rm = TRUE
)
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
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
Outlier tidak otomatis dihapus.
Pada contoh ini, nilai ekstrem akan ditangani menggunakan pendekatan winsorization agar pengaruh ekstrem dapat dikurangi tanpa menghilangkan observasi.
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
Winsorization mengubah nilai ekstrem menjadi batas yang telah ditentukan.
Metode ini tidak selalu menjadi pilihan terbaik. Dalam data nyata, keputusan harus mempertimbangkan konteks bisnis dan sumber data.
Rumus untuk rentang [0,1] adalah:
\[ 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_imputasi",
"usia_minmax",
"pendapatan_imputasi",
"pendapatan_minmax"
)
]
customer_id usia_imputasi usia_minmax pendapatan_imputasi pendapatan_minmax
1 C001 21.0 0.00000000 4.5e+06 0.0000000000
2 C002 25.0 0.13793103 4.9e+06 0.0008072654
3 C003 23.0 0.06896552 5.2e+06 0.0014127144
4 C004 50.0 1.00000000 4.8e+06 0.0006054490
5 C005 27.0 0.20689655 4.9e+06 0.0008072654
6 C006 27.5 0.22413793 5.1e+06 0.0012108981
7 C007 31.0 0.34482759 5.0e+08 1.0000000000
8 C008 29.0 0.27586207 4.7e+06 0.0004036327
9 C009 22.0 0.03448276 4.6e+06 0.0002018163
10 C010 35.0 0.48275862 5.3e+06 0.0016145308
11 C011 28.0 0.24137931 4.9e+06 0.0008072654
Rumus:
\[ 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
Z-score menunjukkan posisi suatu nilai terhadap rata-rata dalam satuan simpangan baku.
Nilai positif berarti berada di atas rata-rata, sedangkan nilai negatif berarti berada di bawah rata-rata.
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
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
Min–maks mengubah nilai ke rentang tertentu, umumnya
[0,1].
Z-score menempatkan data berdasarkan jarak terhadap rata-rata.
Decimal scaling mengecilkan skala berdasarkan pangkat 10.
Pemilihan metode bergantung pada karakteristik data dan kebutuhan analisis.
pelanggan$pendapatan_winsor_minmax <-
minmax(
pelanggan$pendapatan_winsor
)
plot(
pelanggan$pendapatan_minmax,
pelanggan$pendapatan_winsor_minmax,
pch = 19,
col = "#285943",
xlab = "Min–maks Data Asli",
ylab = "Min–maks Data Winsorized",
main = "Dampak Penanganan Outlier terhadap Normalisasi"
)
abline(
0,
1,
col = "#9B2C2C",
lty = 2
)
Keberadaan outlier dapat membuat sebagian besar nilai lain terkumpul pada rentang min–maks yang sangat sempit.
Hal ini menunjukkan bahwa deteksi outlier penting dilakukan sebelum memilih strategi transformasi.
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"
Ditemukan perbedaan identifier:
C011 terdapat pada data pelanggan tetapi tidak terdapat
pada data transaksi;C012 terdapat pada data transaksi tetapi tidak terdapat
pada data pelanggan.Perbedaan ini penting diperhatikan sebelum melakukan merge.
transaksi <-
transaksi_raw
names(
transaksi
)[
names(transaksi) == "cust_id"
] <- "customer_id"
all.x = TRUE digunakan agar seluruh pelanggan tetap
dipertahankan meskipun tidak mempunyai pasangan transaksi.
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
all.x = TRUE?Karena fokus analisis adalah pelanggan.
Dengan all.x = TRUE, pelanggan yang belum memiliki
transaksi tetap muncul pada dataset hasil integrasi.
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
Jumlah baris tetap sama dengan jumlah pelanggan karena proses penggabungan mempertahankan seluruh pelanggan.
Missing pada variabel transaksi menunjukkan bahwa pelanggan tersebut tidak memiliki pasangan transaksi pada sumber data transaksi.
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_cleaning <-
audit_data(
pelanggan
)
audit_cleaning
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
min
function (..., na.rm = FALSE) .Primitive("min")
audit_integrasi <-
audit_data(
data_terintegrasi
)
audit_integrasi
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
missing_comparison <-
data.frame(
kondisi = c(
"Sebelum preprocessing",
"Setelah cleaning"
),
total_missing = c(
sum(
is.na(
pelanggan_raw
)
),
sum(
is.na(
pelanggan
)
)
)
)
missing_comparison
kondisi total_missing
1 Sebelum preprocessing 4
2 Setelah cleaning 4
duplicate_comparison <-
data.frame(
kondisi = c(
"Sebelum preprocessing",
"Setelah cleaning"
),
duplicate_id = c(
sum(
duplicated(
pelanggan_raw$customer_id
)
),
sum(
duplicated(
pelanggan$customer_id
)
)
)
)
duplicate_comparison
kondisi duplicate_id
1 Sebelum preprocessing 1
2 Setelah cleaning 0
par(
mfrow = c(1, 2)
)
barplot(
c(
sum(
is.na(
pelanggan_raw
)
),
sum(
duplicated(
pelanggan_raw$customer_id
)
)
),
names.arg = c(
"Missing",
"Duplicate"
),
main = "Sebelum Preprocessing",
col = "#B8C4C2",
ylab = "Jumlah"
)
barplot(
c(
sum(
is.na(
pelanggan
)
),
sum(
duplicated(
pelanggan$customer_id
)
)
),
names.arg = c(
"Missing",
"Duplicate"
),
main = "Sesudah Cleaning",
col = "#52796F",
ylab = "Jumlah"
)
par(
mfrow = c(1, 1)
)
Dataset telah melalui beberapa tahap:
Raw Data → Audit → Cleaning → Missing Value Treatment → Outlier Evaluation → Transformation → Integration → Validation
Tujuan akhirnya bukan hanya data yang lebih rapi, tetapi data yang lebih siap digunakan untuk analisis.
Untuk kebutuhan analisis berikutnya, digunakan variabel hasil preprocessing.
data_final <-
data_terintegrasi[
,
c(
"customer_id",
"nama",
"usia_imputasi",
"kota_imputasi",
"status",
"pendapatan_imputasi",
"pendapatan_missing",
"outlier_pendapatan",
"usia_minmax",
"pendapatan_minmax",
"usia_z",
"pendapatan_z",
"pendapatan_decimal",
"jumlah_transaksi",
"total_purchase"
)
]
names(data_final)[
names(data_final) == "usia_imputasi"
] <- "usia"
names(data_final)[
names(data_final) == "kota_imputasi"
] <- "kota"
data_final
customer_id nama usia kota status pendapatan_imputasi
1 C001 Ani 21.0 Pekanbaru Aktif 4.5e+06
2 C002 Budi 25.0 Pekanbaru Aktif 4.9e+06
3 C003 Citra 23.0 Pekanbaru Aktif 5.2e+06
4 C004 Dodi 50.0 Dumai Aktif 4.8e+06
5 C005 Eka 27.0 Pekanbaru Tidak Aktif 4.9e+06
6 C006 Fani 27.5 Dumai Tidak Aktif 5.1e+06
7 C007 Gilang 31.0 Pekanbaru Aktif 5.0e+08
8 C008 Hana 29.0 Siak Aktif 4.7e+06
9 C009 Indra 22.0 Pekanbaru Aktif 4.6e+06
10 C010 Joko 35.0 Dumai Tidak Aktif 5.3e+06
11 C011 Kiki 28.0 Tidak diketahui Aktif 4.9e+06
pendapatan_missing outlier_pendapatan usia_minmax pendapatan_minmax
1 0 FALSE 0.00000000 0.0000000000
2 1 FALSE 0.13793103 0.0008072654
3 0 FALSE 0.06896552 0.0014127144
4 0 FALSE 1.00000000 0.0006054490
5 0 FALSE 0.20689655 0.0008072654
6 0 FALSE 0.22413793 0.0012108981
7 0 TRUE 0.34482759 1.0000000000
8 0 FALSE 0.27586207 0.0004036327
9 0 FALSE 0.03448276 0.0002018163
10 0 FALSE 0.48275862 0.0016145308
11 1 FALSE 0.24137931 0.0008072654
usia_z pendapatan_z pendapatan_decimal jumlah_transaksi total_purchase
1 -0.984199667 -0.3041235 0.0045 5 1.5e+06
2 -0.489287834 -0.3014440 0.0049 3 9.0e+05
3 -0.736743750 -0.2994343 0.0052 7 2.7e+06
4 2.603911118 -0.3021138 0.0048 2 6.0e+05
5 -0.241831918 -0.3014440 0.0049 6 2.1e+06
6 -0.179967939 -0.3001042 0.0051 4 1.3e+06
7 0.253079914 3.0151095 0.5000 20 2.5e+07
8 0.005623998 -0.3027837 0.0047 5 1.7e+06
9 -0.860471709 -0.3034536 0.0046 3 8.0e+05
10 0.747991747 -0.2987645 0.0053 8 3.2e+06
11 -0.118103960 -0.3014440 0.0049 NA NA
c(
jumlah_baris =
nrow(data_final),
jumlah_kolom =
ncol(data_final),
total_missing =
sum(is.na(data_final))
)
jumlah_baris jumlah_kolom total_missing
11 15 2
Preprocessing diperlukan karena data mentah dapat mengandung missing value, duplikasi, kategori yang tidak konsisten, nilai ekstrem, kesalahan domain, dan perbedaan struktur antar sumber.
Tidak.
Missing value dapat ditangani dengan beberapa strategi, seperti menghapus baris, imputasi, atau mempertahankan informasi missing melalui indikator.
Tidak.
Outlier perlu diperiksa terlebih dahulu untuk mengetahui apakah nilai tersebut merupakan kesalahan atau memang merupakan kondisi yang nyata.
Karena terdapat nilai pendapatan yang sangat ekstrem sehingga median lebih stabil dibandingkan mean pada contoh ini.
all.x = TRUE digunakan?Karena kita ingin mempertahankan seluruh pelanggan meskipun sebagian pelanggan tidak mempunyai transaksi yang cocok.
Karena proses penggabungan data dapat menghasilkan perubahan jumlah baris, duplikasi, atau missing value baru.
01 — Memahami data
Struktur, dimensi, tipe atribut, dan kondisi awal diperiksa.
02 — Membersihkan data
Spasi, kapitalisasi, kategori, duplikasi, dan domain diperiksa.
03 — Menangani missing value
Beberapa strategi dibandingkan dan imputasi diterapkan.
04 — Mengevaluasi outlier
Boxplot dan metode IQR digunakan untuk mengidentifikasi kandidat outlier.
05 — Melakukan transformasi
Min–maks, Z-score, dan decimal scaling diterapkan.
06 — Mengintegrasikan data
Data pelanggan dan transaksi digabungkan menggunakan identifier.
07 — Melakukan validasi
Dataset diperiksa kembali setelah proses preprocessing.
Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann.
Every reliable analysis begins with reliable data.