1. Gambaran Kasus

Pada praktikum ini digunakan data pelanggan dari sebuah perusahaan e-commerce. Data yang digunakan berjumlah 100 observasi. Sebelum data digunakan untuk analisis, dilakukan beberapa tahapan preprocessing untuk memastikan data berada dalam kondisi yang lebih baik.

Tahapan preprocessing yang dilakukan meliputi pemeriksaan struktur data, pengecekan kualitas data, pembersihan data, penanganan missing value, identifikasi outlier, transformasi data, integrasi data, dan validasi akhir.

2. Pembentukan Data

Pada tahap awal dibuat data pelanggan sebanyak 100 observasi. Data dibuat secara simulasi menggunakan set.seed() agar hasil yang diperoleh tetap sama ketika kode dijalankan kembali.

set.seed(2026)

n <- 100

pelanggan_raw <- data.frame(
  customer_id = sprintf("C%03d", 1:n),
  nama = paste("Pelanggan", sprintf("%03d", 1:n)),
  usia = sample(18:60, n, replace = TRUE),
  pendapatan = round(
    rnorm(n, mean = 5000000, sd = 900000),
    -4
  ),
  kota = sample(
    c("Pekanbaru", "Dumai", "Siak", "Bengkalis"),
    n,
    replace = TRUE
  ),
  status = sample(
    c("Aktif", "Tidak Aktif"),
    n,
    replace = TRUE,
    prob = c(0.75, 0.25)
  ),
  stringsAsFactors = FALSE
)

Membuat beberapa missing value

pelanggan_raw$usia[c(8, 37, 74)] <- NA
pelanggan_raw$pendapatan[c(12, 45, 88, 96)] <- NA
pelanggan_raw$kota[c(21, 63, 91)] <- NA

Membuat penulisan kategori yang tidak seragam

pelanggan_raw$kota[c(2, 15, 34)] <-
  c(" PKU", "PEKANBARU", "pekanbaru")

pelanggan_raw$kota[c(6, 27)] <-
  c("DUMAI", "dumai")

pelanggan_raw$status[c(5, 19, 41)] <-
  c("aktif", "ACTIVE", "A")

pelanggan_raw$status[c(23, 58)] <-
  c("nonaktif", "tidak aktif")

# Membuat nilai yang tidak wajar
pelanggan_raw$usia[55] <- 150
pelanggan_raw$pendapatan[72] <- 500000000

# Membuat duplikasi customer_id
pelanggan_raw$customer_id[100] <-
  pelanggan_raw$customer_id[99]

pelanggan_raw$nama[100] <-
  pelanggan_raw$nama[99]

pelanggan_raw$usia[100] <-
  pelanggan_raw$usia[99]

pelanggan_raw$pendapatan[100] <-
  pelanggan_raw$pendapatan[99]

pelanggan_raw$kota[100] <-
  pelanggan_raw$kota[99]

pelanggan_raw$status[100] <-
  pelanggan_raw$status[99]

head(pelanggan_raw)
##   customer_id          nama usia pendapatan      kota status
## 1        C001 Pelanggan 001   46    3510000 Bengkalis  Aktif
## 2        C002 Pelanggan 002   50    5080000       PKU  Aktif
## 3        C003 Pelanggan 003   55    5580000 Bengkalis  Aktif
## 4        C004 Pelanggan 004   44    5460000 Bengkalis  Aktif
## 5        C005 Pelanggan 005   53    3260000     Dumai  aktif
## 6        C006 Pelanggan 006   22    5010000     DUMAI  Aktif

Output:

Data awal berhasil dibuat dengan jumlah 100 observasi. Pada tahap ini dataset masih sengaja dibuat memiliki beberapa permasalahan, seperti nilai kosong, penulisan kategori yang berbeda, data duplikat, serta nilai yang terlalu ekstrem. Kondisi tersebut nantinya akan diperbaiki pada tahap preprocessing.

3. Pemeriksaan Struktur Data

Sebelum melakukan pembersihan, kondisi awal data perlu diperiksa terlebih dahulu. Pemeriksaan ini bertujuan untuk mengetahui ukuran data, nama variabel, tipe data, serta gambaran nilai pada setiap variabel.

