Pendahuluan

Data Preprocessing

Data yang diperoleh dari suatu sumber biasanya belum tentu langsung siap digunakan untuk analisis. Data dapat mengandung berbagai permasalahan, seperti nilai yang hilang, penulisan kategori yang tidak konsisten, data yang tercatat lebih dari satu kali, nilai yang terlalu ekstrem, serta perbedaan format antar sumber data.

Data preprocessing merupakan tahap untuk menyiapkan data sebelum digunakan dalam proses analisis atau pemodelan. Pada tahap ini, data diperiksa terlebih dahulu untuk mengetahui kondisinya. Setelah itu, masalah yang ditemukan ditangani berdasarkan aturan dan tujuan analisis.

Preprocessing penting dilakukan karena kualitas data dapat memengaruhi hasil analisis. Data yang belum diperiksa dengan baik dapat menghasilkan analisis yang kurang tepat.

Konteks dan Permasalahan Data

Sebuah perusahaan e-commerce ingin melakukan analisis terhadap pelanggannya. Untuk keperluan tersebut, perusahaan mempunyai data yang berasal dari dua sumber.

Sumber pertama adalah data pelanggan yang memuat informasi mengenai identitas pelanggan, nama, usia, kota, pendapatan, dan status pelanggan.

Sumber kedua adalah data transaksi yang memuat informasi mengenai jumlah transaksi dan total pembelian.

Kedua data tersebut belum dapat langsung digunakan untuk analisis karena masih terdapat beberapa permasalahan. Permasalahan tersebut meliputi nilai yang hilang, penulisan kategori yang tidak konsisten, data duplikat, nilai ekstrem, serta perbedaan nama identifier antara kedua sumber data.

Pada praktikum ini, data pelanggan dan data transaksi akan diperiksa dan diproses secara bertahap. Tujuannya adalah menghasilkan satu dataset yang lebih terstruktur, konsisten, dan siap digunakan untuk analisis selanjutnya.

Membangun Dataset

Pada praktikum ini digunakan dua dataset, yaitu data pelanggan dan data transaksi.

Dataset dibuat dengan beberapa kondisi yang sengaja dimasukkan sebagai contoh masalah kualitas data. Dengan demikian, proses preprocessing dapat dilakukan secara bertahap.

Data Pelanggan

Data dibuat menggunakan data.frame().

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    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
## 7         C007 Gilang   31  500000000 Pekanbaru        Aktif
## 8         C008   Hana   29    4700000       Siak       AKTIF
## 9         C009  Indra   22    4600000        PKU           A
## 10        C010   Joko   35    5300000      Dumai Tidak aktif
## 11        C010   Joko   35    5300000      Dumai Tidak aktif
## 12        C011   Kiki   28         NA       <NA>       Aktif

Data Transaksi

Data transaksi terdiri dari:

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        1500000
## 2     C002                3         900000
## 3     C003                7        2700000
## 4     C004                2         600000
## 5     C005                6        2100000
## 6     C006                4        1300000
## 7     C007               20       25000000
## 8     C008                5        1700000
## 9     C009                3         800000
## 10    C010                8        3200000
## 11    C012                1         250000

Konsep Data Preprocessing

Memahami Struktur Data

Sebelum melakukan perubahan terhadap data, kondisi awal dataset perlu diperiksa terlebih dahulu.

Pemeriksaan awal dilakukan untuk mengetahui jumlah data, nama atribut, tipe data, nilai yang hilang, duplikasi, kategori, dan rentang nilai.

Memeriksa Dimensi Data

Fungsi dim() digunakan untuk mengetahui jumlah baris dan kolom.

dim(pelanggan_raw)
## [1] 12  6

Memeriksa Nama Atribut

Fungsi names() digunakan untuk melihat nama atribut yang terdapat pada dataset.

names(pelanggan_raw)
## [1] "customer_id" "nama"        "usia"        "pendapatan"  "kota"       
## [6] "status"

Memeriksa Struktur Data

