1. Pendahuluan

1.1 Latar Belakang

Bayangkan sebuah kafe mencatat setiap penjualan di mesin kasir. Dalam praktiknya, catatan itu sering tidak rapi: ada kolom yang lupa diisi, ada tulisan ERROR yang muncul karena sistem gagal mencatat, dan ada tulisan UNKNOWN karena datanya tidak diketahui. Data seperti ini disebut data kotor (dirty data).

Data yang kotor tidak bisa langsung dianalisis. Rata-rata atau jumlah tidak bisa dihitung kalau di dalam kolom angka masih ada tulisan ERROR. Ada pepatah dalam dunia data, garbage in, garbage out: kalau data yang masuk berantakan, hasil analisisnya juga ikut berantakan.

Karena itu, sebelum dianalisis, data perlu melewati tahap pra-pemrosesan (preprocessing). Dalam laporan ini kami melakukan tiga tahap:

  1. Mengenali masalah pada data mentah (data kosong, ERROR, dan UNKNOWN).
  2. Membersihkan data (data cleaning) sehingga semua masalah tersebut hilang.
  3. Mengubah tulisan menjadi angka (encoding) supaya data bisa diolah oleh komputer dan aplikasi statistik.

1.2 Tujuan

  1. Mengetahui jenis dan jumlah masalah pada data mentah penjualan kafe.
  2. Membandingkan data sebelum dan sesudah dibersihkan.
  3. Mengubah variabel kategori (Item dan Location) menjadi angka dengan label encoding dan one-hot encoding.
  4. Memastikan data akhir sudah siap dipakai untuk analisis lanjutan.

1.3 Metadata Data

Pertanyaan Jawaban
What (data apa) Data transaksi penjualan kafe (Cafe Sales - Dirty Data)
Why (untuk apa) Latihan membersihkan data: mencari data kosong, nilai tidak valid, dan masalah lainnya
Who (siapa penerbit) Ahmad Mohamed
When (kapan) Diterbitkan tahun 2023
Where (di mana) Kafe
How (cara memperoleh) Diunduh dari Kaggle, lalu dianalisis
Unit (satuan data) Satu transaksi penjualan
Format CSV

Data asli berjumlah 10.000 transaksi. Kelompok kami mengambil sampel 10%, yaitu 1.000 transaksi, yang kemudian dianalisis pada laporan ini.

2. Persiapan

2.1 Memanggil Paket R

Paket adalah “kotak peralatan” tambahan di R. Kami memakai readxl untuk membaca file Excel, dplyr dan tidyr untuk mengolah data, ggplot2 untuk grafik, dan knitr untuk tabel. Jika belum terpasang, jalankan sekali: install.packages(c("readxl", "dplyr", "tidyr", "ggplot2", "knitr")).

library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(knitr)

theme_set(theme_minimal(base_size = 12))

2.2 Memanggil Data

Ada tiga file data yang dipakai, dan ketiganya harus berada di folder yang sama dengan file .Rmd ini:

File Isi
data_sampel.xlsx Data mentah (sebelum dibersihkan)
Cleaning_Data_1.csv Data setelah dibersihkan
data_encoded1.xlsx Data setelah diubah menjadi angka (encoding)

Kode berikut mencari ketiga file tersebut. Nama file dicari berdasarkan polanya, sehingga tetap terbaca walaupun namanya sedikit berbeda (misalnya Cleaning_Data_1 (1).csv).

cari <- function(pola) {
  f <- list.files(path = ".", pattern = pola, ignore.case = TRUE)
  if (length(f) == 0) {
    stop("Berkas dengan pola '", pola, "' tidak ditemukan di folder kerja: ", getwd(),
         "\nBerkas csv/xlsx yang ada di folder ini:\n",
         paste(list.files(pattern = "\\.(csv|xlsx)$", ignore.case = TRUE), collapse = "\n"),
         "\nPindahkan berkasnya ke folder ini atau ubah folder kerja dengan setwd().")
  }
  f[1]
}

