conditionally addCircleMarkers to leaflet - r

I have a bunch of reactive filters based on user inputs. I want to addCircleMarkers on my leaflet map only if the filters do not return a NULL value. How can I conditionally addCircleMarkers to a leaflet map in Shiny? Right now it seems that it only plots the results from the second filter instead of both even if user inputs are not NULL for both. My guess is that the second addCircleMarkers function is overwriting the first instead of adding more circles to the map. Here's my server code below:
server.R
server <- function(input, output) {
relig_pal <- colorFactor("magma", unique(all_cleaned$religion))
denom_pal <- colorFactor("viridis", unique(all_cleaned$denom))
output$mymap <- renderLeaflet({
input$years_map
input$map_button
isolate({
map <- leaflet() %>% addTiles()
if(!is.null(geography_1())) {
marker_1 <- addCircleMarkers(map = map, data = geography_1(),
radius = ~ifelse(is.na(denom), log(religion_population),
log(denom_pop)),
color = ~ifelse(is.na(denom), relig_pal(religion),
denom_pal(denom)),
label = ~ifelse(is.na(denom),
paste("Religion:",religion,
"Population:",religion_population),
paste("Denomination:", denom,
"Population:", denom_pop))
)
}
if(!is.null(geography_2())) {
marker_1 %>% addCircleMarkers(data = geography_2(),
radius = ~ifelse(is.na(denom), log(religion_population),
log(denom_pop)),
color = ~ifelse(is.na(denom), relig_pal(religion),
denom_pal(denom)),
label = ~ifelse(is.na(denom),
paste("Religion:", religion,
"Population:", religion_population),
paste("Denomination:", denom,
"Population:", denom_pop))
)
}
})
})
year <- reactive({
req(input$years_map)
all_cleaned %>% filter(year == input$years_map)
})
religion_1 <- reactive({
req(input$religion_1_map)
if(input$religion_1_map == "All") {
year()
}
else if(input$religion_1_map == "None") {
return()
}
else {
year() %>% filter(religion == input$religion_1_map)
}
})
denom_1 <- reactive({
req(input$denom_1_map)
if(input$denom_1_map == "All") {
religion_1()
}
else if(input$denom_1_map == "None") {
religion_1() %>% filter(is.na(denom))
}
else {
religion_1() %>% filter(denom == input$denom_1_map)
}
})
geography_1 <- reactive({
req(input$geography_1_map)
if(input$geography_1_map == "All") {
denom_1()
}
else if(input$geography_1_map == "None") {
return()
}
else {
denom_1() %>% filter(country_name == input$geography_1_map)
}
})
religion_2 <- reactive({
req(input$religion_2_map)
if(input$religion_2_map == "All") {
year()
}
else if(input$religion_2_map == "None") {
return()
}
else {
year() %>% filter(religion == input$religion_2_map)
}
})
denom_2 <- reactive({
req(input$denom_2_map)
if(input$denom_2_map == "All") {
religion_2()
}
else if(input$denom_2_map == "None") {
religion_2() %>% filter(is.na(denom))
}
else {
religion_2() %>% filter(denom == input$denom_2_map)
}
})
geography_2 <- reactive({
req(input$geography_2_map)
if(input$geography_2_map == "All") {
denom_2()
}
else if(input$geography_2_map == "None") {
return()
}
else {
denom_2() %>% filter(country_name == input$geography_2_map)
}
})
}
Error message:
Error:no applicable method for 'filter_' applied to an object of class "NULL"
Thanks in advance for any help!

