How to make ggvis tooltip interactive in shiny app? - r

In the example below, I have an interactive shiny ggvis choropleth with pop up label for income in each state. Users can switch data from drop down list.
My question is how to make the tooltip function interactive. The pop up label still displays the information of the original data set, even though user switches to the second data set. I tried to put it into reactive function and several other ways, but they all doesn't work. In the example below, I just use df1 in tooltip function to let you run and have a look at this app.
Thanks for your help!
Here is sample data
mapdata1<-data.frame(
state=c("alabama","alaska","arizona","arkansas","california","colorado","connecticut","delaware","florida","georgia","hawaii","idaho","illinois","indiana","iowa","kansas","kentucky","louisiana","maine","maryland","massachusetts","michigan", "minnesota","mississippi","missouri","montana","nebraska","nevada","new hampshire","new jersey","new mexico","new york","north carolina","north dakota","ohio","oklahoma", "oregon","pennsylvania","rhode island","south carolina","south dakota","tennessee","texas","utah","vermont","virginia","washington","west virginia","wisconsin","wyoming"),
income=runif(50,min=100,max=9000))
mapdata2<-data.frame(
state=c("alabama","alaska","arizona","arkansas","california","colorado","connecticut","delaware","florida","georgia","hawaii","idaho","illinois","indiana","iowa","kansas","kentucky","louisiana","maine","maryland","massachusetts","michigan", "minnesota","mississippi","missouri","montana","nebraska","nevada","new hampshire","new jersey","new mexico","new york","north carolina","north dakota","ohio","oklahoma", "oregon","pennsylvania","rhode island","south carolina","south dakota","tennessee","texas","utah","vermont","virginia","washington","west virginia","wisconsin","wyoming"),
income=runif(50,min=50,max=14000))
Server code
library(rgdal)
library(ggplot2)
library(ggvis)
tf <- tempfile()
td <- tempdir()
download.file(url,tf, mode="wb")
unzip(tf, exdir=td)
usa <- readOGR(dsn=td, layer="cb_2014_us_state_20m")
shp <- usa[(!usa$STUSPS %in% c("AK","HI")),]
df<- fortify(shp)
df<- merge(df,cbind(id=rownames(shp#data),shp#data),by="id")
df$state <- tolower(df$NAME)
df1<- merge(df,mapdata1,by="state")
df1<- df1[order(df1$order),]
df2<- merge(df,mapdata2,by="state")
df2<- df2[order(df2$order),]
shinyServer(
function(input,output){
dataInput<-reactive({
switch(input$segment,
"K 1"=df1,
"K 2"=df2)
})
###tooltip function
values = function(x){
if(is.null(x)) return(NULL)
row = head(df1[df1$group == unique(x$group), ], 1)
paste0("State: ", row$state,"<br />",
"Income: ", row$income, "<br />")
}
###choropleth
vis<-reactive({
data<-dataInput()
data %>%
group_by(group) %>%
ggvis(~long, ~lat) %>%
hide_axis("x") %>%
hide_axis("y")%>%
add_tooltip(values,"hover")%>%
layer_paths(fill= ~income)
})
vis %>% bind_shiny("visplot")
}
)
ui code
library(shiny)
library(ggvis)
shinyUI(fluidPage(
fluidRow(
column(3,
wellPanel(
selectInput("segment",
"Choose segment:",
choices = c("K 1",
"K 2")
)
)
),
column(9,
ggvisOutput("visplot")
)
)
))
UPDATED:
This is what I tried. I also use values() in add_tooltip instead of values. But it doesn't work.
###tooltip function
values<-reactive({
data<-dataInput()
if(is.null(x)) return(NULL)
row = head(data[data$group == unique(x$group), ], 1)
paste0("State: ", row$state,"<br />",
"Income: ", row$income, "<br />")
})

Here is a simpler mtcars example with a group-level tooltip like yours with layer_paths and grouping. Both the graph and tooltip info change when a different dataset is selected.
ui
library(ggvis)
library(shiny)
shinyUI(fluidPage(
titlePanel("Plotting slopes"),
sidebarLayout(
sidebarPanel(
selectInput("segment", label = "Choose segment", choices = c("K 1", "K 2"))),
mainPanel(ggvisOutput("plot"))
)
))
server:
library(shiny)
library(ggvis)
mtcars$cyl = factor(mtcars$cyl)
df1 = subset(mtcars, am == 0)
df2 = subset(mtcars, am == 1)
shinyServer(function(input, output) {
dataInput = reactive({
switch(input$segment,
"K 1" = df1,
"K 2" = df2)
})
values = function(x){
if(is.null(x)) return(NULL)
dat = dataInput()
row = dat[dat$cyl %in% unique(x$cyl), ]
paste0("Ave Weight: ", mean(row$wt),"<br />",
"Ave Carb: ", mean(row$carb), "<br />")
}
vis1 = reactive({
dat = dataInput()
dat %>%
group_by(cyl) %>%
ggvis(~mpg, ~wt) %>%
layer_paths(stroke = ~cyl, strokeOpacity := 0.3,
strokeWidth := 5) %>%
add_tooltip(values, "hover")
})
vis1 %>% bind_shiny("plot")
})

Related

Code to make my graph is interactive in shiny

I am trying to show the top ten highest temps from each year but the way I coded it, it will not change and just stays the same.
server.R
library(shiny)
library(dplyr)
library(ggplot2)
library(plotly)
library(readr)
library(tidyverse)
temp_df <-read_csv("~/Environment_Temperature_change_E_All_Data_NOFLAG.csv")
year_df <- temp_df[,8:66] #for the widget
info_df <- temp_df %>%
select(Area, Months, Element)
combine_df <- mutate(info_df, year_df)
combine_df <- na.omit(temp_df) # Get rid of NA rows
combine_df <- temp_df[!grepl("Standard Deviation",temp_df$Element), ] # Get rid of SD rows
top_ten_df <-top_n(combine_df, 10)
# Define server
server <-shinyServer(function(input, output) {
observe({
output$selected_var <- renderText({
paste("You have selected", input$year)
})
output$scatter <- renderPlot({
ggplot(data = top_ten_df, aes(x= Months, y = `Area`)) +
geom_point(aes(col=`Area`))
})
output$data <- renderTable({
final_df <-top_ten_df%>%
select(Area, Months, Element, input$year)
brushedPoints(final_df, input$plot_brush)
})
output$plotlyscatter <- renderPlotly({
plot_ly(data = top_ten_df, x = ~Area, y = ~Months, color=~Area, type = "scatter")
})
})
})
ui.R
library(shiny)
library(dplyr)
library(ggplot2)
library(plotly)
library(readr)
library(tidyverse)
temp_df <-read_csv("~/Environment_Temperature_change_E_All_Data_NOFLAG.csv")
year_df <- temp_df[,8:66] #for the widget
info_df <- temp_df %>%
select(Area, Months, Element)
combine_df <- mutate(info_df, year_df)
# Define UI
ui <- shinyUI(navbarPage(inverse = T, "Rising Temperatures",
tabPanel( "Top Ten Highest Tempratures",
sidebarLayout(
sidebarPanel(
h5("Selection"),
selectInput(inputId = "year",
label = "Select the year:",
choices = names(year_df),
),
textOutput("selected_var"),
),
mainPanel(
plotOutput(outputId = "scatter", brush = "plot_brush"),
tableOutput(outputId = "data"),
plotlyOutput(outputId = "plotlyscatter")
)
)
)
)
)
Also, I do not know where to use the app.R in this situation, sorry I am a bit new to all of this. I would like this to be an interactive scatter plot that when you pick an input from the widget.

Multiple group_by shiny app making a plot

\
I'm a really beginner in R Shiny.
I have a similar problem as at the link below.
multiple group_by in shiny app
Instead of making a table which worked out/I managed by following the instructions in the link above.
I would like to make a plot, preferably with hchart. In which i would to switch the information because of the group by. The difficult part / or the thing that doesn't work is putting the group_by on the x-axis.
## hier de tabel versie
df2 <- readRDS("Data.rds")
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
titlePanel("Dashboard"),
sidebarLayout(
sidebarPanel(
uiOutput("groups")
),
mainPanel(
DT::dataTableOutput("summary")
)
)
)
server <- function(input, output) {
mydata <- reactive({
data <- df2
data
})
output$groups <- renderUI({
df <- mydata()
selectInput(inputId = "grouper", label = "Group variable", choices = c("L","Lt","Lp"), selected = "L")
})
summary_data <- reactive({
req(input$grouper)
mydata() %>%
dplyr::group_by(!!!rlang::syms(input$grouper)) %>%
dplyr::summarise(aantal = n()) %>%
dplyr::arrange(desc(aantal))
})
output$summary <- DT::renderDataTable({
DT::datatable(summary_data())
})
}
shinyApp(ui, server)
The above code works, but i tried to make a plot like this:
df2 <- readRDS("Data.rds")
library(shiny)
library(highcharter)
library(dplyr)
ui <- fluidPage(
titlePanel("Dashboard"),
sidebarLayout(
sidebarPanel(
uiOutput("groups")
),
mainPanel(
highchartOutput("plotje")
)
)
)
server <- function(input, output) {
mydata <- reactive({
data <- df2
data
})
output$groups <- renderUI({
df <- mydata()
selectInput(inputId = "grouper", label = "Group variable", choices = c("L","Lt","Lp"), selected = "L")
})
summary_data <- reactive({
req(input$grouper)
mydata() %>%
dplyr::group_by(!!!rlang::syms(input$grouper)) %>%
dplyr::summarise(aantal = n()) %>%
dplyr::arrange(desc(aantal))
})
output$plotje <- renderHighchart({
data <- summary_data()
hchart(data, "column", hcaes(x = "grouper" , y = aantal)) # --> de plot zelf komt in het output deel van de UI
})
}
shinyApp(ui, server)
Could someone help me out?!
Thanks in advance!
Kind regards,
Steffie
You have the grouper column in the input$grouper var.
It's just a matter of unquoting it.
The line hchart(data, "column", hcaes(x = "grouper" , y = aantal)) should be:
hchart(data, "column", hcaes(x = !!input$grouper , y = aantal))
Full example (with iris data as you didn't provide an example of your own data):
library(shiny)
library(DT)
library(highcharter)
library(dplyr)
ui <- fluidPage(titlePanel("Dashboard"),
sidebarLayout(
sidebarPanel(uiOutput("groups")),
mainPanel(DT::dataTableOutput("summary"),
highchartOutput("plot"))
))
server <- function(input, output) {
mydata <- reactive({
iris
})
output$groups <- renderUI({
df <- mydata()
selectInput(
inputId = "grouper",
label = "Group variable",
choices = c("Petal.Length", "Species"),
selected = "Species"
)
})
summary_data <- reactive({
req(input$grouper)
mydata() %>%
dplyr::group_by(!!!rlang::syms(input$grouper)) %>%
dplyr::summarise(aantal = n()) %>%
dplyr::arrange(desc(aantal))
})
output$summary <- DT::renderDataTable({
DT::datatable(summary_data())
})
output$plot <- renderHighchart({
req(input$grouper)
data <- summary_data()
hchart(data, "column", hcaes(x = !!input$grouper, y = aantal))
})
}
shinyApp(ui, server)

Using validate in Shiny to hide plot without relevant data when using reactive function (R)

I have created an app using Shiny that displays data dependent on two different inputs. I'm filtering the data in a reactive function and then passing this through to the plots.
I can't work out how to simply hide the plots (and ideally show a helpful explanation) when there is no relevant data based on the inputs. I could do this if my data was in a dataframe, but as I have filtered it using a reactive function, this doesn't work.
I currently have the validate function nested in the renderPlot function, referencing the dataframe that is filtered by the reactive function...
Does anybody have any thoughts?
Reproducible code (if you select "Bristol" with the default date range, that demonstrates the issue):
library("tidyverse")
location <- as.character(c("London", "London", "Birmingham", "Bristol", "Birmingham", "Birmingham", "London", "Birmingham"))
dog_birthday <- as.POSIXct(c("01-01-2016", "02-02-2016", "03-03-2016", "04-04-2017", "05-05-2017", "06-06-2017", "08-08-2018", "07-07-2018"), format = "%d-%m-%Y")
dog_type <- as.character(c("Poodle", "Pug", "Labrador", "Poodle", "Poodle", "Labrador", "Pug", "Pug"))
dog_data <- data.frame(location, dog_birthday, dog_type)
ui<-
fluidPage(
sidebarLayout(
sidebarPanel(
dateRangeInput(
"dates", label = h3("Birthdate range"), start = ("01-06-2018"),
format = "dd-mm-yyyy", startview = "year"
),
selectInput(
"location", label = h3("Location"), choices = unique(dog_data$location),
multiple = T, selectize = T
)
),
mainPanel(
plotOutput(outputId = "dog_type")
)
)
)
server <- function(input, output) {
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location)
choose_city <- droplevels(choose_city)
return(choose_city)
})
output$dog_type <- renderPlot({
validate(
need(nrow(dog_data) > 0, "No data for this selection.")
)
dog_type_plot <- city_selection() %>%
filter(dog_birthday >= input$dates[1] & dog_birthday <= input$dates[2]) %>%
count(dog_type) %>%
arrange(-n) %>%
mutate(dog_type = factor(dog_type, dog_type)) %>%
ggplot(aes(dog_type, n)) +
geom_bar(stat = "identity")
dog_type_plot
})
}
shinyApp(ui, server)
You need to move the dates filter to the city_selection reactive and update the need condition in validate -
server <- function(input, output) {
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location) %>%
filter(dog_birthday >= input$dates[1] & dog_birthday <= input$dates[2])
choose_city <- droplevels(choose_city)
return(choose_city)
})
output$dog_type <- renderPlot({
validate(
need(nrow(city_selection()) > 0, "No data for this selection.")
)
dog_type_plot <- city_selection() %>%
count(dog_type) %>%
arrange(-n) %>%
mutate(dog_type = factor(dog_type, dog_type)) %>%
ggplot(aes(dog_type, n)) +
geom_bar(stat = "identity")
dog_type_plot
})
}
I also got an error trying to run the code:
Warning: Error in count: Argument 'x' must be a vector: list
A few other things that I noticed:
For me, choose_city <- droplevels(choose_city) doesn't do anything, I think you need choose_city$location <- droplevels(choose_city$location) if you're trying to remove the un-selected factor levels from location
I think #Shree's suggestion will help, but this method still only checks for the location, not the dates. (The reason your version doesn't do anything is because dog_data is your reference data.frame, and it doesn't get changed by your subsetting) #Shree's updated answer moved the date subset and now is probably better than this one :)
I changed your code a decent amount to get it to work for me (just because I don't use pipes and am most familiar with data.table). Obviously you can just remove the data.table dependency and filter with pipes!
The main thing is just that you want to check what dog_type_plot looks like right before making the plot. I added a reactiveVal to hold a message that's output in the sidebar:
library("tidyverse")
library("data.table")
location <- as.character(c("London", "London", "Birmingham", "Bristol", "Birmingham", "Birmingham", "London", "Birmingham"))
dog_birthday <- as.POSIXct(c("01-01-2016", "02-02-2016", "03-03-2016", "04-04-2017", "05-05-2017", "06-06-2017", "08-08-2018", "07-07-2018"), format = "%d-%m-%Y")
dog_type <- as.character(c("Poodle", "Pug", "Labrador", "Poodle", "Poodle", "Labrador", "Pug", "Pug"))
dog_data <- data.frame(location, dog_birthday, dog_type)
ui<-
fluidPage(
sidebarLayout(
sidebarPanel(
dateRangeInput(
"dates", label = h3("Birthdate range"), start = ("01-06-2018"),
format = "dd-mm-yyyy", startview = "year"
),
selectInput(
"location", label = h3("Location"), choices = unique(dog_data$location),
multiple = T, selectize = T
),
textOutput(outputId = "noDataMsg")
),
mainPanel(
plotOutput(outputId = "dog_type")
)
)
)
server <- function(input, output) {
## Subset base data.frame by user-selected location(s)
city_selection <- reactive({
req(input$location)
choose_city <- subset(dog_data, dog_data$location %in% input$location)
choose_city$location <- droplevels(choose_city$location)
return(choose_city)
})
## Value to hold message
message_v <- reactiveVal(); message_v("blank")
## Make Histogram
output$dog_type <- renderPlot({
print("city_selection():")
print(city_selection())
cat("\n")
## Change to data.table
data_dt <- as.data.table(city_selection())
print("original data_dt:")
print(data_dt)
cat("\n")
## Subset by birthday
dog_type_plot <- data_dt[dog_birthday >= input$dates[1] &
dog_birthday <= input$dates[2],]
print("subset by birthday")
print(dog_type_plot)
cat("\n")
## Get counts and sort
dog_type_plot[, N := .N, by = dog_type]
dog_type_plot <- dog_type_plot[order(-N)]
print("add count:")
print(dog_type_plot)
cat("\n")
## Change dog type to factor
dog_type_plot$dog_type <- factor(dog_type_plot$dog_type, levels = unique(dog_type_plot$dog_type))
print("refactor of dog_type:")
print(dog_type_plot$dog_type)
cat("\n")
## Check for data to plot
if (nrow(dog_type_plot) == 0) {
message_v("No dogs to plot using these parameters")
return(NULL)
} else {
## Make plot
plot_gg <- ggplot(data = dog_type_plot, aes(x = dog_type, y = N)) +
geom_bar(stat = "identity")
## Return
return(plot_gg)
} # fi
}) # renderPlot
## Message to user
output$noDataMsg <- renderText({ if (message_v() == "blank") { return(NULL) } else { message_v() } })
}
shinyApp(ui, server)

ggvis visualization does not appear in main pane

I'm new to shiny and this one has been giving me such a difficult. I tried several suggestions I found to the last reactive but none worked. I am not sure what I am doing wrong.
I tried vis <- reactive({}) and vis %>% bind_shiny() that did not work. Any suggestions will be greatly appreciated.
The ui.R appears but the visualization does not and I do not get an error message
server.R
library(shiny)
library(ggvis)
library(dplyr)
dataS <-read.csv("https://raw.githubusercontent.com/indianspice/IS608/master/Final%20Project/Data/shinydata.csv",
stringsAsFactors = FALSE)
function(input, output, session) {
#Filter breaches
breaches <- reactive({
records <- input$records
minyear <- input$year[1]
maxyear <- input$year[2]
# Apply filters
b <- dataS %>%
filter(
TotalRecords >= records,
Year >= minyear,
Year <= maxyear
) %>%
arrange(records)
#Filter by breach
if (input$breach != "All") {
breach <- paste0("%", input$breach, "%")
b <- b %>% filter(Breach %like% breach)
}
#Filter by company
if (!is.null(input$company) && input$company != "") {
company<- paste0("%", input$director, "%")
b <- b %>% filter(Company %like% company)
}
reactive({
xvar_name <- names(axis_vars)[axis_vars == input$year]
yvar_name <- names(axis_vars)[axis_vars == input$records]
xvar <- prop("x", as.symbol(input$xvar))
yvar <- prop("y", as.symbol(input$yvar))
breaches %>%
ggvis(x=xvar, y=yvar, stroke = ~breach) %>%
layer_points() %>%
add_axis("x", title = xvar_name) %>%
add_axis("y", title = yvar_name) %>%
add_legend("stroke", title = "Breach Type",
values = c("Hacking or Malware",
"Unintended Disclosure",
"Insider",
"Portable Device",
"Stationary Device",
"Unknown",
"Payment Card Fraud",
"Physical Loss")) %>%
scale_nominal("stroke", domain = c("Hacking",
"Unintended",
"Insider",
"Portable",
"Stationary",
"Unknown",
"Payment",
"Physical"),
range = c("red", "orange")) %>%
bind_shiny("ggvis", "ggvis_ui")
})
})
}
ui.R
library(shiny)
library(ggvis)
dataS <- read.csv("https://raw.githubusercontent.com/indianspice/IS608/master/Final%20Project/Data/shinydata.csv",
stringsAsFactors = FALSE)
fluidPage(
titlePanel("Data Breaches in the United States"),
#fluidRow(
column(4,
h4("Filter Data"),
sliderInput("records", "Number of records breached",
min = 10,
max = 1000000,
value = 10000,
step = 500),
sliderInput("year", "Year breach reported",
sep = "",
min = 2005,
max = 2017,
value = c(2007, 2010)),
selectInput("breach", "Type of breach",
c("All",
"Hacking or Malware",
"Unintended Disclosure",
"Insider",
"Portable Device",
"Stationary Device",
"Unknown",
"Payment Card Fraud",
"Physical Loss")),
selectInput("organzation", "Select type of organization",
choices = unique(dataS$TypeofOrganization)),
selectInput("company", "Select company",
choices = unique(dataS$Company)
),
textInput("companyName", "Enter company name")
),
#),
mainPanel(
uiOutput("ggvis_ui"),
ggvisOutput("ggvis")
)
)
Data
Company TypeofBreach TypeofOrganization TotalRecords Year
Bullitt Unintended Disclosure Educational Institutions 676 2009
Roane Portable Device Educational Institutions 14783 2009
Halifax Portable Device Healthcare Medical Provider 33000 2009
Suffolk Unintended Disclosure Educational Institutions 300 2009
Penrose Physical Loss Healthcare Medical Providers 175 2009
You are defining a reactive inside a reactive, which is bad. You should define your reactive (changing) data breaches using reactive - that's fine. Then, you should observe changes of that data using observe:
observe({
breaches() ... <do something>
...
%>% bind_shiny("ggvis", "ggvis_ui")
})
and then, at the end, use bind_shiny. See the following minimal example for an introduction how to do it (inspired by ggvis help pages):
library(shiny)
runApp(list(
ui = fluidPage(
sliderInput("slider", "Select rows from mtcars to consider", min=1, max = nrow(mtcars), step = 1, value = c(1,10)),
ggvisOutput("p"),
uiOutput("p_ui")
),
server = function(input, output) {
# define the data according to some input
data <- reactive({
mtcars[ input$slider[1] : input$slider[2], ]
})
# observe changes in the data and update ggvis plot accordingly
observe({
data %>%
ggvis(~wt, ~mpg) %>%
layer_points() %>%
bind_shiny("p", "p_ui")
})
}
))

Delete ggvis plots from shiny

I have multiple ggvis plots in Shiny.
I need to provide an action button, if the button is clicked all the plots need to be deleted.
Below is a sample code for ui.R and server.R:
ui.R
library(ggvis)
library(shiny)
shinyUI(fluidPage(
titlePanel("Plotting slopes"),
sidebarLayout(
sidebarPanel(
selectInput("segment", label = "Choose segment", choices = c("K 1", "K 2")),
actionButton("abutton","Delete plots")),
mainPanel(ggvisOutput("plot"), ggvisOutput("plot2"))
)
))
server.R
library(shiny)
library(ggvis)
mtcars$cyl = factor(mtcars$cyl)
df1 = subset(mtcars, am == 0)
df2 = subset(mtcars, am == 1)
shinyServer(function(input, output) {
dataInput = reactive({
switch(input$segment,
"K 1" = df1,
"K 2" = df2)
})
values = function(x){
if(is.null(x)) return(NULL)
dat = dataInput()
row = dat[dat$cyl %in% unique(x$cyl), ]
paste0("Ave Weight: ", mean(row$wt),"<br />",
"Ave Carb: ", mean(row$carb), "<br />")
}
vis1 = reactive({
dat = dataInput()
dat %>%
group_by(cyl) %>%
ggvis(~mpg, ~wt) %>%
layer_paths(stroke = ~cyl, strokeOpacity := 0.3,
strokeWidth := 5) %>%
add_tooltip(values, "hover")
})
vis1 %>% bind_shiny("plot")
vis2 = reactive({
dat = dataInput()
dat %>%
group_by(cyl) %>%
ggvis(~mpg, ~wt) %>%
layer_paths(stroke = ~cyl, strokeOpacity := 0.3,
strokeWidth := 5) %>%
add_tooltip(values, "hover")
})
vis2 %>% bind_shiny("plot2")
})
Screenshot of the current output:
If you tolerate the plots NOT deleted but LOOK LIKE deleted, I think it'll be easy to give bind_shiny() a blank graph.
server.R
:
vis2 %>% bind_shiny("plot2") # the same up to here
vis3 = mtcars %>% # preparation of a blank graph
ggvis(~mpg, ~wt, opacity := 0) %>%
layer_points() %>%
hide_axis("x") %>%
hide_axis("y")
observeEvent(input$abutton, { # When the button is clicked,
bind_shiny(vis3, "plot") # bind_shiny() reads and outputs a blank graph, vis3.
bind_shiny(vis3, "plot2") # When other Input is done, vis1 and vis2 return.
})
})

Resources