Introduction
In this tutorial, we will walk through the creation of an interactive data exploration application using R Shiny. This app allows users to filter data, view various charts, and download them for further analysis.
Prerequisites
- Basic understanding of R programming
- R and RStudio installed
- Shiny, ggplot2, and DT packages installed
App Overview
Our R Shiny app includes:
- A filterable table
- Interactive charts including bar plots, scatter plots, and line plots
- Data download functionality
Getting Started
First, ensure you have the required libraries:
library(shiny)
library(DT)
library(ggplot2)
Data Preparation
Load and preprocess your data. In our case, we are reading from a CSV file and creating bins for age and income:
dataset = read.csv("dataset.csv")
# Create bins for age and income
dataset$AGE_Bin = cut(dataset$AGE,5,include.lowest = TRUE)
dataset$INCOME_Bin = cut(dataset$INCOME,5,include.lowest = TRUE,dig.lab = 6)
The code contains the UI and Server in two parts. I will layout the complete code of each part here, and later in the article, I will delve into the very intuitive UI design in Shiny.
Building the UI
The user interface (UI) is designed with fluidPage
for a responsive layout.
ui <- fluidPage(
h1("Rshiny Homework"),
h2("Demographic Exploartion"),
h3("Filterable Table"),
DT::dataTableOutput("table"),
br(),
h3("Charts"),
selectInput(
"option",
"Demography",
c("AGE_Bin","INCOME_Bin","GENDER"),
selected = NULL,
multiple = FALSE,
selectize = TRUE,
width = NULL,
size = NULL
),
actionButton("gobutton", "View Chart", class = "btn-success"),
plotOutput("disPlot"),
downloadButton(outputId = "disPlot_download", label = "Download Chart",class = "btn-success"),
br(),
hr(),
br(),
h3("Relationship Between Variables"),
tabsetPanel(
tabPanel("Scatter",
plotOutput("Scatter", brush="selected_range"),
br(),
downloadButton(outputId = "scatter_download", label = "Download Chart",class = "btn-success"),
br(),
br(),
DT::dataTableOutput("brushed_table")
),
tabPanel("Distribution",
plotOutput("displot2"),
downloadButton(outputId = "displot2_download", label = "Download Chart",class = "btn-success"),
br(),
plotOutput("displot3"),
downloadButton(outputId = "displot3_download", label = "Download Chart",class = "btn-success")
)
),
br(),
hr(),
br(),
h3("Line Plot"),
plotOutput("lineplot"),
downloadButton(outputId = "lineplot_download", label = "Download Chart",class = "btn-success"),
br(),
plotOutput("lineplot2"),
downloadButton(outputId = "lineplot2_download", label = "Download Chart",class = "btn-success")
)
Server Logic
The server function contains the logic for rendering plots and tables based on user input. As you may find, all backend data handling and visual design goes in here.
server <- function(input,output, session) {
library(ggplot2)
library(shiny)
library(DT)
# library(stringr)
#setwd("C:/Users/kli4/Downloads/Shiny_HW")
dataset = read.csv("dataset.csv")
dataset$AGE_Bin = cut(dataset$AGE,5,include.lowest = TRUE)
dataset$INCOME_Bin = cut(dataset$INCOME,5,include.lowest = TRUE,dig.lab = 6)
# dataset$INCOME_Bin <- lapply(strsplit(gsub("]|[[(]", "", levels(dataset$INCOME_Bin)), ","),
# prettyNum, big.mark=".", decimal.mark=",", input.d.mark=".", preserve.width="individual")
plot_var <- eventReactive(input$gobutton,{
selection <- input$option
data_agg <-aggregate(x=dataset$Customer, by=list(SELECTION=dataset[,c(selection)],TREATMENT = dataset[,"TREATMENT"]),length)
names(data_agg) = c("SELECTION","TREATMENT", "Customer")
return(data_agg)
})
output$disPlot <- renderPlot({
displot = ggplot(plot_var(), aes(x=SELECTION,y=Customer,fill=TREATMENT)) + geom_bar(position="stack",stat="identity")
output$disPlot_download <- downloadHandler(
filename = function() { paste(input$option, '.jpg', sep='') },
content = function(file){
ggsave(file,plot=displot)
})
displot
})
output$table <- DT::renderDataTable(datatable(dataset))
scatter_plot <- ggplot(dataset, aes(x=AGE,y=INCOME)) + geom_point()
scatter_plot = scatter_plot + facet_grid(GENDER ~ TREATMENT)
output$Scatter <- renderPlot({
scatter_plot
})
scatter_brushed <- reactive({
my_brush <- input$selected_range
sel_range <- brushedPoints(dataset, my_brush)
return(sel_range)
})
output$brushed_table <- DT::renderDataTable(DT::datatable(scatter_brushed()))
displot2 <- ggplot(dataset, aes(online.Activity.A)) + geom_histogram(aes(fill=AGE_Bin), bins = 5)
displot2 = displot2 + facet_grid(GENDER ~ TREATMENT)
displot3 <- ggplot(dataset, aes(online.ACTIVITY.B)) + geom_histogram(aes(fill=AGE_Bin), bins = 5)
displot3 = displot3 + facet_grid(GENDER ~ TREATMENT)
output$displot2 <- renderPlot({
displot2
})
output$displot3 <- renderPlot({
displot3
})
#
# scatter_brushed2 <- reactive({
#
# my_brush <- input$selected_range2
# sel_range <- brushedPoints(dataset, my_brush)
# return(sel_range)
#
# })
# output$brushed_table2 <- DT::renderDataTable(DT::datatable(scatter_brushed2()))
data_agg2 <-aggregate(list(Activity_A=dataset$online.Activity.A), by=list(DAY=dataset$DAY,TREATMENT=dataset$TREATMENT,GENDER=dataset$GENDER),mean)
lineplot <- ggplot(data_agg2, aes(x=DAY, y=Activity_A, group=c(TREATMENT))) + geom_line(aes(color=TREATMENT)) + geom_point()
lineplot = lineplot + facet_grid(GENDER ~ TREATMENT)
output$lineplot <- renderPlot({
lineplot
})
data_agg2 <-aggregate(list(Activity_B=dataset$online.ACTIVITY.B), by=list(DAY=dataset$DAY,TREATMENT=dataset$TREATMENT, GENDER=dataset$GENDER),mean)
lineplot2 <- ggplot(data_agg2, aes(x=DAY, y=Activity_B, group=c(TREATMENT))) + geom_line(aes(color=TREATMENT)) + geom_point()
lineplot2 = lineplot2 + facet_grid(GENDER ~ TREATMENT)
output$lineplot2 <- renderPlot({
lineplot2
})
#Downloads
output$lineplot2_download <- downloadHandler(
filename = "Activity_B Line.jpg",
content = function(file){
ggsave(file,plot=lineplot2)
})
output$lineplot_download <- downloadHandler(
filename = "Activity_A Line.jpg",
content = function(file){
ggsave(file,plot=lineplot)
})
output$displot2_download <- downloadHandler(
filename = "ActivityA_Dist.jpg",
content = function(file){
ggsave(file,plot=displot2)
})
output$displot3_download <- downloadHandler(
filename = "ActivityB_Dist.jpg",
content = function(file){
ggsave(file,plot=displot3)
})
output$scatter_download <- downloadHandler(
filename = "Age_Income.jpg",
content = function(file){
ggsave(file,plot=scatter_plot)
})
}
UI Design in R Shiny
UI design in R Shiny is easy and intuitive. It’s an HTML element as a function concept. Let’s dive into how UI is designed in our R Shiny app, using the provided code as an example.
Basic Structure
R Shiny UI is structured using functions defining the layout and its elements. The fluidPage()
function is often used for its responsive layout capabilities, meaning the app’s interface adjusts nicely to different screen sizes.
ui <- fluidPage(
# UI components are nested here
)
Organizing Content with Headers and Separators
Headers (h1
, h2
, h3
, etc.) and separators (hr()
) are used to organize content and improve readability. In our app, headers indicate different sections:
h1("Rshiny Homework"),
h2("Demographic Exploration"),
h3("Filterable Table"),
Data Display
The DT::dataTableOutput()
function is used to render data tables in the UI. This function takes an output ID as an argument, linking it to the server logic that provides the data:
DT::dataTableOutput("table"),
Interactive Inputs
Interactive inputs, such as selectInput
, allowing users to interact with the app and control what data or plot is displayed. In our app, selectInput
is used for choosing demographic aspects to display in a chart:
selectInput(
"option",
"Demography",
c("AGE_Bin", "INCOME_Bin", "GENDER"),
selected = NULL,
multiple = FALSE,
selectize = TRUE,
width = NULL,
size = NULL
),
Action Buttons
Action buttons, created with actionButton()
, trigger reactive events in the server. Our app uses an action button to generate plots based on user selection:
actionButton("gobutton", "View Chart", class = "btn-success"),
Displaying Plots
To display plots, plotOutput()
is used. This function references an output ID from the server side where the plot is rendered:
plotOutput("disPlot"),
Interactive Plots
I use ggplot2
for creating interactive plots. For example, a scatter plot is generated based on user-selected variables:
scatter_plot <- ggplot(dataset, aes(x=AGE,y=INCOME)) + geom_point()
Tabbed Panels
Tabbed panels, created with tabsetPanel()
, help in organizing content into separate views within the same space. Each tabPanel
holds different content:
tabsetPanel(
tabPanel("Scatter", ...),
tabPanel("Distribution", ...)
),
Download Handlers
We provide functionality for users to download plots as JPEG files:
output$scatter_download <- downloadHandler(
filename = "Age_Income.jpg",
content = function(file){
ggsave(file,plot=scatter_plot)
})
downloadButton(outputId = "scatter_download", label = "Download Chart", class = "btn-success"),
Running the App
Finally, to run the app, use:
shinyApp(ui = ui, server = server)