f_raw   <- cari("^data_sampel.*\\.xlsx$")
f_clean <- cari("^cleaning.*\\.csv$")
f_enc   <- cari("^data_encoded.*\\.xlsx$")

c(Data_mentah = f_raw, Data_cleaning = f_clean, Data_encoding = f_enc)
##           Data_mentah         Data_cleaning         Data_encoding 
##    "data_sampel.xlsx" "Cleaning Data 1.csv"  "data_encoded1.xlsx"

Catatan teknis: file CSV memakai tanda titik koma (;) sebagai pemisah kolom, dan ada beberapa angka desimal yang ditulis dengan koma (misalnya 2,0). Kedua hal ini ditangani pada kode berikut.

# Data mentah: semua kolom dibaca sebagai teks supaya tulisan ERROR/UNKNOWN tidak hilang
raw <- read_excel(f_raw, col_types = "text")

# Data cleaning: pemisah ';' dan buang kolom kosong akibat ';' di akhir baris
clean_chr <- read.csv(f_clean, sep = ";", colClasses = "character", stringsAsFactors = FALSE)
clean_chr <- clean_chr[, !(names(clean_chr) %in% c("X", "")), drop = FALSE]
clean_chr <- clean_chr[match(raw$Transaction.ID, clean_chr$Transaction.ID), ]

# Ubah kolom angka menjadi numerik (koma desimal diganti titik)
num_vars <- c("Quantity", "Price.Per.Unit", "Total.Spent")
clean <- clean_chr %>%
  mutate(across(all_of(num_vars), ~ as.numeric(gsub(",", ".", .x))))

# Data encoding
enc <- as.data.frame(read_excel(f_enc), check.names = FALSE)

3. Gambaran Umum Data

data.frame(
  Keterangan = c("Jumlah data asli (menurut sumber)", "Jumlah data sampel (10%)",
                 "Jumlah variabel yang dianalisis", "Nama variabel"),
  Isi = c("10.000 transaksi", paste(nrow(raw), "transaksi"), "3 variabel",
          "Quantity, Location, Item")
) %>% kable(caption = "Tabel 1: Gambaran umum data")
Tabel 1: Gambaran umum data
Keterangan Isi
Jumlah data asli (menurut sumber) 10.000 transaksi
Jumlah data sampel (10%) 1000 transaksi
Jumlah variabel yang dianalisis 3 variabel
Nama variabel Quantity, Location, Item

Kami memfokuskan analisis pada tiga variabel:

  • Item: nama makanan atau minuman yang dibeli (misalnya Coffee, Tea, Cake).
  • Quantity: jumlah barang yang dibeli dalam satu transaksi.
  • Location: tempat pembelian, yaitu In-store (makan di tempat) atau Takeaway (dibawa pulang).

Berikut enam baris pertama data mentah. Perhatikan sel yang kosong atau berisi ERROR dan UNKNOWN.

kable(head(raw[, c("Transaction.ID", "Item", "Quantity", "Price.Per.Unit",
                   "Total.Spent", "Payment.Method", "Location")], 6),
      caption = "Tabel 2: Enam baris pertama data mentah")
Tabel 2: Enam baris pertama data mentah
Transaction.ID Item Quantity Price.Per.Unit Total.Spent Payment.Method Location
TXN_2176024 Coffee 5 ERROR 10.0 Digital Wallet In-store
TXN_6327139 ERROR 4 4.0 16.0 NA Takeaway
TXN_5488764 Coffee 4 2.0 8.0 Digital Wallet Takeaway
TXN_9530003 Salad 1 5.0 5.0 Digital Wallet NA
TXN_3753993 Sandwich 5 4.0 20.0 Credit Card NA
TXN_2251128 Smoothie 3 4.0 12.0 Credit Card In-store

4. Permasalahan pada Data

Ada tiga jenis masalah pada data mentah:

  1. Data kosong (NA): sel tidak terisi.
  2. Data ERROR: sistem gagal mencatat transaksi.
  3. Data UNKNOWN: nilainya tidak diketahui.

4.1 Jumlah Masalah pada Tiga Variabel

v3 <- c("Item", "Quantity", "Location")

