if else in shiny reactive - r

I have a rather simple problem but can not figure out why it is not working
library(shiny)
library(leaflet)
pts <- data.frame(
id = letters[seq(from = 1, to = 10)],
x = rnorm(10, mean = -93.625),
y = rnorm(10, mean = 42.0285),
stringsAsFactors = F
)
# Define UI
ui <- fluidPage(uiOutput('Select'))
server <- function(input, output, session) {
pts
output$Select <- renderUI({
Range <- sort(unique(pts$id))
selectInput("dataselect",
"select",
choices = Range,
selected = 'a')
})
mydata <- reactive({
if (input$dataselect != 'a') {
data <- pts[pts$id == input$dataselect,]
}
else
{
data <- pts
}
})
observe(print(mydata()))
}
shinyApp(ui = ui, server = server)
I basically try to subset my data set if anything else than 'a' is selected with the selected value. If 'a' is selected I want the whole df returned.
Just run into
Warning: Error in if: argument is of length zero [No stack trace
available]

You need to to not run mydata() if input$dataselect is not available, that can be done by inserting: req(input$dataselect)
As shown below:
library(shiny)
library(leaflet)
pts <- data.frame(
id = letters[seq(from = 1, to = 10)],
x = rnorm(10, mean = -93.625),
y = rnorm(10, mean = 42.0285),
stringsAsFactors = F
)
# Define UI
ui <- fluidPage(uiOutput('Select'))
server <- function(input, output, session) {
output$Select <- renderUI({
Range <- sort(unique(pts$id))
selectInput("dataselect",
"select",
choices = Range,
selected = 'a')
})
mydata <- reactive({
req(input$dataselect)
if (input$dataselect != 'a') {
data <- pts[pts$id == input$dataselect,]
}
else
{
data <- pts
}
})
observe(print(mydata()))
}
shinyApp(ui = ui, server = server)

Related

Generate dynamic sliders for filtering in Shiny

In Shiny, rather than manually typing out each slider for filtering a dataframe, which is in reality much larger than this, I use the following code to dynamically produce sliders, each with the appropriate range for its column, through the use of a single functional (lapply):
library(shiny)
library(tidyverse)
dat <- data.frame(a = 0:10, b = 20:30, c = 80:90)
ui <- fluidPage(
titlePanel("Filter DF"),
mainPanel(
tableOutput("df"),
uiOutput("sliders")
)
)
server <- function(input, output) {
tmp_df <- reactive({
dat %>% filter(a > 5) # blah blah
})
output$df <- renderTable({
tmp_df()
})
output$sliders <- renderUI({
t <- tmp_df()
pvars <- names(t)
lapply(pvars, function(nm) {
min <- min(t[[nm]], na.rm = TRUE)
max <- max(t[[nm]], na.rm = TRUE)
sliderInput(inputId = paste0("range_", nm),
label = nm,
min = min,
max = max,
value = c(min, max))
})
})
}
shinyApp(ui = ui, server = server)
But I would also like to dynamically create the code which enables each slider to filter (using dplyr) the dataframe with each slider's current values.
How could I do this?
Thanks #Limey for the head's up.
Solution:
library(shiny)
library(tidyverse)
dat <- data.frame(a = 0:10, b = 20:30, c = 80:90)
nms <- names(dat)
sliderUI <- function(id) {
ns <- NS(id)
min <- min(dat[[id]], na.rm = TRUE)
max <- max(dat[[id]], na.rm = TRUE)
sliderInput(inputId = ns('slider'),
label = id,
min = min,
max = max,
value = c(min, max))
}
sliderServer <- function(df, id) {
moduleServer(
id,
function(input, output, session) {
id <- as.name(id)
df %>% filter(dplyr::between(!!id, input$slider[1], input$slider[2]))
}
)
}
ui <- fluidPage(
map(nms, sliderUI),
tableOutput("df")
)
server <- function(input, output, session) {
tmp_df <- reactive({ dat })
output$df <- renderTable({
purrr::reduce(nms, sliderServer, .init = tmp_df())
})
}
shinyApp(ui, server)

plot visibility in shiny