Fungsi str() digunakan untuk mengetahui struktur dataset dan tipe data setiap atribut.

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  4500000 NA 5200000 4800000 4900000 5100000 500000000 4700000 4600000 5300000 ...
##  $ kota       : chr  "Pekanbaru" " PKU" "PEKANBARU" "Dumai" ...
##  $ status     : chr  "Aktif" "aktif" "ACTIVE" "A" ...

Melihat Data Awal

Fungsi head() digunakan untuk melihat beberapa baris pertama dari dataset.

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

Melihat Ringkasan Data

Fungsi summary() digunakan untuk mendapatkan ringkasan dari setiap atribut.

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

Mengukur Kualitas Awal

Setelah struktur data diketahui, selanjutnya dilakukan pemeriksaan terhadap beberapa masalah kualitas data.

Memeriksa Missing Value

Jumlah nilai yang hilang pada setiap atribut dapat diperiksa menggunakan:

colSums(is.na(pelanggan_raw))
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0

Persentase missing value dapat dihitung menggunakan:

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

Memeriksa Duplikasi

Jumlah baris yang terduplikasi dapat diperiksa dengan:

sum(duplicated(pelanggan_raw))
## [1] 1

Karena customer_id digunakan sebagai identitas pelanggan, duplikasi ID juga perlu diperiksa.

sum(duplicated(pelanggan_raw$customer_id))
## [1] 1

Memeriksa Kategori Kota

Kategori yang terdapat pada atribut kota dapat dilihat menggunakan:

sort(unique(pelanggan_raw$kota))
## [1] " PKU"       "Dumai"      "DUMAI"      "pekanbaru"  "Pekanbaru" 
## [6] "PEKANBARU"  "Pekanbaru " "PKU"        "Siak"

Memeriksa Kategori Status

Kategori yang terdapat pada atribut status dapat dilihat menggunakan:

sort(unique(pelanggan_raw$status))
## [1] "A"           "ACTIVE"      "aktif"       "Aktif"       "AKTIF"      
## [6] "nonaktif"    "Tidak aktif" "Tidak Aktif"

Memeriksa Rentang Usia

Rentang usia diperiksa menggunakan:

range(
  pelanggan_raw$usia,
  na.rm = TRUE
)
## [1]  21 150

Memeriksa Rentang Pendapatan

Rentang pendapatan diperiksa menggunakan:

range(
  pelanggan_raw$pendapatan,
  na.rm = TRUE
)
## [1]   4500000 500000000

Membuat Fungsi Ringkasan Kualitas

Agar pemeriksaan kondisi data lebih mudah dilakukan, dibuat fungsi audit_data().

Fungsi ini digunakan untuk menampilkan:

  • nama atribut;
  • tipe data;
  • jumlah missing value;
  • persentase missing value; dan
  • jumlah nilai unik.
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

Hasil audit digunakan sebagai dasar untuk menentukan bagian data yang perlu diperiksa lebih lanjut.

Implementasi Data Cleaning

Setelah kondisi awal data diketahui, tahap berikutnya adalah membersihkan data.

Pada tahap ini dilakukan pembersihan spasi, penyeragaman penulisan kategori, pemeriksaan duplikasi, dan pemeriksaan aturan domain.

Membuat Salinan Kerja

Data asli tetap dipertahankan. Untuk proses cleaning digunakan salinan data.

pelanggan <- pelanggan_raw

Membersihkan Spasi

Spasi yang tidak diperlukan dapat menyebabkan kategori yang sebenarnya sama terbaca sebagai kategori berbeda.

Untuk menghapus spasi di awal dan akhir teks digunakan trimws().

pelanggan$kota <- trimws(
  pelanggan$kota
)

pelanggan$status <- trimws(
  pelanggan$status
)

Menyeragamkan Huruf

Untuk mempermudah standardisasi kategori, seluruh teks diubah menjadi huruf kecil.

pelanggan$kota <- tolower(
  pelanggan$kota
)

pelanggan$status <- tolower(
  pelanggan$status
)

Standardisasi Kategori Kota

Periksa kembali kategori kota.

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

Beberapa penulisan kota sebenarnya menunjukkan kota yang sama. Kategori tersebut kemudian diseragamkan.

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

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

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