hitung_cacat <- function(df, vars) {
  data.frame(
    Variabel     = vars,
    Data_Kosong  = sapply(vars, function(v) sum(is.na(df[[v]]))),
    Data_ERROR   = sapply(vars, function(v) sum(df[[v]] == "ERROR", na.rm = TRUE)),
    Data_UNKNOWN = sapply(vars, function(v) sum(df[[v]] == "UNKNOWN", na.rm = TRUE)),
    row.names = NULL
  ) %>% mutate(Total = Data_Kosong + Data_ERROR + Data_UNKNOWN)
}

masalah <- hitung_cacat(raw, v3)
total_baris <- data.frame(Variabel = "TOTAL", t(colSums(masalah[, -1])))
names(total_baris) <- names(masalah)

kable(bind_rows(masalah, total_baris),
      caption = "Tabel 3: Jumlah data bermasalah pada data mentah")
Tabel 3: Jumlah data bermasalah pada data mentah
Variabel Data_Kosong Data_ERROR Data_UNKNOWN Total
Item 28 29 26 83
Quantity 15 14 15 44
Location 322 37 35 394
TOTAL 365 80 76 521
masalah %>%
  pivot_longer(c(Data_Kosong, Data_ERROR, Data_UNKNOWN),
               names_to = "Jenis", values_to = "Jumlah") %>%
  mutate(Jenis = recode(Jenis, Data_Kosong = "Kosong", Data_ERROR = "ERROR",
                        Data_UNKNOWN = "UNKNOWN")) %>%
  ggplot(aes(x = Variabel, y = Jumlah, fill = Jenis)) +
  geom_col(position = "dodge") +
  geom_text(aes(label = Jumlah), position = position_dodge(width = 0.9), vjust = -0.4) +
  labs(title = "Jumlah Data Bermasalah per Variabel (Data Mentah)",
       x = NULL, y = "Jumlah data", fill = "Jenis masalah")

Cara membaca: variabel Location adalah yang paling bermasalah, terutama karena 322 data kosong. Variabel Item dan Quantity masalahnya lebih sedikit dan tersebar merata pada ketiga jenis masalah.

4.2 Total Data Bermasalah

bad_raw <- sapply(raw[, v3], function(x) is.na(x) | x %in% c("ERROR", "UNKNOWN"))
total_data_bermasalah <- sum(bad_raw)
transaksi_bermasalah  <- sum(rowSums(bad_raw) > 0)

Jika seluruh masalah pada ketiga variabel dijumlahkan, terdapat 521 data bermasalah dari 1.000 transaksi. Angka ini dihitung per sel data. Karena satu transaksi bisa bermasalah di lebih dari satu kolom, banyaknya transaksi yang memiliki minimal satu masalah adalah 464 transaksi (46.4%).

Penjelasan sederhana: bayangkan data ini sebagai tabel Excel. Ada 521 “kotak” yang isinya bermasalah, dan kotak-kotak itu tersebar di 464 baris transaksi.

5. Data Cleaning

5.1 Apa yang Dilakukan?

Data cleaning artinya memperbaiki sel yang bermasalah sehingga semua kolom berisi nilai yang valid. Pada data hasil cleaning, jumlah transaksi tetap 1.000 baris: tidak ada transaksi yang dibuang, sel yang bermasalah diisi dengan nilai pengganti.

Berikut contoh transaksi yang bermasalah, sebelum dan sesudah dibersihkan (seperti pada slide Data Cleaning).

contoh <- data.frame(
  Transaction.ID    = raw$Transaction.ID,
  Item_Sebelum      = raw$Item,           Item_Sesudah     = clean$Item,
  Quantity_Sebelum  = raw$Quantity,       Quantity_Sesudah = clean$Quantity,
  Location_Sebelum  = raw$Location,       Location_Sesudah = clean$Location,
  stringsAsFactors = FALSE
)
contoh <- contoh[rowSums(bad_raw) > 0, ]
contoh[is.na(contoh)] <- "(kosong)"

