How to persist event_data across tabs in Shiny? - r

I have a shiny application in which I'd like to capture which bar a user clicks on and store that value in a reactive expression to be referenced elsewhere for filtering. The problem is that the reactive expression reruns when I switch tabs and so the value doesn't sync up between the two tabs.
I have a reproducible example below.
When you load the app and click on the Goats bar, the selection at the bottom changes to 'Goats', but if you then change the tab to Bar2 the reactive expression reruns and therefore returns Giraffes again. So I end up with two separate values for the reactive expression across the different tabs. If I choose Goats on the first tab, I want it to remain when I switch to Bar2 tab and only update when I make another click.
Note that I realize I can resolve this in this example by removing the source argument from the event_data function, but in my application I have other charts which I do not want the user to be able to click on so I need to set the source to only these charts.
library(shiny)
library(plotly)
library(ggplot2)
library(shinydashboard)
df_test <- data.frame(c("Giraffes","Goats"),c(1,4))
df_test <- setNames(df_test,c("species","amount"))
ui <- dashboardPage(
dashboardHeader(title = "Click Example",
titleWidth = 300),
dashboardSidebar(
width = 300,
sidebarMenu(
menuItem("Tab", tabName = "tab")
)
),
dashboardBody(
tabItems(
tabItem(tabName = "tab",
fluidRow(
column(12, tabBox(
title = "",
id = "tabSet",
width = 12,
height = 500,
tabPanel("Bar1", plotlyOutput(outputId="bar_one")),
tabPanel("Bar2", plotlyOutput(outputId="bar_two"))
)
),
column(12,textOutput(outputId = "selection")))
)
)
)
)
server <- function(input, output, session) {
click_reactive = reactive({
d <- event_data("plotly_click",source=input$tabSet)
if (length(d) == 0) {species = as.vector(df_test$species[1])}
else {species = as.character(d[4])}
return(species)
})
output$bar_one <- renderPlotly({
p <- plot_ly(data = df_test, x = ~amount, y = ~species, type = 'bar', orientation = 'h', source = "Bar1")
})
output$bar_two <- renderPlotly({
p <- plot_ly(data = df_test, x = ~amount, y = ~species, type = 'bar', orientation = 'h', source = "Bar2")
})
output$selection <- renderText({
species <- click_reactive()
return(species)
})
}
shinyApp(ui, server)

You need to change the source to be under one name:
library(shiny)
library(plotly)
library(ggplot2)
library(shinydashboard)
df_test <- data.frame(c("Giraffes","Goats"),c(1,4))
df_test <- setNames(df_test,c("species","amount"))
ui <- dashboardPage(
dashboardHeader(title = "Click Example",
titleWidth = 300),
dashboardSidebar(
width = 300,
sidebarMenu(
menuItem("Tab", tabName = "tab")
)
),
dashboardBody(
tabItems(
tabItem(tabName = "tab",
fluidRow(
column(12, tabBox(
title = "",
id = "tabSet",
width = 12,
height = 500,
tabPanel("Bar1", plotlyOutput(outputId="bar_one")),
tabPanel("Bar2", plotlyOutput(outputId="bar_two"))
)
),
column(12,textOutput(outputId = "selection")))
)
)
)
)
server <- function(input, output, session) {
v <- reactiveValues()
observe({
d <- event_data("plotly_click",source="Bar1")
if (length(d) == 0) {species = as.vector(df_test$species[1])}
else {species = as.character(d[4])}
v$click <- species
})
output$bar_one <- renderPlotly({
p <- plot_ly(data = df_test, x = ~amount, y = ~species, type = 'bar', orientation = 'h', source = "Bar1")
})
output$bar_two <- renderPlotly({
p <- plot_ly(data = df_test, x = ~amount, y = ~species, type = 'bar', orientation = 'h', source = "Bar1")
})
output$selection <- renderText({
v$click
})
}
shinyApp(ui, server)

Related