Periksa kembali hasilnya.

sort(unique(pelanggan$kota))
## [1] "Dumai"     "Pekanbaru" "Siak"

Standardisasi Kategori Status

Kategori status juga perlu diseragamkan.

pelanggan$status[
  pelanggan$status %in%
    c("aktif", "active", "a")
] <- "Aktif"

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

Periksa hasil standardisasi.

sort(unique(pelanggan$status))
## [1] "Aktif"       "Tidak Aktif"

Mendeteksi Duplikasi

Data yang memiliki customer_id berulang dapat ditampilkan dengan:

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

Pada data terdapat C010 yang tercatat lebih dari satu kali.

Jika satu customer_id dianggap hanya mewakili satu pelanggan, maka salah satu data perlu dihapus.

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

rownames(pelanggan) <- NULL

Periksa kembali jumlah duplikasi.

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

Memeriksa Aturan Domain

Selain konsistensi penulisan, nilai numerik juga perlu diperiksa berdasarkan aturan yang masuk akal.

Misalnya, usia diperiksa menggunakan rentang 15 sampai 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>

Pada data ditemukan nilai usia 150. Nilai tersebut dianggap sebagai kesalahan input.

Setelah dilakukan pengecekan terhadap sumber data, nilai tersebut dikoreksi menjadi 50.

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

Membuat Log Perubahan

Setiap perubahan yang dilakukan pada data sebaiknya dicatat.

log_perubahan <- data.frame(
  
  tahap = c(
    "Standardisasi",
    "Standardisasi",
    "Deduplikasi",
    "Koreksi domain"
  ),
  
  atribut = c(
    "kota",
    "status",
    "customer_id",
    "usia"
  ),
  
  tindakan = c(
    "Menyeragamkan penulisan kota",
    "Menyeragamkan kategori status",
    "Menghapus duplikasi customer_id",
    "Mengoreksi usia C004"
  ),
  
  stringsAsFactors = FALSE
)

log_perubahan
##            tahap     atribut                        tindakan
## 1  Standardisasi        kota    Menyeragamkan penulisan kota
## 2  Standardisasi      status   Menyeragamkan kategori status
## 3    Deduplikasi customer_id Menghapus duplikasi customer_id
## 4 Koreksi domain        usia            Mengoreksi usia C004

Pencatatan perubahan membuat proses preprocessing lebih mudah ditelusuri karena setiap tindakan mempunyai catatan.

Penanganan Missing Values

Missing value merupakan kondisi ketika suatu atribut tidak memiliki nilai.

Nilai yang hilang tidak selalu harus dihapus. Penanganannya perlu disesuaikan dengan jumlah missing dan karakteristik atribut.

Mengidentifikasi Lokasi Nilai Hilang

Jumlah missing pada setiap atribut diperiksa menggunakan:

colSums(
  is.na(pelanggan)
)
## customer_id        nama        usia  pendapatan        kota      status 
##           0           0           1           2           1           0

Untuk melihat baris yang mempunyai setidaknya satu nilai hilang:

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

Strategi 1 — Menghapus Baris

Salah satu strategi adalah menghapus baris yang tidak lengkap.

pelanggan_complete <-
  pelanggan[
    complete.cases(pelanggan),
  ]

Bandingkan jumlah data sebelum dan sesudah penghapusan.

nrow(pelanggan)
## [1] 11
nrow(pelanggan_complete)
## [1] 8

Persentase data yang hilang akibat penghapusan dapat dihitung menggunakan:

round(
  (
    1 -
      nrow(pelanggan_complete) /
      nrow(pelanggan)
  ) * 100,
  2
)
## [1] 27.27

Strategi 2 — Imputasi Mean dan Median

Untuk atribut pendapatan, terlebih dahulu dibandingkan nilai mean dan median.

mean_pendapatan <-
  mean(
    pelanggan$pendapatan,
    na.rm = TRUE
  )

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

mean_pendapatan
## [1] 59900000
median_pendapatan
## [1] 4900000