kable(head(contoh, 12), row.names = FALSE,
      caption = "Tabel 4: Contoh transaksi bermasalah sebelum dan sesudah cleaning")
Tabel 4: Contoh transaksi bermasalah sebelum dan sesudah cleaning
Transaction.ID Item_Sebelum Item_Sesudah Quantity_Sebelum Quantity_Sesudah Location_Sebelum Location_Sesudah
TXN_6327139 ERROR Sandwich 4 4 Takeaway Takeaway
TXN_9530003 Salad Salad 1 1 (kosong) Takeaway
TXN_3753993 Sandwich Sandwich 5 5 (kosong) Takeaway
TXN_3715065 Salad Salad 3 3 ERROR Takeaway
TXN_7608637 ERROR Salad 1 1 (kosong) Takeaway
TXN_4176272 Coffee Coffee 1 1 (kosong) Takeaway
TXN_5741619 Smoothie Smoothie ERROR 1 (kosong) Takeaway
TXN_1584851 Smoothie Smoothie 1 1 (kosong) Takeaway
TXN_5801294 Salad Salad 1 1 (kosong) Takeaway
TXN_1237674 (kosong) Tea 3 3 UNKNOWN Takeaway
TXN_2887971 Smoothie Smoothie 2 2 UNKNOWN Takeaway
TXN_7818024 Cake Cake 3 3 (kosong) Takeaway

5.2 Apakah Semua Masalah Sudah Hilang?

sesudah <- hitung_cacat(clean_chr, v3)

perbandingan <- data.frame(
  Variabel          = v3,
  Masalah_Sebelum   = masalah$Total,
  Masalah_Sesudah   = sesudah$Total
)
perbandingan$Status <- ifelse(perbandingan$Masalah_Sesudah == 0, "Bersih", "Masih ada")

kable(bind_rows(perbandingan,
                data.frame(Variabel = "TOTAL",
                           Masalah_Sebelum = sum(perbandingan$Masalah_Sebelum),
                           Masalah_Sesudah = sum(perbandingan$Masalah_Sesudah),
                           Status = ifelse(sum(perbandingan$Masalah_Sesudah) == 0, "Bersih", "Masih ada"))),
      caption = "Tabel 5: Jumlah data bermasalah sebelum dan sesudah cleaning")
Tabel 5: Jumlah data bermasalah sebelum dan sesudah cleaning
Variabel Masalah_Sebelum Masalah_Sesudah Status
Item 83 0 Bersih
Quantity 44 0 Bersih
Location 394 0 Bersih
TOTAL 521 0 Bersih
data.frame(
  Hal_yang_Dibandingkan = c("Jumlah baris", "Jumlah kolom", "Jumlah sel kosong (NA)",
                            "Jumlah ERROR dan UNKNOWN (3 variabel)", "Bentuk data"),
  Data_Mentah  = c(nrow(raw), ncol(raw), sum(is.na(raw)),
                   sum(masalah$Data_ERROR + masalah$Data_UNKNOWN),
                   "Campuran angka, tulisan, dan sel kosong"),
  Data_Cleaning = c(nrow(clean), ncol(clean), sum(is.na(clean)),
                    sum(sesudah$Data_ERROR + sesudah$Data_UNKNOWN),
                    "Tulisan dan angka yang valid"),
  Data_Encoding = c(nrow(enc), ncol(enc), sum(is.na(enc)), 0,
                    "Angka 0 dan 1 (siap dihitung)")
) %>% kable(caption = "Tabel 6: Perbandingan data pada tiga tahap")
Tabel 6: Perbandingan data pada tiga tahap
Hal_yang_Dibandingkan Data_Mentah Data_Cleaning Data_Encoding
Jumlah baris 1000 1000 1000
Jumlah kolom 8 7 11
Jumlah sel kosong (NA) 671 0 0
Jumlah ERROR dan UNKNOWN (3 variabel) 156 0 0
Bentuk data Campuran angka, tulisan, dan sel kosong Tulisan dan angka yang valid Angka 0 dan 1 (siap dihitung)

