library(shiny)
library(ggplot2)
ui <- shinyUI(fluidPage(
titlePanel("Central Limit Theorem Simulation"),
sidebarLayout(
sidebarPanel(
numericInput("sample_size", "Size of each random sample\n(max: 100)",
value = 30, min = 1, max = 100, step = 1),
sliderInput("simulation", "THe number of simulation",
value = 100, min = 100, max = 1000, step = 1),
selectInput("sample_dist", "Population Distribution where each sample is from",
choices = c("Binomial","Poisson", "Normal", "Uniform") ),
numericInput("bins", "Number of bins in the histogram\n(max: 50)",
value = 20, min = 1, max = 50, step = 1),
submitButton(text = "Submit")
),
mainPanel(
tabsetPanel(type = "pills",
tabPanel("mean of random sample mean", br(),
textOutput(outputId = "output_mean")),
tabPanel("variance of random sample mean", br(),
textOutput(outputId = "output_var")),
tabPanel("summary table", br(),
tableOutput(outputId = "output_table")),
tabPanel("sample matrix", br(),
verbatimTextOutput(outputId = "output_sample")),
tabPanel("histogram of random normal sample", br(),
plotOutput(outputId = "output_hist"))
)
)
)
))
server <- shinyServer(function(input, output) {
# Return the random sample
rsample <- reactive({
if (input$sample_dist == "Binomial") {
rsample <- rbinom(input$sample_size * input$simulation, 1, 0.5)
} else if (input$sample_dist == "Poisson") {
rsample <- rpois(input$sample_size * input$simulation, 1)
} else if (input$sample_dist == "Normal") {
rsample <- rnorm(input$sample_size * input$simulation)
} else {
rsample <- runif(input$sample_size * input$simulation)
}
rsample
})
# Return the random sample matrix
rsamplematrix <- reactive({
matrix(rsample(), nrow = input$simulation)
})
# output mean of sample mean
output$output_mean <- renderText({
sample_mean <- rowMeans(rsamplematrix())
mean(sample_mean)
})
# output variance of sample mean
output$output_var <- renderText({
sample_mean <- rowMeans(rsamplematrix())
var(sample_mean)
})
# output summary table of sample mean
output$output_table <- renderTable({
sample_mean <- rowMeans(rsamplematrix())
data.frame(mean(sample_mean), var(sample_mean))
})
# output the first 5 rows and 5 columns of the sample matrix
output$output_sample <- renderPrint({
k = rsamplematrix()
k[1:5, 1:5]
})
# output histogram of sample mean
output$output_hist <- renderPlot({
sample_mean <- rowMeans(rsamplematrix())
ggplot(data.frame(sample_mean), aes(x = sample_mean, y = ..density..)) +
geom_histogram(bins = input$bins, fill = "steelblue", col = "white")
})
})
shinyApp(ui = ui, server = server)
The above code runs well. Suppose right now I want to add textinput of distribution parameters for each distribution (for example, p is parameter for Binomial distribution, mu and sigma are parameters for normal distribution). These parameters input pop out when I made a selection for sample distribution.
What should I do? Which function should I use?
If you want to keep the parameter inputs hidden unless a distribution is selected then I think you want a conditionalPanel.
For example:
conditionalPanel("!(output.plot)", textInput("size", "Size"))
This size input panel would only show when output$plot is not yet rendered.
In your case I think you can use an input in the same way.
conditionalPanel('input.sample_dist) != "Poisson"', textInput("parameter2", "Parameter 2"))
This would show parameter 2 only if Poisson is not selected.
You'll need to remove your submitButton to ensure the parameter fields update when you select a new distribution. Replace it with a generic actionButton if you want to keep the same function.
I'd also suggest using numericInput fields and setting the appropriate label, min, max and step with updateNumericInput for each case in the server function when sample_dist updates.
Related
I am trying to create a Shiny app demonstration of bootstrapping/Central Limit Theorem, and it generally works. However, I intended to have a Shiny app where each time "Simulate" is clicked, the plot refreshes and a new, different distribution is generated. However, each time I open the app and click "Simulate", the plot remains static and the same.
Does anyone have any ideas on how to resolve/fix? It would be much appreciated!
library(shiny)
library(tidyverse)
# Define the user interface for the app
ui <- fluidPage(
# Add a title and sidebar
titlePanel("Bootstrapped Distribution"),
sidebarLayout(
sidebarPanel(
sliderInput("num_sims", "Number of Simulations:",
min = 1, max = 10000, value = 100),
sliderInput("sampleSize", "Sample Size:",
min = 1, max = 10000, value = 100),
selectInput("distT", "Distribution Type:",
c("Normal" = "norm",
"Exponential" = "exp",
"Gamma" = "gam",
"Poisson" = "pois",
"Binomial" = "binom")),
actionButton("simulate", "Simulate")
),
# Add a plot to the main panel
mainPanel(plotOutput("dist_plot"))
)
)
# Define the server logic for the app
server <- function(input, output) {
# Create a reactive expression to simulate the bootstrapped distribution
#dist_data <- reactive({
dist_data <- reactive({
if(input$distT == "norm"){
bootstrapped_mean <- replicate(input$num_sims, mean(rnorm(input$sampleSize)))
}else if(input$distT == "exp"){
bootstrapped_mean <- replicate(input$num_sims, mean(rexp(input$sampleSize)))
}else if(input$distT == "gam"){
bootstrapped_mean <- replicate(input$num_sims, mean(rgamma(input$sampleSize, shape=1)))
}else if(input$distT == "pois"){
bootstrapped_mean <- replicate(input$num_sims, mean(rpois(input$sampleSize, lambda = 1)))
}else if(input$distT == "binom"){
bootstrapped_mean <- replicate(input$num_sims, mean(rbinom(input$sampleSize, size = 1, prob = .5)))
}
ggplot(data=tibble(mean=bootstrapped_mean), aes(x=mean)) +
geom_histogram(bins=30) + jtools::theme_apa()
})
# Update the plot when the "Simulate" button is clicked
observeEvent(input$simulate, {
output$dist_plot <- renderPlot({
dist_data()
})
}
)
}
Suggestions:
Break out dist_data to return just data, instead of trying to plot it as well. This app doesn't benefit a lot from this step, but it's a good way to organize what you're doing.
For instance, if you later add a table that summarizes the random data, you don't have an easy way for the table to get the random data since data_dist was returning a ggplot grob, not the raw data.
Add a dependency on input$simulate in your dist_data reactive block: this will cause the random data to be re-generated each time the button is pressed. Without this, the data will be generated once and never redone.
Move output$dist_plot outside of the observer (which is no longer needed).
library(shiny)
# library(tidyverse)
library(ggplot2)
# Define the user interface for the app
ui <- fluidPage(
# Add a title and sidebar
titlePanel("Bootstrapped Distribution"),
sidebarLayout(
sidebarPanel(
sliderInput("num_sims", "Number of Simulations:",
min = 1, max = 10000, value = 100),
sliderInput("sampleSize", "Sample Size:",
min = 1, max = 10000, value = 100),
selectInput("distT", "Distribution Type:",
c("Normal" = "norm",
"Exponential" = "exp",
"Gamma" = "gam",
"Poisson" = "pois",
"Binomial" = "binom")),
actionButton("simulate", "Simulate")
),
# Add a plot to the main panel
mainPanel(plotOutput("dist_plot"))
)
)
# Define the server logic for the app
server <- function(input, output) {
# Create a reactive expression to simulate the bootstrapped distribution
dist_data <- reactive({
req(input$simulate)
bootstrapped_mean <-
if (input$distT == "norm") {
replicate(input$num_sims, mean(rnorm(input$sampleSize)))
} else if (input$distT == "exp") {
replicate(input$num_sims, mean(rexp(input$sampleSize)))
} else if (input$distT == "gam") {
replicate(input$num_sims, mean(rgamma(input$sampleSize, shape = 1)))
} else if (input$distT == "pois") {
replicate(input$num_sims, mean(rpois(input$sampleSize, lambda = 1)))
} else if (input$distT == "binom") {
replicate(input$num_sims, mean(rbinom(input$sampleSize, size = 1, prob = 0.5)))
}
})
output$dist_plot <- renderPlot({
X <- req(dist_data())
ggplot(data = tibble(mean = X), aes(x = mean)) +
geom_histogram(bins = 30) +
jtools::theme_apa()
})
}
shinyApp(ui, server)
I expanded the result from my last question with a new idea.
Error in Running R Shiny App: Operation not allowed without an active reactive context
This time in addition to clustered points in Iris data (see my previous question), I want to show the regression line (on the plot), slope & intercept (on the sidebar) for the selected points as in:
The regression code is available here (separate server.R and ui.R files):
library(shiny)
shinyServer(function(input, output) {
model <- reactive({
brushed_data <- brushedPoints(iris, input$brush1,
xvar = "Petal.Length", yvar = "Petal.Width")
if(nrow(brushed_data) < 2){
return(NULL)
}
lm(Petal.Width ~ Petal.Length, data = brushed_data)
})
output$slopeOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][2]
}
})
output$intOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][1]
}
})
output$plot1 <- renderPlot({
plot(iris$Petal.Length, iris$Petal.Width, xlab = "Petal.Length",
ylab = "Petal.Width", main = "Iris Dataset",
cex = 1.5, pch = 16, bty = "n")
if(!is.null(model())){
abline(model(), col = "blue", lwd = 2)
}
})
})
and
library(shiny)
shinyUI(fluidPage(
titlePanel("Visualize Many Models"),
sidebarLayout(
sidebarPanel(
h3("Slope"),
textOutput("slopeOut"),
h3("Intercept"),
textOutput("intOut")
),
mainPanel(
plotOutput("plot1", brush = brushOpts(
id = "brush1"
))
)
)
))
I used the following code. However, I have a problem with merging these two ideas and the plot is not shown:
Here is the main code for this question (server and ui in one file):
# Loading Libraries and data
library(shiny)
library(caret)
library(ggplot2)
data(iris)
ui <- pageWithSidebar(
# heading 1
headerPanel(h1("Clustering Iris Data")),
sidebarPanel(
sliderInput("k", "Number of clusters:",
min = 1, max = 5, value = 3),
sliderInput("prob", "Training percentage:",
min=0.5, max=0.9, value = 0.7),
# bold text
tags$b("Slope:"),
textOutput("slopeOut"),
# empty line
br(),
# bold text
tags$b("Intercept:"),
textOutput("intOut")
),
# Enabling the submit button disables the hovering feature
# submitButton("submit")),
mainPanel(
# img(src='iris_types.jpg', align = "center", height="50%", width="50%"),
plotOutput("plot1",
click = "plot_click",
brush = brushOpts(id = "brush1")
),
verbatimTextOutput("info")
)
)
#----------------------------------------------------------------------------
server <- function(input, output) {
# the clustering part
get_training_data <- reactive({
inTrain <- createDataPartition(y=iris$Species,
p=input$prob,
list=FALSE)
training <- iris[ inTrain,]
testing <- iris[-inTrain,]
kMeans1 <- kmeans(subset(training,
select=-c(Species)),
centers=input$k)
training$clusters <- as.factor(kMeans1$cluster)
training
})
#-------------------------
# the linear model part
model <- reactive({
brushed_data <- brushedPoints(iris, input$brush1,
xvar = "Petal.Length", yvar = "Petal.Width")
if(nrow(brushed_data) < 2){
return(NULL)
}
lm(Petal.Width ~ Petal.Length, data = brushed_data)
})
# reactive
output$slopeOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][2]
}
})
# reactive
output$intOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][1]
}
})
#------------------------------------------------
# if (x()<4) 1 else 0
output$plot1 <- reactive({
if(is.null(model())) {
# If no regression model exists, show the regular scatter plot
# with clustered points and hovering feature
renderPlot({
plot(Petal.Width,
Petal.Length,
colour = clusters,
data = get_training_data(),
xlab="Petal Width",
ylab="Petal Length")
})
output$info <- renderPrint({
# With ggplot2, no need to tell it what the x and y variables are.
# threshold: set max distance, in pixels
# maxpoints: maximum number of rows to return
# addDist: add column with distance, in pixels
nearPoints(iris, input$plot_click, threshold = 10, maxpoints = 1,
addDist = FALSE)
})
# closing if
}
else
# If there is a regression model, show the plot with the regression line for the brushed points
renderPlot({
plot(Petal.Width,
Petal.Length,
colour = clusters,
data = get_training_data(),
xlab = "Petal.Length",
ylab = "Petal.Width",
main = "Iris Dataset",
cex = 1.5, pch = 16, bty = "n")
if(!is.null(model())){
abline(model(), col = "blue", lwd = 2)
}
})
# closing reactive statement
})
# curly brace for server function
}
shinyApp(ui, server)
You were assigning the wrong data type to the output$plot1.
It expects something that was created by the function renderPlot(...) while you were giving it a result of reactive(...).
Restructure your code such that you immediately assign
output$plot1 <- renderPlot(...)
Since renderPlot opens a reactive environment, just as reactive does, you can just replace the function. But make sure that you remove the renderPlot calls from within the environment.
After changing that, you will run into some more errors you have in your code but I bet you can work it out from there.
library(shiny)
library(ggplot2)
ui <- shinyUI(fluidPage(
titlePanel("Central Limit Theorem Simulation"),
sidebarLayout(
sidebarPanel(
numericInput("sample_size", "Size of each random sample\n(max: 100)",
value = 30, min = 1, max = 100, step = 1),
sliderInput("simulation", "The number of simulation",
value = 100, min = 100, max = 1000, step = 1),
numericInput("bins", "Number of bins in the histogram\n(max: 50)",
value = 20, min = 1, max = 50, step = 1),
selectInput("sample_dist", "Population Distribution where each sample is from",
choices = c("Bernoulli","Poisson", "Normal", "Uniform") ),
conditionalPanel(condition = 'input.sample_dist == "Bernoulli"',
textInput("prob", "Parameter (p)") ),
conditionalPanel(condition = 'input.sample_dist == "Poisson"',
textInput("lambda", "Parameter (lambda)") ),
conditionalPanel(condition = 'input.sample_dist == "Normal"',
textInput("mu", "Parameter (mu)"),
textInput("sigma", "Parameter (sigma)") ),
conditionalPanel(condition = 'input.sample_dist == "Uniform"',
textInput("min_a", "Parameter (a)"),
textInput("max_b", "parameter (b)") ),
actionButton("update", "Update Simulation")
),
mainPanel(
tabsetPanel(type = "pills",
tabPanel("mean of random sample mean", br(),
textOutput(outputId = "output_mean")),
tabPanel("variance of random sample mean", br(),
textOutput(outputId = "output_var")),
tabPanel("summary table", br(),
tableOutput(outputId = "output_table")),
tabPanel("sample matrix", br(),
verbatimTextOutput(outputId = "output_sample")),
tabPanel("histogram of random normal sample", br(),
plotOutput(outputId = "output_hist"))
)
)
)
))
server <- shinyServer(function(input, output) {
# Return the random sample
rsample <- eventReactive(input$update, {
if (isolate(input$sample_dist == "Bernoulli") ) {
rsample <- isolate(rbinom(n = input$sample_size * input$simulation,
size = 1, as.numeric(input$prob) ) )
} else if (isolate(input$sample_dist == "Poisson") ) {
rsample <- isolate(rpois(n = input$sample_size * input$simulation,
as.numeric(input$lambda) ) )
} else if (isolate(input$sample_dist == "Normal") ) {
rsample <- isolate(rnorm(n = input$sample_size * input$simulation,
mean = as.numeric(input$mu), sd = as.numeric(input$sigma) ) )
} else {
rsample <- isolate(runif(n = input$sample_size * input$simulation,
min = as.numeric(input$min_a), max = as.numeric(input$max_b) ) )
}
rsample
})
# Return the random sample matrix
rsamplematrix <- reactive({
matrix(rsample(), nrow = isolate(input$simulation) )
})
# output mean of sample mean
output$output_mean <- renderText({
sample_mean <- rowMeans(rsamplematrix())
mean(sample_mean)
})
# output variance of sample mean
output$output_var <- renderText({
sample_mean <- rowMeans(rsamplematrix())
var(sample_mean)
})
# output summary table of sample mean
output$output_table <- renderTable({
sample_mean <- rowMeans(rsamplematrix())
data.frame(mean(sample_mean), var(sample_mean))
})
# output the first 5 rows and 5 columns of the sample matrix
output$output_sample <- renderPrint({
k = rsamplematrix()
k[1:5, 1:5]
})
# output histogram of sample mean
output$output_hist <- renderPlot({
sample_mean <- rowMeans(rsamplematrix())
ggplot(data.frame(sample_mean), aes(x = sample_mean, y = ..density..)) +
geom_histogram(bins = isolate(input$bins), fill = "steelblue", col = "white")
})
})
shinyApp(ui = ui, server = server)
The code runs well, but there is a little problem with respect to Delay Reaction.
Suppose I run the simulation of binomial distribution with parameter=0.5, then all output would be generated. Then I choose a different distribution (for instance, normal distribution), before I render values to the parameters and click action button, the histogram plot is becoming grey for around a second. t seems that the server function is running although there is no change at all after that second.
What I wish is that when the distribution choice is made, the reaction are supposed to be delayed. So the server function should not run unless the action Button is clicked.
How can I fix this?
library(shiny)
library(ggplot2)
ui <- shinyUI(fluidPage(
titlePanel("Central Limit Theorem Simulation"),
sidebarLayout(
sidebarPanel(
numericInput("sample.size", "Size of each random sample",
value = 30, min = 1, max = 100, step = 1),
sliderInput("simulation", "THe number of simulation",
value = 100, min = 100, max = 1000, step = 1),
selectInput("sample.dist", "Population Distribution where each sample is from",
choices = c("Binomial","Poisson", "Normal", "Uniform") ),
numericInput("bins", "Number of bins in the histogram",
value = 20, min = 1, max = 50, step = 1),
submitButton(text = "Submit")
),
mainPanel(
h3('Illustrating outputs'),
h4('mean of random sample mean'),
textOutput(outputId = "output_mean" ),
h4('variance of random sample mean'),
textOutput(outputId = "output_var"),
h4("Table"),
tableOutput(outputId = "output_table"),
h4('histogram of random normal sample'),
plotOutput(outputId = "output_hist")
)
)
))
server <- shinyServer(function(input, output) {
# Return the random sample
rsample <- ifelse(
input$sample.dist == "Binomial", rbinom(input$sample.size * input$simulation, 1, 0.5),
ifelse(input$sample.dist == "Poisson", rpois(input$sample.size * input$simulation, 1),
ifelse(input$sample.dist == "Normal", rnorm(input$sample.size * input$simulation),
runif(input$sample.size * input$simulation) ) ) )
# Return the random sample matrix
rsample.matrix <- matrix(rsample, nrow = input$simulation)
# output mean of sample mean
output$output_mean <- renderText({
sample.mean <- rowMeans(rsample.matrix)
mean(sample.mean)
})
# output variance of sample mean
output$output_var <- renderText({
sample.mean <- rowMeans(rsample.matrix)
var(sample.mean)
})
# output histogram of sample mean
output$output_hist <- renderPlot({
sample.mean <- rowMeans(rsample.matrix)
ggplot(data.frame(sample.mean), aes(x = sample.mean)) +
geom_histogram(bins = input$bins)
})
})
shinyApp(ui = ui, server = server)
(1) The above codes are to create a shiny application to simulate random sample and verify Central Limit Theorem. However, since I just learned shiny, I have no idea where it is wrong.
(2) Also, if I want to change the parameter of a specific distribution based on the distribution selected by the user, what should I do?
The error returned from Rstudio is as follows:
Warning: Error in .getReactiveEnvironment()$currentContext: Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
Stack trace (innermost first):
49: .getReactiveEnvironment()$currentContext
48: .subset2(x, "impl")$get
47: $.reactivevalues
46: $ [#4]
45: server [#4]
4: <Anonymous>
3: do.call
2: print.shiny.appobj
1: <Promise>
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
The error says that code should be within a reactive or an observe statement. Have a look how I wrapped and used the variables
library(shiny)
library(ggplot2)
ui <- shinyUI(fluidPage(
titlePanel("Central Limit Theorem Simulation"),
sidebarLayout(
sidebarPanel(
numericInput("sample.size", "Size of each random sample",
value = 30, min = 1, max = 100, step = 1),
sliderInput("simulation", "THe number of simulation",
value = 100, min = 100, max = 1000, step = 1),
selectInput("sample.dist", "Population Distribution where each sample is from",
choices = c("Binomial","Poisson", "Normal", "Uniform") ),
numericInput("bins", "Number of bins in the histogram",
value = 20, min = 1, max = 50, step = 1),
submitButton(text = "Submit")
),
mainPanel(
h3('Illustrating outputs'),
h4('mean of random sample mean'),
textOutput(outputId = "output_mean" ),
h4('variance of random sample mean'),
textOutput(outputId = "output_var"),
h4("Table"),
tableOutput(outputId = "output_table"),
h4('histogram of random normal sample'),
plotOutput(outputId = "output_hist")
)
)
))
server <- shinyServer(function(input, output) {
# Return the random sample
rsample <- reactive({
ifelse(
input$sample.dist == "Binomial", rbinom(input$sample.size * input$simulation, 1, 0.5),
ifelse(input$sample.dist == "Poisson", rpois(input$sample.size * input$simulation, 1),
ifelse(input$sample.dist == "Normal", rnorm(input$sample.size * input$simulation),
runif(input$sample.size * input$simulation) ) ) )
})
# Return the random sample matrix
rsamplematrix <- reactive({
matrix(rsample(), nrow = input$simulation)
})
# output mean of sample mean
output$output_mean <- renderText({
sample.mean <- rowMeans(rsamplematrix())
mean(sample.mean)
})
# output variance of sample mean
output$output_var <- renderText({
sample.mean <- rowMeans(rsamplematrix())
var(sample.mean)
})
# output histogram of sample mean
output$output_hist <- renderPlot({
sample.mean <- rowMeans(rsamplematrix())
ggplot(data.frame(sample.mean), aes(x = sample.mean)) +
geom_histogram(bins = input$bins)
})
})
shinyApp(ui = ui, server = server)
I am trying to generate a table/list in Shiny of the values sampled from a probability distribution ( a list of the sampled values in a table format). I'm new to coding so this is like a foreign language to me. There is probably a lot of errors in the code although I can get it to run just not show the table.
library(shiny)
ui <- fluidPage(
sidebarPanel(
selectInput("dis","Please Select Probability Distribution Type:",
choices = c("Normal")),
sliderInput("sampleSize","Please Select Sample Size:",
min = 0,max = 5000,value = 1000,step = 100),
sliderInput("bins","Please Select Number of Bins:",
min = 1,max = 50,value = 10),
numericInput("sampleMean","Please Enter Sample Mean:",
min = 0,max = 5000,value = 2500,step = 10),
numericInput("sampleSd","Please Enter Standard Deviation:",
min = 0,max = 5000,value = 2,step = 10)
),
fluidRow(
column(12,
dataTableOutput("table"))
),
mainPanel(
plotOutput("histogram")
)
)
server <- function(input, output){
output$histogram <- renderPlot({
distType <- input$dis
n <- input$sampleSize
bins <- seq(min(input$bins), max(input$bins), length.out = input$bins + 1)
if(distType=="Normal"){
randomVec <- rnorm(n,mean = as.numeric(input$sampleMean),sd=as.numeric(input$sampleSd))
}
hist(randomVec,breaks=input$bins,col="red")
})
output$table <- renderDataTable({
distType <- input$dis
n <- input$sampleSize
if(distType=="Normal"){
randomVec <- rnorm(n,mean = as.numeric(input$sampleMean),sd= as.numeric(input$sampleSd))
}
sample(randomVec,100,replace = TRUE)
})
}
shinyApp(ui = ui, server = server)
From ?renderDataTable :
Arguments
expr An expression that returns a data frame or a matrix.
So you can do this:
output$table <- renderDataTable({
distType <- input$dis
n <- input$sampleSize
if(distType=="Normal"){
randomVec <- rnorm(n,mean = as.numeric(input$sampleMean),sd= as.numeric(input$sampleSd))
}
data.frame(sample(randomVec,100,replace = TRUE))
})