Preprocessing_Data_Ecommerce

1. Pendahuluan

Dalam proses analisis data pelanggan pada perusahaan e-commerce, data yang berasal dari sistem operasional belum tentu berada dalam kondisi yang siap digunakan. Data dapat mengandung nilai yang hilang, perbedaan penulisan kategori, identitas pelanggan yang tercatat lebih dari satu kali, nilai yang tidak sesuai dengan aturan domain, hingga nilai ekstrem yang dapat memengaruhi hasil analisis.

Pada studi kasus ini, perusahaan memiliki dua sumber data utama, yaitu data pelanggan dan data transaksi. Kedua sumber tersebut perlu diperiksa dan dipersiapkan terlebih dahulu agar memiliki struktur dan format yang konsisten sebelum digunakan untuk menganalisis perilaku belanja pelanggan.

Proses preprocessing dilakukan secara bertahap, dimulai dari memahami struktur data dan mengevaluasi kualitas data awal, kemudian dilanjutkan dengan cleaning, penanganan missing value, pemeriksaan nilai tidak valid, deteksi outlier, transformasi variabel, pengolahan data transaksi, integrasi kedua sumber data, hingga pemeriksaan akhir.

2. Studi Kasus

Sebuah tim analitik pada perusahaan e-commerce sedang menyiapkan data pelanggan untuk digunakan dalam analisis perilaku belanja. Data pelanggan berasal dari sistem administrasi perusahaan, sedangkan data transaksi diperoleh dari sistem penjualan.

Data pelanggan memuat informasi mengenai identitas pelanggan, usia, pendapatan, kota domisili, status pelanggan, metode pembayaran, serta lama menjadi anggota. Sementara itu, data transaksi mencatat informasi mengenai ID transaksi, ID pelanggan, jumlah transaksi, dan total nilai pembelian.

Dalam proses pemeriksaan awal ditemukan beberapa persoalan kualitas data. Permasalahan tersebut meliputi nilai yang hilang, penulisan kategori yang tidak seragam, duplikasi identitas pelanggan, nilai yang tidak sesuai dengan aturan domain, nilai ekstrem pada variabel tertentu, serta perbedaan nama identifier antara data pelanggan dan data transaksi.

Oleh karena itu, preprocessing dilakukan untuk menghasilkan data yang lebih konsisten, terstruktur, dan dapat digunakan sebagai dasar analisis perilaku belanja pelanggan.

Catatan penting: setiap perubahan terhadap data harus didasarkan pada aturan yang jelas. Nilai yang terlihat tidak biasa tidak selalu berarti salah, sehingga perlu diperiksa berdasarkan konteks dan aturan domain sebelum dilakukan perubahan atau penanganan.

Persiapan RStudio

Tahap ini dilakukan untuk menyiapkan lingkungan kerja R yang akan digunakan dalam seluruh proses preprocessing. Package yang diperlukan dimuat terlebih dahulu agar proses pemeriksaan, pembersihan, transformasi, dan pengolahan data dapat dilakukan dengan fungsi yang sesuai.

getwd()
## [1] "D:/ham"
R.version.string
## [1] "R version 4.4.1 (2024-06-14 ucrt)"

Membangun Dataset Studi Kasus

Karena proses preprocessing dilakukan dalam sebuah studi kasus e-commerce, data perlu disiapkan terlebih dahulu agar dapat menggambarkan kondisi yang menyerupai permasalahan kualitas data pada sistem perusahaan.

Data yang dibentuk mencakup informasi pelanggan dan transaksi sehingga setiap tahap preprocessing dapat menunjukkan bagaimana permasalahan kualitas data ditemukan dan ditangani.

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

Bagian I — Profil dan Kualitas Awal Data

Dataset pelanggan digunakan sebagai sumber utama informasi mengenai karakteristik setiap pelanggan. Variabel yang digunakan mencakup identitas pelanggan, informasi demografis, pendapatan, status, metode pembayaran, serta lama menjadi anggota.