Gauge chart from GoogleVis displaying in browser but on shiny app I get the error $ operator is invalid for atomic vectors

I am trying to make a shiny app where I can select a location on the map and display a gauge chart for each corresponding location.
I have been able to make the app reactive but the googlevis gauge display appears on the browser instead of in the app. In the app I get the error $ operator is invalid for atomic vectors. I tried converting the data into a dataframe but I am still getting this error.
the code is as follows
library(shiny)
library(leaflet)
library(shinydashboard)
library(dplyr)
library(googleVis)
#Making the Dataframe
locations<-c("A","B","C")
x<-c(36.05617,36.05626,36.05634)
y<-c(-2.1007,-2.05553,-2.01035)
yield<-c(5.86,3.06,1.07)
df<-data.frame(locations,x,y,yield)
################## Defining UI for application ############################
ui <- shinyUI(dashboardPage(title = "Yield Lookup",
dashboardHeader(title = "Crop Yield (Tonnes per Hectare)",titleWidth = 350),
dashboardSidebar(
sidebarMenu(
menuItem("Map Dashboard", tabName = "datavis", icon = icon("map", verify_fa = FALSE)),
menuItem("Select by Location Name", icon = icon("leaf"),
selectizeInput("locations", "Click on Location", choices = levels(factor(df$locations)))
)
)
),
dashboardBody(
tabItems(
tabItem(tabName = "datavis",
h4("Map and Plot"),
fluidRow(box(width= 8, leafletOutput("map", height = 800)),
box("Gauge for crop yield by area",width = 4, htmlOutput("myplot")))
)
)
)
)
)
################## Defining Server for application ############################
server<- shinyServer(function(input,output, session){
## Sub data
lo<-reactive({
})
locat_data <- reactive({
df[df$locations %in% input$locations,]
})
output[["myplot"]] <- renderGvis({
newdf<-locat_data()%>%select(locations, yield)
newdf<-as.data.frame(newdf)
Gauge <- gvisGauge(as.data.frame(newdf),
options=list(min=0, max=6, greenFrom=4,
greenTo=6, yellowFrom=2, yellowTo=4,
redFrom=0, redTo=2, width=400, height=300))
plot(Gauge)
})
output$map <- renderLeaflet({
leaflet(df) %>%
addTiles() %>%
addCircleMarkers(lng = ~x, lat = ~y, layerId = ~locations, color = "blue", radius = 3) %>%
addCircles(lng = ~x, lat = ~y, weight = 1,
radius = 1, label = ~locations
)
})
observeEvent(input$locations,{
updateSelectInput(session, "locations", "Click on Locations",
choices = levels(factor(df$locations)),
selected = c(input$locations))
})
observeEvent(input$map_marker_click, {
click <- input$map_marker_click
location <- df[which(df$y == click$lat & df$x == click$lng), ]$locations
updateSelectInput(session, "locations", "Click on Location",
choices = levels(factor(df$locations)),
selected = c(input$locations, location))
})
})
shinyApp(ui=ui, server = server)
I am not sure where I am going wrong. Please help.
You were very close.
Just drop the plot() and leave Gauge in the server section. (Alternatively drop the Gauge <- and Gauge on the next line and just leave gvisGauge())
library(shiny)
library(leaflet)
library(shinydashboard)
library(dplyr)
library(googleVis)
#Making the Dataframe
locations<-c("A","B","C")
x<-c(36.05617,36.05626,36.05634)
y<-c(-2.1007,-2.05553,-2.01035)
yield<-c(5.86,3.06,1.07)
df<-data.frame(locations,x,y,yield)
################## Defining UI for application ############################
ui <- shinyUI(dashboardPage(title = "Yield Lookup",
dashboardHeader(title = "Crop Yield (Tonnes per Hectare)",titleWidth = 350),
dashboardSidebar(
sidebarMenu(
menuItem("Map Dashboard", tabName = "datavis", icon = icon("map", verify_fa = FALSE)),
menuItem("Select by Location Name", icon = icon("leaf"),
selectizeInput("locations", "Click on Location", choices = levels(factor(df$locations)))
)
)
),
dashboardBody(
tabItems(
tabItem(tabName = "datavis",
h4("Map and Plot"),
fluidRow(box(width= 8, leafletOutput("map", height = 800)),
box("Gauge for crop yield by area",width = 4, htmlOutput("myplot")))
)
)
)
)
)
################## Defining Server for application ############################
server<- shinyServer(function(input,output, session){
## Sub data
lo<-reactive({
})
locat_data <- reactive({
df[df$locations %in% input$locations,]
})
output[["myplot"]] <- renderGvis({
newdf<-locat_data()%>%select(locations, yield)
newdf<-as.data.frame(newdf)
Gauge <- gvisGauge(as.data.frame(newdf),
options=list(min=0, max=6, greenFrom=4,
greenTo=6, yellowFrom=2, yellowTo=4,
redFrom=0, redTo=2, width=400, height=300))
Gauge
})
output$map <- renderLeaflet({
leaflet(df) %>%
addTiles() %>%
addCircleMarkers(lng = ~x, lat = ~y, layerId = ~locations, color = "blue", radius = 3) %>%
addCircles(lng = ~x, lat = ~y, weight = 1,
radius = 1, label = ~locations
)
})
observeEvent(input$locations,{
updateSelectInput(session, "locations", "Click on Locations",
choices = levels(factor(df$locations)),
selected = c(input$locations))
})
observeEvent(input$map_marker_click, {
click <- input$map_marker_click
location <- df[which(df$y == click$lat & df$x == click$lng), ]$locations
updateSelectInput(session, "locations", "Click on Location",
choices = levels(factor(df$locations)),
selected = c(input$locations, location))
})
})
shinyApp(ui=ui, server = server)