Karena terdapat nilai pendapatan yang cukup ekstrem, median digunakan sebagai nilai imputasi.

Imputasi Pendapatan

pelanggan$pendapatan_imputasi <-
  pelanggan$pendapatan

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

Imputasi Usia

Untuk atribut usia juga digunakan median.

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

pelanggan$usia_imputasi <-
  pelanggan$usia

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

Bandingkan data sebelum dan sesudah imputasi.

pelanggan[
  ,
  c(
    "customer_id",
    "usia",
    "usia_imputasi",
    "pendapatan",
    "pendapatan_imputasi"
  )
]
##    customer_id usia usia_imputasi pendapatan pendapatan_imputasi
## 1         C001   21          21.0    4500000             4500000
## 2         C002   25          25.0         NA             4900000
## 3         C003   23          23.0    5200000             5200000
## 4         C004   50          50.0    4800000             4800000
## 5         C005   27          27.0    4900000             4900000
## 6         C006   NA          27.5    5100000             5100000
## 7         C007   31          31.0  500000000           500000000
## 8         C008   29          29.0    4700000             4700000
## 9         C009   22          22.0    4600000             4600000
## 10        C010   35          35.0    5300000             5300000
## 11        C011   28          28.0         NA             4900000

Strategi 3 — Imputasi Nilai Kategorik

Untuk kategori kota, nilai yang hilang diberi kategori Tidak diketahui.

pelanggan$kota_imputasi <-
  pelanggan$kota

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

Periksa hasilnya.

table(
  pelanggan$kota_imputasi,
  useNA = "ifany"
)
## 
##           Dumai       Pekanbaru            Siak Tidak diketahui 
##               3               6               1               1

Menambahkan Indikator Missing

Indikator missing digunakan untuk mempertahankan informasi bahwa suatu nilai awalnya merupakan nilai yang hilang.

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

table(
  pelanggan$pendapatan_missing
)
## 
## 0 1 
## 9 2

Membandingkan Distribusi Sebelum dan Sesudah Imputasi

Distribusi pendapatan sebelum dan sesudah imputasi dapat dibandingkan menggunakan histogram.

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

Imputasi membuat data menjadi lebih lengkap, tetapi hasil imputasi tetap perlu diperhatikan karena dapat memengaruhi distribusi dan variasi data.

Penanganan Outlier

Outlier adalah nilai yang berada jauh dari sebagian besar pengamatan.

Nilai ekstrem tidak langsung dianggap sebagai kesalahan. Nilai tersebut perlu diperiksa terlebih dahulu untuk mengetahui apakah merupakan kesalahan pencatatan atau memang merupakan kondisi yang sebenarnya.

Visualisasi dengan Boxplot

Boxplot digunakan untuk melihat kemungkinan adanya nilai ekstrem pada data pendapatan.

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

Menghitung Batas IQR

Metode IQR digunakan 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         5150000          400000         4150000         5750000

Menandai Kandidat Outlier

Data yang berada di luar batas IQR ditandai sebagai kandidat outlier.

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           500000000

Mengevaluasi Tindakan

Outlier tidak langsung dihapus.

Beberapa kemungkinan yang perlu dipertimbangkan adalah:

  1. nilai tersebut merupakan kesalahan input;
  2. nilai tersebut benar tetapi memang ekstrem; atau
  3. nilai tersebut berasal dari kondisi atau kelompok tertentu.

Pada contoh ini, nilai ekstrem pada pendapatan dipertahankan karena belum terdapat bukti bahwa nilai tersebut merupakan kesalahan pencatatan.

Winsorization

Sebagai perbandingan, dapat dibuat versi data dengan winsorization.

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

Transformasi Data

Metode transformasi yang saya gunakan adalah normalisasi min–maks, mengubah data menjadi rentang antara 0 sampai 1.

Rumusnya:

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

Fungsi yang digunakan:

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
}