Setelah dataset pelanggan dibentuk, struktur data diperiksa untuk mengetahui jenis variabel, jumlah observasi, serta susunan kolom yang tersedia. Pemeriksaan ini penting sebagai langkah awal untuk memahami bentuk 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  
##                                       
##                                       
##                                       
## 

Sebelum melakukan proses cleaning, kondisi awal dataset perlu diperiksa dan didokumentasikan. Pemeriksaan ini bertujuan untuk mengetahui permasalahan yang terdapat pada data sehingga proses preprocessing dapat dilakukan secara terarah.

Tahap ini juga menjadi dasar untuk membandingkan kondisi data sebelum dan setelah preprocessing.

# Banyak missing value per atribut
colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0
# Persentase missing value per atribut
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

Pemeriksaan missing value dilakukan untuk mengetahui apakah terdapat informasi pelanggan yang tidak tersedia pada variabel tertentu. Nilai yang hilang perlu diidentifikasi karena dapat memengaruhi proses analisis apabila dibiarkan tanpa penanganan.

# Banyak baris duplikat penuh
sum(duplicated(pelanggan_raw))
## [1] 1
# Banyak customer_id yang berulang
sum(duplicated(pelanggan_raw$customer_id))
## [1] 1

Pemeriksaan duplikasi dilakukan untuk memastikan bahwa satu identitas pelanggan tidak tercatat lebih dari satu kali. Duplikasi identitas dapat menyebabkan jumlah pelanggan terlihat lebih besar dari kondisi sebenarnya dan berpotensi menghasilkan kesalahan ketika data digabungkan dengan transaksi.

# Kategori yang tercatat pada atribut kota dan status
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"
# Rentang atribut numerik
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_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

Pemeriksaan kategori dilakukan untuk menemukan penulisan kategori yang tidak seragam. Kategori yang sebenarnya memiliki arti sama dapat tercatat menggunakan variasi penulisan yang berbeda sehingga perlu diidentifikasi sebelum proses analisis dilakukan.

Bagian II — Data Cleaning

Setelah permasalahan kualitas data awal diketahui, tahap berikutnya adalah melakukan data cleaning. Tujuan tahap ini adalah memperbaiki ketidakkonsistenan pada data tanpa menghilangkan informasi yang masih relevan.

pelanggan <- pelanggan_raw

Salinan data dibuat agar proses cleaning tidak langsung mengubah dataset mentah. Dengan demikian, data awal tetap dapat digunakan sebagai referensi apabila diperlukan perbandingan antara kondisi sebelum dan sesudah preprocessing.

# Menghapus spasi di awal dan akhir
pelanggan$kota <- trimws(pelanggan$kota)
pelanggan$status <- trimws(pelanggan$status)

# Menyeragamkan huruf menjadi kecil untuk proses pencocokan
pelanggan$kota <- tolower(pelanggan$kota)
pelanggan$status <- tolower(pelanggan$status)

sort(unique(pelanggan$kota))
## [1] "dumai"     "pekanbaru" "pku"       "siak"

Penulisan nama kota diseragamkan agar variasi format penulisan yang merujuk pada lokasi yang sama tidak dianggap sebagai kategori yang berbeda. Dengan standardisasi ini, distribusi pelanggan berdasarkan wilayah dapat dianalisis dengan lebih tepat.

sort(unique(pelanggan$status))
## [1] "a"           "active"      "aktif"       "nonaktif"    "tidak aktif"

Kategori status pelanggan diperiksa dan diseragamkan sehingga setiap status memiliki satu bentuk penulisan yang konsisten. Hal ini penting agar pelanggan dengan status yang sama tidak terpisah menjadi beberapa kelompok akibat perbedaan penulisan.

