Data preprocessing merupakan salah satu tahap penting dalam proses data mining karena data yang diperoleh dari suatu sumber umumnya belum berada dalam kondisi yang sepenuhnya siap untuk digunakan dalam analisis. Data mentah dapat mengandung berbagai permasalahan, seperti missing value, data duplikat, kategori yang tidak konsisten, kesalahan dalam batas atau aturan nilai (domain error), serta nilai yang terlalu ekstrem atau outlier. Apabila permasalahan tersebut tidak diperiksa dan ditangani dengan tepat, kualitas data dapat menurun dan pada akhirnya dapat memengaruhi hasil analisis maupun proses pemodelan yang dilakukan.
Oleh karena itu, diperlukan proses preprocessing untuk memahami kondisi data sejak awal, mengidentifikasi berbagai permasalahan yang terdapat di dalamnya, serta menentukan tindakan yang sesuai untuk memperbaiki atau mengelola permasalahan tersebut. Proses ini tidak hanya berfokus pada menghilangkan data yang dianggap bermasalah, tetapi juga mempertimbangkan apakah suatu nilai benar-benar merupakan kesalahan atau justru memiliki informasi yang penting. Dengan demikian, preprocessing merupakan bagian yang membutuhkan pemeriksaan dan pengambilan keputusan secara sistematis sebelum data digunakan pada tahap analisis selanjutnya.
Pada tugas ini, proses preprocessing diterapkan secara bertahap menggunakan RStudio dengan mengacu pada materi Data Preprocessing Menggunakan RStudio. Tahapan dimulai dengan memahami struktur dan kondisi awal dataset, kemudian dilanjutkan dengan proses data cleaning untuk memperbaiki ketidakkonsistenan, memeriksa duplikasi, serta mengevaluasi kesesuaian nilai terhadap aturan domain. Setelah itu, dilakukan identifikasi dan penanganan missing values, kemudian dilanjutkan dengan pendeteksian dan evaluasi outlier menggunakan pendekatan yang sesuai.
Setelah kualitas data diperbaiki, tahap berikutnya adalah melakukan transformasi atribut numerik, seperti normalisasi Min-Max, normalisasi Z-Score, dan decimal scaling. Transformasi ini dilakukan untuk melihat bagaimana perubahan skala data dapat memengaruhi karakteristik variabel. Selanjutnya, dua sumber data diintegrasikan dengan memperhatikan kesesuaian identifier atau kunci penghubung antar-data. Hasil integrasi tersebut kemudian diperiksa kembali untuk memastikan bahwa proses penggabungan tidak menimbulkan masalah baru.
Melalui rangkaian tahapan tersebut, tugas ini bertujuan untuk menunjukkan bagaimana data yang awalnya masih memiliki berbagai permasalahan dapat diproses secara sistematis hingga menghasilkan dataset yang lebih konsisten dan siap digunakan untuk tahap analisis berikutnya. Seluruh proses dilakukan menggunakan fungsi dasar R, sehingga implementasinya tidak memerlukan pemasangan paket tambahan. Alur ini sejalan dengan materi praktikum yang menekankan pemahaman kondisi awal data, data cleaning, penanganan missing values, evaluasi outlier, transformasi data, integrasi sumber data, serta validasi kondisi data setelah preprocessing.
Setelah menyelesaikan praktikum, diharapkan dapat:
Kode berikut digunakan untuk mengetahui direktori kerja dan versi R yang digunakan.
getwd()
## [1] "C:/Users/USER/Downloads"
R.version.string
## [1] "R version 4.4.1 (2024-06-14 ucrt)"
Dataset berikut sengaja dibuat memiliki beberapa masalah agar seluruh
tahapan preprocessing dapat dipraktikkan. Masalah tersebut meliputi
duplikasi customer_id, missing value, kategori
tidak konsisten, kesalahan nilai usia, dan nilai pendapatan yang sangat
besar.
pelanggan_raw <- data.frame(
customer_id = c(
"C001", "C002", "C003", "C004", "C005",
"C006", "C007", "C008", "C009", "C010",
"C010", "C011", "C012", "C013", "C014",
"C015", "C016", "C017", "C018", "C019"
),
nama = c(
"Ani", "Budi", "Citra", "Dodi", "Eka",
"Fani", "Gilang", "Hana", "Indra", "Joko",
"Joko", "Kiki", "Lala", "Maya", "Nanda",
"Oki", "Putri", "Rian", "Sari", "Tono"
),
usia = c(
21, 25, 23, 150, 27,
NA, 31, 29, 22, 35,
35, 28, 24, 26, 30,
19, NA, 33, 27, 41
),
pendapatan = c(
4500000, NA, 5200000, 4800000, 4900000,
5100000, 500000000, 4700000, 4600000, 5300000,
5300000, NA, 4200000, 5500000, 4800000,
3900000, 5100000, 5700000, 5000000, 6000000
),
kota = c(
"Pekanbaru", " PKU", "PEKANBARU", "Dumai", "pekanbaru",
"DUMAI", "Pekanbaru ", "Siak", "PKU", "Dumai",
"Dumai", NA, "Pekanbaru", "pekanbaru", "DUMAI",
"Siak", "PEKANBARU", "Pekanbaru", "PKU", "Dumai"
),
status = c(
"Aktif", "aktif", "ACTIVE", "A", "Tidak Aktif",
"nonaktif", "Aktif", "AKTIF", "A", "Tidak aktif",
"Tidak aktif", "Aktif", "active", "A", "Aktif",
"tidak aktif", "AKTIF", "aktif", "nonaktif", "Aktif"
),
stringsAsFactors = FALSE
)
pelanggan_raw
transaksi_raw <- data.frame(
cust_id = c(
"C001", "C002", "C003", "C004", "C005",
"C006", "C007", "C008", "C009", "C010",
"C011", "C012", "C013", "C014", "C015",
"C016", "C017", "C018", "C999", "C019"
),
jumlah_transaksi = c(
5, 3, 7, 2, 6,
4, 25, 5, 3, 8,
1, 4, 6, 9, 3,
NA, 7, 2, 5, 10
),
total_purchase = c(
1500000, 900000, 2700000, 600000, 2100000,
1300000, 35000000, 1700000, 800000, 3200000,
250000, 1400000, 2200000, 4500000, 1000000,
NA, 2800000, 700000, 1200000, 4200000
),
stringsAsFactors = FALSE
)
transaksi_raw
Tahap ini bertujuan memahami ukuran, nama variabel, tipe data, beberapa baris awal, dan ringkasan statistik.
dim(pelanggan_raw)
## [1] 20 6
names(pelanggan_raw)
## [1] "customer_id" "nama" "usia" "pendapatan" "kota"
## [6] "status"
str(pelanggan_raw)
## 'data.frame': 20 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)
summary(pelanggan_raw)
## customer_id nama usia pendapatan
## Length:20 Length:20 Min. : 19.00 Min. : 3900000
## Class :character Class :character 1st Qu.: 24.25 1st Qu.: 4725000
## Mode :character Mode :character Median : 27.50 Median : 5050000
## Mean : 34.78 Mean : 32477778
## 3rd Qu.: 32.50 3rd Qu.: 5300000
## Max. :150.00 Max. :500000000
## NA's :2 NA's :2
## kota status
## Length:20 Length:20
## Class :character Class :character
## Mode :character Mode :character
##
##
##
##
colSums(is.na(pelanggan_raw))
## customer_id nama usia pendapatan kota status
## 0 0 2 2 1 0
round(colMeans(is.na(pelanggan_raw)) * 100, 2)
## customer_id nama usia pendapatan kota status
## 0 0 10 10 5 0
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" "ACTIVE" "aktif" "Aktif"
## [6] "AKTIF" "nonaktif" "tidak aktif" "Tidak aktif" "Tidak Aktif"
range(pelanggan_raw$usia, na.rm = TRUE)
## [1] 19 150
range(pelanggan_raw$pendapatan, na.rm = TRUE)
## [1] 3.9e+06 5.0e+08
Fungsi berikut membuat ringkasan kualitas setiap atribut sehingga pemeriksaan dapat dilakukan secara lebih sistematis.
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
pelanggan <- pelanggan_raw
Pada data mentah terdapat perbedaan spasi dan penggunaan huruf besar-kecil. Tahap pertama adalah membersihkan spasi, kemudian menyeragamkan huruf menjadi kecil untuk memudahkan pencocokan kategori.
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"
Selanjutnya kategori diseragamkan menjadi label yang konsisten.
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"
customer_id digunakan sebagai identifier pelanggan.
Baris dengan identifier yang berulang diperiksa terlebih dahulu.
pelanggan[
duplicated(pelanggan$customer_id) |
duplicated(pelanggan$customer_id, fromLast = TRUE),
]
Kemunculan kedua untuk C010 dihapus.
pelanggan <- pelanggan[
!duplicated(pelanggan$customer_id),
]
rownames(pelanggan) <- NULL
dim(pelanggan)
## [1] 19 6
Aturan domain digunakan untuk menemukan nilai yang tidak masuk akal berdasarkan konteks data. Pada contoh ini usia yang dianggap masuk akal adalah 15–100 tahun.
pelanggan[
pelanggan$usia < 15 |
pelanggan$usia > 100,
]
Pendapatan negatif juga diperiksa sebagai kandidat pelanggaran domain.
pelanggan[
pelanggan$pendapatan < 0,
]
Nilai usia C004 sebesar 150 diperbaiki menjadi 50 berdasarkan sumber asli.
pelanggan$usia[
pelanggan$customer_id == "C004"
] <- 50
Pencatatan perubahan penting agar proses preprocessing dapat ditelusuri.
log_perubahan <- data.frame(
tahap = c(
"Standardisasi",
"Standardisasi",
"Deduplikasi",
"Koreksi domain"
),
atribut = c(
"kota",
"status",
"customer_id",
"usia"
),
tindakan = c(
"PKU dan variasi kapital menjadi Pekanbaru",
"ACTIVE/A menjadi Aktif; nonaktif menjadi Tidak Aktif",
"Menghapus kemunculan kedua C010",
"Usia C004 dikoreksi dari 150 menjadi 50"
),
stringsAsFactors = FALSE
)
log_perubahan
colSums(is.na(pelanggan))
## customer_id nama usia pendapatan kota status
## 0 0 2 2 1 0
pelanggan[
!complete.cases(pelanggan),
]
Sebagai ilustrasi, data tanpa missing value dapat diperoleh
dengan complete.cases(). Jumlah baris sebelum dan sesudah
dibandingkan.
pelanggan_complete <- pelanggan[
complete.cases(pelanggan),
]
nrow(pelanggan)
## [1] 19
nrow(pelanggan_complete)
## [1] 15
round(
(
1 -
nrow(pelanggan_complete) /
nrow(pelanggan)
) * 100,
2
)
## [1] 21.05
Strategi kedua dalam menangani missing value adalah melakukan imputasi menggunakan nilai median. Median dipilih karena lebih tahan terhadap pengaruh nilai ekstrem dibandingkan mean.
Sebelum melakukan imputasi, nilai mean dan median pada atribut numerik dibandingkan terlebih dahulu.
# Menghitung mean dan median usia
mean_usia <- mean(
pelanggan$usia,
na.rm = TRUE
)
median_usia <- median(
pelanggan$usia,
na.rm = TRUE
)
# Menghitung mean dan median pendapatan
mean_pendapatan <- mean(
pelanggan$pendapatan,
na.rm = TRUE
)
median_pendapatan <- median(
pelanggan$pendapatan,
na.rm = TRUE
)
# Menampilkan hasil perbandingan
data.frame(
Atribut = c(
"Usia",
"Pendapatan"
),
Mean = c(
mean_usia,
mean_pendapatan
),
Median = c(
median_usia,
median_pendapatan
)
)
Perbandingan mean dan median digunakan untuk melihat apakah terdapat perbedaan yang cukup besar antara keduanya. Perbedaan yang besar dapat menunjukkan adanya nilai ekstrem yang memengaruhi rata-rata.
Untuk memperjelas perbedaan antara mean dan median, digunakan visualisasi titik dengan nilai mean dan median ditampilkan secara langsung pada grafik. Visualisasi ini membantu pembaca melihat perbedaan kedua ukuran statistik tanpa terpengaruh oleh tinggi batang.
# Menyiapkan data
nilai <- c(
mean_usia,
median_usia,
mean_pendapatan,
median_pendapatan
)
nama <- c(
"Mean Usia",
"Median Usia",
"Mean Pendapatan",
"Median Pendapatan"
)
# Membuat grafik
plot(
nilai,
1:4,
xlab = "Nilai",
ylab = "",
yaxt = "n",
pch = 19,
main = "Perbandingan Mean dan Median"
)
# Nama pada sumbu Y
axis(
2,
at = 1:4,
labels = nama,
las = 1
)
# Menampilkan nilai pada grafik
text(
nilai,
1:4,
labels = format(
round(nilai),
big.mark = ".",
scientific = FALSE
),
pos = 3
)
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
Berdasarkan visualisasi tersebut, nilai mean dan median dapat dibandingkan secara langsung melalui angka yang ditampilkan pada grafik. Perbedaan antara mean dan median, khususnya pada pendapatan, menunjukkan bahwa terdapat nilai yang relatif ekstrem yang memengaruhi nilai mean. Oleh karena itu, median dipilih sebagai nilai imputasi karena lebih tahan terhadap pengaruh nilai ekstrem.
Selanjutnya, nilai NA pada atribut usia dan pendapatan
digantikan dengan nilai median masing-masing.
# Membuat salinan atribut usia
pelanggan$usia_imputasi <- pelanggan$usia
# Mengganti NA pada usia dengan median usia
pelanggan$usia_imputasi[
is.na(pelanggan$usia_imputasi)
] <- median_usia
# Membuat salinan atribut pendapatan
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
# Mengganti NA pada pendapatan dengan median pendapatan
pelanggan$pendapatan_imputasi[
is.na(pelanggan$pendapatan_imputasi)
] <- median_pendapatan
# Menampilkan hasil imputasi
pelanggan[
,
c(
"customer_id",
"usia",
"usia_imputasi",
"pendapatan",
"pendapatan_imputasi"
)
]
Setelah proses imputasi dilakukan, jumlah missing value
diperiksa kembali untuk memastikan bahwa nilai NA telah
ditangani.
c(
missing_usia = sum(
is.na(pelanggan$usia_imputasi)
),
missing_pendapatan = sum(
is.na(pelanggan$pendapatan_imputasi)
)
)
## missing_usia missing_pendapatan
## 0 0
Jika hasil pemeriksaan menunjukkan nilai 0 pada kedua
atribut, maka seluruh missing value pada atribut usia dan
pendapatan telah berhasil ditangani menggunakan metode imputasi
median.
Untuk kota yang tidak diketahui, digunakan label
Tidak diketahui agar ketidakpastian tidak disamarkan
sebagai kategori tertentu.
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
## 5 11 2 1
Indikator biner dapat digunakan untuk mempertahankan informasi bahwa nilai pendapatan pada awalnya merupakan missing value.
pelanggan$pendapatan_missing <-
as.integer(
is.na(pelanggan$pendapatan)
)
table(
pelanggan$pendapatan_missing
)
##
## 0 1
## 17 2
par(mfrow = c(1, 2))
hist(
pelanggan$pendapatan,
main = "Sebelum Imputasi",
xlab = "Pendapatan",
breaks = 8
)
hist(
pelanggan$pendapatan_imputasi,
main = "Sesudah Imputasi Median",
xlab = "Pendapatan",
breaks = 8
)
par(mfrow = c(1, 1))
Boxplot digunakan untuk membantu melihat kandidat nilai ekstrem pada pendapatan.
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
main = "Boxplot Pendapatan",
xlab = "Pendapatan"
)
Metode IQR menggunakan Q1, Q3, dan IQR untuk menentukan batas bawah dan batas atas kandidat outlier.
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 5250000 500000 4000000 6000000
Nilai yang berada di luar batas IQR ditandai sebagai kandidat outlier. Penandaan ini bukan berarti nilai langsung dihapus.
pelanggan$outlier_pendapatan <-
pelanggan$pendapatan_imputasi <
batas_bawah |
pelanggan$pendapatan_imputasi >
batas_atas
pelanggan[
pelanggan$outlier_pendapatan,
c(
"customer_id",
"nama",
"pendapatan_imputasi"
)
]
Sebagai contoh penanganan, nilai yang berada di luar batas digeser ke batas bawah atau batas atas menggunakan winsorization.
pelanggan$pendapatan_winsor <-
pmin(
pmax(
pelanggan$pendapatan_imputasi,
batas_bawah
),
batas_atas
)
pelanggan[
pelanggan$outlier_pendapatan,
c(
"customer_id",
"pendapatan_imputasi",
"pendapatan_winsor"
)
]
Setelah dilakukan winsorization, boxplot kembali digunakan untuk melihat perubahan distribusi pendapatan. Visualisasi ini digunakan untuk membandingkan kondisi data sebelum dan sesudah penanganan outlier.
par(mfrow = c(1, 2))
boxplot(
pelanggan$pendapatan_imputasi,
horizontal = TRUE,
main = "Sebelum Winsorization",
xlab = "Pendapatan"
)
boxplot(
pelanggan$pendapatan_winsor,
horizontal = TRUE,
main = "Sesudah Winsorization",
xlab = "Pendapatan"
)
par(mfrow = c(1, 1))
Berdasarkan perbandingan boxplot, sebelum dilakukan winsorization terdapat nilai pendapatan yang sangat ekstrem dan berada jauh dari kelompok data utama. Setelah dilakukan winsorization, nilai ekstrem tersebut dibatasi berdasarkan batas yang diperoleh dari metode IQR. Dengan demikian, pengaruh nilai ekstrem terhadap distribusi pendapatan dapat dikurangi tanpa menghapus observasi dari dataset.
Transformasi digunakan untuk mengubah skala atribut numerik. Tiga metode yang digunakan adalah min–maks, z-score, dan decimal scaling.
Rumus min–maks 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",
"usia_minmax",
"pendapatan_imputasi",
"pendapatan_minmax"
)
]
Z-score mengubah nilai menjadi ukuran relatif terhadap mean dan simpangan baku.
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
)
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.0039 0.5000
transformasi <- pelanggan[
,
c(
"customer_id",
"pendapatan_imputasi",
"pendapatan_minmax",
"pendapatan_z",
"pendapatan_decimal"
)
]
transformasi
Perbandingan berikut menunjukkan bagaimana penanganan outlier dapat memengaruhi hasil normalisasi min–maks.
pelanggan$pendapatan_winsor_minmax <-
minmax(
pelanggan$pendapatan_winsor
)
plot(
pelanggan$pendapatan_minmax,
pelanggan$pendapatan_winsor_minmax,
pch = 19,
xlab = "Min–maks Data Asli",
ylab = "Min–maks Data Winsorized",
main = "Dampak Penanganan Outlier terhadap Normalisasi"
)
abline(
0,
1,
lty = 2
)
Sebelum melakukan integrasi, identifier harus diperiksa agar tidak menghasilkan penggandaan baris yang tidak diinginkan.
sum(
duplicated(
pelanggan$customer_id
)
)
## [1] 0
sum(
duplicated(
transaksi_raw$cust_id
)
)
## [1] 0
setdiff(
pelanggan$customer_id,
transaksi_raw$cust_id
)
## character(0)
setdiff(
transaksi_raw$cust_id,
pelanggan$customer_id
)
## [1] "C999"
transaksi <- transaksi_raw
names(transaksi)[
names(transaksi) == "cust_id"
] <- "customer_id"
merge(..., all.x = TRUE) digunakan untuk mempertahankan
seluruh pelanggan dan mengambil informasi transaksi yang cocok.
data_terintegrasi <- merge(
pelanggan,
transaksi,
by = "customer_id",
all.x = TRUE
)
data_terintegrasi[
,
c(
"customer_id",
"nama",
"jumlah_transaksi",
"total_purchase"
)
]
c(
sebelum = nrow(pelanggan),
sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah
## 19 19
sum(
duplicated(
data_terintegrasi$customer_id
)
)
## [1] 0
colSums(
is.na(
data_terintegrasi[
,
c(
"jumlah_transaksi",
"total_purchase"
)
]
)
)
## jumlah_transaksi total_purchase
## 1 1
Pelanggan yang tidak memiliki pasangan transaksi ditampilkan untuk diperiksa lebih lanjut.
data_terintegrasi[
is.na(
data_terintegrasi$jumlah_transaksi
),
c(
"customer_id",
"nama"
)
]
Jika setelah pemeriksaan diketahui bahwa NA memang
berarti pelanggan belum pernah bertransaksi, nilai dapat diubah menjadi
0.
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
Hanya variabel yang diperlukan untuk dataset akhir dipertahankan.
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
audit_akhir <- audit_data(
data_final
)
audit_akhir
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
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
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 praktikum, preprocessing dilakukan secara bertahap mulai dari memahami struktur dan kualitas awal data, membersihkan kategori, menghapus duplikasi, memperbaiki pelanggaran domain, menangani missing values, mendeteksi dan mengevaluasi outlier, melakukan transformasi numerik, mengintegrasikan data, hingga melakukan audit akhir.
Hal penting yang perlu diperhatikan adalah bahwa preprocessing bukan sekadar menjalankan kode. Setiap perubahan terhadap data merupakan keputusan yang perlu memiliki alasan. Outlier tidak otomatis dihapus, missing value tidak selalu harus dibuang, dan hasil integrasi harus divalidasi agar tidak menimbulkan duplikasi atau kesalahan baru.
Data mentah
↓
Eksplorasi dan audit awal
↓
Standardisasi kategori
↓
Deduplikasi dan pemeriksaan domain
↓
Penanganan missing value
↓
Deteksi dan evaluasi outlier
↓
Transformasi data
↓
Integrasi data
↓
Audit akhir
↓
Data siap digunakan untuk analisis/data mining