I want to make a shiny app where the user is able to select genes. Then he will see all the plots for those genes.
The selection part works fine (I think)
ui <- fluidPage(
titlePanel("Test"),
sidebarPanel(
selectInput("genes", "Genes:", seurat_genes, multiple = TRUE),
),
mainPanel(
uiOutput('out1')
)
)
Now I want to those selected genes to be plotted next to the sidebarPanel:
server <- function(input, output) {
output$out1 = renderUI({
p = FeaturePlot(sc, features=input$genes, cols=c("lightgrey", param$col), combine=FALSE)
names(p) = input$genes
for(i in names(p)) {
p[[i]] = plot.mystyle(p[[i]], title=i)
renderPlot(
print(p[[i]])
)
}
})
}
seurat_genes is data from the analysis with Seurat, which is a library for single-cell RNA-seq data. So the user specifies which genes he wants to look at and FeaturePlotgenerates those plots.
FeaturePlot is a function from Seurat which "Colors single cells on a dimensional reduction plot according to a 'feature' (i.e. gene expression, PC scores, number of genes detected, etc.)"
I'm fairly new to R and especially Shiny, so feel free to suggest any kind of improvements.
Found a solution that works for me:
library(shiny)
library(Seurat)
# This Data is from my Workspace. I have trouble loading it, so its a workaround and is my next Problem.
seurat_genes = sc.markers[["gene"]]
# Define UI for application that draws a histogram
ui <- fluidPage(
titlePanel("Einzeldarstellungen von Genen"),
sidebarPanel(
selectInput("genes", "Gene:", seurat_genes, multiple = TRUE),
),
mainPanel(
splitLayout(cellWidths = c("50%","50%"),uiOutput('out_umap'), uiOutput('out_ridge'))
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$out_umap = renderUI({
out = list()
if (length(input$genes)==0){return(NULL)}
for (i in 1:length(input$genes)){
out[[i]] <- plotOutput(outputId = paste0("plot_umap",i))
}
return(out)
})
observe({
for (i in 1:length(input$genes)){
local({ #because expressions are evaluated at app init
ii <- i
output[[paste0('plot_umap',ii)]] <- renderPlot({
return(FeaturePlot(sc, features=input$genes[[ii]], cols=c("lightgrey", param$col), combine=FALSE))
})
})
}
})
output$out_ridge = renderUI({
out = list()
if (length(input$genes)==0){return(NULL)}
for (i in 1:length(input$genes)){
out[[i]] <- plotOutput(outputId = paste0("plot",i))
}
return(out)
})
observe({
for (i in 1:length(input$genes)){
local({ #because expressions are evaluated at app init
ii <- i
output[[paste0('plot',ii)]] <- renderPlot({
return(RidgePlot(sc, features=input$genes[[ii]], combine=FALSE))
})
})
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
Related
for (i in 1:4){
v <- rnorm(50)
plot(v, main=paste("Iteration ", i))
}
I have code that iterates through and produces a plot each time, like the above. How would I allow a user to click to see the next plot in a Shiny application?
Use the slickR package to make a nice slideshow.
library(shiny)
library(slickR)
library(svglite)
plots <- lapply(1:5, function(i){
xmlSVG({plot(rnorm(50), main=paste0("Iteration ", i))}, standalone = TRUE)
})
#make the plot self contained SVG to pass into slickR
plotsAsSVG <- sapply(plots, function(sv){
paste0("data:image/svg+xml;utf8,",as.character(sv))
})
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
####
),
mainPanel(
slickROutput("slickr", width="500px")
)
)
)
server <- function(input, output) {
output$slickr <- renderSlickR({
imgs <- plotsAsSVG
slickR(imgs)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have a function that's arranging a plot in a grid:
plotFunc <- function(a,b)
{
p <- qplot(a,b)
p2 <- xyplot(1~1)
r <- grid::rectGrob(gp=gpar(fill="grey90"))
t <- grid::textGrob("text")
g <- gridExtra::grid.arrange(t, p, p2, r, ncol=2)
return(g)
}
So the return value is:
"gtable" "gTree" "grob" "gDesc"
I want to use a shiny app in order to be able to select a and b values display the resulting plot and also have the option to save it to a file.
Here's my code:
data:
set.seed(1)
vals.df <- data.frame(b=1:6,a=sample(1:2,6,replace=T))
Shiny code:
library(shiny)
library(ggplot2)
library(lattice)
library(SpaDES)
library(devtools)
server <- function(input, output)
{
output$b <- renderUI({
selectInput("b", "B", choices = unique(dplyr::filter(vals.df,a == input$a)$b))
})
my.plot <- reactive({function(){plotFunc(a = input$a,b == input$b)}})
output$plot <- renderPlot({
my.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0(input$a,"_",input$b,".png")
},
content = function(file) {
ggsave(my.plot(),filename=file)
}
)
}
ui <- fluidPage(
# App title ----
titlePanel("Feature Plots"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# select name
selectInput("a", "A", choices = unique(vals.df$a)),
uiOutput("b"),
downloadButton('save', 'Save to File')
),
# Main panel for displaying outputs ----
mainPanel(
# The plot is called feature.plot and will be created in ShinyServer part
plotOutput("plot")
)
)
)
When I run shinyApp(ui = ui, server = server) and select a and b values from their lists a figure is not displayed to the screen and when I click the Save to File button I get this error:
ERROR: no applicable method for 'grid.draw' applied to an object of class "function"
I tried wrapping the my.plot() calls with grid.draw but I get the same error:
no applicable method for 'grid.draw' applied to an object of class "function"
Any idea?
Note that I can't get it to work even if plotFunc returns the ggplot2 object (i.e., the grid calls are commented out). But solving this for the example above is more general and would also solve it for the ggplot2 more specific case.
You can do like this:
my.plot <- reactive({
if(!is.null(input$a) & !is.null(input$b)){
plotFunc(a = input$a,b = input$b)
}
})
The change i did was to remove the function. I wasnt sure why you need it and i think it caused the error in the download. Moreover, the second input you give over as a logical statement == which will create an error.
Full code would read:
set.seed(1)
vals.df <- data.frame(b=1:6,a=sample(1:2,6,replace=T))
plotFunc <- function(a,b)
{
p <- qplot(a,b)
p2 <- xyplot(1~1)
r <- grid::rectGrob(gp=gpar(fill="grey90"))
t <- grid::textGrob("text")
g <- gridExtra::grid.arrange(t, p, p2, r, ncol=2)
return(g)
}
library(shiny)
library(ggplot2)
library(lattice)
library(SpaDES)
library(devtools)
server <- function(input, output)
{
output$b <- renderUI({
selectInput("b", "B", choices = unique(dplyr::filter(vals.df,a == input$a)$b))
})
my.plot <- reactive({
if(!is.null(input$a) & !is.null(input$b)){
plotFunc(a = input$a,b = input$b)
}
})
output$plot <- renderPlot({
my.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0(input$a,"_",input$b,".png")
},
content = function(file) {
ggsave(my.plot(),filename=file)
}
)
}
ui <- fluidPage(
# App title ----
titlePanel("Feature Plots"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# select name
selectInput("a", "A", choices = unique(vals.df$a)),
uiOutput("b"),
downloadButton('save', 'Save to File')
),
# Main panel for displaying outputs ----
mainPanel(
# The plot is called feature.plot and will be created in ShinyServer part
plotOutput("plot")
)
)
)
shinyApp(ui = ui, server = server)
I'd like to create data once and reuse it in multiple plots. The example below creates the data in each plot, but how can I create it once (x) and have each plot use x?
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
numericInput(inputId = "mean", label = "Mean", value = 50)
),
mainPanel(
column(6,plotOutput(outputId = "hist1")
),
column(6,plotOutput(outputId = "hist2")
)
)
)
)
)
server <- function(input,output){
# I'd like to create the data just once here, and then reuse it in each plot
# x <- rnorm(100,input$mean,5)
output$hist1 <- renderPlot({
hist(rnorm(100,input$mean,5))
#hist(x)
})
output$hist2 <- renderPlot({
hist(rnorm(100,input$mean,5))
#hist(x)
})
}
runApp(list(ui = ui, server = server))
You can wrap your rnorm in a reactive expression to create a reactive conductor. Then, use the conductor in your endpoints (output$). See http://shiny.rstudio.com/articles/reactivity-overview.html.
server <- function(input,output){
# I'd like to create the data just once here, and then reuse it in each plot
x <- reactive(rnorm(100, input$mean, 5))
output$hist1 <- renderPlot({
hist(x())
})
output$hist2 <- renderPlot({
hist(x())
})
}
Wrapping the server codes with observe would do the job.
server <- function(input,output){
# I'd like to create the data just once here, and then reuse it in each plot
observe({
data <- rnorm(100,input$mean,5)
output$hist1 <- renderPlot({
hist(data)
#hist(rnorm(100,x,5))
})
output$hist2 <- renderPlot({
hist(data)
#hist(rnorm(100,x,5))
})
})
}
I'm creating Shiny app and I want to use checkboxGroupInput in order to print out multiple plots. However, I want to print out plots only for the elements of checkboxGroupInput that were checked. There is a similar example in Shiny gallery to create UI elements in a loop that uses lapply. Here is a simplified version of that example to show what I want to do:
#server.R
library(shiny)
library(ggplot2)
shinyServer(function(input, output, session) {
numberInput <- reactive({
input$checkbox
})
lapply(1:10, function(i) {
output[[paste0('b', i)]] <- renderPlot({
qplot(x = rnorm(100, mean = as.numeric(numberInput()[i]))) +
ggtitle(paste("This plot was plotted with", numberInput()[i], "option"))
})
})
})
#ui.R
library(shiny)
shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
lapply(1:10, function(i) {
plotOutput(paste0('b', i))
})
)
)
))
This works, but obviously when Shiny tries to extract numberInput()[i] where i is bigger than number of currently checked elements, there is nothing to extract and instead of a plot there is an error. Therefore I need to somehow tell lapply to iterate only n number of times where n is length(input$checkbox).
I tried to use length(input$checkbox) directly, tried putting that element in the numberInput() reactive statement and returning it as the list, I tried to use reactiveValues() in a following way:
v <- reactiveValues(n = length(input$checkbox))
lapply(1:isolate(v$n), function(i) {
However, in all of those instances Shiny complains about lack of active reactive context.
So, what am I missing? How can I use length of input in lapply outside of reactive context?
I've generally had more luck using this approach (only because it's easier for me to wrap my head around it), but the idea is to render your plots into a UI on the server and then render the UI in ui.R
#server.R
library(shiny)
library(ggplot2)
server <- shinyServer(function(input, output, session) {
output$checks <- renderText(input$checkbox)
output$plots <- renderUI({
plot_output_list <-
lapply(input$checkbox,
function(i){
plotOutput(paste0("plot", i))
})
do.call(tagList, plot_output_list)
})
observe({
for (i in input$checkbox) {
local({
local_i <- i
output[[paste0("plot", local_i)]] <-
renderPlot({
qplot(x = rnorm(100, mean = as.numeric(local_i))) +
ggtitle(paste("This plot was plotted with", local_i, "option"))
})
})
}
})
})
#ui.R
library(shiny)
ui <- shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
verbatimTextOutput("checks"),
uiOutput('plots')
)
)
))
shinyApp(ui = ui, server = server)
Is it possible to precompute values for ggplot histogram? Computing a number of histograms is really slowing down my ShinyR app, so I'd like to find a way to cache histograms so they are only computed once for a given set of parameters. I checked the docs but didn't see anything about this. Apologies if I missed something, but could someone point me to documentation about how to do this, or tell me that it's not possible?
You could do something like this:
library(shiny)
library(ggplot2)
n <- 400
server <- function(input, output) {
cachedData <<- list()
datasetInput <- reactive({
switch(input$dataset,
"data1" = list("name"="dataset1", "data"=data.frame("x"=runif(n),"y"=runif(n)), "plot"=NULL),
"data2" = list("name"="dataset2", "data"=data.frame("x"=runif(n),"y"=runif(n)), "plot"=NULL),
"data3" = list("name"="dataset3", "data"=data.frame("x"=runif(n),"y"=runif(n)), "plot"=NULL))
})
observeEvent(input$run,{
d <- datasetInput()
# If dataset cached
if ( d$name %in% names(cachedData) ){
}
# Else cache dataset
else{
cachedData[[d$name]] <<- d
}
# See if data is loaded
if ( is.null( cachedData[[d$name]]$plot ) ){
print("Creating Plot")
cachedData[[d$name]]$plot <<- ggplot(data=d$data,aes(x=x)) + geom_histogram(stat="bin")
}
else{
print("Loading plot")
}
# Else save data
output$plot <- renderPlot({ cachedData[[d$name]]$plot})
})
}
ui <- shinyUI(fluidPage(
selectInput("dataset", "Choose a dataset:",
choices = c("data1", "data2", "data3")),
plotOutput("plot"),
actionButton('run','Generate Plot')
)
)
shinyApp(ui = ui, server = server)
The cachedData list can then be loaded and saved with the load and save functions.