Multipage plot in shiny app - r

I am trying to plot multiple plots on different pages in my shiny app. Here is a reproducible example:
My module code is :
library(ggplot2)
dfunc <- function(data, page_number) { p <- ggplot(data = data, aes(x = data[, 3], y = data[, 2])) +
geom_point() for (i in 1:page_number) {
print(p) } }
dfUI <- function(id) { ns <- NS(id)
tagList(titlePanel("Multi plots"),
sidebarLayout(
sidebarPanel(
numericInput(
ns("page_number"),
"Number of Plots",
1,
min = 1,
step = 1
),
actionButton(ns("generateplot"), "Generate Plot")
),
mainPanel(plotOutput(ns("view")))
)) }
df <- function(input, output, session) { observeEvent(input$generateplot, {
output$view <- renderPlot({
data = mtcars
dfunc(data = data,
page_number = input$page_number)
}) }) }
There is a global file which calls this file where these functions are stored:
through this:
library(shiny)
library(shinythemes)
source("modules/dfunc.R")
The server file is like this :
source('global.R')
shinyServer(function(input, output, session) {
callModule(df, "dfunc")
})
And ui.R is like this :
source('global.R')
shinyUI(fluidPage(
shinythemes::themeSelector(),
headerPanel("The Shiny App"),
tabsetPanel(
tabPanel("Multi page plot", dfUI('dfunc')),
type = "pills"
)
))
Now the issue is if I use my dfunc function it prints as many plots as I give in page_number but when I call similar function in shiny app it only prints the single one. Is there any way that shiny app can print as many plots required on same page or on multiple page.
Thanks in advance.

Related

Is it possible to have one function to download various ggplot plots?

My shiny app generates a number of useful graphs. I would like to allow the user to download the graphs in various formats.
I have done this before for a single graph using How to save plots that are made in a shiny app as a guide. However, I am ending up creating more repeated code for each additional plot. I am not a programmer, but it really seems like I should be able to write one function to do this since I am just passing parameters to downloadHandler and ggsave, but I can't figure it out.
The MRE below represents a page with, say, ten different graphs. Is there a way to write a single function that receives the plot ID from a button (like a tag or something?) and the format from the selectInput to pass those parameters to downloadHandler and ggsave to save each of those graphs in the selected format? The function at the bottom shows my thinking, but I don't know where to go from here or if that is even the right direction.
Thanks!
library(shiny)
library(ggplot2)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show plots and download buttons
mainPanel(
plotOutput("distPlot"),
fluidRow(
column(3,
downloadButton("dl_plot1")
),
column(3,
selectInput("plot1_format",label = "Format",choices = c("SVG","PDF","JPEG","PNG"),width = "75px")
)
),
plotOutput("scat_plot"),
column(3,
downloadButton("dl_plot2")
),
column(3,
selectInput("plot2_format",label = "Format",choices = c("SVG","PDF","JPEG","PNG"),width = "75px")
)
)
)
)
# Define server logic required to draw a histogram and scatterplot
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
binwidth<-(max(x)-min(x))/input$bins
p<-ggplot(faithful,aes(waiting))+
geom_histogram(binwidth = binwidth)
p
})
output$scat_plot<-renderPlot({
p<-ggplot(faithful,aes(x=waiting,y=eruptions))+
geom_point()
p
})
downloadPlot <- function(plot_name,file_name,file_format){#concept code
downloadHandler(
filename=function() { paste0(file_name,".",file_format)},
content=function(file){
ggsave(file,plot=plot_name,device=file_format)
}
)
}
}
# Run the application
shinyApp(ui = ui, server = server)
To achieve your desired result without duplicating code you could (or have to) use a Shiny module. Basically a module is a pair of an UI function and a server function. For more on modules I would suggest to have a look at e.g. Mastering shiny, ch. 19.
In the code below I use a module to take care of the download part. The job of downloadButtonUI and downloadSelectUI is to add a download button and a selectInput for the file format. The downloadServer does the hard work and saves the plot in the desired format.
Note: Besides the download module I moved the code for the plots to reactives so that the plots could be passed to the downloadHandler or the download module.
EDIT: Added a fix. We have to pass the reactive (e.g. dist_plot without parentheses) to the download server and use plot() inside the downloadServer instead to export the updated plots.
library(shiny)
library(ggplot2)
# Download Module
downloaButtondUI <- function(id) {
downloadButton(NS(id, "dl_plot"))
}
downloadSelectUI <- function(id) {
selectInput(NS(id, "format"), label = "Format", choices = c("SVG", "PDF", "JPEG", "PNG"), width = "75px")
}
downloadServer <- function(id, plot) {
moduleServer(id, function(input, output, session) {
output$dl_plot <- downloadHandler(
filename = function() {
file_format <- tolower(input$format)
paste0(id, ".", file_format)
},
content = function(file) {
ggsave(file, plot = plot())
}
)
})
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30
)
),
# Show plots and download buttons
mainPanel(
plotOutput("distPlot"),
fluidRow(
column(3, downloaButtondUI("distPlot")),
column(3, downloadSelectUI("distPlot"))
),
plotOutput("scat_plot"),
fluidRow(
column(3, downloaButtondUI("scatPlot")),
column(3, downloadSelectUI("scatPlot"))
),
)
)
)
server <- function(input, output) {
dist_plot <- reactive({
p <- ggplot(faithful, aes(waiting)) +
geom_histogram(bins = input$bins)
p
})
scat_plot <- reactive({
p <- ggplot(faithful, aes(x = waiting, y = eruptions)) +
geom_point()
p
})
output$distPlot <- renderPlot({
dist_plot()
})
output$scat_plot <- renderPlot({
scat_plot()
})
downloadServer("distPlot", dist_plot)
downloadServer("scatPlot", scat_plot)
}
shinyApp(ui = ui, server = server)
#>
#> Listening on http://127.0.0.1:4092