How to display map dynamically changed as per drilldown selectInput() based on previous selections?

I would like to render a map based on selectInput(). I have two selectInput()s: first one product_type and second one product_name. In the second one selectInput() the drop down options should be display only relevant to first selectInput(). Based on these drill down inputs map should change dynamically.
Here is the code:
ui <- shinyUI(dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidPage(
box("",
leafletOutput("abc", width = '100%', height = 300),
height = 350, width = 12),
box("",
selectInput('vtype', label = 'Prod Type',choices = brand$prod_type),
selectInput('vname', label = 'Prod Name',choices = brand$prod_name),
width = 4),
valueBoxOutput("gr", width = 8)
)
)
))
server <- shinyServer(function(input,output,session){
a <- ship %>% select(prod_name,prod_type,LON,LAT) %>% filter(prod_type == input$vtype)
output$gr <- renderValueBox({
box(table(a))
})
output$abc <- renderLeaflet({
leaflet() %>% addProviderTiles(providers$OpenTopoMap )
%>% setView(lat = a$LAT ,lng = A$LON, zoom = 4)
})
})
shinyApp(ui,server)
The dynamically changed points in the map should be marked up. I tried but could not able to build the code.
Any help on this code would be graceful for me.
I hope my example helps. I invented a data.frame 'ship' and made everything dependent on it. That means it is used for your variable 'brand' as well as 'ship'.
I'm not sure how you envisioned the value box, so I put category and products in it.
library(shiny)
library(shinydashboard)
library(dplyr)
library(leaflet)
ship <- data.frame(
product_type = c("food","food","tool","tool","tool","accessories","accessories","lighting","lighting","lighting"),
product_name=c("eggs", "bread","clamp","hammer","screw driver", "watch" ,"sun glases","LED","bulb","briquette"),
LON=c(-61.783,2.632,47.395,20.068,44.563,17.544,-170.730,-65.167,136.189,50.562),
LAT=c(17.078 ,28.163 ,40.430 ,41.143 ,40.534 ,-12.296 ,-14.318 ,-35.377 ,-24.973 ,26.019),
stringsAsFactors = F)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(collapsed = TRUE, disable = FALSE),
dashboardBody(
# fluidPage(
box(
leafletOutput("abc", width = '100%', height = 300),
height = 350,
width = 12),
box(
selectInput('vtype', label = 'Prod Type', choices = c("< select product type>"="", ship$product_type)),
selectInput('vname', label = 'Prod Name', choices = c("< select item>"="", ship$product_name)),
width = 4),
valueBoxOutput("gr", width = 8)
#)
)
)
server <- shinyServer(function(input,output,session){
a <- reactive({
ship %>%
select(product_name, product_type, LON, LAT) %>%
filter(product_type %in% input$vtype)
})
output$gr <- renderValueBox({
valueBox( input$vtype, paste(a()$product_name, collapse=' - ') )
})
observe({
updateSelectInput(session,
inputId='vname',
choices = c("< select item>"="", a()$product_name ))
})
output$abc <- renderLeaflet({
leaflet() %>%
addProviderTiles(providers$OpenTopoMap ) %>%
setView(lng=0, lat=0, zoom = 1)
})
observe({
selection <- a() %>% filter(product_name %in% input$vname)
leafletProxy("abc") %>%
flyTo(lat = selection$LAT,
lng = selection$LON,
zoom = 4)
})
})
shinyApp(ui,server)
Please provide example data next time.

