Why Is my R Shiny app not displaying properly? - r

I am clearly missing something here, but I am pretty new to Shiny apps (I have only every made a couple of them before), and I'm still learning the ropes of them.
This app (which will run on its own) works for the input side (a slider and a text input), but the output (which is supposed to be a table) will not display.
Here is the code:
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
ui <- fluidPage(
# Application title
titlePanel("CHD Risk Calculator"),
sidebarLayout(
sidebarPanel(
sliderInput("BMI",
"Your BMI (kg/m^2 OR (703*lbs)/in^2):",
min = 10,
max = 70,
value = 24),
textInput("Age",
"Your Age:")
),
mainPanel(
tableOutput("")
)
)
)
server <- function(input, output) {
inputdata <- reactive({
data <- data.frame(
MyBMI = as.integer(input$BMI),
MyAge = as.integer(input$age))
data
})
output$result <- renderTable({
data = inputdata()
chdrisk = -6.293 + (0.0292*data$BMI) + (0.07409*data$age)
resultTable = data.frame(
Result = "Your risk of Coronary Heart Disease (CHD) is",
Risk = chdrisk)
resultTable
})
}
# Run the application
shinyApp(ui = ui, server = server)
What am I missing here?
Thank you!

You have a few things going on here
Your tableOutput() has been given outputID=""; change this to "result"
Your inputs for the slider and the text are called BMI and Age, but in the reactive, you refer to them as BMI and age
The data frame in the reactive has two columns, MyBMI and MyAge, but later, you refer to them like this: data$BMI and data$age
Here is a corrected version
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
ui <- fluidPage(
# Application title
titlePanel("CHD Risk Calculator"),
sidebarLayout(
sidebarPanel(
sliderInput("BMI",
"Your BMI (kg/m^2 OR (703*lbs)/in^2):",
min = 10,
max = 70,
value = 24),
textInput("Age",
"Your Age:")
),
mainPanel(
tableOutput("result")
)
)
)
server <- function(input, output) {
inputdata <- reactive({
data <- data.frame(
MyBMI = as.integer(input$BMI),
MyAge = as.integer(input$Age))
data
})
output$result <- renderTable({
data = inputdata()
chdrisk = -6.293 + (0.0292*data$MyBMI) + (0.07409*data$MyAge)
resultTable = data.frame(
Result = "Your risk of Coronary Heart Disease (CHD) is",
Risk = chdrisk)
resultTable
})
}
# Run the application
shinyApp(ui = ui, server = server)

Related

R Shiny app seems to hang when generating data