dim(pelanggan_raw)
## [1] 100   6
names(pelanggan_raw)
## [1] "customer_id" "nama"        "usia"        "pendapatan"  "kota"       
## [6] "status"
str(pelanggan_raw)
## 'data.frame':    100 obs. of  6 variables:
##  $ customer_id: chr  "C001" "C002" "C003" "C004" ...
##  $ nama       : chr  "Pelanggan 001" "Pelanggan 002" "Pelanggan 003" "Pelanggan 004" ...
##  $ usia       : num  46 50 55 44 53 22 48 NA 41 36 ...
##  $ pendapatan : num  3510000 5080000 5580000 5460000 3260000 5010000 3910000 4720000 4880000 3840000 ...
##  $ kota       : chr  "Bengkalis" " PKU" "Bengkalis" "Bengkalis" ...
##  $ status     : chr  "Aktif" "Aktif" "Aktif" "Aktif" ...
head(pelanggan_raw)
##   customer_id          nama usia pendapatan      kota status
## 1        C001 Pelanggan 001   46    3510000 Bengkalis  Aktif
## 2        C002 Pelanggan 002   50    5080000       PKU  Aktif
## 3        C003 Pelanggan 003   55    5580000 Bengkalis  Aktif
## 4        C004 Pelanggan 004   44    5460000 Bengkalis  Aktif
## 5        C005 Pelanggan 005   53    3260000     Dumai  aktif
## 6        C006 Pelanggan 006   22    5010000     DUMAI  Aktif
summary(pelanggan_raw)
##  customer_id            nama                usia          pendapatan       
##  Length:100         Length:100         Min.   : 18.00   Min.   :  3260000  
##  Class :character   Class :character   1st Qu.: 32.00   1st Qu.:  4425000  
##  Mode  :character   Mode  :character   Median : 42.00   Median :  5080000  
##                                        Mean   : 41.49   Mean   : 10116562  
##                                        3rd Qu.: 51.00   3rd Qu.:  5532500  
##                                        Max.   :150.00   Max.   :500000000  
##                                        NA's   :3        NA's   :4          
##      kota              status         
##  Length:100         Length:100        
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Output:

Hasil pemeriksaan menunjukkan jumlah baris dan kolom pada dataset serta tipe data dari setiap variabel. Dari head() dapat dilihat beberapa data awal, sedangkan summary() memberikan gambaran statistik secara umum.

Interpretasi:

Berdasarkan hasil tersebut, dataset sudah berhasil terbentuk dan dapat dilanjutkan ke tahap pemeriksaan kualitas data. Namun, masih perlu dilakukan pengecekan lebih lanjut karena pada data awal terdapat beberapa kondisi yang belum sesuai.

4. Pemeriksaan Missing Value

Setelah struktur data diketahui, langkah berikutnya adalah mencari nilai yang kosong. Pemeriksaan ini penting karena data yang kosong dapat memengaruhi hasil analisis apabila tidak ditangani dengan baik.

colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           3           4           3           0
round(
  colMeans(is.na(pelanggan_raw)) * 100,
  2
)
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           3           4           3           0

Output:

Output menunjukkan jumlah dan persentase nilai NA pada setiap variabel. Dengan hasil tersebut dapat diketahui variabel mana yang memiliki data kosong.

Interpretasi:

Dari hasil pemeriksaan terlihat bahwa terdapat beberapa nilai yang belum tersedia pada variabel tertentu. Oleh karena itu, data kosong tersebut perlu ditangani sebelum dataset digunakan untuk analisis selanjutnya.

5. Pemeriksaan Data Duplikat

Data duplikat perlu diperiksa karena satu pelanggan seharusnya mempunyai satu customer_id. Jika ID yang sama muncul lebih dari satu kali, maka jumlah pelanggan dapat menjadi tidak sesuai.

sum(duplicated(pelanggan_raw))
## [1] 1
sum(
  duplicated(
    pelanggan_raw$customer_id
  )
)
## [1] 1
pelanggan_raw[
  duplicated(
    pelanggan_raw$customer_id
  ) |
  duplicated(
    pelanggan_raw$customer_id,
    fromLast = TRUE
  ),
]
##     customer_id          nama usia pendapatan kota status
## 99         C099 Pelanggan 099   42    4550000 Siak  Aktif
## 100        C099 Pelanggan 099   42    4550000 Siak  Aktif

