Calling a graph from another sheet in R - r

My Shiny app is getting a little long, as I'm plotting a variety of graphs in a number of panels. Accordingly, to help with some of the organization, I was wondering if it was possible to move the code for the graphs into a separate r-script, and call those graphs from the original r script.
Adding some further complication, the graphs that I'd like to display all require user input from the Shiny app.
Is it possible to use code from another script in R to plot graphs, and, if so, how? Additionally, as there will be multiple graphs, is it possible to specify which graph from the new r-script will go in the designated location, or will I need to create a separate r-script for each graph (which would defeat the purpose of increased organizational oversight)?
I've written some simplified, reproducible code (see below) that I hope will give you an idea of what I'm looking for. Essentially, I'd like any code that produces a graph within renderPlot() to come from a separate r-script.
Many thanks for any help!
library(shiny)
ui <- fluidPage(
mainPanel(
selectInput("input1","Select an option",choices = c("First","Second")),
plotOutput("plot1"),
plotOutput("plot2")
)
)
server <- function(input, output, session) {
output$plot1 = renderPlot({
if(input$input1=="First"){
##This is where I'd like to call the code for the graph from another sheet.
plot(1,main = input$input1)
}
if(input$input1=="Second"){
##Again, this is where I'd like to code for the graph from another sheet.
plot(2,main = input$input1)
}
})
output$plot2 = renderPlot({
if(input$input1=="First"){
##This is where I'd like to call the code for the graph from another sheet.
plot(1*rnorm(1,10,2),main = input$input1)
}
if(input$input1=="Second"){
##Again, this is where I'd like to code for the graph from another sheet.
plot(2*rnorm(1,50,2),main = input$input1)
}
})
}
shinyApp(ui, server)

You can make a function that takes arguments for the plot that you make like the data and plot title and then passes these arguments to your code that creates the plot. For example, say the only thing that changes is x and the plot title, you can define a function that takes those arguments and then uses them in the code to make the plot. Then you save this in a separate script and call the script using source() in your shiny app.
plots.R
plot_data <- function(x, y=NULL, plot.title){
if(is.null(y)) {
y <- seq(from = 1, by = 1, length.out = length(x))
}
plot(x, y, main = plot.title)
}
Load the function into your global environment using source('plots.R'), make sure plots.R is saved in the same location as your shiny app.
library(shiny)
source("plots.R")
ui <- fluidPage(
mainPanel(
selectInput("input1","Select an option",choices = c("First","Second")),
plotOutput("plot1"),
plotOutput("plot2")
)
)
server <- function(input, output, session) {
output$plot1 = renderPlot({
if(input$input1=="First"){
##This is where I'd like to call the code for the graph from another sheet.
plot_data(1, plot.title = input$input1)
}
if(input$input1=="Second"){
##Again, this is where I'd like to code for the graph from another sheet.
plot_data(2, plot.title = input$input1)
}
})
output$plot2 = renderPlot({
if(input$input1=="First"){
##This is where I'd like to call the code for the graph from another sheet.
plot_data(1*rnorm(1,10,2),plot.title = input$input1)
}
if(input$input1=="Second"){
##Again, this is where I'd like to code for the graph from another sheet.
plot_data(2*rnorm(1,50,2),plot.title = input$input1)
}
})
}
shinyApp(ui, server)
Of course this doesn't look like much of a difference, but with complex plots that span multiple lines, turning your plot code into a function will turn multiple lines into just one.

Related

How to drag a plot line and back into the resulting line parameters in R Shiny?

Here's a long-shot question. The below code allows the user to build and alter a scaled-logarithmic curve by altering its 4 parameters, via slider inputs. I'd like to reverse the process, so the user clicks/drags the plot line and a new "exponential" curve parameter is backed into. How to do this in R Shiny?
Later, after figuring out how to derive the exponential parameter, I'll try backing into some of the other curve parameters too.
This image illustrates what I'm trying to do:
Code:
library(shiny)
ui <- fluidPage(
sliderInput('periods','Nbr of periods:',min=0,max=36,value=24),
sliderInput('start','Start value:',min=0,max=1,value=0.15),
sliderInput('end','End value:',min=0,max=1,value=0.70),
sliderInput('exponential','Exponential:',min=-100,max=100,value=10),
plotOutput('plot')
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
Periods = c(0:input$periods),
ScaledLog = c(
(input$start-input$end) *
(exp(-input$exponential/100*(0:input$periods))-
exp(-input$exponential/100*input$periods)*(0:input$periods)/input$periods)) +
input$end
)
})
output$plot <- renderPlot(plot(data(),type='l',col='blue',lwd=5))
}
shinyApp(ui,server)

How to fix "object 'mydata' not found" in shiny app when plotting histogram