Normalisasi kemudian diterapkan pada usia dan pendapatan.

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             4500000      0.0000000000
## 2         C002   25  0.13793103             4900000      0.0008072654
## 3         C003   23  0.06896552             5200000      0.0014127144
## 4         C004   50  1.00000000             4800000      0.0006054490
## 5         C005   27  0.20689655             4900000      0.0008072654
## 6         C006   NA  0.22413793             5100000      0.0012108981
## 7         C007   31  0.34482759           500000000      1.0000000000
## 8         C008   29  0.27586207             4700000      0.0004036327
## 9         C009   22  0.03448276             4600000      0.0002018163
## 10        C010   35  0.48275862             5300000      0.0016145308
## 11        C011   28  0.24137931             4900000      0.0008072654

perbandingan sebelum dan sesudah transformasi:

transformasi <-
  pelanggan[
    ,
    c(
      "customer_id",
      "usia_imputasi",
      "usia_minmax",
      "pendapatan_imputasi",
      "pendapatan_minmax"
    )
  ]

transformasi
##    customer_id usia_imputasi usia_minmax pendapatan_imputasi pendapatan_minmax
## 1         C001          21.0  0.00000000             4500000      0.0000000000
## 2         C002          25.0  0.13793103             4900000      0.0008072654
## 3         C003          23.0  0.06896552             5200000      0.0014127144
## 4         C004          50.0  1.00000000             4800000      0.0006054490
## 5         C005          27.0  0.20689655             4900000      0.0008072654
## 6         C006          27.5  0.22413793             5100000      0.0012108981
## 7         C007          31.0  0.34482759           500000000      1.0000000000
## 8         C008          29.0  0.27586207             4700000      0.0004036327
## 9         C009          22.0  0.03448276             4600000      0.0002018163
## 10        C010          35.0  0.48275862             5300000      0.0016145308
## 11        C011          28.0  0.24137931             4900000      0.0008072654

Dampak Outlier terhadap Normalisasi

Untuk melihat pengaruh outlier terhadap normalisasi, hasil normalisasi data asli dibandingkan dengan data yang telah dilakukan winsorization.

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 Outlier terhadap Normalisasi"
)

abline(
  0,
  1,
  lty = 2
)

Integrasi Data

Setelah data pelanggan selesai dibersihkan, data tersebut perlu digabungkan dengan data transaksi.

Sebelum penggabungan dilakukan, identifier dari kedua sumber data perlu diperiksa terlebih dahulu.

Memeriksa Kunci pada Kedua Sumber

Periksa apakah terdapat customer_id yang berulang pada data pelanggan.

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

Periksa apakah terdapat cust_id yang berulang pada data transaksi.

sum(
  duplicated(
    transaksi_raw$cust_id
  )
)
## [1] 0

Memeriksa ID yang Tidak Memiliki Pasangan

ID yang terdapat pada data pelanggan tetapi tidak terdapat pada data transaksi dapat dilihat menggunakan setdiff().

setdiff(
  pelanggan$customer_id,
  transaksi_raw$cust_id
)
## [1] "C011"

Sebaliknya, ID yang terdapat pada data transaksi tetapi tidak terdapat pada data pelanggan dapat diperiksa dengan:

setdiff(
  transaksi_raw$cust_id,
  pelanggan$customer_id
)
## [1] "C012"

Pemeriksaan ini membantu mengetahui apakah terdapat ID yang tidak memiliki pasangan pada sumber data lainnya.

Menyelaraskan Nama Identifier

Data pelanggan menggunakan customer_id, sedangkan data transaksi menggunakan cust_id.

Agar kedua data dapat digabungkan, nama identifier disamakan.

transaksi <- transaksi_raw

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

Melakukan Left Join dengan merge()

Penggabungan dilakukan menggunakan fungsi merge().

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

Hasil integrasi dapat dilihat menggunakan:

data_terintegrasi[
  ,
  c(
    "customer_id",
    "nama",
    "jumlah_transaksi",
    "total_purchase"
  )
]
##    customer_id   nama jumlah_transaksi total_purchase
## 1         C001    Ani                5        1500000
## 2         C002   Budi                3         900000
## 3         C003  Citra                7        2700000
## 4         C004   Dodi                2         600000
## 5         C005    Eka                6        2100000
## 6         C006   Fani                4        1300000
## 7         C007 Gilang               20       25000000
## 8         C008   Hana                5        1700000
## 9         C009  Indra                3         800000
## 10        C010   Joko                8        3200000
## 11        C011   Kiki               NA             NA