I have this simple app - user should pick a starting hour and some values are generated from the starting hour and plotted.
library(shiny)
ui <- fluidPage(
titlePanel("Values by time generator"),
sidebarLayout(
sidebarPanel(
sliderInput("work_start",
"Starting hour:",
min = 7,
max = 17,
value = 9),
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output) {
df_updated <- reactive({
starting_hour = 7
end_hour = 17
df_optim <- data.frame(time = as.double(), value = as.double())
df_optim[nrow(df_optim) + length(starting_hour:end_hour) ,] <- NA
df_optim[["time"]] <- starting_hour:end_hour
df_optim[["value"]] <- ifelse(df_optim[["time"]] < as.numeric(input$work_start), 0, rnorm(length(starting_hour:end_hour)))
output$plot <- renderPlot({
tmp <- df_updated()
plot(tmp[["time"]], tmp[["value"]], xlab = "time", ylab = "value")
})
})
}
shinyApp(ui = ui, server = server)
But when I start my app there is no plot generated. There is no error.
I checked names of values, but I have no idea how to fix this problem.
You plot should be outside the reactive object. Try this
server <- function(input, output) {
df_updated <- reactive({
starting_hour = 7
end_hour = 17
df_optim <- data.frame(time = as.double(), value = as.double())
df_optim[nrow(df_optim) + length(starting_hour:end_hour) ,] <- NA
df_optim[["time"]] <- starting_hour:end_hour
df_optim[["value"]] <- ifelse(df_optim[["time"]] < as.numeric(input$work_start), 0, rnorm(length(starting_hour:end_hour)))
df_optim
})
output$plot <- renderPlot({
tmp <- df_updated()
plot(tmp[["time"]], tmp[["value"]], xlab = "time", ylab = "value")
})
}

Warning: Error in <=: comparison (4) is possible only for atomic and list types

I have this sample app:
User should pick a numer of row from which a random value will be generated and output is a plot of values.
I would like to compare input with the row number but I get this error:
Warning: Error in <=: comparison (4) is possible only for atomic and list types
48: ifelse
library(shiny)
ui <- fluidPage(
titlePanel("Random numbers"),
sidebarLayout(
sidebarPanel(
sliderInput("row",
"Row number:",
min = 1,
max = 50,
value = 30)
),
mainPanel(
plotOutput("rowPlot")
)
)
)
server <- function(input, output) {
rowTable <- data.frame(rowNumber = as.integer(), value = as.integer())
rowTable[1:50, ] <- NA
rowTable[["rowNumber"]] <- 1:50
rowTable[["value"]] <- ifelse(rowTable[["rowNumber"]] <= reactive({input$row}), 0, rnorm(50))
output$rowPlot <- renderPlot({
plot(rowTable[["rowNumber"]], rowTable[["value"]])
})
}
shinyApp(ui = ui, server = server)
Create the data.frame inside the reactive
server <- function(input, output) {
dat <- reactive({
rowTable <- data.frame(rowNumber = as.integer(), value = as.integer())
rowTable[1:50, ] <- NA
rowTable[["rowNumber"]] <- 1:50
rowTable[["value"]] <- ifelse(rowTable[["rowNumber"]] <= as.numeric(input$row), 0, rnorm(50))
rowTable
})
output$rowPlot <- renderPlot({
tmp <- dat()
plot(tmp[["rowNumber"]], tmp[["value"]], xlab = "rowNumber", ylab = "value")
})
})
}
-testing
shinyApp(ui = ui, server = server)
-output

R shiny foreach instead of double for loop

I have a fairly large simulation, that I currently run in Shiny using double for loop and it takes very long. I read about possibility of using foreach, but it does not work out, whatever I try, I and up in errors. Maybe some can spot the error and help me correct it?
app.R that runs (albeit very slowly (on real data) here with example data for reprex
require(shiny)
require(tidyverse)
require(foreach)
require(doMC)
registerDoMC()
options(cores = detectCores())
df <- data.frame(a=rnorm(n=26), b=1:26, c=100:125)
calc <- function(let=0.5, var1=0.1, var2=0.5){
df%>%
mutate(p1=ifelse(a<let,var1,0))%>%
mutate(p2=ifelse(a<let, var2,2))%>%
summarise(mean_b=mean(b*p1),
mean_c=mean(c*p2))
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Example"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput(inputId="selected_let",
label="LET",
value=0.5,
min=0,
max=1,
step=0.1),
submitButton("CALCULATE")
),
# Show a plot of the generated distribution
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data <- reactive({
data <- data.frame()
for (i in seq(0,1,by=0.1)) {
for (j in seq(0,1,by=0.1)) {
tmp <- calc(let = input$selected_let, var1 = i, var2 = j)
tmp_df <- data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
data <- rbind(data, tmp_df)
}
}
return(data)
})
output$table_1 <- renderTable({
data()%>%
select(var1,var2,mean_b)%>%
spread(var2, mean_b)
})
output$table_2 <- renderTable({
data()%>%
select(var1,var2,mean_c)%>%
spread(var2, mean_c)
})
}
# Run the application
shinyApp(ui = ui, server = server)
My goal was to change the data <-... part with foreach package and as my PC runs on UNIX I use the doMC.
to be replaced with:
data <- reactive({
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=input$selected_let,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
})
But this ends up in permanent errors:
I tried to out require(dplyr) in the the server part, but that did not help either.
Any suggestions for solutions?
As stand alone, the foreach part runs well with let=0.5 as input, given its not in reactive
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=0.5,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
Here is a way to avoid the double for-loop using library(data.table):
library(shiny)
library(data.table)
set.seed(0)
DF <- data.frame(a = rnorm(n = 26), b = 1:26, c = 100:125)
setDT(DF)
DT <- setDT(expand.grid(var1 = seq(0, 1, by = 0.1), var2 = seq(0, 1, by = 0.1)))
setorder(DT, var1, var2)
calc <- function(DF, let = 0.5, var1 = 0.1, var2 = 0.5) {
DF[, c("mean_b", "mean_c") := .(b * fifelse(a < let, var1, 0), c * fifelse(a < let, var2, 2))]
as.list(colMeans(DF[, .(mean_b, mean_c)]))
}
ui <- fluidPage(titlePanel("Example"),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "selected_let",
label = "LET",
value = 0.5,
min = 0,
max = 1,
step = 0.1
),
submitButton("CALCULATE")
),
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
))
server <- function(input, output) {
data <- reactive({
DT[, c("mean_b", "mean_c") := calc(DF, let = input$selected_let, var1 = var1, var2 = var2), by = seq_len(NROW(DT))]
})
output$table_1 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_b")
})
output$table_2 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_c")
})
}
shinyApp(ui = ui, server = server)
Here you can find a benchmark taking into account dplyr and data.table (among others).

