I am working on a Shiny app and as I go I have been adding figures and tables in a haphazard way. I would like to have a better framework so that I can flexibly add reactive figures and tables to the output as it develops further.
At the moment I have been using tabPanel and fluidrow to add additional a summary table and a second plot. However I have had trouble adapting this. For example I currently generate 3 plots but have only able to plot 2 at a time. Could anyone show me a way to modify the code to display all three plots (distPlot1, distPlot2, distPlot3) and the summary table on the same page? Ideally in a way that it would be simple to add additional tables and plots in the future.
Thank you in advance.
My current code is below.
ui.R
library(reshape2)
library(shiny)
library(ggplot2)
# Define UI for application that draws a histogram
fluidPage(
# Application title
titlePanel("Mutation Probability"),
# Sidebar with a slider input for the number of bins
sidebarLayout(
sidebarPanel(
sliderInput("x", "Probability of mutation (per bp):",
min=1/1000000000000, max=1/1000, value=1/10000000),
sliderInput("y", "Size of region (bp):",
min = 10, max = 10000, value = 1000, step= 100),
sliderInput("z", "Number of samples:",
min = 1, max = 100000, value = 1000, step= 10)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("Plot",
fluidRow(
splitLayout(cellWidths = c("50%", "50%"), plotOutput("distPlot1"), plotOutput("distPlot3"), plotOutput("distPlot3)"))
)),
tabPanel("Summary", verbatimTextOutput("summary"))
)
)
)
)
server.R
server <- function(input, output) {
mydata <- reactive({
x <- input$x
y <- input$y
z <- input$z
Muts <- as.data.frame(rpois(100,(x*y*z)))
Muts
})
output$distPlot1 <- renderPlot({
Muts <- mydata()
ggplot(Muts, aes(Muts)) + geom_density() +xlab("Observed variants")
})
output$distPlot2 <-renderPlot({
Muts <- mydata()
ggplot(Muts, aes(Muts)) + geom_histogram() + xlab("Observed variants")
})
#get a boxplot working
output$distPlot3 <-renderPlot({
Muts <- mydata()
ggplot(data= melt(Muts), aes(variable, value)) + geom_boxplot() + xlab("Observed variants")
})
output$summary <- renderPrint({
Muts <- mydata()
summary(Muts)
})
}
I like laying out the graphics in the server using tools like grid.arrange from the package gridExtra or the package cowplot - they offer a lot of layout flexiblity. This for example:
library(reshape2)
library(shiny)
library(ggplot2)
library(gridExtra)
# Define UI for application that draws a histogram
u <- fluidPage(
# Application title
titlePanel("Mutation Probability"),
# Sidebar with a slider input for the number of bins
sidebarLayout(
sidebarPanel(
sliderInput("x", "Probability of mutation (per bp):",
min=1/1000000000000, max=1/1000, value=1/10000000),
sliderInput("y", "Size of region (bp):",
min = 10, max = 10000, value = 1000, step= 100),
sliderInput("z", "Number of samples:",
min = 1, max = 100000, value = 1000, step= 10)
),
# Show a plot of the generated distribution
mainPanel(
tabsetPanel(
tabPanel("Plot",
fluidRow(
plotOutput("distPlot4"),
verbatimTextOutput("summary"))
)),
tabPanel("Summary", verbatimTextOutput("summary1"))
)
)
)
)
s <- function(input, output) {
mydata <- reactive({
x <- input$x
y <- input$y
z <- input$z
Muts <- as.data.frame(rpois(100,(x*y*z)))
Muts
})
output$distPlot4 <- renderPlot({
Muts <- mydata()
p1 <- ggplot(Muts, aes(Muts)) + geom_density() +xlab("Observed variants")
p2 <- ggplot(Muts, aes(Muts)) + geom_histogram() + xlab("Observed variants")
p3 <- ggplot(data= melt(Muts), aes(variable, value)) + geom_boxplot() + xlab("Observed variants")
grid.arrange(p1,p2,p3, ncol=3,widths = c(2,1,1))
})
output$summary <- renderPrint({
Muts <- mydata()
summary(Muts)
})
}
shinyApp(u,s)
which yields:
For summary tables, I just add them to the bottom, one after the other, not much else you can do there I think.
Related
I have created a shiny app should take input from three sliders and:
Plots a distribution in ggplot
Show a summary table of values underneath the plot in #1 above
If I just want to plot the histogram (and I comment out the table data), I can get the code to work correctly. However, when I add the table, the plot disappears even though the plot header is still there. I have tried moving the commas a braces around to see if it's a simple syntax error but haven't had any luck.
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Test Shiny Layout"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
h4("Input Data"),
sliderInput("bins", "Bin Width", min = 4,max = 12, value = 8),
),
# Show a plot of the generated distribution
mainPanel(
h4("Histogram"),
plotOutput("distPlot", width = "600", height = "600"),
h4("Table of Values"),
tableOutput("table")
)
)
))
Server
library(shiny)
library(ggplot2)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
bins <- input$bins
df1 <- (iris$Sepal.Length)
x <- mean(df1)
y <- sd(df1)
ggplot(data = iris) +
geom_histogram(mapping = aes(x = Sepal.Length), color = "blue", binwidth = "bins")
# Create an empty dataframe and then plug in the mean and standard deviation
results <- data.frame("0", "0")
results[1,1] = x
results[1,2] = y
colnames(results) <- c("Mean", "SD")
rownames(results) <- c("Sepal Length")
output$table <- renderTable(results)
})
})
Your renderTable() is inside your renderPlot() call. So renderPlot isn't returning anything.
You were right: it was a simple syntax error. But you also had several other issues in your code. At least a dozen. Three in binwidth = "bins" alone.
Here's a working version. I suspect you will still want to make tweaks, but at least you have both a histogram and a summary table that both look reasonably sensible.
library(shiny)
library(ggplot2)
data(iris)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
ggplot(data = iris) +
geom_histogram(aes(x = Sepal.Length), color = "blue", bins = input$bins)
})
output$table <- renderTable({
iris %>%
summarise(Mean=mean(Sepal.Length),
SD=sd(Sepal.Length))
})
}
ui <- fluidPage(
titlePanel("Test Shiny Layout"),
sidebarLayout(
sidebarPanel(
h4("Input Data"),
sliderInput("bins", "Bin Width", min = 4,max = 12, value = 8),
),
mainPanel(
h4("Histogram"),
plotOutput("distPlot", width = "600", height = "600"),
h4("Table of Values"),
tableOutput("table")
)
)
)
shinyApp(ui = ui, server = server)
I am getting an error with the plotting index using plotly in conjunction with reactive values in shiny. The sidebar panel loads with no issues but there is a problem displaying the chart that I cannot determine. Any help solving the index problem would be much appreciated. Thanks!
library(shiny)
library(plotly)
data(economics, package = "ggplot2")
nms <- names(economics)
ui <- fluidPage(
headerPanel("TEST"),
sidebarPanel(
selectInput('x', 'X', choices = nms, selected = nms[[1]]),
selectInput('y', 'Y', choices = nms, selected = nms[[2]]),
sliderInput('plotHeight', 'Height of plot (in pixels)',
min = 100, max = 2000, value = 1000)
),
mainPanel(
plotlyOutput('trendPlot', height = "900px")
)
)
server <- function(input, output) {
#add reactive data information. Dataset = built in diamonds data
dataset <- reactive({economics[, c(input$xcol, input$ycol)]
})
output$trendPlot <- renderPlotly({
# build graph with ggplot syntax
p <- ggplot(dataset(), aes_string(x = input$x, y = input$y)) +
geom_line()
ggplotly(p) %>%
layout(height = input$plotHeight, autosize=TRUE)
})
}
shinyApp(ui, server)
Warning: Error in : Unsupported index type: NULL
You have mistakenly used xcol and ycol not sure why. Without those names the code works fine.
library(shiny)
library(plotly)
library(tidyverse)
data(economics, package = "ggplot2")
nms <- names(economics)
ui <- fluidPage(
headerPanel("TEST"),
sidebarPanel(
selectInput('x', 'X', choices = nms, selected = nms[[1]]),
selectInput('y', 'Y', choices = nms, selected = nms[[2]]),
sliderInput('plotHeight', 'Height of plot (in pixels)',
min = 100, max = 2000, value = 1000)
),
mainPanel(
plotlyOutput('trendPlot', height = "900px")
)
)
server <- function(input, output) {
#add reactive data information. Dataset = built in diamonds data
dataset <- reactive({
economics[, c(input$x, input$y)]
})
output$trendPlot <- renderPlotly({
# build graph with ggplot syntax
p <- ggplot(dataset(), aes_string(input$x, input$y)) +
geom_line()
ggplotly(p, height = input$plotHeight)
})
}
shinyApp(ui, server)
I'd like to include the reactive outputs of two data sets as different geom_lines in the same ggplotly figure. The code runs as expected when only one reactive data.frame is included as a geom_line. Why not two?
ui <- fluidPage(
sidebarLayout(
selectInput("Var1",
label = "Variable", #DATA CHOICE 1
selected = 10,
choices = c(10:100)),
selectInput("Var1",
label = "Variable2", #DATA CHOICE 2
selected = 10,
choices = c(10:100))
# Show a plot of the generated distribution
),
mainPanel(
plotlyOutput('plot') #Draw figure
)
)
server <- function(input, output) {
out <- reactive({
data.frame(x = rnorm(input$Var1), #Build data set 1
y = 1:input$Var1)
})
out2 <- reactive({
data.frame(x = rnorm(input$Var2), #Build data set 2
y = 1:input$Var2)
})
output$plot <- renderPlotly({
p <- ggplot() +
geom_line(data = out(), aes(x = x, y = y)) #Add both data sets in one ggplot
geom_line(data = out2(), aes(x = x, y = y), color = "red")
ggplotly(p)
})
}
# Run the application
shinyApp(ui = ui, server = server)
When you put the data into long format and give each group a group identifier it seems to work. Note that you should be able to change sliderInput back to selectInput - this was one of the entries I toggled during testing, but the choice of UI widget should not matter.
This works -- code can be simplified inside the reactive from here:
library(plotly)
ui <- fluidPage(
sidebarLayout(
sliderInput("Var1",
label = "Variable", #DATA CHOICE 1
min=10, max=100, value=10),
sliderInput("Var2",
label = "Variable2", #DATA CHOICE 2
min=10, max=100, value=10),
),
mainPanel(
plotlyOutput('plot') #Draw figure
)
)
server <- function(input, output) {
out <- reactive({
x1 <- rnorm(input$Var1)
y1 <- seq(1:input$Var1)
x2 <- rnorm(input$Var2)
y2 <- seq(1:input$Var2)
xx <- c(x1,x2)
yy <- c(y1,y2)
gg <- c( rep(1,length(y1)), rep(2,length(y2)) )
df <- data.frame(cbind(xx,yy,gg))
df
})
output$plot <- renderPlotly({
p <- ggplot() +
geom_line(data=out(), aes(x = xx, y = yy, group=gg, colour=gg))
ggplotly(p)
})
}
shinyApp(ui = ui, server = server)
I want to draw 20 graphs in shiny by loop and I don't want to write the output one by one. So I am thing doing a loop to output these graphs. I found a very good example in the shiny gallery which shows how to output texts. I tiried it and it worked.
Now my problem is: How can I replace the text output to plotly? I have the plotly ready(to simplify I am not showing here). What I tried is first replace the strong(paste0(.. line with my plotly object. Second, replace renderUI to renderplotly and replace uiOutput to plotOutput. I am getting errors ggplotly has no applicable method for shiny.tag which I understand that plotOutput is not compatible with tagged output. So what can I do here?
server.r:
shinyServer(function(input, output,session) {
lapply(1:2, function(i) {
output[[paste0('b', i)]] <- renderUI({
strong(paste0('Hi, this is output B#', i)) })# to be replaced with a plotly object p
})})
ui.r:
fluidRow(
lapply(1:2, function(i) {
uiOutput(paste0('b', i))
})
)
Check out this example Shiny app that displays a dynamic number of plots: https://gist.github.com/wch/5436415/
I adapted the above app to plot the cars dataset with ggplotly.
library(shiny)
library(ggplot2)
library(plotly)
shinyApp(
##### ui #######
ui = fluidPage(
fluidRow(
sliderInput("n",
"Number of plots",
value = 1, min = 1, max = 5)),
fluidRow(
uiOutput("plots"))
),
##### server ######
server = function(input, output) {
data("cars")
# define max number of plots
max_plots <- 5
# generate the plots
output$plots <- renderUI({
plot_output_list <- lapply(1:input$n, function(i) {
plotname <- paste0("plot", i)
plotlyOutput(plotname)
})
# convert the list to a tagList - this is necessary for the list of
# items to display properly
do.call(tagList, plot_output_list)
})
# call renderPlotly for each plot. Plots are only generated when they are
# visible on the web page
for(i in 1:max_plots) {
# Need local so that each item gets its own number. Without it, the value
# of i in the renderPlotly() will be the same across all instances, because
# of when the expression is evaluated
local({
my_i <- i
plotname <- paste0("plot", my_i)
output[[plotname]] <- renderPlotly({
g <- ggplot(cars, aes(x = speed, y = dist)) +
geom_point() +
labs(title = paste0("Plot ", my_i))
g <- ggplotly(g)
dev.off()
g
})
})
}
}
)
Creating one plot with many subplots:
library(shiny)
library(ggplot2)
library(plotly)
library(grid)
shinyApp(
##### ui #######
ui = fluidPage(
fluidRow(
sliderInput("n",
"Number of plots",
value = 1, min = 1, max = 5)),
fluidRow(
plotlyOutput("plots")
)
),
##### server ######
server = function(input, output) {
data("cars")
# define max number of plots
max_plots <- 5
# generate the plots
output$plots <- renderPlotly({
plot_list <- lapply(1:input$n, function(i) {
g <- ggplot(cars, aes(x = speed, y = dist)) +
geom_point() +
theme(plot.margin = unit(c(3, 1, 1, 1), "lines"))
ggplotly(g)
})
p <- subplot(plot_list[1:input$n], shareX = TRUE, shareY = TRUE) %>%
layout(title = "Car Plots")
dev.off()
p
})
}
)
I'm having trouble with the server.R getting shiny to plot the data based on drop down selections from the ui.R. I would like to select a 'Site' and a 'Parameter' and plot the 'Obs' that reflects that 'Site' and 'Parameter'. Obs as the Y and Date on the X. Here is some sample code.
Site_Names=data.frame(c(A=rep("A",10),B=rep("B",10),C=rep("C",10)))
Site_Names=Site_Names[,1]
Parameters=data.frame(c(pH=rep("pH",10),DO=rep("DO",10),Temp=rep("Temp",10)))
Parameters=Parameters[,1]
Obs=rnorm(30)
Dates=c(seq(as.Date("2000/1/1"), by = "year", length.out =10 ),
seq(as.Date("2005/1/1"), by = "year", length.out =10 ),
seq(as.Date("1990/1/1"), by = "year", length.out =10 ))
data=data.frame(Site_Names,Parameters,Obs,Dates)
#ui.R
Sites=levels(data$Site_Name)
setNames(as.list(Sites), Sites)
params=levels(data$Parameters)
setNames(as.list(params), params)
library(shiny)
library(ggplot2)
shinyUI(fluidPage(
# Application title
titlePanel("Data"),
sidebarLayout(
sidebarPanel(
selectInput("site", "Select Site:", Sites),
selectInput("parameters", "parameter", params)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("Plot")
)
)
))
#server.R
library(shiny)
library(ggplot2)
shinyServer(function(input, output) {
dataset <- reactive({
data[ , (input$Sites),]
})
output$distPlot <- renderPlot({
p <- ggplot(dataset(), aes(x=Dates, y=input$params and input$Sites))
+ geom_point(data$Obs)
print(p)
})
You can use subset within your reactive expression to get the plot data. Be careful though, as you can end up with null values if the parameter isn't included in the site data.
library(shiny)
library(ggplot2)
shinyServer(function(input, output) {
dataset <- reactive({
subset(data, Site_Names == input$Sites & Parameters == input$params)
})
output$distPlot <- renderPlot({
p <- ggplot(dataset(), aes(x = Dates, y = Obs)) +
geom_line()
print(p)
})