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:
ERROR, dan UNKNOWN).Item dan
Location) menjadi angka dengan label encoding dan
one-hot encoding.| 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.
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")).
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)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")| 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")| 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 |
Ada tiga jenis masalah pada data mentah:
ERROR: sistem gagal mencatat
transaksi.UNKNOWN: nilainya tidak
diketahui.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")| 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.
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.
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")| 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 |
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")| 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")| 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.
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")| Kategori | Sebelum | Sesudah |
|---|---|---|
| In-store | 300 | 300 |
| Takeaway | 306 | 700 |
| Bermasalah | 394 | 0 |
Cara membaca:
Item dan Quantity, bentuk sebaran
sebelum dan sesudah cleaning mirip: tidak ada kategori
yang melonjak tajam.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.
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")| 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 |
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.
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")| 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")| 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.
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.
| 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.
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")| 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")| 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 |
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")| 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.
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.
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.
Kesiapan data. Data akhir siap dipakai untuk perhitungan lanjutan menggunakan program komputer atau aplikasi statistik.
Beberapa hal berikut perlu diingat saat memakai data ini:
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.Total.Spent-nya tidak sama
dengan Quantity × Price.Per.Unit (lihat Tabel
8).Price.Per.Unit,
Total.Spent, Payment.Method,
Transaction.Date) yang juga memuat data bermasalah.ERROR tidak masuk ke data.Location, misalnya
dengan membuat kategori tersendiri untuk data yang tidak diketahui.Quantity sebelum dipakai pada
metode yang berbasis jarak.## 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