I am trying to build my first shiny app and have come across an error I can't resolve. I have searched and searched but can't seem to find an answer that resolves this question.
My code is below using the mtcars dataset. The goal is to develop visual and text analysis tools for a survey dataset. I currently have two drop down menus with survey questions, a drop down menu with a grouping variable menu, and some radio buttons for plot types. I am trying to create the first plot - a histogram. After I run the code I get the correctly rendered data tables, but no histogram plot - and the error:
object 's1' not found
I have tried this with and without wrapping my ggplot code with print().
Any help would be very appreciated !
Thanks!
library(shiny)
install.packages("shinythemes")
library(shinythemes)
library(ggplot2)
library(dplyr)
mt <- mtcars
ui <- fluidPage(theme=shinytheme("readable"),
titlePanel("Test"),
sidebarLayout(
sidebarPanel(
# use mpg and cyl as survey option groups
selectizeInput(inputId = 's1',
label = 'Survey Question 1 (X-Axis)',
choices = c("mpg", "cyl")),
selectizeInput(inputId = 's2',
label ='Survey Question 2 (Y-Axis)',
choices = c("mpg", "cyl")),
# use gear and vs as grouping variables
selectizeInput(inputId = 'g',
label = 'Group By',
choices = c("Gear"="gear", "VS" = "vs")),
# use radio buttons for pot type options
radioButtons(inputId = 'plottype',
label = 'Plot Type',
choices = c("Histogram" = "geom_histogram", "Box Plot" = "geom_boxplot", "Scatterplot" = "geom_point")
)
),
mainPanel(
plotOutput("plot1"), # ui for plot
verbatimTextOutput("table1") # ui for table
)
)
)
server <- function(input, output, session) {
## subset dataset to include only two options for now - include grouping after
plotData <- reactive({
((mt[,c(input$s1,input$s2)])) ## data for plot
})
## render hist plot in ggplot
output$plot1 <- renderPlot({
d1<-(plotData())
print(ggplot(d1, aes(x=s1)) + geom_histogram(fill = "dark green", alpha = 0.6, binwidth = 1))
print(str(d1))
})
## render summary table for survey questions
output$table1 <- renderPrint({
summary(plotData())
})
}
shinyApp(ui = ui, server = server)
## but this works
ggplot(mt, aes(x=mpg)) + geom_histogram(fill = "dark green", alpha = 0.6, binwidth = 1)
There's no column s1 in the data set d1. Use ggplot(d1, aes_string(x=input$s1)).
Related
Not sure why my code shows all the countries' plots at the same time, I want to make it display only the country that's selected by the user. Does anyone know what went wrong with the code?
library(shiny)
require(readr)
countries <- read.csv("~/Desktop/share-deaths-suicide.csv")
# Define UI for application that draws a scatterplot
ui <- fluidPage(
# Application title
titlePanel("Country Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("Entity",
"countries",
paste(countries$Entity),
selected = "China", multiple = FALSE)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("countryPlot")
)
)
)
# Define server logic required to draw a scatterplot
server <- function(input, output) {
output$countryPlot <- renderPlot({
country = input$Entity
plot(countries$Year, countries$`Deaths...Self.harm...Sex..Both...Age..All.Ages..Percent`, col=ifelse(countries$Entity==country, "red","black"),
main = "Sucide Rate of Countries", xlab = "Year", ylab = "Sucide rate",log="xy")
options(scipen=999)
})
}
# Run the application
shinyApp(ui = ui, server = server)
The issue is that you do not filter your data for the selected country. According to your code only the color is changed for the selected country.
To fix you issue you have to filter your data before passing it to plot. To this end you could add a reactive which filters the data. The filtered data could then be used via countries_filtered() where the parantheses are important. Additionally I have fixed an issue with your selectInput. Instead of passing the vector of entities use only the unique values for the choices argument.
As you provided no example data (to this end I would suggest to have a look at how to provide provide a minimal reproducible example) I use the gapminder dataset as example data:
library(shiny)
# Example data
library(gapminder)
countries <- gapminder
names(countries)[c(1, 3, 4)] <- c("Entity", "Year", "Deaths...Self.harm...Sex..Both...Age..All.Ages..Percent")
# Define UI for application that draws a scatterplot
ui <- fluidPage(
# Application title
titlePanel("Country Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
selectInput("Entity",
"countries",
choices = unique(countries$Entity),
selected = "China", multiple = FALSE)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("countryPlot")
)
)
)
# Define server logic required to draw a scatterplot
server <- function(input, output) {
countries_filtered <- reactive({
req(input$Entity)
countries[countries$Entity == input$Entity, ]
})
output$countryPlot <- renderPlot({
country = input$Entity
plot(countries_filtered()$Year, countries_filtered()$`Deaths...Self.harm...Sex..Both...Age..All.Ages..Percent`,
col=ifelse(countries$Entity==country, "red","black"),
main = "Sucide Rate of Countries", xlab = "Year", ylab = "Sucide rate",log="xy")
options(scipen=999)
})
}
# Run the application
shinyApp(ui = ui, server = server)
#>
#> Listening on http://127.0.0.1:4061
1: You need to filter the dataset for the input country
And I will demonstrate this using ggplot:
output$countryPlot <- renderPlot({
countries %>%
filter(country == input$Entity) %>%
plot(aes(Year, countries$`Deaths...Self.harm...Sex..Both...Age..All.Ages..Percent`, color= Entity)) +
geom_point() +
labs(main = paste("Sucide Rate of", input$Entity), xlab = "Year", ylab = "Sucide rate") +
scale_y_log10() +
scale_x_log10() +
scale_fill_manual(values=c("red", "black"))
options(scipen=999)
})
I am not exactly sure why you wanted to color the points of the scatter plot based on "entity" as it seems that entity is the country input and the original plot would change the color of the points based on the user selection. In my above code, it would change the entire plot to only show the selected country, and changes the title based on the country selected as well.
I want to start a shiny app for practice where a use can choose from a dropdown the values in the "cut" column from the diamonds dataset (from ggplot2).
My ui looks as following:
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Reactive Boxplot"),
# Show a boxplot of the selected cut
mainPanel(
selectInput("column", label = h3("Column to plot"),
choices = c("", diamonds$cut),
selected = 1,
width='55%',
multiple = FALSE),
plotOutput("diamondshist")
)
)
)
I don't know how to define the input variables as the five distinct values in the "cut" column of diamonds dataset. Any input on this?
My server file looks like shared below. I assume I would also need to adapt the input data for the plot.
library(shiny)
library(ggplot2)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
compute_plot <- reactive({
if (input$column != ""){
ggplot(diamonds[, input$column])+
labs(title = "From diamonds dataset")+
geom_boxplot(aes(x = cut, y = price))+
scale_y_reverse()
}
})
output$diamondshist <- renderPlot({
compute_plot();
})
})
I assume this is what you are after:
pass the levels of diamonds$cut as input selection
subset the diamonds dataset to the selected cut
library(shiny)
library(ggplot2)
# Define UI for application that draws a histogram
ui=shinyUI(fluidPage(
# Application title
titlePanel("Reactive Boxplot"),
# Show a boxplot of the selected cut
mainPanel(
selectInput("column", label = h3("Column to plot"),
choices = c("", levels(diamonds$cut)),
selected = NULL,
width='55%',
multiple = FALSE),
plotOutput("diamondshist")
)
)
)
# Define server logic required to draw a histogram
server=shinyServer(function(input, output) {
compute_plot <- reactive({
if (input$column != ""){
ggplot(subset(diamonds, cut==input$column))+
labs(title = "From diamonds dataset")+
geom_boxplot(aes(x = cut, y = price))+
scale_y_reverse()
}
})
output$diamondshist <- renderPlot({
compute_plot();
})
})
shinyApp(ui = ui, server = server)
I'm trying to plot a continous variable (y) versus another one (x), and color the data points in the plot depending on a third categorical variable (z). To select these three variables I use a selectInput function, but to select the possible categories (levels(input$z)) I want to plot, I use a uiOutput function. I'm trying to subset the selected levels in a filtered dataframe (dataf) but this is not working. Some reactive expressions are working, but I rolled back the code with the subsetting because when I use ( dataf <- filter(data(), input$z %in% input$show_levels)) in the renderPlot function I don't get any data point plotted.
I have prepared a simplified version of what I need using the diamonds dataset. For example, I would need my shinyApp to plot prize vs carats, with the points colored depending on the cut, and being able to represent only the ones with a certain cut (for instance cut == c("Fair", "Good").)
library(shiny)
library(ggplot2)
library(RColorBrewer)
library(dplyr)
cont_vars <- c("price", "carat", "x", "y", "z", "depth", "table")
discr_vars <- c("cut", "color", "clarity")
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Analysis of 'diamonds' dataset"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
# Select variable for y-axis
selectInput(inputId = "y",
label = "Y-axis:",
choices = cont_vars,
selected = cont_vars[1]),
# Select variable for x-axis
selectInput(inputId = "x",
label = "X-axis:",
choices = cont_vars,
selected = cont_vars[2]),
# Select variable for color
selectInput(inputId = "z",
label = "Z-axis:",
choices = discr_vars,
selected = discr_vars[1]),
# Select level/s to show for the z category
uiOutput("selected_z")
),
# Show the plot
mainPanel(
plotOutput("scatterplot")
)
)
)
# Define server logic required to draw a scatterplot
server <- function(input, output) {
# Show levels for the discrete variable selected in input$selected_z
output$selected_z <- renderUI({
checkboxGroupInput(inputId = "show_levels",
label = "Select category/ies to represent:",
choices = choices_z(),
selected = choices_z())
})
choices_z <- reactive({
df <- select(diamonds, input$z)
return(levels(df[[1]]))
})
output$scatterplot <- renderPlot({
# generate df based on inputs selected
data <- select(diamonds, input$x, input$y, input$z)
# dataf <- filter(data(), input$z %in% input$show_levels)
ggplot(data, aes_string(x = input$x, y = input$y,
color = input$z)) +
geom_point(size = 4) +
scale_color_brewer(palette = "Paired") +
theme_light()
})
}
# Run the application
shinyApp(ui = ui, server = server)
I expected to select the input$show_levels categorical variables (levels) I'm interested in the checkboxGroupInput (input$showlevels), and show only the points in the seleceted categories in the scatterplot. Right now I got the checkboxGroupInput function for the levels to appear (see image below), but I'm afraid it's not connected to the server, and all the levels of input$z are being plotted.
how the shinyApp is now
It looks like you had it just about set when you tried to filter the data (your commented line starting with dataf). You need to get filter() to recognize that you want it to use the column represented by input$z, not to use the actual value input$z (which is "cut"). If you update renderPlot so that the data are filtered like this
data <-
select(diamonds, input$x, input$y, input$z) %>%
filter(!!(as.name(input$z)) %in% input$show_levels)
then the app should work as you expected. This answer has more details about why/how to deal with passing shiny inputs to dplyr functions.
Edited for clarity:
I'm building an app in shiny to explore data in a scatterplot. The simplified code is below:
library(shiny)
library(ggplot2)
library(DT)
library(tools)
library(dplyr)
library(tidyverse)
#load data
Data <- Electorate_Data
# Define UI for application
ui <- fluidPage(
titlePanel("Scatterplot"),
br(),
# Sidebar layout with a input and output definitions
sidebarLayout(
# Inputs
sidebarPanel(
# Select variable for y-axis
selectInput(inputId = "y", label = "Y-axis:",
choices = colnames(Data[6:32])
),
# Select variable for x-axis
selectInput(inputId = "x", label = "X-axis:",
choices = colnames(Data[6:32])
),
width = 6
),
# Output:
mainPanel(
# Create a container for tab panels
tabsetPanel(
tabPanel(
title = "Explore the Data",
# Show scatterplot
plotOutput(outputId = "scatterplot")
)
),
width = 6
)
)
)
# Define server function required to create the scatterplot
server <- function(input, output) {
# Create scatterplot object the plotOutput function is expecting
output$scatterplot <- renderPlot({
ggplot(data = Data, aes_string(x = input$x, y = input$y)) +
geom_point()
})
}
# Create a Shiny app object
shinyApp(ui = ui, server = server)
My issue is, when I select the column names (using the function colnames) to be the choices in the selectInput function, and the colnames contain spaces (i.e. "Year 12" instead of Year12), my scatterplot doesn't work, giving the error message:
Error: :1:6: unexpected 'in'
1: Born in
Code Example
# Select variable for y-axis
selectInput(inputId = "y", label = "Y-axis:",
choices = colnames(Data[6:32])
Now, if I alias each variable, for example using the following code, the app works perfectly fine:
# Select variable for y-axis
selectInput(inputId = "y", label = "Y-axis:",
choices = "Year12" = "Year 12",
"Born_Aus" = "Born in Australia",
"NoSchool" = "% of the Population Above 15 Years of Age that Didn't Attend School")
),
My question is - is there a way to alias the names more efficiently in such a way that I can automate the aliasing process. I've tried a few hack approaches using the names() function, but so far it has only thrown up errors.
At the end of the day I can solve this manually, but surely there is a better way.
Thanks
Edit:
I've included a subset of the data if that helps. You would have to change the code of:
# Select variable for x-axis
selectInput(inputId = "x", label = "X-axis:",
choices = colnames(Data[6:32])
),
to just
choices = colnames(Data)
for both the X and Y selectInputs
Data:
> head(Electorate_Data[14:18])
Born in Australia LOTE NoSchool Year12 Median_age
1 0.6126271 0.29805068 0.012132744 0.5481394 36
2 0.6419959 0.27278743 0.006160949 0.4610346 39
3 0.8234175 0.05199925 0.002323880 0.3564276 40
4 0.5633673 0.45200442 0.011578501 0.4933828 38
5 0.8186847 0.06066808 0.005270832 0.2701636 44
6 0.4439803 0.59099798 0.017304021 0.5374834 35
In general, lets assume you have a vector of names that should be returned by the selectInput() called x. And you have a second, equally long vector, of names that you what the user to see when making the selection, called y. You can do what you want by first giving x the names of y like so:
names(x)<-y
Then you can use x in the choices= argument to selectInput(). The user will see the names from y as the choices, but the selectInput() will return the values from x.
I don't know why your shiny does not support the columns names containing spaces. The code below works fine on my machine. Provide a reproducible example is always helpful for others to help you.
library(shiny)
df <- iris[,-5]
colnames(df) <- sub('\\.', ' ', colnames(df))
ui <- fluidPage(
titlePanel("Scatterplot and Regression Calculator"),
br(),
# Sidebar layout with a input and output definitions
sidebarLayout(
# Inputs
sidebarPanel(
# Select variable for y-axis
selectInput(inputId = "y", label = "Y-axis:",
choices = colnames(df)
)
),
mainPanel(
plotOutput(outputId = "plot")
)
)
)
server <-function(input, output) {
output$plot <- renderPlot({
hist(df[[input$y]])
})
}
shinyApp(ui = ui, server = server)
I am learning Shiny and wanted help on a app that I am creating. I am creating an app that will take dynamic inputs from the user and should generate bar and line charts. I managed to create the bar chart but it is generating incorrect result.
What I am looking for is variable selected in row should be my x-axis and y-axis should be percentage. scale to be 100%. column variable should be the variable for comparison and for that I am using position = "dodge". My data is big and I have created a sample data to depict the situation. Since actual data is in data.table format I am storing the sample data as data.table. Since I am not sure how I can include this data which is not in a file format, I create it first so that it is in R environment and then run the app -
Location <- sample(1:5,100,replace = T)
Brand <- sample(1:3,100,replace = T)
Year <- rep(c("Year 2014","Year 2015"),50)
Q1 <- sample(1:5,100,replace = T)
Q2 <- sample(1:5,100,replace = T)
mydata <- as.data.table(cbind(Location,Brand,Year,Q1,Q2))
Below is the Shiny code that I am using -
library("shiny")
library("ggplot2")
library("scales")
library("data.table")
library("plotly")
ui <- shinyUI(fluidPage(
sidebarPanel(
fluidRow(
column(10,
div(style = "font-size: 13px;", selectInput("rowvar", label = "Select Row Variable", ''))
),
tags$br(),
tags$br(),
column(10,
div(style = "font-size: 13px;", selectInput("columnvar", "Select Column Variable", ''))
))
),
tabPanel("First Page"),
mainPanel(tabsetPanel(id='charts',
tabPanel("charts",tags$b(tags$br("Graphical Output" )),tags$br(),plotlyOutput("plot1"))
)
)
))
server <- shinyServer(function(input, output,session){
updateTabsetPanel(session = session
,inputId = 'myTabs')
observe({
updateSelectInput(session, "rowvar", choices = (as.character(colnames(mydata))),selected = "mpg")
})
observe({
updateSelectInput(session, "columnvar", choices = (as.character(colnames(mydata))),selected = "cyl")
})
output$plot1 <- renderPlotly({
validate(need(input$rowvar,''),
need(input$columnvar,''))
ggplot(mydata, aes(x= get(input$rowvar))) +
geom_bar(aes(y = ..prop.., fill = get(input$columnvar)), position = "dodge", stat="count") +
geom_text(aes( label = scales::percent(..prop..),
y= ..prop.. ), stat= "count", vjust = -.5) +
labs(y = "Percent", fill=input$rowvar) +
scale_y_continuous(labels=percent,limits = c(0,1))
})
})
shinyApp(ui = ui, server = server)
If you see the problem is -
All bars are 100%. Proportions are not getting calculated properly. Not sure where I am going wrong.
If I try to use the group parameter it gives me error saying "input" variable not found. I tried giving group as group = get(input$columnvar)
I believe I need to restructure my data for line chart. Can you help with how I can dynamically restructure the data.table and then re-use for the line chart. How can I generate the same bar chart as a line chart.
I am using renderplotly so that I use the features of plotly to have the percentages displayed with the mouse movement / zoom etc. However I can see input$variable on mouse movement. How can I get rid of it and have proper names.
Have tried to detail out the situation. Do suggest some solution.
Thank you!!
To properly group variables for plotting, geom_bar requires that the x values be numeric and the fill values be factors or that the argument group be used to explicitly specify grouping variables. However, plotly throws an error when group is used. The approach below converts x variables to integer and fill variables to factor so that they are properly grouped. This retains the use of geom_bar to calculate the percentages.
First, however, I wonder if mydata is specified correctly. Given that the data is a mix of character and integer, cbind(Location, Brand, Year, Q1, Q2) gives a character matrix which is then converted to a data.table where all variables are character mode. In the code below, I've defined mydata directly as a data.table but have converted Q1 to character mode so that mydata contains a mix of character and numeric.
The approach used below is to create a new data frame, plotdata, containing the x and fill data. The x data is converted to numeric, if necessary, by first making it a factor variable and then using unclass to get the factor integer codes. The fill data converted to a factor. plotdata is then used generate the ggplot plot which is then displayed using plotly. The code includes a couple of other modifications to improve the appearance of the chart.
EDIT
The code below has been updated to show the name of the row variable beneath it's bar. Also the percentage and count for each bar are only shown when the mouse pointer hovers above the bar.
library("shiny")
library("ggplot2")
library("scales")
library(plotly)
library(data.table)
Location <- sample(1:5,100,replace = T)
Brand <- sample(1:3,100,replace = T)
Year <- rep(c("Year 2014","Year 2015"),50)
Q1 <- sample(1:5,100,replace = T)
Q2 <- sample(1:5,100,replace = T)
Q3 <- sample(seq(1,3,.5), 100, replace=T)
mydata <- data.table(Location,Brand,Year,Q1,Q2, Q3)
#
# convert Q1 to character for demonstation purposes
#
mydata$Q1 <- as.character(mydata$Q1)
ui <- shinyUI(fluidPage(
sidebarPanel(
fluidRow(
column(10,
div(style = "font-size: 13px;", selectInput("rowvar", label = "Select Row Variable",
choices=colnames(mydata)))),
tags$br(),
tags$br(),
column(10,
div(style = "font-size: 13px;", selectInput("columnvar", label="Select Column Variable",
choices=colnames(mydata))))
)
),
tabPanel("First Page"),
mainPanel(tabsetPanel(id='charts',
tabPanel("charts",tags$b(tags$br("Graphical Output" )),tags$br(),plotlyOutput("plot1"))
)
)
))
server <- shinyServer(function(input, output,session){
updateTabsetPanel(session = session
,inputId = 'myTabs')
observe({
updateSelectInput(session, "rowvar", choices = colnames(mydata), selected=colnames(mydata)[1])
})
observe({
updateSelectInput(session, "columnvar", choices = colnames(mydata), selected=colnames(mydata)[2])
})
output$plot1 <- renderPlotly({
#
# create data frame for plotting containing x variables as integer and fill variables as factors
#
if(is.numeric(get(input$rowvar))) {
rowvar_brks <- sort(unique(get(input$rowvar)))
rowvar_lbls <- as.character(rowvar_brks)
plotdata <- data.frame(get(input$rowvar), factor(get(input$columnvar)) )
}
else {
rowvar_factors <- factor(get(input$rowvar))
rowvar_brks <- 1:nlevels(rowvar_factors)
rowvar_lbls <- levels(rowvar_factors)
plotdata <- data.frame(unclass(rowvar_factors), factor(get(input$columnvar)) )
}
colnames(plotdata) <- c(input$rowvar, input$columnvar)
validate(need(input$rowvar,''),
need(input$columnvar,''))
col_width <- .85*mean(diff(rowvar_brks))
sp <- ggplot(plotdata, aes_(x = as.name(input$rowvar), fill = as.name(input$columnvar))) +
geom_bar( aes(y= ..prop..), stat="count", position=position_dodge(width=col_width)) +
geom_text(aes( label = paste(scales::percent(..prop..),"<br>", "count:",..count..,"<br>"), y= ..prop.. + .01),
stat= "count", position=position_dodge(width=col_width), size=3, alpha=0) +
labs(x= input$rowvar, y = "Percent", fill=input$columnvar) +
scale_y_continuous(labels=percent) +
scale_x_continuous(breaks=rowvar_brks, labels=rowvar_lbls)
ggplotly(sp, tooltip="none")
})
})
shinyApp(ui = ui, server = server)