Displaying the table details from sankey chart in R shiny

The script below works on the patients data from bupaR package,and creates a sankey plot listing the relation between a resource from the "employee" column with the activity he is involved in from the "handling" column in the patients data. Besides the plot there is a data table available from DT which gives details of every sankey plot path when clicked. I want a functionality such that when I click on any path, say path connecting "r1" employee and "Registration" handling activity, I want all the rows from patients data with both these fields available in the plot besides, similarly for all other paths, this should be dynamic as I shall apply the functionality on larger datasets. Attaching the snapshot for reference. Thanks and please help.
## app.R ##
library(shiny)
library(shinydashboard)
library(devtools)
library(ggplot2)
library(plotly)
library(proto)
library(RColorBrewer)
library(gapminder)
library(stringr)
library(broom)
library(mnormt)
library(DT)
library(bupaR)
ui <- dashboardPage(
dashboardHeader(title = "Sankey Chart"),
dashboardSidebar(
width = 0
),
dashboardBody(
box(title = "Sankey Chart", status = "primary",height = "455" ,solidHeader = T,
plotlyOutput("sankey_plot")),
box( title = "Case Summary", status = "primary", height = "455",solidHeader = T,
dataTableOutput("sankey_table"))
)
)
server <- function(input, output)
{
output$sankey_plot <- renderPlotly({
sankeyData <- patients %>%
group_by(employee,handling) %>%
count()
sankeyNodes <- list(label = c(sankeyData$employee,sankeyData$handling))
trace2 <- list(
domain = list(
x = c(0, 1),
y = c(0, 1)
),
link = list(
label = paste0("Case",1:nrow(sankeyData)),
source = sapply(sankeyData$employee,function(e) {which(e ==
sankeyNodes$label) }, USE.NAMES = FALSE) - 1,
target = sapply(sankeyData$handling,function(e) {which(e ==
sankeyNodes$label) }, USE.NAMES = FALSE) - 1,
value = sankeyData$n
),
node = list(label = sankeyNodes$label),
type = "sankey"
)
data2 <- list(trace2)
p <- plot_ly()
p <- add_trace(p, domain=trace2$domain, link=trace2$link,
node=trace2$node, type=trace2$type)
p
})
output$sankey_table <- renderDataTable({
d <- event_data("plotly_click")
d
})
}
shinyApp(ui, server)
Hi I interpreted the output from event_data as such that pointNumber is the index of the link but I might be wrong here. Any way this is my Solution and it works for this data
library(shiny)
library(shinydashboard)
library(devtools)
library(ggplot2)
library(plotly)
library(proto)
library(RColorBrewer)
library(gapminder)
library(stringr)
library(broom)
library(mnormt)
library(DT)
library(bupaR)
library(dplyr)
ui <- dashboardPage(
dashboardHeader(title = "Sankey Chart"),
dashboardSidebar(
width = 0
),
dashboardBody(
box(title = "Sankey Chart", status = "primary",height = "455" ,solidHeader = T,
plotlyOutput("sankey_plot")),
box( title = "Case Summary", status = "primary", height = "455",solidHeader = T,
dataTableOutput("sankey_table"))
)
)
server <- function(input, output)
{
sankeyData <- reactive({
sankeyData <- patients %>%
group_by(employee,handling) %>%
count()
sankeyNodes <- list(label = c(sankeyData$employee,sankeyData$handling) %>% unique())
trace2 <- list(
domain = list(
x = c(0, 1),
y = c(0, 1)
),
link = list(
label = paste0("Case",1:nrow(sankeyData)),
source = sapply(sankeyData$employee,function(e) {which(e ==
sankeyNodes$label) }, USE.NAMES = FALSE) - 1,
target = sapply(sankeyData$handling,function(e) {which(e ==
sankeyNodes$label) }, USE.NAMES = FALSE) - 1,
value = sankeyData$n
),
node = list(label = sankeyNodes$label),
type = "sankey"
)
trace2
})
output$sankey_plot <- renderPlotly({
trace2 <- sankeyData()
p <- plot_ly()
p <- add_trace(p, domain=trace2$domain, link=trace2$link,
node=trace2$node, type=trace2$type)
p
})
output$sankey_table <- renderDataTable({
d <- event_data("plotly_click")
req(d)
trace2 <- sankeyData()
sIdx <- trace2$link$source[d$pointNumber+1]
Source <- trace2$node$label[sIdx + 1 ]
tIdx <- trace2$link$target[d$pointNumber+1]
Target <- trace2$node$label[tIdx+1]
patients %>% filter(employee == Source & handling == Target)
})
}
shinyApp(ui, server)
hope it helps!