Kesimpulan bagian ini: ketiga variabel pada data hasil cleaning sudah bersih dari data kosong, ERROR, dan UNKNOWN, sehingga angka-angkanya bisa dihitung.

5.3 Perubahan Sebaran Data

Karena sel bermasalah diisi nilai pengganti, kami juga memeriksa apakah sebaran data berubah setelah dibersihkan. Pada grafik berikut, kelompok “Bermasalah” pada data sebelum cleaning adalah gabungan data kosong, ERROR, dan UNKNOWN.

susun <- function(v) {
  sebelum <- raw[[v]]
  sebelum[is.na(sebelum) | sebelum %in% c("ERROR", "UNKNOWN")] <- "Bermasalah"
  bind_rows(
    data.frame(Variabel = v, Tahap = "Sebelum cleaning", Kategori = sebelum),
    data.frame(Variabel = v, Tahap = "Sesudah cleaning", Kategori = clean_chr[[v]])
  )
}

bind_rows(lapply(v3, susun)) %>%
  count(Variabel, Tahap, Kategori) %>%
  ggplot(aes(x = Kategori, y = n, fill = Tahap)) +
  geom_col(position = "dodge") +
  facet_wrap(~ Variabel, scales = "free_x") +
  labs(title = "Sebaran Data Sebelum dan Sesudah Cleaning",
       x = NULL, y = "Jumlah transaksi", fill = NULL) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

loc_sebelum <- raw$Location
loc_sebelum[is.na(loc_sebelum) | loc_sebelum %in% c("ERROR", "UNKNOWN")] <- "Bermasalah"

tabel_loc <- data.frame(
  Kategori = c("In-store", "Takeaway", "Bermasalah"),
  Sebelum  = as.integer(table(factor(loc_sebelum, levels = c("In-store", "Takeaway", "Bermasalah")))),
  Sesudah  = as.integer(table(factor(clean$Location, levels = c("In-store", "Takeaway", "Bermasalah"))))
)
kable(tabel_loc, caption = "Tabel 7: Jumlah transaksi per kategori Location")
Tabel 7: Jumlah transaksi per kategori Location
Kategori Sebelum Sesudah
In-store 300 300
Takeaway 306 700
Bermasalah 394 0

Cara membaca:

  • Pada Item dan Quantity, bentuk sebaran sebelum dan sesudah cleaning mirip: tidak ada kategori yang melonjak tajam.
  • Pada Location, data bermasalah sangat banyak (394 transaksi). Setelah dibersihkan, In-store tetap 300 transaksi, sedangkan Takeaway bertambah menjadi 700 transaksi. Artinya sel Location yang bermasalah diisi dengan kategori yang paling sering muncul, yaitu Takeaway.

Penjelasan sederhana: ketika sebuah sel bermasalah, kita harus menebak isinya. Cara yang paling mudah adalah mengisinya dengan kategori yang paling sering muncul. Cara ini praktis, tetapi membuat kategori tersebut terlihat lebih banyak daripada kenyataannya. Karena itu hasil analisis Location perlu dibaca dengan hati-hati.

5.4 Pemeriksaan Kewajaran Hasil Cleaning

Pada data penjualan, Total.Spent seharusnya sama dengan Quantity × Price.Per.Unit. Berikut pemeriksaannya pada data hasil cleaning:

selisih <- clean %>%
  mutate(Hitung = Quantity * Price.Per.Unit) %>%
  filter(abs(Total.Spent - Hitung) > 1e-8) %>%
  select(Transaction.ID, Item, Quantity, Price.Per.Unit, Total.Spent, Hitung)

sesuai <- nrow(clean) - nrow(selisih)

Dari 1.000 transaksi, 996 transaksi sesuai dengan rumus tersebut dan 4 transaksi tidak sesuai (0.4%). Hasil ini menunjukkan bahwa hasil pengisian data umumnya wajar, dengan sedikit pengecualian:

kable(selisih, caption = "Tabel 8: Transaksi dengan Total.Spent tidak sama dengan Quantity x Price.Per.Unit")
Tabel 8: Transaksi dengan Total.Spent tidak sama dengan Quantity x Price.Per.Unit
Transaction.ID Item Quantity Price.Per.Unit Total.Spent Hitung
TXN_5741619 Smoothie 1 4 20 4
TXN_3132488 Sandwich 1 4 16 4
TXN_8692653 Juice 1 3 6 3
TXN_6700745 Juice 3 3 6 9

6. Encoding

6.1 Mengapa Perlu Encoding?

Komputer dan aplikasi statistik bekerja dengan angka, sedangkan Item dan Location berisi tulisan. Encoding adalah proses mengubah tulisan menjadi angka. Ada dua cara yang kami pakai.

6.2 Label Encoding

Pada label encoding, setiap kategori diberi satu nomor. Nomor diberikan menurut urutan abjad.

lv_item <- sort(unique(clean$Item))
lv_loc  <- sort(unique(clean$Location))

kable(data.frame(Item = lv_item, Kode = seq_along(lv_item)),
      caption = "Tabel 9: Label encoding variabel Item")
Tabel 9: Label encoding variabel Item
Item Kode
Cake 1
Coffee 2
Cookie 3
Juice 4
Salad 5
Sandwich 6
Smoothie 7
Tea 8
kable(data.frame(Location = lv_loc, Kode = seq_along(lv_loc)),
      caption = "Tabel 10: Label encoding variabel Location")
Tabel 10: Label encoding variabel Location
Location Kode
In-store 1
Takeaway 2

Penjelasan sederhana: Cake diberi nomor 1, Coffee nomor 2, dan seterusnya. Kekurangannya, nomor ini bisa terkesan berurutan (Tea = 8 seolah “lebih besar” dari Cake = 1), padahal menu-menu itu tidak punya urutan. Karena itu kami juga memakai one-hot encoding.

6.3 One-Hot Encoding

Pada one-hot encoding, setiap kategori menjadi kolom sendiri yang berisi angka 1 (ya) atau 0 (bukan). Pada setiap baris hanya ada satu kolom yang bernilai 1.

kable(head(enc, 8), caption = "Tabel 11: Delapan baris pertama data hasil one-hot encoding")
Tabel 11: Delapan baris pertama data hasil one-hot encoding
Quantity Item_Cake Item_Coffee Item_Cookie Item_Juice Item_Salad Item_Sandwich Item_Smoothie Item_Tea Location_In-store Location_Takeaway
5 0 1 0 0 0 0 0 0 1 0
4 0 0 0 0 0 1 0 0 0 1
4 0 1 0 0 0 0 0 0 0 1
1 0 0 0 0 1 0 0 0 0 1
5 0 0 0 0 0 1 0 0 0 1
3 0 0 0 0 0 0 1 0 1 0
3 0 0 0 0 1 0 0 0 0 1
1 0 0 0 0 1 0 0 0 0 1

Data hasil encoding memiliki 1000 baris dan 11 kolom: 1 kolom Quantity, 8 kolom untuk Item, dan 2 kolom untuk Location.

6.4 Apakah Hasil Encoding Benar?

Agar yakin hasilnya benar, kami memeriksa data encoding terhadap data hasil cleaning:

item_cols <- grep("^Item_", names(enc), value = TRUE)
loc_cols  <- grep("^Location_", names(enc), value = TRUE)
m_item <- as.matrix(enc[, item_cols])
m_loc  <- as.matrix(enc[, loc_cols])

item_balik <- sub("^Item_", "", item_cols[max.col(m_item, ties.method = "first")])
loc_balik  <- sub("^Location_", "", loc_cols[max.col(m_loc, ties.method = "first")])