Colleagues,
I'm creating a Shiny app that can generate a data set with user-defined properties. The intended data-generation function can take some time, so I've substituted a very simple one.
My problem is that the app seems to just hang, or nothing happens at all, when I hit the GO button.
DEBUG in Rstudio shows nothing, and reactlog also gives no information.
Similar questions on this stackoverflow forum are more than 8 years old, and suggestions don't seem to work either.
I'm sure the solution is head-slapping simple but, right now, I'm lost.
Any suggestions from those more knowledgeable than this Shiny newbie?
## generate data set with user-defined parameters
## load libraries
library(shiny)
library(ggplot2)
library(DT)
##
options(shiny.reactlog = TRUE)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Synthesise data"),
# Sidebar
sidebarLayout(
sidebarPanel(
## Sample size
numericInput("sample_n", "Sample size", 30,
min = 10, max = 300, step = 1
),
h4("Desired scale moments"),
numericInput("target_mean", "Target mean", 4),
numericInput("target_sd", "Target st dev", 1),
hr(style = "border-top: 1px solid #000000;"),
actionButton("goButton", "Go!"),
actionButton("goChart", "show chart"),
),
mainPanel(
h4("generated data"),
# table of generated data
DT::dataTableOutput("mytable"),
# Show a plot of the generated distribution
plotOutput("resultPlot")
)
)
)
# Define server logic
server <- function(input, output) {
mytable <- reactive(input$goButton, {
## substituting data-gen function that can take some time
mydata <- rnorm(sample_n, target_mean, target_sd) |>
data.frame()
colnames(mydata) <- "scale"
# saveRDS(mydata, file = "generatedData.RDS")
output$mytable <- DT::renderDataTable(DT::datatable({
mydata
}))
})
myplot <- eventReactive(input$goChart, {
output$resultPlot <- renderPlot({
ggplot(mydata, aes(x = scale)) +
geom_density()
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
Few code errors here :
forgot input$ when using sample_n, target_mean and target_sd in server
put some output definition inside eventReactive or reactive is a terrible habit
reactive is not used like you did. EventReactive is what you needed here.
Here is a corrected version of you code
library(shiny)
library(ggplot2)
library(DT)
ui <- fluidPage(
titlePanel("Synthesise data"),
sidebarLayout(
sidebarPanel(
numericInput("sample_n", "Sample size", 30,
min = 10, max = 300, step = 1
),
h4("Desired scale moments"),
numericInput("target_mean", "Target mean", 4),
numericInput("target_sd", "Target st dev", 1),
hr(style = "border-top: 1px solid #000000;"),
actionButton("goButton", "Go!"),
actionButton("goChart", "show chart"),
),
mainPanel(
h4("generated data"),
DT::dataTableOutput("mytable"),
plotOutput("resultPlot")
)
)
)
server <- function(input, output) {
mydata <- eventReactive(input$goButton, {
mydata <- data.frame(scale = rnorm(input$sample_n, input$target_mean, input$target_sd))
return(mydata)
})
output$mytable <- DT::renderDataTable(DT::datatable(
mydata()
))
output$resultPlot <- renderPlot({
input$goChart
isolate(ggplot(mydata(), aes(x = scale)) +
geom_density())
})
}
shinyApp(ui = ui, server = server)

Trouble with Reactive Dataframes in Shiny

Here's the minimal reproducible example:
# This is a Shiny web application.
library(shiny)
# UI for application
ui <- fluidPage(
# Application title
titlePanel("A Problematic App - Part 2"),
# Sidebar with two slider inputs
sidebarLayout(
sidebarPanel(
sliderInput(
"NoOfSamples",
label = "Sample Size",
value = 100,
min = 10,
max = 150,
step = 10,
width = "40%"
),
sliderInput(
"KeepSamples",
label = "Samples to Keep",
value = 50,
min = 10,
max = 150,
step = 10,
width = "40%"
)
),
# Shows the resulting table
mainPanel(
tableOutput("table1"),
tableOutput("table2")
)
)
)
# Server logic
server <- function(input, output) {
# Using the iris dataset
datExpr <- as.data.frame(iris)
n = reactive({
input$NoOfSamples
})
datExpr0 <- reactive({
datExpr[1:n(), ]
})
output$table1 <- renderTable({
datExpr0()
})
# Displays the first table correctly if the rest is commented out
keepSamples = reactive({
input$KeepSamples
})
datExpr <- reactive({
datExpr0()[keepSamples(),]
})
output$table2 <- renderTable({
datExpr()
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have created live examples for demonstration.
With the second part of the program commented out.
The complete program. [Shinyapps.io] is supressing the error details, so attached is a screenshot of a local run.
The error is object of type 'closure' is not subsettable. While many questions (and answers) regarding this error exist, I am yet to find any explaining the behaviour demonstrated above.
Why does this happen?
The normal (script-equivalent) works as expected.
datExpr <- as.data.frame(iris)
n = 50
datExpr0 <- datExpr[1:n, ]
datExpr0
keepSamples = 10
datExpr <- datExpr0[keepSamples,]
datExpr
Is there a way to achieve what the normal script does in the shiny app?
The issue is that you have both a dataframe and a reactive in your app called datExpr. Simply rename one of both (I decided for the reactive).
EDIT There is of course nothing special about that in shiny.
A simple example to illustrate the issue:
datExpr <- iris
datExpr <- function() {}
datExpr[1:2]
#> Error in datExpr[1:2]: object of type 'closure' is not subsettable
And you see that we get the famous object of type 'closure' is not subsettable error too. The general issue or lesson is that in R you can't have two different objects with the same name at the same time.
# This is a Shiny web application.
library(shiny)
# UI for application
ui <- fluidPage(
# Application title
titlePanel("A Problematic App - Part 2"),
# Sidebar with two slider inputs
sidebarLayout(
sidebarPanel(
sliderInput(
"NoOfSamples",
label = "Sample Size",
value = 100,
min = 10,
max = 150,
step = 10,
width = "40%"
),
sliderInput(
"KeepSamples",
label = "Samples to Keep",
value = 50,
min = 10,
max = 150,
step = 10,
width = "40%"
)
),
# Shows the resulting table
mainPanel(
tableOutput("table1"),
tableOutput("table2")
)
)
)
# Server logic
server <- function(input, output) {
# Using the iris dataset
datExpr <- as.data.frame(iris)
n = reactive({
input$NoOfSamples
})
datExpr0 <- reactive({
datExpr[1:n(), ]
})
output$table1 <- renderTable({
datExpr0()
})
# Displays the first table correctly if the rest is commented out
keepSamples = reactive({
input$KeepSamples
})
datExpr1 <- reactive({
datExpr0()[keepSamples(),]
})
output$table2 <- renderTable({
datExpr1()
})
}
# Run the application
shinyApp(ui = ui, server = server)
#>
#> Listening on http://127.0.0.1:3648

How to create different dashboards for different users of a Shiny app? (on the same app code)

I need to create a Shiny App that will generate 6 different versions of the same dashboard layout, to 6 different users. Each user will see its own historical data during production, and it's all in the same database (I'm guessing I just need to filter the whole database for each specific user).
Specifically:
1 - How do I detect which user is which? I'm gonna use Authentication so I'm guessing I can probably retrieve the information from the user by how he logged. But how do I retrieve this information in code terms?
2 - Knowing which user is which, how do I create the 6 different versions on the same app code? They'll be the same layout, the only difference is the filtering of the dataset based on the user.
(optional) 3 - How does Shiny servers conciliate different users' displays? Thinking about a dashboard that has user interaction, different inputs don't interfere each others' displays? Do they have to replicate the code for each access so they're independent results?
I haven't made it yet, and even if I did I think it would be too complex to resolve here, so I'm posting the Hello World of Shiny. This way, imagine that the dataset used for plotting the Histogram has a column called 'user'. What would be the code used for discriminating the users?
library(shiny)
output$distPlot <- renderPlot({
dist <- dataset[1:obs,1] %>% filter(???)
hist(dist)
})
})
shinyUI(fluidPage(
titlePanel("Hello Shiny!"),
# Sidebar with a slider input for number of observations
sidebarLayout(
sidebarPanel(
sliderInput("obs",
"Number of observations:",
min = 1,
max = 1000,
value = 500)
),
mainPanel(
plotOutput("distPlot")
)
)
))
Thanks!
login1 <- c("user1", "pw1")
login2 <- c("user2", "pw2")
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
uiOutput("ui")
# Sidebar with a slider input for number of bins
)
# Define server logic required to draw a histogram
server <- function(input, output) {
logged <- reactiveValues(logged = FALSE, user = NULL)
observeEvent(input$signin, {
if(input$name == "user1" & input$pw == "pw1") {
logged$logged <- TRUE
logged$user <- "user1"
} else if (input$name == "user2" & input$pw == "pw2") {
logged$logged <- TRUE
logged$user <- "user2"
} else {}
})
output$ui <- renderUI({
if(logged$logged == FALSE) {
return(
tagList(
textInput("name", "Name"),
passwordInput("pw", "Password"),
actionButton("signin", "Sign In")
)
)
} else if(logged$logged == TRUE & logged$user == "user1") {
return(
tagList(
titlePanel("This is user 1 Panel"),
tags$h1("User 1 is only able to see text, but no plots")
)
)
} else if(logged$logged == TRUE & logged$user == "user2") {
return(
tagList(
titlePanel("This is user 2 Panel for Executetives"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
)
} else {}
})
output$distPlot <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
This is a SIMPLE way to make it work. You get reactiveValues passed as conditional inputs to the renderUI function.
However, this is a very dangerous solution, since passwords and users are not encrypted. For professional deployment with R Shiny, think about Shiny-Server or my personal favorite ShinyProxy (https://www.shinyproxy.io/)
If you are using the authentication provided in shinyapps.io here is a simple solution to showing different UI elements to different users.
library(shiny)
library(dplyr)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
# Sidebar with a slider input for number of observations
sidebarLayout(
sidebarPanel(
uiOutput("slider")
),
mainPanel(
plotOutput("distPlot")
)
)
)
server <- function(input, output, session) {
# If using shinyapps.io the users email is stored in session$user
#session$user = "testuser1"
# session$user = "testuser2"
session$user = "testuser3"
slider_max_limit <- switch(session$user,
"testuser1" = 100,
"testuser2" = 200,
"testuser3" = 500)
output$slider <- renderUI(sliderInput("hp",
"Filter Horsepower:",
min = min(mtcars$hp),
max = slider_max_limit,
value = 70))
output$distPlot <- renderPlot({
req(input$hp)
mtcars %>%
filter(hp < input$hp) %>%
.$mpg %>%
hist(.)
})
}
shinyApp(ui, server)
By uncommenting the different users in the server function you can see how the slider changes.

Invalid (NULL) left side of assignment error in Shiny

I know this question has been posted a few times but this is my first time developing something i Shiny and I am getting confused with a couple different things. One of them is inputting the data frame correctly and using it in the output functions.
My only goals right now is to:
Display the head or complete dataframe depending on user choice
I have a binary column called status (status being Pass or Fail). I want to group by dates to count the status (any one would do) and plot it.
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(readxl)
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Data Quality Result Monitoring"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")
),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
radioButtons("disp", "Display",
choices = c(Head = "head",
All = "all"),
selected = "head")
),
# Show a plot of the generated distribution
mainPanel(
#plotOutput("linechart"),
h4("Observations"),
tableOutput("contents")
)
)
# Define server logic required to draw a histogram'
library(ggplot2)
server <- function(input, output) {
df <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
df <- read_xlsx(inFile$datapath, sheet = 1)
return(inFile)})
output$linechart <- renderPlot({
ndf() <- group_by(df,Execution_Date) %>% summarize( count = n() )
ggplot(ndf()) + geom_bar(aes(x=week,y=count),stat="identity")
})
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects
# and uploads a file, head of that data file by default,
# or all rows if selected, will be shown.
dataset() <- df
if(input$disp == "head") {
return(head(dataset()))
}
else {
return(dataset())
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
dataset() <- df
This is where you get the error:
"Error in <-: invalid (NULL) left side of assignment"
You can not assign a value to a reactive expression. It works the other way round:
dataset <- df()
Play around with this by using the print function.
Another error in your code is this:
df <- read_xlsx(inFile$datapath, sheet = 1)
return(inFile)
You return the wrong variable, you want to return the df.
Here is the code which should work for you:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(readxl)
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Data Quality Result Monitoring"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
fileInput('file1', 'Choose xlsx file',
accept = c(".xlsx")
),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
radioButtons("disp", "Display",
choices = c(Head = "head",
All = "all"),
selected = "head")
),
# Show a plot of the generated distribution
mainPanel(
#plotOutput("linechart"),
h4("Observations"),
tableOutput("contents")
)
)
# Define server logic required to draw a histogram'
library(ggplot2)
server <- function(input, output) {
df <- reactive({
inFile <- input$file1
if (is.null(inFile))
return(NULL)
df <- read_xlsx(inFile$datapath, sheet = 1)
df
})
output$linechart <- renderPlot({
ndf <- group_by(df(),Execution_Date) %>% summarize( count = n() )
ggplot(ndf + geom_bar(aes(x=week,y=count),stat="identity"))
})
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects
# and uploads a file, head of that data file by default,
# or all rows if selected, will be shown.
dataset <- df()
if(input$disp == "head") {
return(head(dataset))
}
else {
return(dataset)
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
I also recommend that you implement a check for structure and names in your code.
This is due to ndf() <- group_by(df,Execution_Date) %>% summarize( count = n() )
ndf() is NULL function that does not exist.
df is a reactive and you use it with df() instead of df, meaning that code is evaluated each time the reactivity changes.

checkbox not selected in a shiny app

I need to prepare a shiny app for a school project.
This is a link of what it is supposed to look like
https://yuvaln.shinyapps.io/olympics/
If you look at the app you see there is a checkbox named medals.When you
open the app they are all selected but in the event the user decides to uncheck them all there should be a small error and no graph should be drawn.
I am having trouble getting to this, when I uncheck all the boxes in my app
it draws an empty drawing
This is the important part of the code:
fluidRow(
column(3,checkboxGroupInput("Medals", label = strong("Medals"),
choices = list("Total" = "TOTAL", "Gold" = 'GOLD',
"Silver" = 'SILVER','Bronze'='BRONZE'),
selected = c('TOTAL','GOLD','SILVER','BRONZE')))),
fluidRow(
mainPanel(plotOutput('coolplot'),width = '40%'))
)
)
server <- function(input, output){output$coolplot<-renderPlot(plot.medals2(input$country,
input$Startingyear,input$Endingyear,input$Medals))}
shinyApp(ui = ui, server = server)
I am using a function plot.medals2 that gets a vector of medals ,start year, ending year, country and returns a drawing of the graph.
Since you didn't post the complete code, I have recreated an example using the Iris data set. I guess the code below answers your question...
library(shiny)
library(ggplot2)
library(dplyr)
ui <- shinyUI(fluidPage(
# Application title
titlePanel("Checkbox example"),
fluidRow(
column(3,checkboxGroupInput("example", label = strong("Species"),
choices = levels(iris$Species),
selected = levels(iris$Species)))),
fluidRow(
mainPanel(plotOutput('coolplot'),width = '40%'))
))
server <- shinyServer(function(input, output) {
irisSubset <- reactive({
validate(
need(input$example != "", 'Please choose at least one feature.')
)
filter(iris, Species %in% input$example)
})
output$coolplot<-renderPlot({
gg <- ggplot(irisSubset(), aes(x = Species, y = Sepal.Length))
gg <- gg + geom_boxplot()
print(gg)
})
})
# Run the application
shinyApp(ui = ui, server = server)

Resources