Displaying the value of bar created in R using shiny and plotly

If you run the R shiny script below, we get two boxes in a dashboard, the left box has a bar chart and right has a DT table, when I click on any bar of the chart using event_data("plotly_click"), I want the corresponding Employee to be displayed in the table besides, like when clicked on first bar, "r1" should be displayed in the table besides. I tried doing "user_cases$base1[d[3]]" but it throws an error as "Error: invalid subscript type 'list'". I will attach the snapshot for the reference, please help me with the same.
## app.R ##
library(shiny)
library(shinydashboard)
library(ggplot2)
library(plotly)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "Sankey Chart"),
dashboardSidebar(
width = 0
),
dashboardBody(
box(title = "Sankey Chart", status = "primary",height = "455" ,solidHeader =
T,
plotlyOutput("sankey_plot")),
box( title = "Case Summary", status = "primary", height = "455",solidHeader
= T,
dataTableOutput("sankey_table"))
)
)
server <- function(input, output)
{
output$sankey_plot <- renderPlotly({
height2 = c(56,45,23,19,8)
base1 = c("r1","r4","r2","r5","r3")
user_cases = data.frame(base1,height2)
pp1 <<- ggplot(user_cases, aes(x = reorder(base1,-height2), y = height2)) +
geom_bar(stat = "identity", fill = "#3399ff" ) + scale_y_discrete(name
="Cases") + scale_x_discrete(name = "Employee")
ggplotly(pp1, tooltip="text",height = 392)
})
output$sankey_table <- renderDataTable({
d <- event_data("plotly_click")
user_cases$base1[d[3]]
})
}
shinyApp(ui, server)
Dataset to be fetched
I am trying to fetch subset of the data from the patients dataset from bupaR library. The code for doing it is as follows:
patients_final <- patients[patients$employee == as.data.frame(
user_time$employee[as.numeric(d[3])])]
but the error I get is: "Can't use matrix or array for column indexing" attaching the snapshot for the help.
Have a look at the modified code, I have changed user_cases$base1[d[3]] to as.data.frame(user_cases$base1[as.numeric(d[3])])
## app.R ##
library(shiny)
library(shinydashboard)
library(ggplot2)
library(plotly)
library(DT)
height2 = c(56,45,23,19,8)
base1 = c("r1","r4","r2","r5","r3")
user_cases = data.frame(base1,height2)
ui <- dashboardPage(
dashboardHeader(title = "Sankey Chart"),
dashboardSidebar(
width = 0
),
dashboardBody(
box(title = "Sankey Chart", status = "primary",height = "455" ,solidHeader =
T,
plotlyOutput("sankey_plot")),
box( title = "Case Summary", status = "primary", height = "455",solidHeader
= T,
dataTableOutput("sankey_table"))
)
)
server <- function(input, output)
{
output$sankey_plot <- renderPlotly({
pp1 <<- ggplot(user_cases, aes(x = reorder(base1,-height2), y = height2)) +
geom_bar(stat = "identity", fill = "#3399ff" ) + scale_y_discrete(name
="Cases") + scale_x_discrete(name = "Employee")
ggplotly(pp1, tooltip="text",height = 392)
})
output$sankey_table <- renderDataTable({
d <- event_data("plotly_click")
as.data.frame( user_cases$base1[as.numeric(d[3])])
})
}
shinyApp(ui, server)
The output is as below:
You can modify the dataframe output as per your requirement.
Hope it helps!