Output:

Hasil pemeriksaan menunjukkan adanya customer_id yang muncul lebih dari satu kali.

Interpretasi:

Data yang mempunyai ID sama perlu diperiksa dan dibersihkan. Pada tahap ini duplikasi dianggap sebagai data yang tidak diperlukan sehingga hanya satu data untuk setiap pelanggan yang akan dipertahankan.

6. Standardisasi Data Kategorik

Pada dataset terdapat beberapa kategori yang ditulis dengan format berbeda. Contohnya terdapat penggunaan huruf besar, huruf kecil, serta spasi yang tidak diperlukan. Kondisi ini dapat membuat kategori yang sebenarnya sama dibaca sebagai kategori yang berbeda.

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] "bengkalis" "dumai"     "pekanbaru" "pku"       "siak"
sort(
  unique(
    pelanggan$status
  )
)
## [1] "a"           "active"      "aktif"       "nonaktif"    "tidak aktif"

Output:

Setelah dilakukan trimws() dan tolower(), penulisan kategori menjadi lebih seragam karena spasi tambahan dan perbedaan huruf besar-kecil sudah dihilangkan.

Interpretasi:

Standardisasi ini dilakukan sebagai langkah awal sebelum kategori disamakan sepenuhnya. Dengan format teks yang sudah seragam, proses pengelompokan kategori menjadi lebih mudah.

7. Menyamakan Kategori

Kategori yang mempunyai arti sama kemudian disatukan. Tujuannya agar satu informasi tidak dianggap sebagai beberapa kategori yang berbeda.

pelanggan$kota[
  pelanggan$kota %in% c(
    "pku",
    "pekanbaru"
  )
] <- "Pekanbaru"

pelanggan$kota[
  pelanggan$kota == "dumai"
] <- "Dumai"

pelanggan$kota[
  pelanggan$kota == "siak"
] <- "Siak"

pelanggan$kota[
  pelanggan$kota == "bengkalis"
] <- "Bengkalis"

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] "Bengkalis" "Dumai"     "Pekanbaru" "Siak"
sort(
  unique(
    pelanggan$status
  )
)
## [1] "Aktif"       "Tidak Aktif"

Output:

Kategori kota dan status yang sebelumnya memiliki beberapa variasi sekarang sudah diseragamkan menjadi kategori yang lebih konsisten.

Interpretasi:

Hasil tersebut menunjukkan bahwa kategori yang memiliki makna sama sudah digabungkan. Langkah ini penting agar proses analisis kategorik nantinya tidak menghasilkan kategori yang sebenarnya merupakan informasi yang sama.

8. Menghapus Data Duplikat

Setelah data diperiksa, baris yang memiliki customer_id sama dihapus. Penghapusan dilakukan dengan mempertahankan kemunculan pertama.

pelanggan <-
  pelanggan[
    !duplicated(
      pelanggan$customer_id
    ),
  ]

rownames(pelanggan) <- NULL

dim(pelanggan)
## [1] 99  6
sum(
  duplicated(
    pelanggan$customer_id
  )
)
## [1] 0

Output:

Jumlah duplikasi setelah proses pembersihan menjadi 0.

Interpretasi:

Hal tersebut menunjukkan bahwa setiap pelanggan sekarang memiliki customer_id yang unik. Data juga sudah lebih aman digunakan karena satu pelanggan tidak dihitung lebih dari satu kali.

9. Pemeriksaan Nilai Tidak Wajar

Nilai numerik perlu diperiksa untuk memastikan tidak terdapat angka yang berada di luar batas yang masuk akal. Pada variabel usia, misalnya, nilai yang terlalu tinggi dapat menunjukkan adanya kesalahan input.

pelanggan[
  pelanggan$usia < 15 |
    pelanggan$usia > 100,
]
##      customer_id          nama usia pendapatan  kota status
## NA          <NA>          <NA>   NA         NA  <NA>   <NA>
## NA.1        <NA>          <NA>   NA         NA  <NA>   <NA>
## 55          C055 Pelanggan 055  150    5340000 Dumai  Aktif
## NA.2        <NA>          <NA>   NA         NA  <NA>   <NA>
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>
## NA.2        <NA> <NA>   NA         NA <NA>   <NA>
## NA.3        <NA> <NA>   NA         NA <NA>   <NA>