cek <- data.frame(
  Pemeriksaan = c("Jumlah baris sama dengan data cleaning",
                  "Setiap baris hanya punya satu angka 1 untuk Item",
                  "Setiap baris hanya punya satu angka 1 untuk Location",
                  "Semua nilai dummy hanya 0 atau 1",
                  "Quantity sama dengan data cleaning",
                  "Item jika dikembalikan menjadi tulisan sama dengan data cleaning",
                  "Location jika dikembalikan menjadi tulisan sama dengan data cleaning"),
  Hasil = c(nrow(enc) == nrow(clean),
            all(rowSums(m_item) == 1),
            all(rowSums(m_loc) == 1),
            all(m_item %in% c(0, 1)) && all(m_loc %in% c(0, 1)),
            all(enc$Quantity == clean$Quantity),
            all(item_balik == clean$Item),
            all(loc_balik == clean$Location))
)
cek$Hasil <- ifelse(cek$Hasil, "Lolos", "Gagal")
kable(cek, caption = "Tabel 12: Hasil pemeriksaan data encoding")
Tabel 12: Hasil pemeriksaan data encoding
Pemeriksaan Hasil
Jumlah baris sama dengan data cleaning Lolos
Setiap baris hanya punya satu angka 1 untuk Item Lolos
Setiap baris hanya punya satu angka 1 untuk Location Lolos
Semua nilai dummy hanya 0 atau 1 Lolos
Quantity sama dengan data cleaning Lolos
Item jika dikembalikan menjadi tulisan sama dengan data cleaning Lolos
Location jika dikembalikan menjadi tulisan sama dengan data cleaning Lolos

Semua pemeriksaan lolos, artinya hasil encoding sudah sesuai dengan data hasil cleaning.

kable(data.frame(Kolom = c(item_cols, loc_cols),
                 Jumlah_Bernilai_1 = c(colSums(m_item), colSums(m_loc))),
      row.names = FALSE, caption = "Tabel 13: Jumlah transaksi pada setiap kolom dummy")
Tabel 13: Jumlah transaksi pada setiap kolom dummy
Kolom Jumlah_Bernilai_1
Item_Cake 138
Item_Coffee 118
Item_Cookie 110
Item_Juice 114
Item_Salad 126
Item_Sandwich 123
Item_Smoothie 136
Item_Tea 135
Location_In-store 300
Location_Takeaway 700

7. Gambaran Data Akhir

Setelah dibersihkan dan di-encoding, data sudah bisa dianalisis. Berikut gambaran singkatnya.

kable(data.frame(
  Ukuran = c("Rata-rata Quantity", "Median Quantity", "Total unit terjual",
             "Total pendapatan", "Rata-rata nilai per transaksi"),
  Nilai  = c(round(mean(clean$Quantity), 2), median(clean$Quantity), sum(clean$Quantity),
             sum(clean$Total.Spent), round(mean(clean$Total.Spent), 2))
), caption = "Tabel 14: Ringkasan data hasil cleaning")
Tabel 14: Ringkasan data hasil cleaning
Ukuran Nilai
Rata-rata Quantity 3.05
Median Quantity 3.00
Total unit terjual 3049.00
Total pendapatan 9103.00
Rata-rata nilai per transaksi 9.10
clean %>%
  count(Item) %>%
  ggplot(aes(x = reorder(Item, n), y = n)) +
  geom_col(fill = "#3b6ea5") +
  geom_text(aes(label = n), hjust = -0.2) +
  coord_flip() +
  expand_limits(y = max(table(clean$Item)) * 1.1) +
  labs(title = "Jumlah Transaksi per Menu (Data Hasil Cleaning)",
       x = "Menu", y = "Jumlah transaksi")

clean %>%
  count(Quantity) %>%
  ggplot(aes(x = factor(Quantity), y = n)) +
  geom_col(fill = "#6aa84f") +
  geom_text(aes(label = n), vjust = -0.4) +
  labs(title = "Jumlah Barang yang Dibeli per Transaksi", x = "Quantity", y = "Jumlah transaksi")

clean %>%
  group_by(Item) %>%
  summarise(Pendapatan = sum(Total.Spent), .groups = "drop") %>%
  ggplot(aes(x = reorder(Item, Pendapatan), y = Pendapatan)) +
  geom_col(fill = "#e69138") +
  coord_flip() +
  labs(title = "Total Pendapatan per Menu", x = "Menu", y = "Total pendapatan")

