Creating hover info box and reactive dropdown menu in Shiny - r
This is my first Shiny app, and I just got the basics working to where it allows the user to select from a dropdown menu of clients, then a dropdown menu of test codes to receive a plot of the results for the selected test.
I'd like the second dropdown menu to be updated with the available test codes for that client (all are not present for each client). Also, I would like to be able to hover over the point in the plot and receive more information from the row in the original dataframe.
I've looked into tooltips and the nearPoints() function, but I'm not sure if these can be used on this data since it is manipulated. I'm not sure if at this point it would be easier to import the data in a different way (it will ultimately need to accept either excel files or .csv). Thanks for any help that you would be able to provide, please let me know if there is any other supporting info I can give.
Here is my code:
library(shiny)
library(scales)
library(ggplot2)
labData <-
read.table("MockNLData.csv",
header=TRUE, sep=",")
#convert '<10' and '<20' results
labData$ModResult <- labData$Result
levels(labData$ModResult)[levels(labData$ModResult)=="<10"]
<- "0"
levels(labData$ModResult)[levels(labData$ModResult)=="<20"]
<- "0"
#convert results to scientific notation
SciNotResult <-
formatC(as.numeric(as.character(labData$ModResult)),
format="e", digits=2)
ui <- fluidPage(
headerPanel("Dilution History"),
sidebarLayout(
sidebarPanel(
selectInput(inputId="client", label="Select Client
Name", choices=levels(labData$Client.Name)
),
selectInput(inputId="test", label="Select Test Code",
choices=levels(labData$Analysis))
),
mainPanel(
plotOutput("line", hover="plot_hov"),
verbatimTextOutput("info"))
)
)
server <- function(input, output) {
#selected client into data frame
selDF <- reactive({labData[labData[,1]==input$client,]
})
#selected test code into data frame
subsetDF <- reactive({selDF()[selDF()[,5]==input$test,]
})
#points to be plotted
points <-
reactive({as.numeric(levels(subsetDF()$ModResult))
[subsetDF()$ModResult]
})
#plot
output$line <- renderPlot({
qplot(seq_along(points()), points(), xlab ="Index",
ylab ="Result")
})
#hover information
output$info <- renderText({
paste0("x=", input$plot_hov$x, "\ny=",
input$plot_hov$y)
})
}
shinyApp(ui = ui, server = server)
Here is what the data looks like:
MockNLData.csv
EDIT: I figured out updating the menu with updateSelectInput()
In the future, make sure you share a reproducible example :)
Since your code is not reproducible please find below something you can understand and adapt to your case.
On your first question, if I understand correctly, you want to programatically generate a dropdown (selectInput) which is perfectly do-able. *Inputs are, in essence, just HTML content which you can dynamically generate, just like your plots. You do so with uiOutput (in your ui) and renderUI in your server.
library(shiny)
ui <- fluidPage(
selectInput("dataset", "Select a dataset", choices = c("cars", "mtcars")),
uiOutput("column"), # dynamic column selector
verbatimTextOutput("selected_column")
)
server <- function(input, output, session){
data <- reactive({
if(input$dataset == "cars")
return(cars)
else
return(mtcars)
})
output$column <- renderUI({
# build your selectInput as you normally would
selectInput("column_selector", "Select a column", choices = colnames(data()))
})
output$selected_column <- renderPrint({
# use input$column_selector!
print(input$column_selector)
})
}
shinyApp(ui, server)
On your second question, what you want is an interactive plot. There are numerous packages that will let you do that in R and Shiny. Below are some examples, by no means a comprehensive list:
plotly which will also let you make your ggplot2 charts interactive
highcharter another great, well tested library
echarts4r ECharts for R.
billboarder billboard.js for R and Shiny
Below is an example using highcharter. They all follow the same principle within Shiny, an *Output function coupled with a render* function.
library(shiny)
library(highcharter)
ui <- fluidPage(
highchartOutput("chart")
)
server <- function(input, output, session){
output$chart <- renderHighchart({
hchart(mpg, "scatter", hcaes(x = displ, y = hwy, group = class))
})
}
shinyApp(ui, server)
EDIT
Following your question on the flashing error. You need to require (req) the required input. When launching the app below the error will flash, uncomment the req(input$y) line and it'll go away.
library(shiny)
ui <- fluidPage(
uiOutput("sel"),
plotOutput("plot")
)
server <- function(input, output){
output$sel <- renderUI({
numericInput("y", "N:", value = 200, min = 5, max = 1000, step = 100)
})
output$plot <- renderPlot({
# req(input$y)
hist(runif(input$y, 1, 10))
})
}
shinyApp(ui, server)
In essence, since your plot relies on a dynamically generating input for a fraction of second that input is not available as it is being rendered, using req prevents that.
What I understand from your problem above are:
You want to make next dropdown menu based on what the user have chosen from previous dropdown menu.
When the mouse over the point on the plot, it will show row value.
So, here i will give you reproducible example and i hope it is useful for you.
In this example I use Rabbit dataset from library MASS.
To filter data for next dropdown menu, I use filter from library
dplyr (See line 30).
I use reactive expression to manage next dropdown menu (See line
29).
I use nearPoints() to manage hover point (See line 55).
library(shiny)
library(MASS)
library(dplyr)
library(ggplot2)
ui <- fluidPage(
titlePanel("Rabbit dataset from MASS library"),
fluidRow(
column(4, selectInput("var",
"Animal:",
unique(sort(Rabbit$Animal)))),
column(4, uiOutput("selected_var")),
column(4, uiOutput("selected_var1")),
column(12, plotOutput("selected_var2", hover = "plot_hover")),
column(12, verbatimTextOutput("info"))
)
)
server <- function(input, output) {
###FILTER NEXT DROPDOWN MENU BASED ON PREVIOUS SELECTED BY USER
dataset3 <- reactive({
unique(Rabbit %>% filter(Animal == input$var) %>% select(Treatment))
})
output$selected_var <- renderUI({
selectInput("var1", "Treatment:", c(dataset3()))
})
dataset4 <- reactive({
Rabbit %>% filter(Animal == input$var) %>% filter(Treatment == input$var1) %>% select(Run)
})
output$selected_var1 <- renderUI({
selectInput("var2", "Run:", c(dataset4()))
})
####
output$selected_var2 <- renderPlot({
ggplot(Rabbit %>% filter(Animal == input$var) %>% filter(Treatment == input$var1) %>% filter(Run == input$var2), aes(x = BPchange, y = Dose)) + geom_point()
})
###HOVER POINT USING nearPoints()
output$info <- renderPrint({
nearPoints(Rabbit %>% filter(Animal == input$var) %>% filter(Treatment == input$var1) %>% filter(Run == input$var2), input$plot_hover)
})
}
shinyApp(ui = ui, server = server)
Related
Autoplot in shiny with Select Input not working
I'm trying to create an autoplot that will show a plot based on what variable the user selects, but it just shows up as a straight line even though the name on the y axis does change depening on what the user chooses. Here is a basic version of the code: library(shiny) library(fpp3) ui <- fluidPage( selectInput("select", "Choose variable", choices = names(aus_production)), plotOutput("plot") ) server <- function(input,output){ output$plot <- renderPlot({ aus_production %>% autoplot(input$select) }) } shinyApp(ui = ui,server = server)
You are calling ?autoplot.tbl_ts and that method requires an expressio for the variable, not a string which is what input$select returns. Instead you can use the .data pronoun server <- function(input,output){ output$plot <- renderPlot({ aus_production %>% autoplot(.data[[input$select]]) }) }
Select specific data from R DataTable in Shiny with checkboxes and create histogram
I have created a data table with DT in Shiny that looks like this: I would like to select data with checkboxes on a side panel that satisfies certain attributes (e.g. Mfr=Mitsubish, Joint=1, etc.) and then updates a histogram of deg/s in real time to view. I've read through the material I could find on the web, but I can't figure out how to do this. Does anyone have any hints?
#guero64 Here is an example I had that I believe has examples of what you're looking for. I hope this is helpful. It is based on the diamonds dataset and has a couple of checkbox filters you can apply to the data. library(shiny) library(DT) library(tidyverse) ui <- shinyUI(pageWithSidebar( headerPanel("Example"), sidebarPanel( checkboxInput("cb_cut", "Cut (Ideal)", FALSE), checkboxInput("cb_color", "Color (I)", FALSE) ), mainPanel( DT::dataTableOutput("data_table"), plotOutput("data_plot") ) )) server <- shinyServer(function(input, output) { filtered_data <- reactive({ dat <- diamonds if (input$cb_cut) { dat <- dat %>% filter(dat$cut %in% "Ideal") } if (input$cb_color) { dat <- dat %>% filter(dat$color %in% "I") } dat }) output$data_table <- DT::renderDataTable({ filtered_data() }) output$data_plot <- renderPlot({ hist(filtered_data()$price, main = "Distribution of Price", ylab = "Price") }) }) shinyApp(ui = ui, server = server)
In an Shiny App, I want a plot to update, based on the search results in a datatable.
Say I have a Shiny app with a datatable and a plot. I want to be able to search/filter the datatable, and have a plot reflect the results. How do I do this? Is this even possible? Is there any way to output the filtered datatable to an object I can use? Here is a basic shiny application which does not work. library(DT) ui <- basicPage( h2("The mtcars data"), DT::dataTableOutput("mytable"), plotOutput('plot1') ) server <- function(input, output) { output$mytable = DT::renderDataTable({ datatable(mtcars,filter = 'top') }) output$plot1 <- renderPlot({ plot(input$mytable$wt, input$mytable$mpg) }) } shinyApp(ui, server)
I have edited your code a bit since your way has some mistakes as pointed out by #r2evans. Anyways, you can get the filtered rows of a datatable using input$tableId_rows_all. It gives the indices of rows on all pages (after the table is filtered by the search strings). In my code filtered_table() gives you a dataframe object after all search filters are applied. output$test shows this table in real-time. library(shiny) library(DT) ui <- basicPage( h2("The mtcars data"), DT::dataTableOutput("mytable"), verbatimTextOutput("test"), plotOutput('plot1') ) server <- function(input, output) { mc <- head(mtcars) # could be reactive in real world case output$mytable = DT::renderDataTable({ datatable(mc, filter = 'top') }) filtered_table <- reactive({ req(input$mytable_rows_all) mc[input$mytable_rows_all, ] }) output$plot1 <- renderPlot({ plot(filtered_table()$wt, filtered_table()$mpg, col = "red", lwd = 10) }) output$test <- renderPrint({ filtered_table() }) } shinyApp(ui, server)
Suggestions: Tour input$mytable reference in output$plot1 is just a string, not a frame like you'd hope, so this needs to be replaced. You can hard-code mtcars, but hard-coding data doesn't really lend to an extensible and interactive experience. Additionally, since you are going to be showing the same data in two different blocks ($mytable and $plot1), I suggest breaking the data into its own reactive block and referencing that block in the others. Lastly, I think it's good defensive practice to use req(...) in blocks so that they do not try to execute before the data is available (common when reactive pathways are unclear or the inputs are not set yet). Try this: library(DT) library(shiny) ui <- basicPage( h2("The mtcars data"), DT::dataTableOutput("mytable"), plotOutput('plot1') ) server <- function(input, output) { mydat <- reactive({ # eventually you'll support filtering here mtcars }) output$mytable = DT::renderDataTable({ req(mydat()) datatable(mydat(), filter = 'top') }) output$plot1 <- renderPlot({ req(mydat()) plot(mydat()$wt, mydat()$mpg) }) } shinyApp(ui, server)
Shiny plot doesn't change with inputs
I am an absolute beginner to Shiny, so I would appreciate your patience and any advice to my issue. Here's the server function that I'm using to output a ggplot, which works on its own, but doesn't change at all when I change the inputs: server <- function(input, output) { output$plooot<-renderPlot({ df = df %>% group_by(input$Category,Type) %>% summarise(Distribution=sum(Distribution)) ggplot(df,aes(input$Category,Distribution,fill=Type))+geom_bar(stat="identity",position="dodge")}) } shinyApp(ui=ui,server=server) Here's my ui function as well just for reference: ui <- fluidPage( titlePanel("chart"), # Generate a row with a sidebar sidebarLayout( # Define the sidebar with one input sidebarPanel( selectInput("Category","Category:",choices=c("a","b","c","d","e","f")), selectInput("a","a:", choices=unique(Table$a), selected="All"), selectInput("b","b:", choices=unique(Table$b), selected="All"), selectInput("c","c:", choices=unique(Table$c), selected="All"), selectInput("d","d:", choices=unique(Table$d), selected="All"), selectInput("e","e:", choices=unique(Table$e), selected="All"), selectInput("f","f:", choices=unique(Table$f), selected="All") ), # Create a spot for the barplot mainPanel( plotOutput("plooot") ) ) ) Unfortunately, I can't post the data for legal reasons, but here are two plots of what I want vs. what I have: This is probably a very rudimentary mistake, but I'm having trouble understanding what I'm doing wrong.
I agree with #AndS., re-assigning back to df = ... is not likely what you want/need but will almost certainly irreversibly reduce your data. Additionally, input$Category is a character and not a symbol that group_by is expecting. Try this: library(shiny) library(dplyr) library(ggplot2) ui <- fluidPage( titlePanel("chart"), # Generate a row with a sidebar sidebarLayout( # Define the sidebar with one input sidebarPanel( selectInput("Category","Category:",choices=colnames(mtcars)) ), # Create a spot for the barplot mainPanel( plotOutput("plooot") ) ) ) server <- function(input, output) { output$plooot<-renderPlot({ req(input$Category) icq <- sym(input$Category) mtcars %>% group_by(!!!icq, vs) %>% summarise(disp=sum(disp)) %>% ggplot(aes_string(input$Category, "disp", fill="vs")) + geom_bar(stat="identity", position="dodge") }) } shinyApp(ui=ui,server=server)
Not knowing what your data looks like, see below. The best thing to do is for any data set that will be affected by a user input, is to put it in a reactive expression. Then use that reactive expression in your output plots. I also added an "ALL" to your choices and an if function in case you want to see them all together like you have in your picture. ui <- fluidPage( titlePanel("Chart"), sidebarLayout( sidebarPanel( selectInput("Category","Category:",choices=c("All","a","b","c","d","e","f")) ), mainPanel( plotOutput("Plot") ) ) ) server <- function(input, output) { Distribution <- c(1,2,3,4,1,2,3,5,2,4) Category <- c("a","b","c","e","f","a","b","c","e","f") Type <- c("Blue","Blue","Blue","Blue","Blue","Red","Red","Red","Red","Red") df <- data.frame(Distribution ,Category,Type) df_subset <- reactive({ if (input$Category == "All") {df} else{df[df$Category == input$Category,]} }) output$Plot <- renderPlot({ dat <- df_subset() dat <- dat %>% group_by(Category,Type) %>% summarise(Distribution=sum(Distribution)) plot <- ggplot(dat,aes(Category,Distribution,fill=Type))+geom_bar(stat="identity",position="dodge") return(plot) }) } shinyApp(ui=ui,server=server)
Shiny reactivity -change plot data row dynamically
I know renderPlot produces plot that can be shown on Shiny plotOutput function. I also know autoinvalidate() helps to calculate data reactively. I am displaying a radar chart (in fact can be any chart) using the below codes: output$plot2 <- renderPlot({ autoInvalidate() p2<<-ggradar(mtcars_radar[i,]) }) What I dont know is how to change the value of i from 1 to 300 during every event of autoinvalidate(). Or is there anyway I can change the row of data in plot so that the plot is dynamically animating every sec with a new row of data. Can anyone help me plz? The full code is here: library(shiny) library(ggplot2) mtcars %>% rownames_to_column( var = "group" ) %>% mutate_at(vars(-group),funs(rescale)) %>% tail(4) %>% select(1:10) -> mtcars_radar ui <- fluidPage( sidebarPanel( actionButton("button", "Go!") ), # Show the plot mainPanel( plotOutput("plot2") ) ) server <- function(input, output) { library(ggplot2) library(ggradar) suppressPackageStartupMessages(library(dplyr)) library(scales) autoInvalidate <- reactiveTimer(2000) plot2 <- NULL output$plot2 <- renderPlot({ ggradar(mtcars_radar[1,]) }) observeEvent(input$button,{ output$plot2 <- renderPlot({ autoInvalidate() p2<<-ggradar(mtcars_radar[i,]) p2 }) }) } # Run the application shinyApp(ui = ui, server = server) Any help please?
This is where you need a reactive value that stores the row index and changes every second. I do not have the library ggradar, so I will just print out the current row index value instead. I also used invalidateLater instead of reactiveTimer as suggested by Shiny documentation. library(shiny) ui <- fluidPage( verbatimTextOutput("debug") ) server <- function(input, output) { row_idx_max <- 15 row_idx <- reactiveVal(0) observe({ isolate(row_idx(row_idx() + 1)) cur_row_idx <- isolate(row_idx()) if (cur_row_idx < row_idx_max) { invalidateLater(1000) } }) output$debug <- renderPrint({ row_idx() }) } shinyApp(ui, server)