Output:

Terdapat nilai usia yang berada di luar batas yang ditentukan. Sementara itu, pemeriksaan pendapatan digunakan untuk memastikan tidak terdapat nilai pendapatan negatif.

Interpretasi:

Nilai usia yang terlalu tinggi perlu diperiksa karena kemungkinan merupakan kesalahan pengisian data. Pada dataset ini nilai tersebut akan diperbaiki agar sesuai dengan kondisi yang masuk akal.

10. Memperbaiki Nilai Tidak Wajar

Nilai usia yang tidak masuk akal diperbaiki berdasarkan data simulasi yang digunakan dalam praktikum.

pelanggan$usia[
  pelanggan$customer_id == "C055"
] <- 50

pelanggan[
  pelanggan$customer_id == "C055",
  c(
    "customer_id",
    "usia"
  )
]
##    customer_id usia
## 55        C055   50

Output:

Nilai usia pelanggan dengan ID C055 sudah berubah menjadi 50.

Interpretasi:

Setelah diperbaiki, nilai usia tersebut sudah berada pada rentang yang lebih wajar sehingga dapat digunakan pada tahap analisis berikutnya.

11. Penanganan Missing Value

Setelah proses cleaning dasar selesai, nilai kosong kembali diperiksa untuk menentukan metode penanganan yang sesuai.

colSums(
  is.na(pelanggan)
)
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           3           4           3           0
pelanggan[
  !complete.cases(pelanggan),
]
##    customer_id          nama usia pendapatan      kota      status
## 8         C008 Pelanggan 008   NA    4720000 Pekanbaru Tidak Aktif
## 12        C012 Pelanggan 012   46         NA Pekanbaru       Aktif
## 21        C021 Pelanggan 021   29    3850000      <NA>       Aktif
## 37        C037 Pelanggan 037   NA    5590000 Bengkalis       Aktif
## 45        C045 Pelanggan 045   20         NA     Dumai       Aktif
## 63        C063 Pelanggan 063   40    5200000      <NA>       Aktif
## 74        C074 Pelanggan 074   NA    5020000 Bengkalis       Aktif
## 88        C088 Pelanggan 088   59         NA Bengkalis       Aktif
## 91        C091 Pelanggan 091   51    5480000      <NA> Tidak Aktif
## 96        C096 Pelanggan 096   34         NA     Dumai Tidak Aktif

Output:

Masih terdapat beberapa nilai kosong pada variabel numerik dan kategorik.

Interpretasi:

Karena jumlah data tidak terlalu besar, nilai kosong tidak langsung dihapus. Sebagai gantinya digunakan metode imputasi agar jumlah observasi tetap dapat dipertahankan.

12. Imputasi Variabel Numerik

Untuk variabel numerik digunakan median sebagai nilai pengganti. Median dipilih karena lebih tahan terhadap pengaruh nilai ekstrem dibandingkan mean.

median_usia <-
  median(
    pelanggan$usia,
    na.rm = TRUE
  )

median_pendapatan <-
  median(
    pelanggan$pendapatan,
    na.rm = TRUE
  )

pelanggan$usia_imputasi <-
  pelanggan$usia

pelanggan$pendapatan_imputasi <-
  pelanggan$pendapatan

pelanggan$usia_imputasi[
  is.na(
    pelanggan$usia_imputasi
  )
] <- median_usia

pelanggan$pendapatan_imputasi[
  is.na(
    pelanggan$pendapatan_imputasi
  )
] <- median_pendapatan

summary(
  pelanggan[
    ,
    c(
      "usia_imputasi",
      "pendapatan_imputasi"
    )
  ]
)
##  usia_imputasi   pendapatan_imputasi
##  Min.   :18.00   Min.   :  3260000  
##  1st Qu.:32.00   1st Qu.:  4440000  
##  Median :42.00   Median :  5080000  
##  Mean   :40.49   Mean   :  9969293  
##  3rd Qu.:50.50   3rd Qu.:  5510000  
##  Max.   :60.00   Max.   :500000000

Output:

Setelah imputasi dilakukan, nilai NA pada variabel usia dan pendapatan digantikan menggunakan nilai median masing-masing variabel.