Display and save a grid's gtable/gTree/grob/gDesc in a shiny app

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)

why scatterplot3js overlays plots when input dataset is changed?

I'm trying to use scatterplot3js and give it different datasets given by the user. However, if I change the dataset, it tries to plot them together. I tried to change the renderer (renderer='webgl') it does not work.
library(threejs)
library(shiny)
get.dt <- function(x) switch(x, rock = rock, mtcars = mtcars)
app <- shinyApp(
ui = bootstrapPage(
selectInput("data", "dataset", c("rock", "mtcars") ),
scatterplotThreeOutput('plot')
),
server = function(input, output) {
output$plot <- renderScatterplotThree({
data <- get.dt(input$data)
scatterplot3js(as.matrix(data[,1:3]))
})
}
)
runApp(app)
}

R shiny: Display data set in shiny app

I am trying to print dataset values in shiny web app. But I am only able to print data set name using below code. How can I print dataset values?
library(MASS)
library(shinythemes)
library(shiny)
library(ggplot2)
mass.tmp <- data(package = "MASS")[3]
mass.datasets <- as.vector(mass.tmp$results[,3])
ui <- fluidPage(
theme = shinytheme("superhero"),
titlePanel("Linear Regression Modelling"),
sidebarLayout(
sidebarPanel(
selectInput("dsname", "Dataset:",choices = c(mass.datasets))
,
uiOutput("x_axis")
# ,
# textOutput("txt"),
# tableOutput("tab")
),
mainPanel(
tags$br(),
tags$br()
)
)
)
server <- function(input, output) {
num_ds <- function(ds)
{
nums <- sapply(ds,is.numeric)
num_ds <- ds[,nums]
return(num_ds)
}
ds_ext <- reactive({ num_ds(input$dsname) })
output$x_axis <- renderUI({
col_opts <- get(ds_ext())
selectInput("x_axis2", "Independent Variable:", choices = names(col_opts))
})
}
shinyApp(ui = ui, server = server)
Actually I am trying to solve error in above code "Incorrect number of dimensions". I have written function which would return data frame with only numeric variables so that I can analyze. But getting error in line I guess where I am creating object x_axis. pls help.

rShiny Selecting between different plotting packages