Once you draw markers on the map, you need to save those to a variable (e.g. see how we solved this problem here.
For the second question (you should really post them separately), try paste instead of cat, e.g. paste(statement1, statement2, sep = "\n").

Update:
I have managed to solve the problem using #Roman Luštrik 's suggestion of storing the first circle marker as a variable and using a placeholder to plot a point of opacity 0 instead of dealing with NULL values whenever nothing is supposed to appear on the plot, which I couldn't quite figure out.

Related

Dateinput function not functioning in filter

Below is my UI function in Flexdashboard. But when I try to on dates range, I am not getting the out. Below is the sample dataframe
df
Date Variable
21-09-2017 22:05:00 A
22-09-2017 22:05:00 B
23-09-2017 22:05:00 C
24-09-2017 22:05:00 D
24-09-2017 22:05:00 E
Filter function function below.(Not sure what wrong I am doing here in Filter function. I need both Variable and Date in filters
output$g1 <- renderPlotly({
req(input$Plot1)
if (input$Plot1 == "Trend") {
plot_data <- df
}
if (input$Plot1 == "Trend" & input$b != "All" & input$s2 != NULL) {
#plot_data <- df %>% filter(variable == input$b)
plot_data <- df[df$variable %in% input$b & df$Date %in% input$s2,]
}
UI function below
selectInput("Plot1","Filter1",choices = c("","Trend","Correlation"))
output$filter_2 <- renderUI({
if (input$Plot1 == "") {
return()
} else if (input$Plot1 == "Trend") {
label = "Trend"
selectInput("b",label,choices = c("All",levels(factor(df$variable))),multiple = TRUE)
} else {
label = "First Variable"
selectInput("b",
label,
choices = c(levels(factor(df$variable))))
}
})
uiOutput("filter_2")
uiOutput("filter_4")
output$filter_4 <- renderUI({
if(input$Plot1 == "Trend"){
dateRangeInput("s2","Date",min = min(df$Date),max = max(df$Date))
} else if(input$Plot1 == "Correlation"){
dateRangeInput("s2","Date",min = min(df$Date),max = max(df$Date))
}
})

simplify the subset of a table using multiple conditions in R shiny

I am writing a shiny app (shinydashboard) that looks like the figure (the app run on my company private network,so I can't share the link to it).
The dataset consists of a table containing the expression values of different genes (rows) for different samples (columns).
The app should return a subset of that table based on the search criteria selected by the user. Information about the samples are stored in a different table (B38.Metadata in the code), that looks like this:
SampleID,RNA.ID,RNAseq.ID,Name,Description,Tissue Type,...
CP3027,CP3027,74,Hs514,Aortic_Endothelial,Vascular system,Endothelial,...
CP3028,CP3028,76,HEr1,Aortic_Endothelial,Vascular system,Endothelial,...
At every search, the metadata are checked and the main table is subset accordingly.
My approach has been to write a function for each search types (SearchByGene,SearchByTissue,...), and
use if-else statements to account for all the possible combinations.
For example, filter by GeneName, Tissue type, and Name, but not for the other options.
This led to a massive 14 if-else block, spanning almost 50 lines of code (see below).
everything works, but the code is dreadful to read and debug.
Furthermore the idea of adding additional search possibilities (e.g. search by sequencing technique)
made me shiver.
I considered using a switch construct, but, having multiple conditions to test I'm not sure it will clean the code too much.
Is there a way of simplify the if-else block with something easier to read and, especially, maintain?
Searchfunction <- function(dataSet2){
selectedTable <- reactive({
# Create a DF with only the gene names
DFgeneLevel <- DummyDFgeneLevel(dataSet2) # not used for now
# Subset by Columns first
if(is.null(input$tissues) && is.null(input$samples) && is.null(input$Name)){
TableByColumns <- dataSet2
} else if(!is.null(input$tissues) && !is.null(input$samples) && !is.null(input$Name)){
TableByTissue <- SearchByTissue(input$tissues,B38.metadata,dataSet2)
TableBySample <- SearchBySample(input$samples,TableByTissue)
TableByColumns <- SearchByName(input$Name,B38.metadata,TableBySample)
} else if(!is.null(input$tissues)){
if(is.null(input$samples) && is.null(input$Name)){
TableByColumns <- SearchByTissue(input$tissues,B38.metadata,dataSet2)
} else if(is.null(input$samples) && !is.null(input$Name)){
TableByTissue <- SearchByTissue(input$tissues,B38.metadata,dataSet2)
TableByColumns <- SearchByName(input$Name,B38.metadata,TableByTissue)
} else if(!is.null(input$samples) && is.null(input$Name)){
TableByTissue <- SearchByTissue(input$tissues,B38.metadata,dataSet2)
TableByColumns <- SearchBySample(input$samples,TableByTissue)
}
} else if(is.null(input$tissues)){
if(is.null(input$samples) && !is.null(input$Name)){
TableByColumns <- SearchByName(input$Name,B38.metadata,dataSet2)
} else if(!is.null(input$samples) && is.null(input$Name)){
TableByColumns <- SearchBySample(input$samples,dataSet2)
} else if(!is.null(input$samples) && !is.null(input$Name)){
TableByName <- SearchBySample(input$samples,dataSet2)
TableByColumns <- SearchByName(input$Name,B38.metadata,TableByName)
}
}
# Collect all the inputs & subset by Rows
#genes.Selected <- toupper(genes.Selected) # can't use it as some genes contains lowerletters
genesFromList <- unlist(strsplit(input$genesLists,","))
genes.Selected <- unlist(strsplit(input$SearchCrit," "))
if(input$SearchCrit == '' && input$genesLists == 0){
TableByRow <- TableByColumns
} else if(input$SearchCrit != '' && input$genesLists != 0){
TableByList <- subset(TableByColumns, TableByColumns$GeneName %in% genesFromList)
TableByRow <- subset(TableByList, TableByList$GeneName %in% genes.Selected)
} else if(input$SearchCrit != '' && input$genesLists == 0){
TableByRow <- subset(TableByColumns, TableByColumns$GeneName %in% genes.Selected)
} else if(input$SearchCrit == '' && input$genesLists != 0) {
TableByRow <- subset(TableByColumns, TableByColumns$GeneName %in% genesFromList)
}
return(TableByRow)
})
}
Is that what you are trying to achieve ?
Filter samples that match your attributes based on your metadata and display gene expressions only for these samples ?
library(shiny)
library(dplyr)
ui <- fluidPage(
titlePanel("mtcars"),
sidebarLayout(
sidebarPanel(
selectInput("vs",
label = "vs",
choices = c(0, 1),
selected = NULL,
multiple = TRUE),
selectInput("carb",
label = "carb",
choices = c(1, 2, 3, 4, 6, 8),
selected = NULL,
multiple = TRUE),
selectInput("gear",
label = "gear",
choices = c(3, 4, 5),
selected = NULL,
multiple = TRUE)
),
mainPanel(
tabsetPanel(
tabPanel("Expression values", tableOutput("mainTable")),
tabPanel("ID filtering", tableOutput("table"))
)
)
)
)
server <- function(input, output) {
samples.df <- data.frame(ID = paste0("ID", as.character(round(runif(nrow(mtcars),
min = 0,
max = 100 * nrow(mtcars))))),
gear = as.factor(mtcars$gear),
carb = as.factor(mtcars$carb),
vs = as.factor(mtcars$vs))
values.df <- cbind(paste0("Feature", 1:20),
as.data.frame(matrix(runif(20 * nrow(samples.df)), nrow = 20)))
colnames(values.df) <- c("Feature", as.character(samples.df$ID))
vs.values <- reactive({
if (is.null(input$vs)) {
return(c(0, 1))
} else {
return(input$vs)
}
})
carb.values <- reactive({
if (is.null(input$carb)) {
return(c(1, 2, 3, 4, 6, 8))
} else {
return(input$carb)
}
})
gear.values <- reactive({
if (is.null(input$gear)) {
return(c(3, 4, 5))
} else {
return(input$gear)
}
})
filtered.samples.df <- reactive({
return(samples.df %>% filter(gear %in% gear.values(),
vs %in% vs.values(),
carb %in% carb.values()))
})
filtered.values.df <- reactive({
selected.samples <- c("Feature", names(values.df)[names(values.df) %in% filtered.samples.df()$ID])
return(values.df %>% select(selected.samples))
})
output$mainTable <- renderTable({
filtered.values.df()
})
output$table <- renderTable({
filtered.samples.df()
})
}
shinyApp(ui = ui, server = server)
You can try something like this, where we loop over the inputs and subset on the according column if the input is not null.
Hope this helps!
library(shiny)
ui <- fluidPage(
selectizeInput('mpg','mpg:',unique(mtcars$mpg),multiple=T),
selectizeInput('cyl','cyl:',unique(mtcars$cyl),multiple=T),
selectizeInput('gear','gear:',unique(mtcars$gear),multiple=T),
selectizeInput('carb','carb:',unique(mtcars$carb),multiple=T),
tableOutput('mytable')
)
server <- function(input,output)
{
output$mytable <- renderTable({
df = mtcars
select_inputs = c('mpg','cyl','gear','carb')
for (inp in select_inputs)
{
if(!is.null(input[[inp]]))
{
df = df[df[[inp]] %in% input[[inp]],]
}
}
df
})
}
shinyApp(ui,server)

Only filtering in Shiny if every option is selected

I have finally figured out how to get the plot to react to user input...unfortunately, the user needs to select the Year range, the Golfer, and the Course at the moment. I want them to be able to filter by just year and golfer, or just year and course if they want. How can I fix my code so that it returns the plot even if something isn't filled out? Any help would be greatly appreciated as I'm still pretty new at this! Thanks!
Define server logic required to draw a histogram
server <- function(input, output, session) {
updateSelectizeInput(session, inputId = 'golfer', choices = dkDatabase$Golfer, selected = "Woodland, Gary", server = TRUE)
updateSelectizeInput(session, inputId = 'course', choices = dkDatabase$Course, server = TRUE)
output$points <- renderPlot({
dkDatabase %>%
filter(dkDatabase$Year >= input$year[1],
dkDatabase$Year <= input$year[2],
dkDatabase$Golfer == input$golfer,
dkDatabase$Course == input$course) %>%
ggplot(aes(dkPoints)) + geom_histogram(fill="green", colour = "black") +
xlab("DK Points") +
ylab("Events")
})
}
Without seeing a bit more of the code I can't really test this, but it should work with some small modifications.
In the ui code, set your default select input:
selectInput(..., Choose = '')
And then in your server side code add an if statement (can't recall off the top of my head if you would be using single quotes, double quotes or null- test all of them out and 1 should work):
if (input$golfer == '') {
dkDatabase %>%
filter(dkDatabase$Year >= input$year[1],
dkDatabase$Year <= input$year[2],
dkDatabase$Course == input$course)
}
else if (input$Course == ''){
dkDatabase %>%
filter(dkDatabase$Year >= input$year[1],
dkDatabase$Year <= input$year[2],
dkDatabase$Golfer == input$golfer)
}
else {
dkDatabase %>%
filter(dkDatabase$Year >= input$year[1],
dkDatabase$Year <= input$year[2],
dkDatabase$Golfer == input$golfer,
dkDatabase$Course == input$course)
}
output$points <- renderPlot({
ggplot(aes(dkPoints())) + geom_histogram(fill="green", colour = "black") +
xlab("DK Points") +
ylab("Events")
})
I didn't address what to do if the year isn't entered above, but what I would do is automatically set the start and end dates to include all of the data, and then the user can change as desired.

Error in renderDataTable

When I'm trying to run this code as a Shiny app in R, I'm facing this error:
Error in renderDataTable({ : unused argument (rownames = FALSE)
output$table <- renderDataTable({
if(is.null(fdata()))
{return ()}
if(input$flevel=="Weekly")
{
if(input$flevel2=="Store")
{
data<-fdata()
data <- data[data$SKU == input$xcol,]
data <- data[data$Store == input$ycol,]
data
}
else if(input$flevel2=="Region")
{
data<-fdata()
data <- data[data$SKU == input$xcol,]
data <- data[data$Region == input$ycol,]
# data <- aggregate(Sales~Date+SKU+Region_Name,data = data,FUN = sum,na.rm=TRUE)
data
}
}
else if(input$flevel=="Monthly")
{
if(input$flevel2=="Store")
{
dmsales<-MonthManp()
data<-dmsales[[4]]
data <- data[data$SKU == input$xcol,]
data <- data[data$Store == input$ycol,]
data
}
else if(input$flevel2=="Region")
{
dmsales<-MonthManp()
data<-dmsales[[4]]
data <- data[data$SKU == input$xcol,]
data <- data[data$Region == input$ycol,]
data
# data <- aggregate(Sales~Date+product_id+loc_id+Channel_Name,data = data,FUN = sum,na.rm=TRUE)
}
} }, options = list(searching = FALSE),rownames=FALSE)
All my brackets are properly closed and the rownames is inside the datatable not the options tab. Can anyone pls help me in this. I'm a newbie in Shiny.
The params for
renderDataTable are:
renderDataTable(expr, options = NULL, searchDelay = 500,
callback = "function(oTable) {}", escape = TRUE, env = parent.frame(),
quoted = FALSE, outputArgs = list())
You could use the following format:
output$table <- DT::renderDataTable({
DT::datatable(df,options = list(searching=FALSE),rownames= FALSE)
})
Hope this helps!

Best way to draw on leaflet map in R

I want to draw several things on a leaflet map (through Shiny/R)
I initialize the map like this
map = leaflet() %>% addProviderTiles("Stamen.TonerLite") %>% setView(-1.5, 53.4, 9)
output$myMap = renderLeaflet(map)
Then, depending on what is clicked in the App I ether want to draw Markers or a Polygon
sp <- reactiveValues()
ep <- reactiveValues()
area <- reactiveValues()
area$mp <- matrix(...) # empty matrix with 2 cols named lat/lng
observeEvent(input$map_click, {
coords <- input$map_click
if ( (!is.null(as.integer(input$button)) && (!is.null(coords))) ) {
if (as.integer(input$button) == 1) {
sp[["lat"]] <- coords$lat
sp[["lng"]] <- coords$lng
} else if (as.integer(input$button) == 2) {
ep[["lat"]] <- coords$lat
ep[["lng"]] <- coords$lng
} else if (as.integer(input$button) == 3) {
cm <- matrix(data = c(coords$lat, coords$lng), nrow = 1, ncol = 2)
area$mp <- rbind(area$mp, cm)
} else {
print("Kawum!")
}
})
What I cannot get into my head is how to draw something now on the leaflet map.
What is group ID, what is layer ID. Where comes leafletProxy into play?
How would I, depending on which if else statement kicks in, send the data to leaflet and add a marker or a polygon?
Any help or pointing into the right direction is highly appreciated!
Maybe this can clarify things:
library(shiny)
library(leaflet)
ui <- shinyUI(fluidPage(
actionButton("button", "Change style!"),
leafletOutput("myMap")
))
server <- function(input, output){
map = leaflet() %>% addProviderTiles("Stamen.TonerLite") %>% setView(-1.5, 53.4, 9)
output$myMap = renderLeaflet(map)
sp <- reactiveValues()
ep <- reactiveValues()
area <- reactiveValues()
observeEvent(sp$lat, {
leafletProxy("myMap") %>% addMarkers(lat = sp$lat, lng = sp$lng)
})
observeEvent(ep$lat, {
leafletProxy("myMap") %>% addCircles(lat = ep$lat, lng = ep$lng)
})
observeEvent(area$mp, {
leafletProxy("myMap") %>% addPolygons(lat = area$mp[ , 1], lng = area$mp[ , 2])
})
observeEvent(input$myMap_click, {
coords <- input$myMap_click
if ( (!is.null(input$button) && (!is.null(coords))) ) {
if (input$button %% 4 == 1) {
sp[["lat"]] <- coords$lat
sp[["lng"]] <- coords$lng
} else if (input$button %% 4 == 2) {
ep[["lat"]] <- coords$lat
ep[["lng"]] <- coords$lng
} else if (input$button %% 4 == 3) {
cm <- matrix(data = c(coords$lat, coords$lng), nrow = 1, ncol = 2)
area$mp <- if(!is.null(area$mp)){rbind(area$mp, cm)}else{cm}
} else {
print("Kawum!")
}
}
})
}
shinyApp(ui, server)
First thing, the click event needs to be named after the output element. So input$myMap_click gives you the coords. Second, the leaflet proxy is designed to draw points, things etc. into existing maps. Imagine you'd always re-render the map to do leaflet() %>% addMarkers(...). leafletProxy just needs the output element's name and draws the markers on top of it.
The code above shows some things you can do with that. E.g. using the polygons.
Try using it and comment, if there is something unclear.

Resources