# Standardisasi kota
pelanggan$kota[pelanggan$kota %in% c("pku", "pekanbaru")] <- "Pekanbaru"
pelanggan$kota[pelanggan$kota == "dumai"] <- "Dumai"
pelanggan$kota[pelanggan$kota == "siak"] <- "Siak"

# Standardisasi status
pelanggan$status[pelanggan$status %in% c("aktif", "active", "a")] <- "Aktif"
pelanggan$status[pelanggan$status %in% c("tidak aktif", "nonaktif")] <- "Tidak Aktif"

sort(unique(pelanggan$kota))
## [1] "Dumai"     "Pekanbaru" "Siak"
sort(unique(pelanggan$status))
## [1] "Aktif"       "Tidak Aktif"
# Menampilkan seluruh baris dengan customer_id yang berulang
pelanggan[duplicated(pelanggan$customer_id) |
          duplicated(pelanggan$customer_id, fromLast = TRUE), ]
##    customer_id nama usia pendapatan  kota      status
## 10        C010 Joko   35    5300000 Dumai Tidak Aktif
## 11        C010 Joko   35    5300000 Dumai Tidak Aktif
# Mempertahankan kemunculan pertama untuk customer_id yang identik
pelanggan <- pelanggan[!duplicated(pelanggan$customer_id), ]
rownames(pelanggan) <- NULL

dim(pelanggan)
## [1] 11  6
# Kandidat usia tidak valid berdasarkan aturan bisnis 15–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>
# Kandidat pendapatan tidak valid jika bernilai 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>
pelanggan$usia[pelanggan$customer_id == "C004"] <- 50
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

Bagian III — Penanganan Missing Values

Setelah missing value berhasil diidentifikasi, diperlukan strategi penanganan yang sesuai dengan karakteristik variabel. Tujuannya adalah mengurangi kehilangan informasi tanpa memasukkan nilai secara sembarangan.

colSums(is.na(pelanggan))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0
# Baris yang memiliki sedikitnya satu missing value
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
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
mean_pendapatan <- mean(pelanggan$pendapatan, na.rm = TRUE)
median_pendapatan <- median(pelanggan$pendapatan, na.rm = TRUE)

mean_pendapatan
## [1] 59900000
median_pendapatan
## [1] 4900000
pelanggan$pendapatan_imputasi <- pelanggan$pendapatan
pelanggan$pendapatan_imputasi[is.na(pelanggan$pendapatan_imputasi)] <-
  median_pendapatan

# Imputasi usia dengan median
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 usia yang hilang ditangani menggunakan nilai median usia. Median digunakan karena relatif tidak mudah dipengaruhi oleh nilai ekstrem dan dapat mewakili nilai tengah populasi pelanggan.

Selain nilai hasil imputasi, status missing value tetap dicatat agar proses perubahan terhadap data dapat ditelusuri.

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 variabel kategorik seperti kota, nilai yang kosong ditangani menggunakan kategori yang paling sering muncul. Pendekatan ini digunakan agar data pelanggan tetap memiliki kategori yang dapat digunakan pada analisis tanpa membuat kategori baru yang tidak memiliki dasar pada data.

pelanggan$pendapatan_missing <- as.integer(is.na(pelanggan$pendapatan))
table(pelanggan$pendapatan_missing)
## 
## 0 1 
## 9 2

Pendapatan yang memiliki nilai hilang juga ditangani menggunakan median pendapatan. Pendekatan ini digunakan untuk mempertahankan jumlah observasi pelanggan sekaligus mengurangi pengaruh nilai ekstrem terhadap nilai pengganti.

Kolom penanda missing value tetap dipertahankan sehingga dapat diketahui observasi mana yang sebelumnya memiliki data pendapatan yang kosong.

par(mfrow = c(1, 2))
hist(pelanggan$pendapatan,
     main = "Sebelum Imputasi",
     xlab = "Pendapatan", col = "skyblue", breaks = 8)
hist(pelanggan$pendapatan_imputasi,
     main = "Sesudah Imputasi Median",
     xlab = "Pendapatan", col = "lightgreen", breaks = 8)

