Praktikum Visualisasi Data Interaktif
Setelah praktikum ini, Anda mampu:
- Menjelaskan struktur dasar aplikasi Shiny (UI + Server).
- Menggunakan elemen input Shiny: dropdown, slider, dan checkbox.
- Menampilkan output berupa plot, teks, dan tabel yang reaktif.
- Membuat plot interaktif yang merespons perubahan input pengguna.
- Menjalankan aplikasi Shiny dari RStudio.
Catatan Penting: Kode Shiny di halaman ini tidak dapat dijalankan langsung di browser. Salin setiap blok kode ke RStudio, simpan sebagai app.R, lalu klik Run App.
1. Apa Itu Visualisasi Interaktif?
Visualisasi statis (seperti plot() atau ggplot2) menghasilkan gambar tetap yang tidak bisa diubah oleh pengguna. Visualisasi interaktif memungkinkan pengguna untuk:
- Memilih variabel yang ingin ditampilkan
- Mengatur parameter (jumlah bin, rentang data)
- Menjelajahi data secara mandiri tanpa menulis kode
Perbandingan Cepat
| Statis | Interaktif (Shiny) | |
|---|---|---|
| Output | Gambar PNG/PDF | Aplikasi web |
| Pengguna | Hanya melihat | Bisa berinteraksi |
| Kebutuhan | plot() / ggplot2 |
library(shiny) |
| Cocok untuk | Laporan, paper | Dashboard, eksplorasi |
Pilih visualisasi interaktif ketika:
(1) Pengguna perlu menjelajahi berbagai subset data
(2) Ada banyak variabel yang bisa dipilih
(3) Audiens adalah pengambil keputusan non-teknis yang ingin eksplorasi mandiri
Tetap gunakan visualisasi statis untuk laporan akademis, jurnal, atau ketika hanya ada satu tampilan yang perlu dikomunikasikan.
Pertanyaan: Untuk presentasi hasil skripsi, mana yang lebih tepat, statis atau interaktif?
2. Struktur Dasar Aplikasi Shiny
Setiap aplikasi Shiny tersusun dari dua bagian:
app.R
├── ui → Tampilan: apa yang dilihat pengguna
└── server → Logika: bagaimana data diproses
2.1 Template Paling Minimal
# Salin ke RStudio → simpan sebagai app.R → klik Run App
library(shiny)
ui <- fluidPage(
titlePanel("Aplikasi Pertama Saya"),
sidebarLayout(
sidebarPanel(
p("Kontrol di sini.")
),
mainPanel(
p("Output di sini.")
)
)
)
server <- function(input, output) {
# Logika di sini (kosong untuk sekarang)
}
shinyApp(ui = ui, server = server)Pemisahan UI dan Server mengikuti prinsip separation of concerns:
- UI mendeskripsikan apa yang ditampilkan
- Server mendeskripsikan bagaimana data diproses
Ini membuat kode lebih mudah dibaca, diuji, dan dimodifikasi. Perubahan tampilan tidak perlu menyentuh logika, dan sebaliknya.
Pertanyaan: Jika Anda ingin mengubah warna latar belakang aplikasi, di bagian mana (UI atau Server) perubahan itu dilakukan?
3. Histogram Interaktif dengan Input Dropdown dan Slider
Aplikasi ini memungkinkan pengguna memilih variabel dari mtcars dan mengatur jumlah bin histogram.
library(shiny)
ui <- fluidPage(
titlePanel("Histogram Interaktif – mtcars"),
sidebarLayout(
sidebarPanel(
# Input 1: dropdown untuk memilih variabel
selectInput(
inputId = "variabel",
label = "Pilih Variabel:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)],
selected = "mpg"
),
# Input 2: slider untuk jumlah bin
sliderInput(
inputId = "bins",
label = "Jumlah Bin:",
min = 5, max = 50, value = 15
)
),
mainPanel(
# Output: histogram
plotOutput("histogram"),
hr(),
# Output: ringkasan statistik
verbatimTextOutput("ringkasan")
)
)
)
server <- function(input, output) {
# reactive(): hitung data saat input berubah
data_dipilih <- reactive({
mtcars[[input$variabel]]
})
# renderPlot(): buat histogram
output$histogram <- renderPlot({
x <- data_dipilih()
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "steelblue", border = "white",
main = paste("Histogram:", input$variabel),
xlab = input$variabel, ylab = "Frekuensi")
})
# renderPrint(): tampilkan ringkasan
output$ringkasan <- renderPrint({
summary(data_dipilih())
})
}
shinyApp(ui = ui, server = server)Yang Terjadi Saat Anda Mengubah Input
- Pengguna geser slider: nilai
input$binsberubah - Shiny mendeteksi perubahan pada
data_dipilih(reactive) output$histogramotomatis di-render ulang dengan data baru
reactive() membuat “blok kode yang otomatis dijalankan ulang” saat input yang digunakannya berubah. Tanpa reactive(), Anda harus menduplikasi kode pemrosesan di setiap renderXxx(). Dengan reactive(), perhitungan cukup dilakukan sekali dan hasilnya bisa dipakai di banyak output.
Pertanyaan: Mengapa data_dipilih didefinisikan sebagai reactive() dan bukan langsung mtcars[[input$variabel]] di dalam renderPlot()?
4. Menambahkan Tabel Interaktif
Paket DT menampilkan data frame sebagai tabel yang bisa dicari, diurutkan, dan difilter.
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("Plot + Tabel Interaktif"),
sidebarLayout(
sidebarPanel(
selectInput("variabel", "Pilih Variabel:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)]),
sliderInput("bins", "Jumlah Bin:", min = 5, max = 50, value = 15)
),
mainPanel(
# tabsetPanel: tampilkan output dalam tab terpisah
tabsetPanel(
tabPanel("Histogram", plotOutput("histogram")),
tabPanel("Data Lengkap", DTOutput("tabel"))
)
)
)
)
server <- function(input, output) {
output$histogram <- renderPlot({
x <- mtcars[[input$variabel]]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "steelblue", border = "white",
main = paste("Histogram:", input$variabel), xlab = input$variabel)
})
output$tabel <- renderDT({
datatable(mtcars, options = list(pageLength = 10, scrollX = TRUE))
})
}
shinyApp(ui = ui, server = server)tabsetPanel() memisahkan beberapa output ke dalam tab berbeda, sehingga halaman tidak terlalu penuh. Pengguna bisa fokus pada satu tampilan sekaligus tanpa scrolling berlebihan. Ini praktik UI yang baik ketika ada beberapa jenis output (plot, tabel, ringkasan).
Pertanyaan: Apa perbedaan antara renderPlot() dan renderDT()? Mengapa tidak bisa ditukar?
5. Scatter Plot Interaktif
Pengguna bisa memilih variabel untuk sumbu X dan Y, serta mengaktifkan garis regresi.
library(shiny)
ui <- fluidPage(
titlePanel("Scatter Plot Interaktif – mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("xvar", "Sumbu X:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)],
selected = "wt"),
selectInput("yvar", "Sumbu Y:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)],
selected = "mpg"),
checkboxInput("garis", "Tampilkan garis regresi", value = FALSE)
),
mainPanel(
plotOutput("scatter"),
textOutput("korelasi")
)
)
)
server <- function(input, output) {
output$scatter <- renderPlot({
x <- mtcars[[input$xvar]]
y <- mtcars[[input$yvar]]
plot(x, y,
xlab = input$xvar, ylab = input$yvar,
main = paste("Scatter:", input$xvar, "vs", input$yvar),
pch = 16, col = "steelblue", cex = 1.3)
if (input$garis) {
abline(lm(y ~ x), col = "tomato", lwd = 2)
}
})
output$korelasi <- renderText({
r <- cor(mtcars[[input$xvar]], mtcars[[input$yvar]])
paste("Korelasi Pearson:", round(r, 4))
})
}
shinyApp(ui = ui, server = server)checkboxInput() mengembalikan nilai TRUE (dicentang) atau FALSE (tidak dicentang). Di server, nilai ini diakses via input$garis. Pernyataan if (input$garis) { ... } berarti “jalankan kode di dalam kurung kurawal hanya jika kotak dicentang”.
Pertanyaan: Jika korelasi antara dua variabel mendekati 0, apakah masuk akal menampilkan garis regresi? Mengapa?
6. Aplikasi Multi-Plot: Satu Dropdown, Banyak Jenis Visualisasi
Bagian ini mencontohkan pola yang sering dibutuhkan dalam tugas dan proyek nyata: satu aplikasi Shiny dengan dua dropdown: satu untuk memilih variabel, satu untuk memilih jenis visualisasi. Output berubah secara reaktif mengikuti kedua pilihan.
Dataset yang digunakan: airquality (bawaan R), berisi pengukuran kualitas udara harian (Ozone, Solar.R, Wind, Temp, dll.) yang strukturnya mirip dengan data observasi lapangan multi-kolom.
Jika data Anda bersumber dari file .xlsx, gunakan paket readxl:
install.packages("readxl") # jalankan sekali
library(readxl)
# Letakkan file .xlsx di folder yang sama dengan app.R, lalu:
data_saya <- read_excel("nama_file.xlsx")Setelah data_saya terbaca, gunakan persis seperti data frame biasa di semua kode Shiny di bawah ini, ganti saja nama airquality dengan data_saya.
6.1 Kerangka Lengkap
library(shiny)
library(DT)
# ── Persiapan data ──────────────────────────────────────────
# Gunakan airquality; hapus baris yang ada NA agar plot tidak error
data_plot <- na.omit(airquality)
# Kolom numerik yang bisa divisualisasikan
var_numerik <- c("Ozone", "Solar.R", "Wind", "Temp")
# ── UI ──────────────────────────────────────────────────────
ui <- fluidPage(
titlePanel("Eksplorasi Data Kualitas Udara"),
sidebarLayout(
sidebarPanel(
# Dropdown 1: pilih variabel X (khusus Scatter Plot)
conditionalPanel(
condition = "input.jenis_plot == 'Scatter Plot'",
selectInput(
inputId = "variabel_x",
label = "Pilih Variabel X:",
choices = var_numerik,
selected = "Ozone"
)
),
# Dropdown 2: pilih variabel Y (Utama)
selectInput(
inputId = "variabel_y",
label = "Pilih Variabel Y:",
choices = var_numerik,
selected = "Temp"
),
# Dropdown 3: pilih jenis plot
selectInput(
inputId = "jenis_plot",
label = "Jenis Visualisasi:",
choices = c("Scatter Plot", "Line Plot", "Histogram", "Tabel Data"),
selected = "Scatter Plot"
)
),
mainPanel(
# Output plot ATAU tabel, ditampilkan secara kondisional
uiOutput("output_utama")
)
)
)
# ── Server ───────────────────────────────────────────────────
server <- function(input, output) {
# Ambil kolom Y yang dipilih sebagai vektor
data_y <- reactive({
data_plot[[input$variabel_y]]
})
# Ambil kolom X yang dipilih (khusus scatter plot)
data_x <- reactive({
data_plot[[input$variabel_x]]
})
# Render output yang sesuai berdasarkan jenis plot
output$output_utama <- renderUI({
if (input$jenis_plot == "Tabel Data") {
DTOutput("tabel") # tampilkan tabel DT
} else {
plotOutput("plot_grafik") # tampilkan grafik
}
})
# Render grafik (scatter / line / histogram)
output$plot_grafik <- renderPlot({
y <- data_y()
lab_y <- input$variabel_y
switch(input$jenis_plot,
"Scatter Plot" = {
x <- data_x()
lab_x <- input$variabel_x
plot(x, y,
pch = 16, col = "steelblue", cex = 1.1,
main = paste("Scatter Plot:", lab_x, "vs", lab_y),
xlab = lab_x, ylab = lab_y)
},
"Line Plot" = {
# Sumbu X berupa urutan observasi
x_idx <- seq_along(y)
plot(x_idx, y,
type = "l", col = "tomato", lwd = 2,
main = paste("Line Plot:", lab_y),
xlab = "Observasi ke-", ylab = lab_y)
points(x_idx, y, pch = 16, col = "tomato", cex = 0.7)
},
"Histogram" = {
hist(y,
col = "steelblue",
border = "white",
main = paste("Histogram:", lab_y),
xlab = lab_y,
ylab = "Frekuensi")
}
)
})
# Render tabel
output$tabel <- renderDT({
datatable(
data_plot,
options = list(pageLength = 10, scrollX = TRUE),
caption = "Dataset: airquality (NA sudah dihapus)"
)
})
}
shinyApp(ui = ui, server = server)Cara Kerja Pola Ini
| Komponen | Fungsi |
|---|---|
conditionalPanel(...) |
Menampilkan dropdown Variabel X hanya jika “Scatter Plot” dipilih |
selectInput("jenis_plot", ...) |
Dropdown untuk memilih jenis visualisasi |
uiOutput("output_utama") |
Placeholder yang bisa berubah antara plot dan tabel |
renderUI({ if (...) DTOutput else plotOutput }) |
Menentukan jenis output secara kondisional |
switch(input$jenis_plot, ...) |
Memilih kode plot yang dijalankan |
reactive({ data_plot[[input$variabel_y]] }) |
Kolom data yang dipilih, dihitung sekali, dipakai ulang |
plotOutput dan DTOutput adalah dua jenis elemen HTML yang berbeda: tidak bisa ditukar hanya dengan kondisi di server. renderUI() memungkinkan kita mengganti elemen UI itu sendiri secara dinamis dari server, sehingga tabel dan plot bisa benar-benar saling menggantikan di area yang sama.
Cukup ganti tiga hal:
Nama dataset: ganti
airqualitydandata_plotdengan nama data frame Anda.Daftar variabel: ganti isi
var_numerikdengan nama kolom numerik di data Anda. Cara otomatis:var_numerik <- colnames(data_saya)[sapply(data_saya, is.numeric)]Judul aplikasi: sesuaikan
titlePanel()dan teksmaindi setiap plot.
Jika switch() tidak menemukan nilai yang cocok, fungsi ini mengembalikan NULL secara diam-diam, tidak ada error, tapi renderPlot() akan menghasilkan plot kosong atau error tak terduga.
Cara menambahkan fallback (nilai default) adalah dengan meletakkan ekspresi tanpa nama di posisi terakhir switch():
switch(input$jenis_plot,
"Scatter Plot" = { plot(x, y, ...) },
"Line Plot" = { plot(x_idx, y, type = "l", ...) },
"Histogram" = { hist(y, ...) },
# Fallback: dijalankan jika tidak ada yang cocok
plot(seq_along(y), y, main = "Plot Default", xlab = "Index", ylab = lab_y)
)Argumen terakhir tanpa label string berfungsi seperti default di switch bahasa lain. Ini berguna sebagai pengaman jika daftar choices di UI tidak sinkron dengan kondisi di server.
7. Referensi Elemen Input Shiny
| Fungsi | Jenis Kontrol | Nilai yang Dikembalikan |
|---|---|---|
selectInput() |
Dropdown | String (nama pilihan) |
sliderInput() |
Slider | Angka |
checkboxInput() |
Kotak centang | TRUE / FALSE |
radioButtons() |
Tombol pilihan | String |
textInput() |
Kotak teks | String |
numericInput() |
Kotak angka | Angka |
8. Latihan
Latihan 1: Tambahkan Input Warna
Modifikasi aplikasi Bagian 3 (Histogram) dengan menambahkan dropdown untuk memilih warna histogram.
library(shiny)
ui <- fluidPage(
titlePanel("Histogram dengan Pilihan Warna"),
sidebarLayout(
sidebarPanel(
selectInput("variabel", "Variabel:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)]),
sliderInput("bins", "Jumlah Bin:", min = 5, max = 50, value = 15),
# Tambahkan selectInput untuk warna di sini
selectInput(
inputId = "___", # isi nama input ID
label = "Warna:",
choices = c("___", "___", "___"), # isi 3 pilihan warna
selected = "___"
)
),
mainPanel(
plotOutput("histogram")
)
)
)
server <- function(input, output) {
output$histogram <- renderPlot({
x <- mtcars[[input$variabel]]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins,
col = input$___, # gunakan input warna
border = "white",
main = paste("Histogram:", input$variabel),
xlab = input$variabel)
})
}
shinyApp(ui = ui, server = server)# Bagian UI — tambahkan di sidebarPanel:
selectInput(
inputId = "warna",
label = "Warna:",
choices = c("steelblue", "tomato", "forestgreen"),
selected = "steelblue"
)
# Bagian Server — ubah col di hist():
col = input$warna,Latihan 2: Tambahkan Output Teks Mean dan Median
Lanjutkan dari Latihan 1. Tambahkan textOutput("statistik") di UI dan renderText() di server yang menampilkan nilai mean dan median variabel yang dipilih.
# Di bagian mainPanel (UI), tambahkan:
# textOutput("statistik")
# Di bagian server, tambahkan:
output$statistik <- renderText({
x <- mtcars[[input$variabel]]
paste0(
"Mean: ", round(___(x), 2), # isi fungsi mean
" | Median: ", round(___(x), 2) # isi fungsi median
)
})# UI — di dalam mainPanel():
plotOutput("histogram"),
textOutput("statistik")
# Server:
output$statistik <- renderText({
x <- mtcars[[input$variabel]]
paste0(
"Mean: ", round(mean(x), 2),
" | Median: ", round(median(x), 2)
)
})Latihan 3: Scatter Plot dengan Pilihan Kelompok Warna
Modifikasi aplikasi Scatter Plot (Bagian 5) agar titik-titik diwarnai berdasarkan variabel kategori yang dipilih pengguna (misalnya cyl atau gear).
library(shiny)
# Variabel yang bersifat kategori di mtcars
var_kategori <- c("cyl", "gear", "carb", "vs", "am")
ui <- fluidPage(
titlePanel("Scatter Plot dengan Pewarnaan Kelompok"),
sidebarLayout(
sidebarPanel(
selectInput("xvar", "Sumbu X:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)],
selected = "wt"),
selectInput("yvar", "Sumbu Y:",
choices = colnames(mtcars)[sapply(mtcars, is.numeric)],
selected = "mpg"),
selectInput("grp", "Warna berdasarkan:",
choices = ___) # isi dengan var_kategori
),
mainPanel(plotOutput("scatter"))
)
)
server <- function(input, output) {
output$scatter <- renderPlot({
x <- mtcars[[input$xvar]]
y <- mtcars[[input$yvar]]
grp <- as.factor(mtcars[[___]]) # isi dengan input grup
plot(x, y,
col = as.integer(grp),
pch = 16, cex = 1.3,
xlab = input$xvar, ylab = input$yvar,
main = paste("Scatter:", input$xvar, "vs", input$yvar,
"— warna:", input$grp))
legend("topright", legend = levels(grp),
col = seq_along(levels(grp)), pch = 16)
})
}
shinyApp(ui = ui, server = server)# Di selectInput untuk grup:
choices = var_kategori
# Di renderPlot, untuk mengakses variabel grup:
grp <- as.factor(mtcars[[input$grp]])as.factor() mengubah kolom numerik seperti cyl menjadi kategori, sehingga setiap nilai unik mendapat warna berbeda. as.integer(grp) mengubah faktor menjadi angka untuk digunakan sebagai indeks warna.
Ringkasan
| Fungsi Shiny | Tipe | Keterangan |
|---|---|---|
selectInput() |
Input | Dropdown pilihan |
sliderInput() |
Input | Slider angka |
checkboxInput() |
Input | Kotak centang TRUE/FALSE |
reactive() |
Reaktif | Kalkulasi yang diperbarui otomatis |
renderPlot() |
Output | Menghasilkan grafik |
renderPrint() |
Output | Menghasilkan teks/ringkasan |
renderText() |
Output | Menghasilkan teks sederhana |
renderDT() |
Output | Menghasilkan tabel interaktif |