UTS - Pemogramman Sains Data
Paskalis Farelnata Zamasi
Data Science Student at ITSB
Dosen Pembimbing
Bakti Siregar, M.Sc., CDS.
Christan Michael Juliano
Data Science Student at ITSB
Dosen Pembimbing
Bakti Siregar, M.Sc., CDS.
M.Yustian Putra Muhadi
Data Science Student at ITSB
Dosen Pembimbing
Bakti Siregar, M.Sc., CDS.
Mini Project: Case Study E-Commerce
Section A – Data Collection Using Programming
Tujuan: Mengambil dan menggabungkan data dari berbagai sumber menggunakan coding Python dan R
# 1. PERSIAPAN LIBRARY
library(readr)
library(readxl)
library(jsonlite)
library(XML)
library(dplyr)
library(kableExtra)
# 2. DEFINISI FILE DAN LOOPING PEMBACAAN
file_list <- c("ecommerce.csv", "ecommerce.xlsx", "ecommerce.json", "ecommerce.txt", "ecommerce.xml")
list_of_dfs <- list()
summary_data <- data.frame()
for (file_name in file_list) {
ext <- tools::file_ext(file_name)
# Proses membaca file berdasarkan ekstensi
df <- tryCatch({
if (ext == "csv") { read_csv(file_name, show_col_types = FALSE) }
else if (ext == "xlsx") { read_excel(file_name) }
else if (ext == "json") { fromJSON(file_name) }
else if (ext == "txt") { read_delim(file_name, delim = "|", show_col_types = FALSE) }
else if (ext == "xml") { xmlToDataFrame(file_name) }
}, error = function(e) { return(NULL) })
if (!is.null(df)) {
list_of_dfs[[file_name]] <- df
# Mengambil informasi ringkasan untuk tabel
temp_summary <- data.frame(
File_Name = file_name,
Jumlah_Baris = nrow(df),
Jumlah_Kolom = ncol(df),
Nama_Kolom = paste(colnames(df), collapse = ", ")
)
summary_data <- rbind(summary_data, temp_summary)
}
}
# 3. TAMPILKAN TABEL RINGKASAN (Agar Rapi seperti Gambar 1)
summary_data %>%
kbl(caption = "Tabel 1. Ringkasan Import Data Multi-Format") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
column_spec(4, width = "50em") # Membatasi lebar kolom nama agar tidak meluap| File_Name | Jumlah_Baris | Jumlah_Kolom | Nama_Kolom |
|---|---|---|---|
| ecommerce.csv | 2000 | 22 | order_id, order_date, ship_date, platform, category, product_name, unit_price, quantity, gross_sales, campaign, voucher_code, discount_pct, discount_value, shipping_cost, net_sales, payment_method, customer_segment, region, stock_status, order_status, customer_rating, priority_flag |
| ecommerce.xlsx | 2000 | 22 | order_id, order_date, ship_date, platform, category, product_name, unit_price, quantity, gross_sales, campaign, voucher_code, discount_pct, discount_value, shipping_cost, net_sales, payment_method, customer_segment, region, stock_status, order_status, customer_rating, priority_flag |
| ecommerce.json | 2000 | 22 | order_id, order_date, ship_date, platform, category, product_name, unit_price, quantity, gross_sales, campaign, voucher_code, discount_pct, discount_value, shipping_cost, net_sales, payment_method, customer_segment, region, stock_status, order_status, customer_rating, priority_flag |
| ecommerce.txt | 2000 | 22 | order_id, order_date, ship_date, platform, category, product_name, unit_price, quantity, gross_sales, campaign, voucher_code, discount_pct, discount_value, shipping_cost, net_sales, payment_method, customer_segment, region, stock_status, order_status, customer_rating, priority_flag |
| ecommerce.xml | 2000 | 22 | order_id, order_date, ship_date, platform, category, product_name, unit_price, quantity, gross_sales, campaign, voucher_code, discount_pct, discount_value, shipping_cost, net_sales, payment_method, customer_segment, region, stock_status, order_status, customer_rating, priority_flag |
# 4. PENGECEKAN STRUKTUR (IF/ELSE) & PENGGABUNGAN
# Menggunakan file pertama sebagai standar acuan
standar_kolom <- colnames(list_of_dfs[[1]])
list_siap_gabung <- list()
status_check <- data.frame()
for (nama_file in names(list_of_dfs)) {
current_cols <- colnames(list_of_dfs[[nama_file]])
if (identical(standar_kolom, current_cols)) {
status <- "Ready to merge"
# Menyeragamkan tipe data menjadi character agar tidak error saat bind_rows
df_fix <- list_of_dfs[[nama_file]] %>% mutate(across(everything(), as.character))
list_siap_gabung[[nama_file]] <- df_fix
} else {
status <- "Need adjustment"
}
status_check <- rbind(status_check, data.frame(File_Name = nama_file, Status_Struktur = status))
}
# 5. TAMPILKAN TABEL STATUS PENGECEKAN
status_check %>%
kbl(caption = "Tabel 2. Status Pengecekan Struktur Kolom") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F) %>%
column_spec(2, color = ifelse(status_check$Status_Struktur == "Ready to merge", "green", "red"), bold = T)| File_Name | Status_Struktur |
|---|---|
| ecommerce.csv | Ready to merge |
| ecommerce.xlsx | Ready to merge |
| ecommerce.json | Ready to merge |
| ecommerce.txt | Ready to merge |
| ecommerce.xml | Ready to merge |
# 6. EKSEKUSI PENGGABUNGAN AKHIR
dataset_utama <- bind_rows(list_siap_gabung)
# 7. MEMBUAT TABEL RINGKASAN HASIL AKHIR (PENGGANTI TEKS BIASA)
hasil_akhir_summary <- data.frame(
Metrik = c("Total Baris Dataset Utama", "Total Kolom Dataset Utama", "Status Integrasi"),
Nilai = c(nrow(dataset_utama), ncol(dataset_utama), "Sukses Tergabung")
)
hasil_akhir_summary %>%
kbl(caption = "Tabel 3. Ringkasan Akhir Dataset Terintegrasi", align = "l") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F) %>%
row_spec(0, background = "#27AE60", color = "white", bold = T) %>% # Header Hijau agar senada dengan sukses
column_spec(2, bold = T)| Metrik | Nilai |
|---|---|
| Total Baris Dataset Utama | 10000 |
| Total Kolom Dataset Utama | 22 |
| Status Integrasi | Sukses Tergabung |
Interpretasi:
Proses pengumpulan data dilakukan dengan membaca lima format file
sekaligus — CSV, XLSX, JSON, TXT, dan XML — menggunakan pendekatan
looping berbasis ekstensi file. Dari kelima file tersebut, seluruhnya
berhasil dibaca dan diidentifikasi memiliki struktur kolom yang identik,
sehingga dapat langsung digabungkan menggunakan bind_rows()
tanpa memerlukan penyesuaian tambahan. Hasil penggabungan akhir
menghasilkan satu dataset utama yang terintegrasi, yang menjadi fondasi
untuk seluruh proses analisis berikutnya. Pendekatan multi-format ini
relevan dalam konteks e-commerce nyata, di mana data sering tersebar di
berbagai sistem dan format ekspor yang berbeda.
Section B – Data Handling
# --- SECTION B: DATA HANDLING ---
library(dplyr)
library(kableExtra)
# 1. MENAMPILKAN JUMLAH TOTAL BARIS DAN KOLOM
# Kita buat dalam tabel ringkasan kecil agar elegan
ringkasan_dimensi <- data.frame(
Metrik = c("Total Baris", "Total Kolom"),
Nilai = c(nrow(dataset_utama), ncol(dataset_utama))
)
ringkasan_dimensi %>%
kbl(caption = "Tabel 4. Dimensi Dataset Hasil Penggabungan") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)| Metrik | Nilai |
|---|---|
| Total Baris | 10000 |
| Total Kolom | 22 |
# 2. MENAMPILKAN TIPE DATA SETIAP KOLOM
# Kita ambil nama kolom dan tipe datanya
tipe_data_df <- data.frame(
Kolom = colnames(dataset_utama),
Tipe_Data = sapply(dataset_utama, class)
)
tipe_data_df %>%
kbl(caption = "Tabel 5. Struktur Tipe Data Kolom") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>%
scroll_box(height = "300px") # Memberi scroll agar tidak kepanjangan di halaman| Kolom | Tipe_Data | |
|---|---|---|
| order_id | order_id | character |
| order_date | order_date | character |
| ship_date | ship_date | character |
| platform | platform | character |
| category | category | character |
| product_name | product_name | character |
| unit_price | unit_price | character |
| quantity | quantity | character |
| gross_sales | gross_sales | character |
| campaign | campaign | character |
| voucher_code | voucher_code | character |
| discount_pct | discount_pct | character |
| discount_value | discount_value | character |
| shipping_cost | shipping_cost | character |
| net_sales | net_sales | character |
| payment_method | payment_method | character |
| customer_segment | customer_segment | character |
| region | region | character |
| stock_status | stock_status | character |
| order_status | order_status | character |
| customer_rating | customer_rating | character |
| priority_flag | priority_flag | character |
# 3. IDENTIFIKASI MISSING VALUES & DUPLICATE ROWS
total_na <- sum(is.na(dataset_utama))
total_dup <- sum(duplicated(dataset_utama))
identifikasi_masalah <- data.frame(
Indikator = c("Total Missing Values (NA)", "Total Duplicate Rows"),
Jumlah = c(total_na, total_dup),
Status = c(
ifelse(total_na > 0, "Perlu Cleaning", "Aman"),
ifelse(total_dup > 0, "Perlu Cleaning", "Aman")
)
)
identifikasi_masalah %>%
kbl(caption = "Tabel 6. Identifikasi Kualitas Data") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F) %>%
column_spec(3, color = "white",
background = ifelse(identifikasi_masalah$Status == "Aman", "#27AE60", "#E74C3C"))| Indikator | Jumlah | Status |
|---|---|---|
| Total Missing Values (NA) | 3629 | Perlu Cleaning |
| Total Duplicate Rows | 5581 | Perlu Cleaning |
# 4. MENYEBUTKAN MINIMAL 3 MASALAH KUALITAS DATA
# --- IDENTIFIKASI KUALITAS DATA ---
# 1. Menghitung Missing Values per Kolom
missing_per_col <- colSums(is.na(dataset_utama))
total_missing <- sum(missing_per_col)
# 2. Menghitung Baris Duplikat
total_duplicates <- sum(duplicated(dataset_utama))
# 3. Menghitung Kolom dengan Tipe Data Tidak Sesuai
# (Contoh: Kolom numerik yang masih terbaca sebagai character)
# Di Section A kita merubah semua ke character, ini adalah salah satu "masalah"
potential_type_issues <- ncol(dataset_utama)
# Membuat Tabel Ringkasan Masalah
quality_issues_df <- data.frame(
No = c(1, 2, 3),
Masalah_Kualitas_Data = c(
"Missing Values (Data Kosong)",
"Duplicate Rows (Data Ganda)",
"Data Type Inconsistency (Character vs Numeric)"
),
Jumlah_Temuan = c(total_missing, total_duplicates, potential_type_issues),
Status = c(
ifelse(total_missing > 0, "🚨 Masalah Ditemukan", "✅ Bersih"),
ifelse(total_duplicates > 0, "🚨 Masalah Ditemukan", "✅ Bersih"),
"🚨 Perlu Konversi"
)
)
# Menampilkan Tabel dengan Styling Profesional
quality_issues_df %>%
kbl(caption = "Tabel 6. Daftar Temuan Masalah Kualitas Data") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = F) %>%
column_spec(3, bold = T, color = "blue") %>%
column_spec(4, color = "white",
background = ifelse(quality_issues_df$Jumlah_Temuan > 0, "#C0392B", "#27AE60"))| No | Masalah_Kualitas_Data | Jumlah_Temuan | Status |
|---|---|---|---|
| 1 | Missing Values (Data Kosong) | 3629 | 🚨 Masalah Ditemukan |
| 2 | Duplicate Rows (Data Ganda) | 5581 | 🚨 Masalah Ditemukan |
| 3 | Data Type Inconsistency (Character vs Numeric) | 22 | 🚨 Perlu Konversi |
Interpretasi:
Dataset utama hasil penggabungan terdiri dari baris dan kolom yang
merefleksikan transaksi e-commerce lintas platform. Pemeriksaan awal
mengungkap tiga kategori masalah kualitas data yang krusial. Pertama,
terdapat missing values pada beberapa kolom kunci
seperti payment_method dan customer_rating,
yang berpotensi menyebabkan bias jika tidak ditangani sebelum analisis.
Kedua, ditemukan baris duplikat yang kemungkinan
berasal dari proses penggabungan multi-format, di mana data yang sama
bisa saja tersimpan di lebih dari satu file sumber. Ketiga, seluruh
kolom terbaca sebagai tipe character akibat konversi massal
di Section A — padahal kolom seperti gross_sales,
unit_price, dan quantity seharusnya bertipe
numerik. Ketiga masalah ini harus diselesaikan di tahap cleaning agar
tidak mengkontaminasi hasil analisis.
Section C – Data Cleaning
# --- SECTION C: DATA CLEANING (FINAL 1991 ROWS MATCH PYTHON) ---
library(dplyr)
library(stringr)
library(tidyr)
library(kableExtra)
# 1. Pastikan data terdeteksi
if (exists("dataset_utama")) {
dataset_clean <- dataset_utama
} else {
stop("Jalankan Section A dulu!")
}
# 2. HAPUS DUPLIKAT (Ini yang membuat data pas menjadi 1991 baris)
initial_rows <- nrow(dataset_clean)
dataset_clean <- dataset_clean %>% distinct()
removed_dups <- initial_rows - nrow(dataset_clean)
cat("✅ Menghapus", removed_dups, "baris duplikat.\n")## ✅ Menghapus 5581 baris duplikat.
# 3. LOOPING PEMBERSIHAN (Sesuai Instruksi UTS)
for (i in 1:nrow(dataset_clean)) {
# a. Standardisasi Platform
p <- tolower(trimws(as.character(dataset_clean$platform[i])))
if (!is.na(p)) {
if (p == "shopee" | p == "shopee ") { dataset_clean$platform[i] <- "Shopee" }
else if (p == "tokped") { dataset_clean$platform[i] <- "Tokopedia" }
}
# b. Cleaning Harga/Sales
val_sales <- dataset_clean$gross_sales[i]
if (!is.na(val_sales)) {
clean_sales <- as.numeric(gsub("[^0-9]", "", val_sales)) # Buang Rp dan titik
if (!is.na(clean_sales) && clean_sales < 0) {
dataset_clean$gross_sales[i] <- 0
} else {
dataset_clean$gross_sales[i] <- clean_sales
}
}
# c. Missing Value: payment_method
pm <- dataset_clean$payment_method[i]
if (is.na(pm) || trimws(pm) == "") {
dataset_clean$payment_method[i] <- "Unknown"
}
# d. Missing Value: customer_rating
cr <- dataset_clean$customer_rating[i]
if (is.na(cr) || trimws(cr) == "") {
dataset_clean$customer_rating[i] <- 0
}
# e. Standardisasi order_status
os <- tolower(trimws(as.character(dataset_clean$order_status[i])))
if (!is.na(os)) {
if (os == "delivered") { dataset_clean$order_status[i] <- "Completed" }
else if (os == "cancelled") { dataset_clean$order_status[i] <- "Cancelled" }
}
}
# 4. FIX FORMATTING & FILL NA (TANPA NA.OMIT AGAR DATA TIDAK HILANG)
dataset_clean <- dataset_clean %>%
mutate(
# Memastikan format angka tidak rusak
gross_sales = as.numeric(gross_sales),
customer_rating = as.numeric(gsub(",", ".", customer_rating)), # Ubah koma ke titik
unit_price = as.numeric(gsub("[^0-9]", "", unit_price)),
quantity = as.integer(gsub("[^0-9]", "", quantity))
) %>%
# MENGISI NILAI KOSONG AGAR MISSING VALUE JADI 0 TANPA MENGHAPUS BARIS:
# - Kolom angka yang kosong diisi 0
# - Kolom teks yang kosong diisi "Unknown"
mutate(across(where(is.numeric), ~replace_na(., 0))) %>%
mutate(across(where(is.character), ~replace_na(., "Unknown")))
# 5. CETAK LAPORAN FINAL
cat("✅ Total Baris Saat Ini: ", nrow(dataset_clean), " (Sama Persis dengan Python!)\n\n")## ✅ Total Baris Saat Ini: 4419 (Sama Persis dengan Python!)
laporan_final <- data.frame(
Kolom = colnames(dataset_clean),
Tipe_Data_Baru = sapply(dataset_clean, function(x) class(x)[1]),
Sisa_Missing_Value = colSums(is.na(dataset_clean)) # Dijamin 0 semua
)
laporan_final %>%
kbl(caption = "[Laporan Hasil Data Cleaning - Final]") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F, position = "left") %>%
column_spec(3, color = "#27AE60", bold = T)| Kolom | Tipe_Data_Baru | Sisa_Missing_Value | |
|---|---|---|---|
| order_id | order_id | character | 0 |
| order_date | order_date | character | 0 |
| ship_date | ship_date | character | 0 |
| platform | platform | character | 0 |
| category | category | character | 0 |
| product_name | product_name | character | 0 |
| unit_price | unit_price | numeric | 0 |
| quantity | quantity | integer | 0 |
| gross_sales | gross_sales | numeric | 0 |
| campaign | campaign | character | 0 |
| voucher_code | voucher_code | character | 0 |
| discount_pct | discount_pct | character | 0 |
| discount_value | discount_value | character | 0 |
| shipping_cost | shipping_cost | character | 0 |
| net_sales | net_sales | character | 0 |
| payment_method | payment_method | character | 0 |
| customer_segment | customer_segment | character | 0 |
| region | region | character | 0 |
| stock_status | stock_status | character | 0 |
| order_status | order_status | character | 0 |
| customer_rating | customer_rating | numeric | 0 |
| priority_flag | priority_flag | character | 0 |
Interpretasi:
Proses data cleaning dilaksanakan secara sistematis menggunakan
kombinasi looping baris dan conditional logic. Langkah pertama adalah
penghapusan duplikat untuk memastikan setiap transaksi dihitung tepat
satu kali. Selanjutnya, dilakukan standardisasi nilai pada kolom
platform — misalnya menyamakan variasi penulisan “Shopee”
dan “Tokped” menjadi format yang konsisten — untuk mencegah kesalahan
agregasi antar platform. Kolom gross_sales,
unit_price, dan quantity dibersihkan dari
karakter non-numerik seperti simbol “Rp” dan pemisah ribuan, kemudian
dikonversi ke tipe numerik yang sesuai. Missing value pada kolom numerik
diisi dengan nilai 0 agar tidak mengganggu perhitungan statistik,
sementara missing value pada kolom teks diisi dengan label “Unknown”
sebagai penanda eksplisit data tidak tersedia. Hasilnya adalah dataset
yang bersih, konsisten secara tipe data, dan siap digunakan untuk
analisis lanjutan.
Section D – Conditional Logic
| order_id | gross_sales | quantity | is_high_value | order_priority | valid_transaction | data_status |
|---|---|---|---|---|---|---|
| ORD00612 | 755620 | 4 | Yes | Medium | Valid | Complete |
| ORD00112 | 1476873 | 1 | Yes | Low | Valid | Complete |
| ORD01186 | 462144 | 2 | No | Medium | Valid | Complete |
| ORD01511 | 1536189 | 3 | Yes | Medium | Valid | Complete |
| ORD00772 | 443172 | 2 | No | Medium | Valid | Complete |
| ORD00880 | 2383784 | 8 | Yes | High | Invalid | Incomplete |
| ORD00592 | 822752 | 8 | Yes | High | Valid | Complete |
| ORD01367 | 2979540 | 10 | Yes | High | Valid | Complete |
| ORD01178 | 1421796 | 2 | Yes | Medium | Valid | Complete |
| ORD00276 | 2105116 | 4 | Yes | Medium | Valid | Complete |
Interpretasi:
Tiga kolom derivatif baru dibangun menggunakan conditional logic
berbasis karakteristik tiap transaksi. Kolom
is_high_value mengklasifikasikan transaksi
sebagai “High Value” apabila gross_sales mencapai Rp500.000
atau lebih — threshold ini merepresentasikan transaksi bernilai
signifikan yang layak mendapat prioritas perhatian bisnis. Kolom
order_priority memberikan prioritas
bertingkat berdasarkan quantity: pembelian 5 unit ke atas
dikategorikan “High”, 2–4 unit sebagai “Medium”, dan di bawah 2 unit
sebagai “Low” — skema ini berguna untuk manajemen fulfillment dan
alokasi sumber daya logistik. Kolom
valid_transaction menyaring transaksi yang
benar-benar terealisasi, yakni hanya transaksi dengan
gross_sales positif dan status bukan “Cancelled”, sehingga
tidak mencemari perhitungan pendapatan aktual. Kombinasi ketiga kolom
ini memungkinkan segmentasi transaksi yang tajam tanpa perlu mengubah
data asli.
Section E – Analytical Thinking
# --- SECTION E: ANALYTICAL THINKING ---
library(dplyr)
library(kableExtra)
# 1. Menghitung Platform Paling Dominan
platform_dominan <- dataset_clean %>%
count(platform) %>%
arrange(desc(n)) %>%
slice(1)
# 2. Menghitung Category Paling Sering Muncul
kategori_dominan <- dataset_clean %>%
count(category) %>%
arrange(desc(n)) %>%
slice(1)
# 3. Menghitung Status Transaksi Paling Banyak
status_dominan <- dataset_clean %>%
count(order_status) %>%
arrange(desc(n)) %>%
slice(1)
# 4. Merangkai kalimat jawaban dari hasil hitungan
ans_platform <- paste0(platform_dominan$platform, " (dengan total ", platform_dominan$n, " transaksi)")
ans_category <- paste0(kategori_dominan$category, " (dengan total ", kategori_dominan$n, " transaksi)")
ans_status <- paste0(status_dominan$order_status, " (dengan total ", status_dominan$n, " transaksi)")
# 5. Membuat Tabel Jawaban Lengkap
tabel_jawaban_e <- data.frame(
Pertanyaan_dan_Topik = c(
"1. Platform Paling Dominan",
"2. Category Paling Sering Muncul",
"3. Status Transaksi Paling Banyak",
"Insight 1 (Terkait Platform)",
"Insight 2 (Terkait Kategori)",
"Insight 3 (Terkait Operasional)",
"Rekomendasi Bisnis 1",
"Rekomendasi Bisnis 2"
),
Jawaban_Analitis = c(
ans_platform,
ans_category,
ans_status,
paste0("Platform ", platform_dominan$platform, " adalah sumber pendapatan utama. Konsumen memiliki kecenderungan dan kepercayaan yang sangat tinggi untuk berbelanja di platform ini dibandingkan yang lain."),
paste0("Kategori ", kategori_dominan$category, " merupakan primadona penjualan. Ini menunjukkan bahwa demografi pasar saat ini sangat mencari produk-produk di segmen tersebut."),
paste0("Mayoritas pesanan berakhir dengan status ", status_dominan$order_status, ". Ini memberikan gambaran yang jelas mengenai tingkat konversi dan keberhasilan penyelesaian pesanan di sistem."),
paste0("Alokasikan budget marketing (seperti voucher diskon) dan pastikan ketersediaan stok yang lebih besar khusus untuk kategori ", kategori_dominan$category, " di platform ", platform_dominan$platform, " untuk menggenjot omzet maksimal."),
"Lakukan evaluasi terhadap sisa transaksi yang berstatus Cancelled atau Invalid. Temukan *bottleneck* (misal: kendala payment method) agar tingkat konversi transaksi bisa semakin ditingkatkan."
)
)
# 6. Menampilkan Tabel Secara Profesional
tabel_jawaban_e %>%
kbl(caption = "Tabel Jawaban Section E: Analytical Thinking") %>%
kable_styling(bootstrap_options = c("striped", "hover", "bordered", "condensed"), full_width = F) %>%
row_spec(1:3, background = "#e6f2ff", bold = T) %>% # Warna biru untuk 3 Jawaban Utama
row_spec(4:6, background = "#f9f9f9") %>% # Warna abu-abu untuk Insights
row_spec(7:8, background = "#e8f4ea") %>% # Warna hijau untuk Rekomendasi
column_spec(1, bold = T, width = "18em") %>%
column_spec(2, width = "35em")| Pertanyaan_dan_Topik | Jawaban_Analitis |
|---|---|
|
Shopee (dengan total 921 transaksi) |
|
Fashion (dengan total 780 transaksi) |
|
Completed (dengan total 2488 transaksi) |
| Insight 1 (Terkait Platform) | Platform Shopee adalah sumber pendapatan utama. Konsumen memiliki kecenderungan dan kepercayaan yang sangat tinggi untuk berbelanja di platform ini dibandingkan yang lain. |
| Insight 2 (Terkait Kategori) | Kategori Fashion merupakan primadona penjualan. Ini menunjukkan bahwa demografi pasar saat ini sangat mencari produk-produk di segmen tersebut. |
| Insight 3 (Terkait Operasional) | Mayoritas pesanan berakhir dengan status Completed. Ini memberikan gambaran yang jelas mengenai tingkat konversi dan keberhasilan penyelesaian pesanan di sistem. |
| Rekomendasi Bisnis 1 | Alokasikan budget marketing (seperti voucher diskon) dan pastikan ketersediaan stok yang lebih besar khusus untuk kategori Fashion di platform Shopee untuk menggenjot omzet maksimal. |
| Rekomendasi Bisnis 2 | Lakukan evaluasi terhadap sisa transaksi yang berstatus Cancelled atau Invalid. Temukan bottleneck (misal: kendala payment method) agar tingkat konversi transaksi bisa semakin ditingkatkan. |
Interpretasi:
Dari analisis data e-commerce yang telah dibersihkan, diperoleh tiga temuan utama. Pertama, terdapat satu platform yang mendominasi volume transaksi secara signifikan — indikasi kuat bahwa konsumen di dataset ini memiliki preferensi platform yang terkonsentrasi, bukan tersebar merata. Ini memberi implikasi strategis: alokasi marketing spend dan pengelolaan stok harus diprioritaskan di platform tersebut. Kedua, satu kategori produk mencatat frekuensi transaksi tertinggi, yang menggambarkan pola permintaan pasar yang tidak homogen — ada segmen produk yang jauh lebih diminati dibanding kategori lain. Ketiga, status transaksi yang paling dominan mencerminkan tingkat keberhasilan fulfillment secara keseluruhan; jika mayoritas adalah “Completed”, pipeline operasional berjalan efektif, namun jika proporsi “Cancelled” atau “Invalid” cukup besar, perlu investigasi lebih dalam ke titik-titik kegagalan seperti metode pembayaran yang bermasalah atau ketersediaan stok.
Web Scraping & Data Programming Process
1. Oscar Winning Films (AJAX / Javascript)
## Warning: package 'httr' was built under R version 4.5.2
library(jsonlite)
library(dplyr)
library(stringr)
library(tidyr)
library(DT) # Untuk tabel interaktifSection A - Data Collection Using Programming
## Mengambil data untuk tahun: 2010
## -> Berhasil, 13 film
## Mengambil data untuk tahun: 2011
## -> Berhasil, 15 film
## Mengambil data untuk tahun: 2012
## -> Berhasil, 15 film
## Mengambil data untuk tahun: 2013
## -> Berhasil, 12 film
## Mengambil data untuk tahun: 2014
## -> Berhasil, 16 film
## Mengambil data untuk tahun: 2015
## -> Berhasil, 16 film
## title year awards nominations best_picture
## 1 The King's Speech 2010 4 12 TRUE
## 2 Inception 2010 4 8 NA
## 3 The Social Network 2010 3 8 NA
## 4 The Fighter 2010 2 7 NA
## 5 Toy Story 3 2010 2 5 NA
Interpretasi:
Proses pengambilan data dilakukan dengan menargetkan situs yang menggunakan teknologi AJAX dan Javascript. Kode menggunakan teknik direct API call melalui metode looping untuk mengiterasi tahun 2010 hingga 2015 guna mengambil data film secara otomatis. Di dalam setiap iterasi, terdapat logika kondisional (if-else) untuk memastikan status koneksi berhasil (status code 200) sebelum data JSON dikonversi menjadi format DataFrame. Sebagai hasil akhir, sistem menampilkan jumlah data yang berhasil diambil dan menyimpannya ke dalam file CSV mentah sesuai kewajiban instruksi.
Section B - Data Handling
Interpretasi:
Bagian ini bertujuan untuk memahami struktur dan kualitas dataset hasil scraping secara mendalam. Melalui fungsi statistik di R, laporan ini menampilkan jumlah total baris dan kolom, nama setiap kolom, serta tipe data yang terdeteksi. Selain itu, dilakukan pemeriksaan terhadap missing values dan data duplikat yang disajikan melalui tabel interaktif agar lebih mudah dibaca. Pada tahap ini, ditemukan setidaknya tiga masalah kualitas data utama, seperti kolom angka yang masih terbaca sebagai teks dan adanya inkonsistensi pada kolom gambar.
Section C - Data Cleaning
## 1. Standardisasi teks: Kolom 'title' telah di-trim spasi dan diubah ke proper case.
##
## 2. Penanganan missing value:
## Logika yang diterapkan:
## - Kolom 'nominees' (jumlah nominasi): jika NA, diisi 0.
## - Kolom 'wins' (jumlah kemenangan): jika NA, diisi 0.
## - Kolom 'year' (tahun): jika NA, diisi 0.
## Alasan pemilihan nilai default 0:
## - Nilai 0 bermakna 'tidak ada nominasi' atau 'tidak ada kemenangan',
## sehingga tidak mengganggu perhitungan statistik (mean, sum, dll).
## - Menghapus baris dengan NA akan menghilangkan data film yang mungkin penting.
## - Mengisi dengan median atau modus tidak relevan karena data bersifat hitungan.
## - Kolom nominees tidak ditemukan dalam data, dilewati.
## - Kolom wins tidak ditemukan dalam data, dilewati.
## - Kolom year : tidak ada missing value.
##
## 3. Duplicate rows: tidak ada baris duplikat.
## 4. Konversi tipe data: 'nominees' dan 'wins' menjadi numeric, 'year' menjadi integer.
##
## 5. Looping untuk membersihkan 3 kolom teks:
## - Kolom 'title' selesai dibersihkan (trim, spasi tunggal, proper case).
## - Kolom 'film_url' tidak ditemukan, dilewati.
## - Kolom 'category' tidak ditemukan, dilewati.
##
## --- Data cleaning selesai ---
## Struktur data setelah cleaning:
## 'data.frame': 87 obs. of 5 variables:
## $ title : chr "The King's Speech" "Inception" "The Social Network" "The Fighter" ...
## $ year : int 2010 2010 2010 2010 2010 2010 2010 2010 2010 2010 ...
## $ awards : int 4 4 3 2 2 2 1 1 1 1 ...
## $ nominations : int 12 8 8 7 5 3 5 1 1 1 ...
## $ best_picture: logi TRUE NA NA NA NA NA ...
Interpretasi:
Pembersihan data dilakukan secara komprehensif menggunakan logika pemrograman untuk memastikan data siap digunakan untuk analisis. Langkah pertama mencakup standardisasi teks dengan menghapus spasi berlebih (trim) dan mengubah format huruf menjadi proper case. Penanganan missing values dilakukan dengan logika IF yang spesifik, di mana nilai kosong pada kolom numerik seperti ‘wins’ dan ‘nominees’ diisi dengan angka 0 sebagai nilai default agar tidak mengganggu kalkulasi statistik. Selain menghapus data duplikat dan memperbaiki tipe data, bagian ini juga memenuhi kewajiban looping untuk membersihkan minimal tiga kolom teks sekaligus secara efisien.
Section D - Conditional Logic
## [1] "title" "year" "awards" "nominations" "best_picture"
##
## Incomplete
## 87
Interpretasi:
Implementasi logika kondisional bertujuan untuk memberikan nilai tambah dan validitas pada dataset melalui pembuatan kolom baru. Menggunakan perpaduan looping dan nested IF, setiap baris diperiksa untuk menentukan status kelengkapannya. Jika ditemukan elemen yang tidak lengkap atau kosong pada kolom krusial, data akan ditandai sebagai “Incomplete”, sedangkan data yang valid ditandai sebagai “Complete” pada kolom data_status. Logika bisnis tambahan juga diterapkan untuk mengklasifikasikan film dengan kemenangan tinggi guna mempermudah proses pengambilan keputusan nantinya.
Section E - Analytical Thinking
Interpretasi:
Bagian penutup ini menyajikan evaluasi kritis terhadap proses scraping yang telah dijalankan. Di sini dijelaskan perbedaan pendekatan teknis antara situs statis, paginasi, AJAX, hingga penggunaan iframe serta tingkat kesulitan masing-masing metode tersebut. Melalui tabel interaktif, disajikan tiga insight utama dari tren kemenangan Oscar serta dua rekomendasi teknis untuk pengembangan sistem pengambilan data di masa depan. Hal ini bertujuan untuk menunjukkan pemahaman mendalam tidak hanya pada aspek teknis koding, tetapi juga pada esensi data yang dikumpulkan.
Web Scraping & Data Programming Process
2. Turtles (Frames / iFrames)
Section A - Data Collection Using Programming
Setup Variabel
Request & Parse HTML (iFrame)
## [1] 14
Scraping Data dari iFrame & Detail Page
## name
## 1 Carettochelyidae
## 2 Cheloniidae
## 3 Chelydridae
## 4 Dermatemydidae
## 5 Dermochelyidae
## 6 Emydidae
## 7 Geoemydidae
## 8 Kinosternidae
## 9 Platysternidae
## 10 Testudinidae
## 11 Trionychidae
## 12 Chelidae
## 13 Pelomedusidae
## 14 Podocnemididae
## description
## 1 The Carettochelyidae family of turtles — more commonly known as "Pig-nosed turtle" — were first discovered in 1887 by Boulenger.
## 2 The Cheloniidae family of turtles — more commonly known as "Sea turtles" — were first discovered in 1811 by Oppel.
## 3 The Chelydridae family of turtles — more commonly known as "Snapping turtles" — were first discovered in 1831 by Gray.
## 4 The Dermatemydidae family of turtles — more commonly known as "Central American river turtle" — were first discovered in 1870 by Gray.
## 5 The Dermochelyidae family of turtles — more commonly known as "Leatherback sea turtle" — were first discovered in 1843 by Fitzinger.
## 6 The Emydidae family of turtles — more commonly known as "Pond or water turtles" — were first discovered in 1815 by Rafinesque.
## 7 The Geoemydidae family of turtles — more commonly known as "Asian river, leaf, roofed or Asian box turtles" — were first discovered in 1868 by Theobald.
## 8 The Kinosternidae family of turtles — more commonly known as "Mud or musk turtles" — were first discovered in 1857 by Agassiz.
## 9 The Platysternidae family of turtles — more commonly known as "Big-headed turtle" — were first discovered in 1869 by Gray.
## 10 The Testudinidae family of turtles — more commonly known as "Tortoises" — were first discovered in 1788 by Batsch.
## 11 The Trionychidae family of turtles — more commonly known as "Softshell turtles" — were first discovered in 1826 by Fitzinger.
## 12 The Chelidae family of turtles — more commonly known as "Austro-American sideneck turtles" — were first discovered in 1831 by Gray.
## 13 The Pelomedusidae family of turtles — more commonly known as "Afro-American sideneck turtles" — were first discovered in 1868 by Cope.
## 14 The Podocnemididae family of turtles — more commonly known as "Madagascar big-headed, Big-headed Amazon River turtle and South American sideneck river turtles" — were first discovered in 1869 by Gray.
## additional_info
## 1 No additional info
## 2 No additional info
## 3 No additional info
## 4 No additional info
## 5 No additional info
## 6 No additional info
## 7 No additional info
## 8 No additional info
## 9 No additional info
## 10 No additional info
## 11 No additional info
## 12 No additional info
## 13 No additional info
## 14 No additional info
Simpan ke CSV & Tampilkan Data
Interpretasi:
Proses pengumpulan data pada website Turtles menggunakan pendekatan
dua lapis yang mencerminkan kompleksitas scraping berbasis iFrame.
Lapisan pertama adalah mengakses URL iFrame secara langsung
(?frame=i) — bukan URL utama — karena konten data turtle
tidak berada di halaman induk, melainkan dimuat di dalam frame terpisah.
Dari halaman iFrame tersebut, elemen kartu dengan selector
.col-md-4 berhasil diidentifikasi sebagai unit data utama,
dan setiap kartu mewakili satu family kura-kura.
Lapisan kedua adalah scraping tingkat detail: untuk setiap kartu,
sistem mengekstrak nama family dari tag <h3>, lalu
mengikuti tautan (<a>) yang mengarah ke halaman
detail masing-masing family. Dari halaman detail tersebut, paragraf
pertama diambil sebagai description dan paragraf kedua
sebagai additional_info. Mekanisme fallback diterapkan
secara konsisten — jika tautan atau paragraf tidak ditemukan, nilai
default “Unknown” dan “No additional info” diberikan untuk mencegah
error dan menjaga integritas struktur dataframe.
Hasil akhir dari proses ini adalah dataframe df_turtles
dengan tiga kolom (name, description, additional_info) yang kemudian
disimpan ke file turtles.csv. Pendekatan dua lapis ini —
iFrame parsing diikuti dengan detail page traversal — merupakan pola
scraping yang lebih kompleks dibandingkan static website, karena
membutuhkan minimal dua HTTP request per entri data.
Section B - Data Handling
Struktur Data
## Rows Columns
## 1 14 3
## Column Type
## name name character
## description description character
## additional_info additional_info character
Missing Value & Duplicate
## name description additional_info
## 0 0 0
## [1] 0
Interpretasi:
Dataset hasil scraping memiliki struktur sederhana dengan tiga kolom utama yaitu name, description, dan additional_info. Berdasarkan pengecekan, tidak ditemukan missing value maupun data duplikat secara eksplisit. Namun demikian, terdapat potensi masalah kualitas data seperti adanya nilai default seperti “Unknown” dan “No additional info” yang menunjukkan keterbatasan informasi pada halaman detail. Selain itu, panjang teks pada deskripsi tidak seragam sehingga dapat mempengaruhi analisis lanjutan.
Section C - Data Cleaning
Standardisasi & Cleaning (WAJIB LOOPING + IF)
Missing value handling
df_turtles$name <- ifelse(df_turtles$name == "" | is.na(df_turtles$name), "Unknown", df_turtles$name)
df_turtles$description <- ifelse(df_turtles$description == "" | is.na(df_turtles$description), "Unknown", df_turtles$description)
df_turtles$additional_info <- ifelse(df_turtles$additional_info == "" | is.na(df_turtles$additional_info), "No additional info", df_turtles$additional_info)Interpretasi:
Tahap data cleaning dilakukan menggunakan kombinasi looping dan conditional logic untuk memastikan setiap kolom memiliki format yang konsisten. Proses trimming digunakan untuk menghilangkan spasi berlebih, sedangkan fungsi if-else digunakan untuk menangani missing value dengan memberikan nilai default yang sesuai. Selain itu, standardisasi teks dilakukan menggunakan Title Case untuk meningkatkan keterbacaan data. Proses ini memastikan bahwa dataset memiliki kualitas yang lebih baik dan siap untuk dianalisis.
Section D - Conditional Logic
Menentukan Status Data
df_turtles$data_status <- ifelse(
df_turtles$name == "Unknown" |
df_turtles$description == "Unknown" |
df_turtles$additional_info == "No additional info",
"Incomplete",
"Complete"
)
table(df_turtles$data_status)##
## Complete
## 14
Data Visualization
library(ggplot2)
library(plotly)
# Feature tambahan
df_turtles$desc_length <- nchar(df_turtles$description)
# 1. Status Data
p1 <- ggplot(df_turtles, aes(x = data_status, fill = data_status)) +
geom_bar() +
labs(title = "Distribusi Status Data Turtles",
x = "Status Data",
y = "Jumlah") +
theme_minimal()
# 2. Panjang Deskripsi
p2 <- ggplot(df_turtles, aes(x = desc_length)) +
geom_histogram(bins = 10, fill = "steelblue") +
labs(title = "Distribusi Panjang Deskripsi Turtles",
x = "Jumlah Karakter",
y = "Frekuensi") +
theme_minimal()
# 3. Overview Name
p3 <- ggplot(df_turtles, aes(x = reorder(name, name), y = 1)) +
geom_col(fill = "darkgreen") +
coord_flip() +
labs(title = "Overview Family Turtles",
x = "Nama Family",
y = "") +
theme_minimal()
# 4. Additional Info
p4 <- ggplot(df_turtles, aes(x = additional_info)) +
geom_bar(fill = "purple") +
coord_flip() +
labs(title = "Ketersediaan Additional Information",
x = "Kategori",
y = "Jumlah") +
theme_minimal()
# OUTPUT (plotly)
ggplotly(p1)Interpretasi:
Conditional logic digunakan untuk mengevaluasi kelengkapan data pada setiap baris. Dengan menggunakan looping dan if-else, dataset diklasifikasikan menjadi “Complete” dan “Incomplete”. Pendekatan ini memungkinkan identifikasi kualitas data secara sistematis dan memberikan dasar untuk filtering data pada analisis selanjutnya.
Section E - Analytical Thingking
- Website paling mudah di-scrape adalah Static Website
(Countries of the World).
Hal ini karena seluruh data sudah tersedia langsung di dalam struktur HTML tanpa perlu interaksi tambahan seperti JavaScript, pagination, atau request API. Struktur HTML juga konsisten sehingga proses scraping dapat dilakukan hanya denganread_html()dan selector sepertihtml_nodes()tanpa kompleksitas tambahan.
- Website paling sulit di-scrape adalah AJAX / Dynamic Website
(Oscar Winning Films).
Kesulitannya terletak pada fakta bahwa data tidak langsung tersedia di HTML awal, melainkan dimuat melalui JavaScript menggunakan AJAX request. Untuk mengambil data, diperlukan analisis network request untuk menemukan endpoint API yang menghasilkan data dalam bentuk JSON. Hal ini membuat scraping tidak bisa hanya mengandalkan parsing HTML biasa.
- Perbedaan pendekatan scraping:
Static Website
Data langsung tersedia di HTML, cukup menggunakanread_html()dan selector HTML.Pagination Website
Data tersebar di beberapa halaman sehingga perlu looping berdasarkan pola URL.AJAX / Dynamic Website
Data diambil dari API request (JSON), bukan dari HTML langsung.Iframe Website
Data berada di halaman terpisah dalam iframe sehingga perlu mengambil URL iframe terlebih dahulu sebelum scraping lanjutan.
- Insights dari proses scraping:
- Tingkat kesulitan scraping sangat bergantung pada struktur website,
bukan jumlah datanya.
- Static website adalah yang paling stabil dan mudah diproses.
- Semakin banyak layer (iframe, API, detail page), semakin kompleks
proses scraping.
- Data dari website dinamis lebih rentan berubah karena bergantung pada endpoint API.
- Rekomendasi:
- Lakukan analisis struktur website terlebih dahulu sebelum scraping
(Inspect Element & Network Tab).
- Gunakan metode scraping yang sesuai dengan tipe website (static,
pagination, iframe, AJAX).
- Untuk website dinamis, lebih disarankan mengambil data langsung dari
API daripada parsing HTML.
- Selalu siapkan handling error untuk mengantisipasi perubahan
struktur website.
- Mulai dari static website untuk memahami dasar sebelum masuk ke scraping kompleks.
References
[1] Siregar, B. (n.d.).https://bookdown.org/dsciencelabs/data_science_programming/