par(mfrow = c(1, 1))

Setelah proses imputasi selesai, dataset diperiksa kembali untuk memastikan bahwa nilai yang hilang telah ditangani sesuai dengan strategi yang ditetapkan.

Pemeriksaan ulang ini diperlukan agar proses preprocessing tidak hanya berhenti setelah pengisian nilai, tetapi juga memastikan kondisi akhir dataset sesuai dengan yang diharapkan.

Bagian IV — Penanganan Outlier

Outlier merupakan observasi yang memiliki nilai jauh berbeda dibandingkan sebagian besar data. Dalam studi kasus ini, perhatian diberikan terutama pada variabel pendapatan karena terdapat kemungkinan pelanggan dengan pendapatan yang jauh lebih tinggi dibandingkan pelanggan lainnya.

Deteksi outlier dilakukan sebagai proses identifikasi, bukan sebagai alasan untuk langsung menghapus observasi. Nilai ekstrem tetap perlu dilihat dalam konteks agar tidak menghilangkan informasi pelanggan yang sebenarnya valid.

boxplot(pelanggan$pendapatan_imputasi,
        horizontal = TRUE,
        col = "lightblue",
        main = "Boxplot Pendapatan",
        xlab = "Pendapatan")

Boxplot digunakan untuk memperoleh gambaran awal mengenai distribusi pendapatan pelanggan dan melihat apakah terdapat observasi yang berada jauh di luar pola umum data. Visualisasi ini membantu mengidentifikasi kemungkinan adanya nilai ekstrem.

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

Batas outlier dihitung menggunakan metode Interquartile Range (IQR). Nilai kuartil pertama dan kuartil ketiga digunakan untuk menentukan rentang data utama, kemudian diperoleh batas bawah dan batas atas untuk mengidentifikasi observasi yang berada di luar rentang tersebut.

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

Observasi yang berada di luar batas IQR diberi penanda sebagai outlier. Penandaan dilakukan agar informasi mengenai nilai ekstrem tetap tersedia sehingga dapat dipertimbangkan dalam analisis berikutnya, bukan langsung dihapus dari dataset.

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

Selain mempertahankan nilai asli dan penanda outlier, dibuat pula versi data yang telah melalui proses winsorization. Pendekatan ini membatasi nilai yang terlalu rendah atau terlalu tinggi pada batas yang telah ditentukan.

Versi winsorized digunakan sebagai alternatif ketika analisis membutuhkan data yang lebih terkendali terhadap pengaruh nilai ekstrem, sementara nilai asli tetap dipertahankan untuk keperluan audit dan perbandingan.

Bagian V — Transformasi Data

Transformasi variabel numerik dilakukan untuk menghasilkan bentuk data yang lebih sesuai dengan kebutuhan analisis. Transformasi tidak mengubah makna dasar variabel, tetapi membantu menyetarakan skala atau bentuk representasi variabel numerik.

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

Min-Max Scaling digunakan untuk mengubah nilai pendapatan ke dalam rentang skala yang seragam. Transformasi ini bermanfaat ketika beberapa variabel numerik memiliki rentang nilai yang berbeda jauh.

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 digunakan untuk menunjukkan posisi suatu nilai terhadap rata-rata dalam satuan simpangan baku. Hasil transformasi ini dapat membantu membandingkan nilai numerik pada skala yang lebih terstandar.

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
pelanggan$pendapatan_winsor_minmax <- minmax(pelanggan$pendapatan_winsor)

plot(pelanggan$pendapatan_minmax,
     pelanggan$pendapatan_winsor_minmax,
     pch = 19, col = "navy",
     xlab = "Min–maks Data Asli",
     ylab = "Min–maks Data Winsorized",
     main = "Dampak Penanganan Outlier terhadap Normalisasi")
abline(0, 1, col = "red", lty = 2)