R Shiny loop logical operator

I have a running example: I am updating a data.table depending on users input via checkboxes. So far Iam filtering the data explicitly, but I would like to do that with the help of a loop using a for loop or a function of the apply-family. Unfortunately I cannot get either to work.
library(shiny)
library(data.table)
library(DT)
tdata <- data.table(fruit = c(rep("Apple",4),rep( "Ban",4)),
bug1 = c(rep(c("+","+", "-","-"),2)),
bug2 = c(rep(c("+","-"),4)),
value = c(rep(c(0.25),4), 0.6,0.4,0,0))
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(dataTableOutput('fruit_table'))
))))
server <- function(input, output) {
fileData <- reactive(
return(tdata)
)
colname_list <- reactive(
colnames(fileData())
)
output$file_input <- renderUI ({
if(is.null(fileData())){
return()
}else{
tagList(
lapply(1:(length(fileData())-1), function(i){
choice_list = unique(fileData()[,get(colnames(fileData()[,i, with = FALSE]))])
checkboxGroupInput(inputId = colnames(fileData()[,i, with = FALSE]),
label = colnames(fileData()[,i, with = FALSE]),
choices = choice_list,
inline = TRUE,
selected = fileData()[1, i, with = FALSE])
})
)
}
})
# works fine, but usually the number of columns changes so I want to keep it flexible
fruitFilter <- reactive({
fileData()[[paste0(colname_list()[1])]] %in% input[[paste0(colname_list()[1])]] &
fileData()[[paste0(colname_list()[2])]] %in% input[[paste0(colname_list()[2])]] &
fileData()[[paste0(colname_list()[3])]] %in% input[[paste0(colname_list()[3])]]
})
# fruitFilter <- reactive({
# for(i in 1: ((length(fileData()))-1)){
# fileData()[[paste0(colname_list()[i])]] %in% input[[paste0(colname_list()[i])]]
# }
# })
output$fruit_table <- renderDataTable({
datatable(fileData()[fruitFilter(),])
})
}
shinyApp(ui = ui, server = server)
I still consider myself a newby to Shiny. I appreciate any help! Thanks.
In the loop approach, we could initialize a list and then Reduce the output to a single logical vector
fruitFilter <- reactive({
i1 <- head(seq_along(fileData()), -1)
l1 <- vector('list', length(i1))
for(i in i1){
l1[[i]] <- fileData()[[colname_list()[i]]] %in% input[[colname_list()[i]]]
}
Reduce(`&`, l1)
})
With the full code
library(shiny)
library(data.table)
library(DT)
tdata <- data.table(fruit = c(rep("Apple",4),rep( "Ban",4)),
bug1 = c(rep(c("+","+", "-","-"),2)),
bug2 = c(rep(c("+","-"),4)),
value = c(rep(c(0.25),4), 0.6,0.4,0,0))
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(dataTableOutput('fruit_table'))
))))
server <- function(input, output) {
fileData <- reactive(
return(tdata)
)
colname_list <- reactive(
colnames(fileData())
)
output$file_input <- renderUI ({
if(is.null(fileData())){
return()
}else{
tagList(
lapply(1:(length(fileData())-1), function(i){
choice_list = unique(fileData()[,get(colnames(fileData()[,i, with = FALSE]))])
checkboxGroupInput(inputId = colnames(fileData()[,i, with = FALSE]),
label = colnames(fileData()[,i, with = FALSE]),
choices = choice_list,
inline = TRUE,
selected = fileData()[1, i, with = FALSE])
})
)
}
})
fruitFilter <- reactive({
i1 <- head(seq_along(fileData()), -1)
l1 <- vector('list', length(i1))
for(i in i1){
l1[[i]] <- fileData()[[colname_list()[i]]] %in% input[[colname_list()[i]]]
}
Reduce(`&`, l1)
})
output$fruit_table <- renderDataTable({
datatable(fileData()[fruitFilter()])
})
}
shinyApp(ui = ui, server = server)
--output

Resources