Display R Shiny Plot After Inputting File

I would like to display a chart (for a Shiny app), based on data inputted by a user through a file. With the current setup, there is an error message claiming the data is not found, so the plot (from the rCharts package) does not get displayed.
Code attached below:
ui.R
library(rCharts)
library(shinydashboard)
library(shiny)
dashboardPage(
skin = "black",
header <- dashboardHeader(
titleWidth = 475
),
sidebar <- dashboardSidebar(
sidebarMenu(
)
),
body <- dashboardBody(
tabItems(
tabItem("setup",
box(width = 4,title = tags$b('Input Dataset'), solidHeader = T, status = 'primary', collapsible = T,
helpText("Default max. file size is 5 MB. Please upload both files for analysis in csv format."),
fileInput("file1","Upload the first file"),
fileInput("file2","Upload the second file")
),
box(height = 500, width = 12,title = tags$b('Visualize Data'), solidHeader = T, status = 'primary',
showOutput("myPlot", "Highcharts")
)
)
)
)
)
server.R
library(shiny)
library(rCharts)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
observe({
file1 = input$file1
file2 = input$file2
if (is.null(file1) || is.null(file2)) {
return(NULL)
}
data1 = read.csv(file1$datapath)
data2 = read.csv(file2$datapath)
})
output$myPlot<-renderChart2({
# Prepare data
data1[,2] <- (data1[,2])/sum(data1[,2])
# Create chart
a <- rCharts:::Highcharts$new()
a$chart(type = "column")
a$xAxis(categories = rownames(x))
a$yAxis(title = list(text = "Normalized Intensity"))
a$data(data1)
a$set(width = 600, height = 500)
return(a)
})
})
Try adding something like this. Make sure you check for nrow and return and empty Highcharts$new() object as renderChart2 expects one.
library(shiny)
library(rCharts)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
data1 <- reactive({read.csv(file1$datapath)})
data2 <- reactive({read.csv(file2$datapath)})
output$myPlot<-renderChart2({
data1 <- data1()
# Prepare data
if(nrow(data1==0)){return(Highcharts$new())}
data1[,2] <- (data1[,2])/sum(data1[,2])
# Create chart
a <- rCharts:::Highcharts$new()
a$chart(type = "column")
a$xAxis(categories = rownames(x))
a$yAxis(title = list(text = "Normalized Intensity"))
a$data(data1)
a$set(width = 600, height = 500)
return(a)
})
})

Resources