I'm trying to create a Shiny app that lets users
create a dataset by entering frequency counts for different values
plot a histogram of that dataset
A paired back example of the code is as follows:
library(shiny)
library(ggplot2)
# Define UI for application
ui <- fluidPage(
# Sidebar with inputs
sidebarLayout(
sidebarPanel(
numericInput("data1s",
"How many have a score of 1?",
value = 0,
min = 0
),
numericInput("data2s",
"How many have a score of 2?",
value = 0,
min = 0
),
sliderInput("bins",
"Number of bins:",
min = 1,
max = 3,
value = 1)
),
# Show a plot of the data
mainPanel(
htmlOutput("mydatatable"),
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#show the data
output$mydatatable <- renderTable({
#create the dataframe from the frequncies
mydata <- data.frame(our_data=c(rep(1,input$data1s),rep(2,input$data2s))
)
}
)
#show the histogram
output$distPlot <- renderPlot({
ggplot(mydata, aes(x=our_data)) +
geom_histogram(bins = input$bins)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have achieved the creation of the dataset, but the code for displaying a histogram of the data returns an error: "object 'mydata' not found" instead of showing the histogram. The histogram should update whenever any of the inputs are changed.
Any help to resolve would be much appreciated.
The mydata that you define in the mydatatable reactive is not visible anywhere else. To understand why, I suggest you read about R's namespaces and environments; one good tutorial on it is Hadley's Advanced R -- Environments.
To fix it, I suggest you make the data itself a reactive block, and depend on it in your two other blocks (table and plot):
server <- function(input, output) {
mydata <- reactive({
req(input$data1s, input$data2s)
data.frame(our_data=c(rep(1,input$data1s),rep(2,input$data2s)))
})
#show the data
output$mydatatable <- renderTable({ req(mydata()); })
#show the histogram
output$distPlot <- renderPlot({
req(mydata())
ggplot(mydata(), aes(x=our_data)) +
geom_histogram(bins = input$bins)
})
}
(Untested.)
I added the use of req solely to prevent start-up jittering and warnings/errors in the app. When the shiny app is warming up, it's common to have input variables empty (NULL), and things that depend on it will temporarily produce errors until the inputs stabilize. (For an example of why things will stumble, input$data1s may initially show a NULL value, and try to see if data.frame(our_data=rep(1,NULL)) will work.)
req just looks for something that is "truthy", meaning: not NULL, not NA, not FALSE, length greater than 0, etc. See ?shiny::req for more details.
While req is not strictly required, it has its advantages. As you may infer from the table code, req(x) will return the "first value that was passed in" (from ?req), so it can be used in this shortcut mode for brevity.
And one last soap-box: in my limited experience with shiny reactivity, there are few times that I've generated data within a reactive block and used it solely within that reactive block. Given that, whenever you make a data.frame (or list or ... some important structure that is dependent on user input), it is often beneficial to make it its own reactive component (specifically, not an output component), and then depend on it as many times as necessary.

renderPlot issue when rendering a list of plots

I'm writing an R shiny app which should allow the user to create customisable plots of some data. The idea is that my app offers a "create new plot" button, which renders the plot and stores it in a reactive. A renderUI function "watches" this list and renders all plots in that reactive.
I found a couple of related questions r-markdown-shiny-renderplot-list-of-plots-from-lapply or shiny-r-renderplots-on-the-fly which however did not really help in my case. I hope I didn't miss a good answer somewhere (which I would assume there is because I think this is not a rare use case).
When implementing, I noticed a strange behaviour: When there is only one plot to be shown, everything works well. However, when I have n (n>1) plots, instead of rendering plot 1, plot 2, ..., plot n, the app only showed n times the plot n.
See my example app below. I simplified the problem by just letting the user choose the number of plots to be displayed. The renderUI function then has a loop creating thees plots in a variable p and then calls renderPlot(p). I assume shiny does some caching and for some reason fails to recognise that p changes in the loop?!
I found a workaround by replacing the renderPlot(p) by do.call("renderPlot", list(expr = p). This does the job but I'm still curious to learn why the direct renderPlot does not work.
Here is my example app:
library(shiny)
library(ggplot2)
# Define UI
ui <- shinyUI(fluidPage(
titlePanel("renderPlot Test"),
sidebarLayout(
sidebarPanel(
numericInput(inputId = "n", label = "Number of Plots", value = 1L, min = 1L, max = 5L, step = 1L),
checkboxInput(inputId = "use_do.call", label = "use 'do.call'", value = FALSE)
),
mainPanel(
uiOutput("show_plots")
)
)
))
# Define server logic
server <- shinyServer(function(input, output) {
output$show_plots <- renderUI({
ui <- tags$div(tags$h4("Plots"))
for( i in 1:input$n ) {
p <- ggplot() + ggtitle(paste("plot", i))
if( input$use_do.call ) { # this works
ui <- tagAppendChild(ui, do.call("renderPlot", args=list(expr=p, width = 200, height = 200)))
} else { # this doesn't ...
ui <- tagAppendChild(ui, renderPlot(p, width = 200, height = 200))
}
}
return(ui)
})
})
# Run the application
shinyApp(ui = ui, server = server)
I agree with #JonMinton, and I've had the same problem. I've found that when I reuse the same variable to save the plots and render them (such as what you do with p), the plots get overwritten by the next plot and only the final plot is copied n times like you said.
To get around this, I define a new variable for each plot, which may not be sustainable for your project, but it is a workaround.

Passing outputId into ggplotly()

I've just started at an internship where my first task is to improve a Shiny application by updating all existing ggplot graphs into Plotly graphs. I'm pretty new to R and Shiny, so I'm having a couple of issues getting started.
Currently, one of the graphs in this application are generated as follows:
output$barBoxPlot <- renderUI({
plotOutput("Barplot", width = 700, height = 300
, click = "plot_click"
, hover = "plot_hover"
, brush = "plot_brush")
})
Where "Barplot" is an outputId that corresponds to a ggplot graph generated in another part of the code.
I'm trying to plot this graph ("Barplot") as a Plotly graph, using the ggplotly() function, using something like this:
output$barBoxPlot <- renderPlotly({
ggplotly(<insert graph here>)
})
However, I cannot figure out how to pass in the "Barplot" parameter (which is an outputId) into ggplotly(). ggplotly takes in an instance of a ggplot graph, and not an outputId.
Is there any way to pass in an outputId into ggplotly()?
Thank you!
Would've put this in a comment but can't to that yet because of reputation:
There are multiple ways to solve this wihtout trying to pass an outputID to ggplotly().
In the reactive that is in the output$Barplot <- renderPlot(this_reactive_here()) , use ggplotly() directly (if you don't need the original output$barBoxPlot
Create a function or a reactive that takes a ggplot object and only does return(ggplotly(x)) , then put that into your renderPlotly()
EDIT for clarification on 2:
The Barplot that is in plotOutput should have a server entry that looks something like this:
output$Barplot <- renderPlot({
ggplot(data, aes(...)) # etc
})
Take the code inside and put it in a reactive, like this:
barplot_ggplot <- reactive({
ggplot(data, aes(...)) # etc
})
Then you can use it in the output$Barplot as barplot_ggplot() and in the renderPlotly() as ggplotly(barplot_ggplot()).
I hope that's a little bit clearer.

Create a number of renderPlot functions, based on the number of plots I have in a list of ggplots?

Is there any way I can dynamically create a number of renderPlot functions, based on the number of plots I have in a list of ggplots?
I have a Shiny app where instead of having a stable UI, and instead of using renderUI, I am relying on a user-supplied config file to tell Shiny how many plots to show. The config file also supplies data and pretty much helps do most of the heavy lifting.
After much battling, I'm mostly there. With the handy-dandy config file, I can build the correct UI, and generate the correct number of ggplots. The ggplots live in a list, creatively named list_of_ggplots.
But now, I'm at a point where I have a list of ggplots, and I need to allow them to be plotted by using them like this:
output$plot1 <- renderPlot({
print(list_of_ggplots[[1]])
})
But now I have an existentialist crisis -- I can't do it like this, since the user-supplied config file tells me how many plots I have. I can no longer hard code the renderPlot call like is usually done in Shiny, since the number of these functions needed is defined in the config file.
Given my list of ggplots, I need some way to generate the renderPlot calls.
Has anyone done this or have any ideas? Much appreciated.
Here's my code:
SERVER.R:
library(shiny)
library(ggplot2)
# 3 simple plots of different colors -- used here instead of all the complicated stuff
# where someone uses the config file that specified 3 plots, with data, etc.
ggplot_names <- c("p1", "p2", "p3")
ggplot_colors <- c("red", "blue", "green")
list_of_ggplots <- list()
j = 1
for (i in ggplot_names){
i <- ggplot(data.frame(x = c(-3, 3)))
i <- i + aes(x)
i <- i + stat_function(fun = dnorm, colour=ggplot_colors[[j]])
list_of_ggplots[[j]] <- i
j <- j+ 1
}
## here's the problem -- the user specified 3 plots.
## I can't hardcode the following shinyServer functions!!!
## What if tomorrow, the user specifies 2 plots instead?
shinyServer(function(input, output) {
output$plot1 <- renderPlot({
print(list_of_ggplots[[1]])
})
output$plot2 <- renderPlot({
print(list_of_ggplots[[2]])
})
output$plot3 <- renderPlot({
print(list_of_ggplots[[3]])
})
})
UI.R
## this top part is actually sourced from the config file
## since Shiny needs to know how many tabPages to use,
## names for the tabs, etc
number_of_tabPages <- 3
tab_names <- c("", "Tab1", "Tab2", "Tab3")
tabs<-list()
tabs[[1]]=""
for (i in 2:(number_of_tabPages+1)){
tabs[[i]]=tabPanel(tab_names[i],plotOutput(paste0("plot",i-1)))}
## Here's the familiar UI part
shinyUI(fluidRow(
column(12,
"",
do.call(navbarPage,tabs)
)
)
)
You can use this solution (I modified only the shinyServer part of your scripts, so I don't list the repeating code here):
shinyServer(function(input, output) {
observe(
lapply(seq(3),function(i) output[[paste0("plot",i)]] <- renderPlot(list_of_ggplots[[i]]))
)
})
Of course, you can replace 3 by a variable.

Resources