Bagian VI — Integrasi Data

Setelah data pelanggan dan data transaksi memiliki identifier yang sama, kedua sumber data digabungkan menggunakan identitas pelanggan sebagai kunci.

Hasil integrasi memberikan satu dataset yang menggabungkan informasi karakteristik pelanggan dengan ringkasan perilaku transaksinya. Dengan demikian, setiap pelanggan dapat dianalisis berdasarkan karakteristik pribadi sekaligus aktivitas belanjanya.

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"
transaksi <- transaksi_raw
names(transaksi)[names(transaksi) == "cust_id"] <- "customer_id"
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
# Apakah jumlah baris berubah secara tidak wajar?
c(sebelum = nrow(pelanggan), sesudah = nrow(data_terintegrasi))
## sebelum sesudah 
##      11      11
# Apakah customer_id tetap unik?
sum(duplicated(data_terintegrasi$customer_id))
## [1] 0
# Missing value baru akibat tidak ditemukan pasangan transaksi
colSums(is.na(data_terintegrasi[, c("jumlah_transaksi", "total_purchase")]))
## jumlah_transaksi   total_purchase 
##                1                1
# Pelanggan tanpa pasangan transaksi
data_terintegrasi[is.na(data_terintegrasi$jumlah_transaksi),
                  c("customer_id", "nama")]
##    customer_id nama
## 11        C011 Kiki
# Contoh jika telah dikonfirmasi bahwa NA berarti belum pernah bertransaksi
data_terintegrasi$jumlah_transaksi_final <- data_terintegrasi$jumlah_transaksi
data_terintegrasi$total_purchase_final <- data_terintegrasi$total_purchase

data_terintegrasi$jumlah_transaksi_final[
  is.na(data_terintegrasi$jumlah_transaksi_final)
] <- 0

data_terintegrasi$total_purchase_final[
  is.na(data_terintegrasi$total_purchase_final)
] <- 0

Tidak seluruh pelanggan harus memiliki riwayat transaksi. Pelanggan yang tidak memiliki transaksi tetap dipertahankan dalam dataset utama karena keberadaan mereka tetap relevan dalam analisis populasi pelanggan.

Untuk pelanggan tanpa transaksi, variabel jumlah transaksi dan nilai pembelian diisi dengan nilai nol agar kondisi tersebut dapat dibedakan dari missing value yang menunjukkan data tidak tersedia.

Dataset Akhir dan Evaluasi

Pada tahap ini dipilih variabel-variabel yang diperlukan untuk membentuk dataset final. Dataset akhir mencakup informasi identitas pelanggan, karakteristik pelanggan, variabel hasil imputasi dan transformasi, penanda outlier, serta ringkasan aktivitas transaksi.

Dataset ini menjadi hasil utama dari seluruh proses preprocessing dan akan digunakan sebagai dasar untuk analisis perilaku belanja pelanggan.

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
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

Sebelum dataset digunakan untuk analisis, dilakukan audit akhir untuk memastikan bahwa hasil preprocessing telah memenuhi tujuan yang ditetapkan.

sum(duplicated(data_final$customer_id))
## [1] 0

Pemeriksaan duplikasi dilakukan kembali pada identifier pelanggan untuk memastikan bahwa proses cleaning dan integrasi tidak menghasilkan identitas pelanggan yang tercatat lebih dari satu kali.

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

Missing value diperiksa kembali pada seluruh variabel dataset final. Pemeriksaan ini bertujuan memastikan bahwa seluruh nilai yang memang harus ditangani telah diproses sesuai dengan strategi preprocessing yang ditetapkan.

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

Kondisi dataset sebelum dan sesudah preprocessing dibandingkan menggunakan beberapa indikator, seperti jumlah baris, jumlah kolom, jumlah missing value, dan jumlah duplikasi identifier.

Perbandingan ini digunakan untuk melihat secara langsung perubahan kualitas data setelah seluruh tahapan preprocessing dilakukan.