Interpretasi:

Penggunaan median membantu mempertahankan jumlah observasi tanpa terlalu dipengaruhi oleh nilai pendapatan yang ekstrem. Kolom baru juga dibuat agar nilai asli tetap dapat dibandingkan dengan nilai setelah imputasi.

13. Imputasi Variabel Kategorik

Untuk variabel kota, data yang kosong diberikan kategori khusus Tidak diketahui.

pelanggan$kota_imputasi <-
  pelanggan$kota

pelanggan$kota_imputasi[
  is.na(
    pelanggan$kota_imputasi
  )
] <- "Tidak diketahui"

table(
  pelanggan$kota_imputasi,
  useNA = "ifany"
)
## 
##       Bengkalis           Dumai       Pekanbaru            Siak Tidak diketahui 
##              21              29              26              20               3

Output:

Nilai NA pada variabel kota sudah diganti menjadi kategori Tidak diketahui.

Interpretasi:

Cara ini digunakan agar data yang tidak memiliki informasi kota tetap dipertahankan tanpa memasukkannya secara paksa ke salah satu kota yang tersedia.

14. Membuat Indikator Missing

Selain melakukan imputasi, dibuat variabel indikator untuk mencatat apakah suatu data sebelumnya memiliki nilai kosong.

pelanggan$pendapatan_missing <-
  as.integer(
    is.na(
      pelanggan$pendapatan
    )
  )

table(
  pelanggan$pendapatan_missing
)
## 
##  0  1 
## 95  4

Output:

Nilai 1 menunjukkan bahwa data pendapatan awalnya kosong, sedangkan 0 menunjukkan bahwa data tersedia.

Interpretasi:

Variabel indikator ini berguna karena informasi mengenai keberadaan missing value tetap dapat disimpan walaupun nilai aslinya sudah diimputasi.

15. Identifikasi Outlier

Outlier merupakan nilai yang letaknya jauh dari sebagian besar data. Pada variabel pendapatan, kondisi ini diperiksa menggunakan boxplot.

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

Output:

Boxplot memperlihatkan sebaran pendapatan serta beberapa titik yang berada jauh dari bagian utama data.

Interpretasi:

Titik yang berada jauh dari sebaran utama merupakan kandidat outlier. Nilai tersebut tidak langsung dihapus karena perlu dilihat apakah memang merupakan kesalahan atau merupakan kondisi yang benar-benar terjadi.

16. Menentukan Outlier dengan IQR

Untuk memperoleh batas yang lebih jelas, digunakan metode IQR. Nilai yang berada di luar batas Q1 - 1.5(IQR) dan Q3 + 1.5(IQR) dianggap sebagai 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% 
##         4440000         5510000         1070000         2835000         7115000

Output:

Output memberikan nilai Q1, Q3, IQR, batas bawah, dan batas atas.

Interpretasi:

Nilai pendapatan yang berada di luar batas tersebut akan dikategorikan sebagai kandidat outlier. Metode IQR digunakan karena cukup sederhana dan tidak terlalu dipengaruhi oleh nilai ekstrem.

17. Membuat Data Winsorized

Nilai ekstrem tidak langsung dihapus dari dataset. Sebagai alternatif, dibuat variabel baru dengan metode winsorization.

pelanggan$pendapatan_winsor <-
  pmin(
    pmax(
      pelanggan$pendapatan_imputasi,
      batas_bawah
    ),
    batas_atas
  )

summary(
  pelanggan$pendapatan_winsor
)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 3260000 4440000 5080000 4990657 5510000 7115000

Output:

Nilai pendapatan yang berada di luar batas IQR dibatasi pada batas bawah atau batas atas yang telah ditentukan.

Interpretasi:

Winsorization digunakan untuk mengurangi pengaruh nilai ekstrem tanpa langsung membuang observasi. Data pendapatan asli tetap dipertahankan sehingga hasil sebelum dan sesudah perlakuan dapat dibandingkan.

18. Transformasi Min-Max

Transformasi Min-Max digunakan untuk mengubah nilai pendapatan ke rentang 0 sampai 1. Cara ini berguna ketika skala antarvariabel terlalu berbeda.

Rumus Min-Max:

\[ x' = \frac{x-\min(x)}{\max(x)-\min(x)} \]

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

summary(
  pelanggan$pendapatan_minmax
)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.3061  0.4721  0.4489  0.5837  1.0000

Output:

Nilai pendapatan setelah transformasi berada pada rentang 0 sampai 1.

Interpretasi:

Transformasi ini membuat skala pendapatan menjadi lebih seragam sehingga dapat lebih mudah digunakan pada metode analisis yang membutuhkan skala variabel yang relatif sama.

19. Transformasi Logaritmik

Transformasi logaritmik digunakan sebagai alternatif untuk mengurangi rentang nilai pendapatan yang terlalu besar.

pelanggan$pendapatan_log <-
  log1p(
    pelanggan$pendapatan_winsor
  )

summary(
  pelanggan$pendapatan_log
)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   15.00   15.31   15.44   15.41   15.52   15.78

Output:

Variabel baru pendapatan_log memiliki skala yang lebih terkompresi dibandingkan pendapatan awal.

Interpretasi:

Transformasi log membantu mengurangi pengaruh nilai pendapatan yang sangat besar sehingga distribusi data dapat menjadi lebih mudah dianalisis.

20. Integrasi Data Transaksi

Selain data pelanggan, digunakan data transaksi yang memiliki identifier berbeda. Data transaksi dibuat sebagai sumber data kedua.

set.seed(2026)

id_transaksi <- c(
  sprintf("C%03d", 1:95),
  "C101",
  "C102",
  "C103"
)

transaksi_raw <- data.frame(
  cust_id = id_transaksi,
  jumlah_transaksi = sample(
    1:20,
    length(id_transaksi),
    replace = TRUE
  ),
  total_purchase = sample(
    seq(
      250000,
      25000000,
      by = 250000
    ),
    length(id_transaksi),
    replace = TRUE
  ),
  stringsAsFactors = FALSE
)

head(transaksi_raw)
##   cust_id jumlah_transaksi total_purchase
## 1    C001                1       20250000
## 2    C002                6       18250000
## 3    C003               13       22000000
## 4    C004               15       22250000
## 5    C005               12        3250000
## 6    C006                4       12750000

Output:

Data transaksi mempunyai kolom cust_id, jumlah transaksi, dan total pembelian. Identifier pada tabel ini menggunakan nama cust_id, sedangkan dataset pelanggan menggunakan customer_id.

Interpretasi:

Karena nama identifier berbeda, perlu dilakukan penyesuaian sebelum kedua dataset digabungkan.

21. Menyamakan Identifier

Nama identifier pada data transaksi diubah menjadi customer_id supaya sama dengan data pelanggan.

transaksi <- transaksi_raw

names(transaksi)[
  names(transaksi) == "cust_id"
] <- "customer_id"

names(transaksi)
## [1] "customer_id"      "jumlah_transaksi" "total_purchase"

Output:

Nama kolom cust_id sudah berubah menjadi customer_id.

Interpretasi:

Dengan identifier yang sama, kedua dataset sudah siap untuk digabungkan menggunakan kolom customer_id.

22. Menggabungkan Data

Penggabungan dilakukan menggunakan merge(). Data pelanggan tetap dipertahankan walaupun tidak semua pelanggan mempunyai data transaksi.

data_terintegrasi <-
  merge(
    pelanggan,
    transaksi,
    by = "customer_id",
    all.x = TRUE
  )

head(
  data_terintegrasi[
    ,
    c(
      "customer_id",
      "nama",
      "jumlah_transaksi",
      "total_purchase"
    )
  ],
  10
)
##    customer_id          nama jumlah_transaksi total_purchase
## 1         C001 Pelanggan 001                1       20250000
## 2         C002 Pelanggan 002                6       18250000
## 3         C003 Pelanggan 003               13       22000000
## 4         C004 Pelanggan 004               15       22250000
## 5         C005 Pelanggan 005               12        3250000
## 6         C006 Pelanggan 006                4       12750000
## 7         C007 Pelanggan 007               16        5250000
## 8         C008 Pelanggan 008                5        4500000
## 9         C009 Pelanggan 009               12       11000000
## 10        C010 Pelanggan 010                2        7000000

Output:

Data pelanggan sudah bergabung dengan informasi transaksi berdasarkan customer_id.

Interpretasi:

Penggunaan all.x = TRUE membuat seluruh pelanggan tetap berada dalam data hasil penggabungan. Jika seorang pelanggan tidak mempunyai transaksi, maka informasi transaksi untuk pelanggan tersebut akan bernilai NA.

23. Validasi Data Setelah Merge

Setelah dua tabel digabungkan, hasilnya perlu diperiksa kembali. Pemeriksaan ini dilakukan untuk memastikan tidak muncul duplikasi baru dan mengetahui apakah masih ada data transaksi yang kosong.

nrow(pelanggan)
## [1] 99
nrow(data_terintegrasi)
## [1] 99
sum(
  duplicated(
    data_terintegrasi$customer_id
  )
)
## [1] 0
colSums(
  is.na(
    data_terintegrasi[
      ,
      c(
        "jumlah_transaksi",
        "total_purchase"
      )
    ]
  )
)
## jumlah_transaksi   total_purchase 
##                4                4

Output:

Output memberikan jumlah observasi sebelum dan sesudah penggabungan, jumlah duplikasi customer_id, serta jumlah nilai kosong pada variabel transaksi.

Interpretasi:

Pemeriksaan ini dilakukan untuk memastikan proses integrasi tidak membuat masalah baru. Jika terdapat NA pada variabel transaksi, hal tersebut dapat menunjukkan adanya pelanggan yang tidak mempunyai pasangan data transaksi.

24. Validasi Akhir Dataset

Tahap terakhir adalah melakukan pengecekan ulang terhadap dataset yang sudah diproses. Tujuannya untuk memastikan masalah utama yang ditemukan di awal sudah ditangani.

hasil_akhir <- data.frame(
  pemeriksaan = c(
    "Jumlah observasi",
    "Duplikasi customer_id",
    "Total missing value",
    "Usia di luar batas",
    "Jumlah kandidat outlier"
  ),
  
  hasil = c(
    nrow(pelanggan),
    
    sum(
      duplicated(
        pelanggan$customer_id
      )
    ),
    
    sum(
      is.na(
        pelanggan
      )
    ),
    
    sum(
      pelanggan$usia < 15 |
        pelanggan$usia > 100,
      na.rm = TRUE
    ),
    
    sum(
      pelanggan$pendapatan_imputasi <
        batas_bawah |
      pelanggan$pendapatan_imputasi >
        batas_atas
    )
  )
)

hasil_akhir
##               pemeriksaan hasil
## 1        Jumlah observasi    99
## 2   Duplikasi customer_id     0
## 3     Total missing value    10
## 4      Usia di luar batas     0
## 5 Jumlah kandidat outlier     1

Output:

Tabel hasil akhir memberikan ringkasan kondisi dataset setelah seluruh proses preprocessing dilakukan.

Interpretasi:

Dari hasil validasi dapat dilihat kondisi data setelah melalui tahapan pembersihan. Data sudah diperiksa dari sisi duplikasi, missing value, batas nilai usia, serta outlier pada pendapatan.

25. Kesimpulan

Berdasarkan tahapan preprocessing yang telah dilakukan, data pelanggan yang pada awalnya masih memiliki beberapa permasalahan telah melalui proses pembersihan dan penyesuaian. Permasalahan yang ditemukan meliputi nilai kosong, kategori yang tidak konsisten, data duplikat, nilai yang tidak wajar, serta nilai pendapatan yang terlalu ekstrem.

Data kosong ditangani menggunakan metode imputasi, sedangkan kategori diseragamkan agar tidak terdapat beberapa label yang sebenarnya mempunyai arti yang sama. Data duplikat pada customer_id juga dihapus sehingga setiap pelanggan memiliki identifier yang unik.

Selanjutnya dilakukan pemeriksaan outlier menggunakan boxplot dan metode IQR. Nilai ekstrem tidak langsung dihapus, tetapi dibuat versi winsorized sebagai alternatif. Pendapatan juga ditransformasikan menggunakan Min-Max dan logaritmik.

Pada tahap akhir, data pelanggan digabungkan dengan data transaksi menggunakan customer_id. Setelah proses tersebut selesai, dilakukan validasi kembali untuk memastikan dataset berada dalam kondisi yang lebih rapi dan siap digunakan untuk tahap analisis atau proses data mining berikutnya.