Penggunaan all.x = TRUE membuat seluruh pelanggan pada data pelanggan tetap dipertahankan meskipun tidak memiliki data transaksi.

Memvalidasi Hasil Integrasi

Jumlah baris sebelum dan sesudah integrasi dibandingkan.

c(
  sebelum = nrow(pelanggan),
  sesudah = nrow(data_terintegrasi)
)
## sebelum sesudah 
##      11      11

Periksa kembali duplikasi customer_id.

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

Periksa missing value pada atribut transaksi.

colSums(
  is.na(
    data_terintegrasi[
      ,
      c(
        "jumlah_transaksi",
        "total_purchase"
      )
    ]
  )
)
## jumlah_transaksi   total_purchase 
##                1                1

Pelanggan yang tidak memiliki pasangan transaksi dapat ditampilkan dengan:

data_terintegrasi[
  is.na(
    data_terintegrasi$jumlah_transaksi
  ),
  c(
    "customer_id",
    "nama"
  )
]
##    customer_id nama
## 11        C011 Kiki

Mengisi Nol atau Mempertahankan NA?

Nilai NA pada data transaksi tidak boleh langsung diubah menjadi nol tanpa mengetahui arti dari nilai tersebut.

Jika NA diketahui berarti pelanggan memang belum melakukan transaksi, maka nilai tersebut 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

Dataset Akhir dan Evaluasi

Setelah seluruh proses preprocessing selesai, atribut yang akan digunakan untuk analisis dipilih.

Memilih Atribut Akhir

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) == "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             4500000
## 2         C002   Budi 25.0       Pekanbaru       Aktif             4900000
## 3         C003  Citra 23.0       Pekanbaru       Aktif             5200000
## 4         C004   Dodi 50.0           Dumai       Aktif             4800000
## 5         C005    Eka 27.0       Pekanbaru Tidak Aktif             4900000
## 6         C006   Fani 27.5           Dumai Tidak Aktif             5100000
## 7         C007 Gilang 31.0       Pekanbaru       Aktif           500000000
## 8         C008   Hana 29.0            Siak       Aktif             4700000
## 9         C009  Indra 22.0       Pekanbaru       Aktif             4600000
## 10        C010   Joko 35.0           Dumai Tidak Aktif             5300000
## 11        C011   Kiki 28.0 Tidak diketahui       Aktif             4900000
##    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              1500000
## 2                       3               900000
## 3                       7              2700000
## 4                       2               600000
## 5                       6              2100000
## 6                       4              1300000
## 7                      20             25000000
## 8                       5              1700000
## 9                       3               800000
## 10                      8              3200000
## 11                      0                    0

Audit Akhir

Setelah dataset akhir dibuat, kondisi data diperiksa kembali menggunakan fungsi audit_data().

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

Periksa kembali duplikasi customer_id.

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

Periksa kembali missing value.

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

Membandingkan Kondisi Sebelum dan Sesudah

Untuk melihat perubahan kondisi data, dibuat tabel perbandingan.

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

Setelah seluruh tahapan dilakukan, dataset menjadi lebih terstruktur.

Perubahan yang dapat diamati antara lain:

  • duplikasi customer_id telah ditangani;
  • missing value telah diberikan penanganan;
  • kategori kota telah diseragamkan;
  • kategori status telah diseragamkan;
  • nilai ekstrem telah diidentifikasi;
  • data pelanggan dan transaksi telah diintegrasikan; dan
  • dataset akhir telah diperiksa kembali.

Kesimpulan

Data preprocessing merupakan tahap penting untuk memastikan data yang digunakan dalam analisis memiliki kualitas dan struktur yang sesuai.

Preprocessing tidak hanya berfokus pada membuat data menjadi rapi, tetapi juga memastikan bahwa setiap perubahan yang dilakukan mempunyai alasan, aturan, dan dapat ditelusuri kembali.