write.csv(data_final,
          "data_pelanggan_setelah_preprocessing.csv",
          row.names = FALSE)

write.csv(log_perubahan,
          "log_perubahan_preprocessing.csv",
          row.names = FALSE)

Setelah seluruh proses preprocessing dan audit selesai, dataset final disimpan agar dapat digunakan kembali pada tahap analisis berikutnya.

Selain dataset final, hasil audit juga disimpan sebagai dokumentasi sehingga perubahan dan kondisi akhir data dapat ditelusuri.

Kesimpulan

Berdasarkan seluruh tahapan yang dilakukan, data pelanggan dan transaksi pada perusahaan e-commerce belum dapat langsung digunakan untuk analisis karena masih terdapat berbagai persoalan kualitas data. Permasalahan tersebut mencakup missing value, ketidakkonsistenan kategori, duplikasi identitas pelanggan, nilai yang tidak sesuai dengan aturan domain, nilai ekstrem, serta perbedaan identifier antar sumber data.

Preprocessing dilakukan secara bertahap untuk memperbaiki kualitas data dengan tetap mempertahankan informasi yang masih relevan. Missing value ditangani berdasarkan jenis variabel, kategori diseragamkan, duplikasi identitas diperbaiki, nilai yang tidak valid diperiksa berdasarkan aturan domain, dan nilai ekstrem diberi penanda serta dibandingkan dengan versi winsorized.

Data transaksi kemudian dibersihkan dan diagregasi berdasarkan pelanggan sebelum digabungkan dengan data pelanggan. Hasil akhirnya adalah dataset yang memiliki struktur lebih konsisten dan dapat digunakan sebagai dasar untuk analisis perilaku belanja pelanggan.

Refleksi

Dari proses preprocessing dapat dipahami bahwa kualitas data tidak hanya ditentukan oleh ada atau tidaknya missing value. Data yang lengkap tetap dapat bermasalah apabila memiliki kategori yang tidak konsisten, identitas yang terduplikasi, nilai yang tidak sesuai dengan domain, atau nilai ekstrem yang belum diperiksa.

Dalam konteks e-commerce, konsistensi identitas pelanggan sangat penting karena informasi pelanggan dan transaksi harus dapat dihubungkan dengan benar. Selain itu, nilai ekstrem perlu ditinjau berdasarkan konteks sehingga observasi yang sebenarnya valid tidak dihilangkan hanya karena memiliki nilai yang besar.

Setiap perubahan yang dilakukan terhadap data juga perlu dapat ditelusuri sehingga proses preprocessing dapat dipertanggungjawabkan dan hasil analisis berikutnya memiliki dasar data yang jelas.

Ringkasan

Secara keseluruhan, proses preprocessing pada studi kasus ini dimulai dari pemeriksaan kualitas data pelanggan dan transaksi, kemudian dilanjutkan dengan cleaning, penanganan nilai tidak valid dan missing value, deteksi serta penanganan outlier, transformasi variabel, pengolahan dan agregasi transaksi, penyamaan identifier, integrasi kedua sumber data, audit akhir, hingga pembentukan dataset yang siap digunakan untuk analisis.

Alur tersebut dapat diringkas menjadi:

Data pelanggan dan transaksi → pemeriksaan kualitas → data cleaning → penanganan nilai tidak valid → penanganan missing value → deteksi outlier → transformasi → pengolahan dan agregasi transaksi → penyamaan identifier → integrasi data → audit akhir → dataset siap analisis.

Preprocessing bukan hanya proses menjalankan fungsi pada R, tetapi juga merupakan proses pengambilan keputusan mengenai bagaimana setiap permasalahan kualitas data ditangani berdasarkan karakteristik dan tujuan analisis.

Daftar Pustaka

Han, J., Kamber, M., & Pei, J. (2012). Data Mining: Concepts and Techniques (3rd ed.). Morgan Kaufmann.