Cara membaca: jumlah transaksi antar menu relatif seimbang, tetapi pendapatannya berbeda karena harga tiap menu berbeda. Menu yang harganya lebih tinggi, seperti Salad, Smoothie, dan Sandwich, menyumbang pendapatan lebih besar.

8. Kesimpulan

  1. Memperbaiki data yang rusak (data cleaning). Dari 1.000 transaksi, terdapat 521 data bermasalah pada tiga variabel (Item, Quantity, Location) yang tersebar di 464 transaksi, berupa data kosong, tulisan ERROR, dan data UNKNOWN. Seluruhnya berhasil dibersihkan sehingga data hasil cleaning tidak lagi memuat data kosong, ERROR, maupun UNKNOWN.

  2. Mengubah tulisan menjadi angka (data encoding). Item dan Location diubah menjadi kode angka melalui label encoding dan one-hot encoding (kolom 0 dan 1). Seluruh pemeriksaan menunjukkan hasil encoding sesuai dengan data hasil cleaning.

  3. Kesiapan data. Data akhir siap dipakai untuk perhitungan lanjutan menggunakan program komputer atau aplikasi statistik.

Catatan Penting

Beberapa hal berikut perlu diingat saat memakai data ini:

  • Sebaran Location berubah. Karena banyak sel Location yang diisi Takeaway, jumlah Takeaway pada data akhir lebih besar daripada kenyataan sebenarnya. Hasil analisis yang memakai Location sebaiknya dibaca dengan hati-hati.
  • Hasil pengisian data hampir selalu wajar, tetapi masih ada sedikit transaksi yang Total.Spent-nya tidak sama dengan Quantity × Price.Per.Unit (lihat Tabel 8).
  • Analisis difokuskan pada tiga variabel. Data mentah sebenarnya memiliki kolom lain (Price.Per.Unit, Total.Spent, Payment.Method, Transaction.Date) yang juga memuat data bermasalah.

Saran

  1. Perbaiki pencatatan di kasir. Tambahkan aturan pada sistem Point of Sale agar kolom penting wajib diisi dan teks ERROR tidak masuk ke data.
  2. Cari cara pengisian yang lebih teliti untuk variabel yang banyak kosong seperti Location, misalnya dengan membuat kategori tersendiri untuk data yang tidak diketahui.
  3. Tandai nilai yang diisi ulang pada kolom tambahan supaya mudah ditelusuri.
  4. Pada model regresi, buang satu kolom dummy sebagai pembanding agar tidak terjadi masalah perhitungan.
  5. Lakukan penskalaan (normalisasi atau standardisasi) pada variabel angka seperti Quantity sebelum dipakai pada metode yang berbasis jarak.

Informasi Sesi

sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
## 
## Matrix products: default
##   LAPACK version 3.12.1
## 
## locale:
## [1] LC_COLLATE=English_United States.utf8 
## [2] LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: Asia/Jakarta
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] knitr_1.52    ggplot2_4.0.2 tidyr_1.3.2   dplyr_1.1.4   readxl_1.5.0 
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       jsonlite_2.0.0     compiler_4.5.1     tidyselect_1.2.1  
##  [5] jquerylib_0.1.4    scales_1.4.0       yaml_2.3.12        fastmap_1.2.0     
##  [9] R6_2.6.1           labeling_0.4.3     generics_0.1.4     tibble_3.3.0      
## [13] bslib_0.12.0       pillar_1.11.1      RColorBrewer_1.1-3 rlang_1.1.6       
## [17] cachem_1.1.0       xfun_0.60          sass_0.4.10        S7_0.2.1          
## [21] otel_0.2.0         cli_3.6.5          withr_3.0.2        magrittr_2.0.4    
## [25] digest_0.6.39      grid_4.5.1         rstudioapi_0.19.0  lifecycle_1.0.4   
## [29] vctrs_0.6.5        evaluate_1.0.5     glue_1.8.0         farver_2.1.2      
## [33] cellranger_1.1.0   rmarkdown_2.32     purrr_1.2.2        tools_4.5.1       
## [37] pkgconfig_2.0.3    htmltools_0.5.9