I have a task where i need to build an rShiny app that allows the user to choose which kind of R plotting package is used in-order to display a plot.
Currently the only way i have gotten it to work (semi-decently) is using package specific functions for each package on the server side and using a series of conditional panels on the UI side.
However the problem is that when the user enters the page for the first time then all plots are initialized. Second problem is when the user changes some plot input values and after that chooses another package then the old plot will be displayed until a new plot is created.
Questions:
Is this the only available approach?
I feel that there must be a way to use reactive functions for the package selection?
I feel that it should be possible to use a single rShiny's htmlOutput (or something similar) in the ui and therefore not needing the switchPanel?
I have created a small app to demonstrate my current implementation and both problems:
server.R
library(shiny)
#library(devtools)
#install_github("ramnathv/rCharts")
library(rCharts)
shinyServer(function(input, output) {
names(iris) = gsub("\\.", "", names(iris))
#Render the Generic plot
output$GenericPlot <- renderPlot({
data = iris[0:input$variable,]
plot(data$SepalLength ~ data$SepalWidth)
})
#Render the Polychart plot
output$PolychartPlot <- renderChart({
plotData <- rPlot(SepalLength ~ SepalWidth, data = iris[0:input$variable,], color = 'Species', type = 'point')
plotData$addParams(dom = 'PolychartPlot')
return(plotData)
})
#Render the NDV3 plot
output$NDV3Plot <- renderChart({
plotData <- nPlot(SepalLength ~ SepalWidth, data = iris[0:input$variable,], group = 'Species', type = 'scatterChart')
plotData$addParams(dom = 'NDV3Plot')
return(plotData)
})
})
ui.R
library(shiny)
library(rCharts)
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("lib", label = "Library:",
choices = list("Generic", "rCharts Polychart", "rCharts NDV3"),
selected = "Generic"
),
numericInput("variable", "Observations:",
min = 5,
max = 150,
value = 10
)
),
mainPanel(
conditionalPanel(
condition = "input.lib == 'Generic'",
h3("Generic plot"),
plotOutput("GenericPlot")
),
conditionalPanel(
condition = "input.lib == 'rCharts Polychart'",
h3("rCharts Polychart plot"),
showOutput("PolychartPlot", "polycharts")
),
conditionalPanel(
condition = "input.lib == 'rCharts NDV3'",
h3("rCharts NDV3 plot"),
showOutput("NDV3Plot", "nvd3")
)
)
)
))
The final version will use a different dataset and more charting packages. The provided code is more of a toy example, with most of the stuff stripped out.
Make a single part in the output part of the app that includes some logic based on the input. For example,
library(shiny)
library(ggplot2)
data(cars)
server <- function(input, output) {output$plot<- renderPlot({
if (input$lib == "base") {
p <- plot(cars$speed, cars$dist)
} else if (input$lib == "ggplot") {
p <- ggplot(cars, aes(x = speed, y = dist)) + geom_point()
}
p
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("lib", "Library: ", choices = list("base", "ggplot"),
selected = "base")
),
mainPanel(plotOutput("plot"))
)
)
shinyApp(ui = ui, server = server)
This provides one plot and as soon as I change the lib option it regenerates.
Found a solution to my problem. The solution is basically to use uiOutput() in the ui.R and move the plotOutput(), showOutput() methods to the server.R.
The solution based on iacobus code:
ui.R
library(shiny)
library(rCharts)
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("lib", "Library: ", choices = list("base", "ggplot", "Polychart"),
selected = "base")
),
mainPanel(uiOutput("plot"))
)
))
server.R
library(shiny)
library(ggplot2)
library(rCharts)
data(cars)
server <- function(input, output) {
output$plot<- renderUI({
if (input$lib == "base") {
plotOutput("base")
} else if (input$lib == "ggplot") {
plotOutput("ggplot")
} else if (input$lib == "Polychart") {
showOutput("polychart", "polycharts")
}
})
output$base <- renderPlot({
plot(cars$speed, cars$dist)
})
output$ggplot <- renderPlot({
ggplot(cars, aes(x = speed, y = dist)) + geom_point()
})
output$polychart <- renderChart({
p <- rPlot(speed ~ dist, data = cars, type = "point")
p$addParams(dom = 'plot')
p
})
}
The difficulty arose for me, because i assumed that plotOutput(), showOutput() etc methods can only be used in the ui.R. This however is not the case.
EDIT:
It turned out that this was not enough for pollyCharts to work properly along with other rCharts packages.
instead i am using renderUI and rCharts $show to display the chart inline. The following link was helpful for me: https://github.com/ramnathv/rCharts/issues/373. In the ui i'm using htmlOutput

Resources