How can I apply reactive() on reactive object? - r

I want to plot a graph within a shiny app. Then I want to add multiple shades on the same graph. Before i show you my code let me define my sampe data.
dg= reactive ({dygraph(X1(), main ="interactive graph",
xlab = "time frame",
ylab = "records" ) %>% dyRangeSelector() })
# I have a table for the shades to be added, it's defined with reactive
shade_tab=reactive({ Panne[Panne$Equipement==input$machine,] })
# add shades
for( i in 1:nrow(shade_tab()))
{ dg()= reactive({
dyShading(dg(), from= shade_tab()$Date[i],
to = shade_tab()$Date[i] + 24*60*60 ,
color = 'black'
})
}
output$dygraph <- renderDygraph({ dg()() })
This is the code I tried to run, but it does not work. Any help will be greatly appreciated.

Reactive expressions must be called inside reactive environments. So shade_tab() will not work in that for since is outside a reactive environment. You could fix that, wrapping the forinside an observe.
The basics section at https://shiny.rstudio.com/articles/ explains this really well :)

Related

How to remove NA value from the ggplot in shiny app?

library(shiny)
library(palmerpenguins)
library(ggplot2)
library(dplyr)
penguin <- penguins
penguin$year <- as.factor(penguin$year)
ui <- fluidPage(
titlePanel("Data Visualisation of Penguins Data"),
sidebarPanel(
selectInput("yaxis",
label = "Choose a y-axis variable to display",
choices = list("bill_length_mm",
"bill_depth_mm",
"flipper_length_mm",
"body_mass_g"),
selected = "bill_length_mm"),
selectInput("xaxis",
label = "Choose a x-axis variable to display",
choices = c("species",
"sex",
"year"),
selected = "sex"),
checkboxGroupInput("islandlevels",
label = "Check to display different island levels",
choices = c("island"),
selected = NULL),
br(), br(),
selectInput("species",
label = "Choose species to view separate plot",
choices = list("Adelie",
"Chinstrap",
"Gentoo"),
selected = NULL)),
mainPanel(
plotOutput("plot1"),
br(), br(),
plotOutput("plot2")
)
)
server <- function(input, output){
output$plot1 <- renderPlot({
if(is.null(penguin))
return(NULL)
ggplot(penguin, aes(x = penguin[[input$xaxis]], y = penguin[[input$yaxis]])) +
geom_boxplot()
})
}
shinyApp(ui = ui, server = server)
This is my shiny code, but I'd like to remove NA value when x-axis variable is sex.
I can't just remove row with NA values because I have to use variable (that is not missing value but the row has missing value such as row 9 in image 2) when I change x-axis variable or/and y-axis variable.
I wanted to find the solution but I wonder what function should I use. Do I have to use if statement, reactive function, or else?
Thank you for help in advance.
sex variable with NA value(want to delete NA on my plot)
You can prevent the NA values of showing up as categories by making use of scale_x_discrete(na.translate = FALSE):
library(ggplot2)
library(palmerpenguins)
ggplot(penguins, aes(x = sex, y = bill_length_mm)) +
geom_boxplot() +
scale_x_discrete(na.translate = FALSE)
#> Warning: Removed 11 rows containing missing values (stat_boxplot).
Conditionally filter your data, perhaps something like this:
dat <- reactive({
if (input$xaxis == "sex") penguin[ !is.na(penguin$sex), ] else penguin
})
output$plot1 <- renderPlot({
req(penguin, input$xaxis, input$yaxis)
ggplot(dat(), aes_string(x = isolate(input$xaxis), y = input$yaxis)) +
geom_boxplot()
})
Several critical changes here:
In case you want to do more than a single plot with the filtered data, I make a reactive data component named dat with the filtered data. In this way, if you ever add (say) a table or another plot or something, you don't need to handle selective filtering in each of them, you just need to use dat() everywhere and everything benefits from it.
Reactive can be volatile, and having both the data and the plot reacting to input$xaxis will cause the plot to be rendered twice for each change to xaxis. Because of this, I isolate(input$xaxis) in the plot reactive. When the user changes xaxis, the dat will change which will trigger (once!) the plot to change. (No need to isolate yaxis, as that's correct in this case.)
In general, you should not use ggplot2(x, aes(x$a, x$b)). More specifically, using $ and/or [[ in aesthetic definitions is poor practice, and will fail given certain situations. It is much better to use aes with symbols (e.g., cyl from mtcars) or aes_string with strings ("cyl"). Since you're defining the aesthetics programmatically, it is better to use aes_string.
I changed your if (is.null(penguin)) to shiny's more canonical req, and added checks in the inputs as well. While most simpler shiny apps don't always need this, I've found that more complex apps can cause just enough delay in input instantiation that an output reactive block may trigger before all inputs have been assigned, meaning in this example it might be possible for input$xaxis to be null. While unlikely in simpler shiny apps like this, I still think it's safe.
There may be reasons to use individual req lines, one for each input. The results in this case will be the same, but there are times when it makes sense to break them out.
The use of req prohibits the rest of the plot rendering from occurring, but it also does it in a way that shiny components recognize, not causing errors or rendering issues. (I prefer it to manual if (is.null(.)) return(NULL) logic.)
Note: I think #stefan's answer may be the more canonical way in ggplot2 to omit NA values from the axis, so perhaps that is the best way to go for that side of things. However, I still believe that points 3 and 4 are still (also) relevant to your app even with stefan's change.

Calling a graph from another sheet in 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.

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.

use function to plot in shiny

I use a function to plot in R, because this is pretty large code for the plot (hexagonals etc). For the sake of neat code I put this in a function.
It is about a SOM algorithm , that does not really matter for now, but I create the data I need, then I normally plot it with the following code:
mydata <- som_model$codes
var <- 1
title <- names(som_model$data)[var]
dataplot <- matrix(mydata[,var],nrow=gridsize,ncol=gridsize,byrow=TRUE)
source('plotHexMap.R')
plotHexMap(dataplot,title,gridsize)
So now I want to do this in Shiny:
observe({
if(input$plottrained>0){
var <- 1
title <- names(som_model$data)[var]
dataplot<-matrix(mydata[,var],nrow=gridsize,ncol=gridsize,byrow=TRUE)
source("plotHexMap.R")
output$plot1 <-renderPlot({plotHexMap(dataplot,title,gridsize)})
}
})
Ok so this does not work, but when I just write the plot to a global variable (with <<-) it does exist and I can see it in the plots, so by doing this:
observe({
if(input$plottrained>0){
var <- 1
title <- names(som_model$data)[var]
dataplot<-matrix(mydata[,var],nrow=gridsize,ncol=gridsize,byrow=TRUE)
source("plotHexMap.R")
plot1 <<-renderPlot({plotHexMap(dataplot,title,gridsize)})
}
})
What am I missing here? what does shiny need that normal R environment does not to plot this from function?
would really appreciate any help with this! Thanks in advance,
Pieter
Instead of observe, try renderPlot in server:
output$myplot <- renderPlot({
if(input$plottrained>0){
var <- 1
title <- names(som_model$data)[var]
dataplot<- matrix(mydata[,var],nrow=gridsize,ncol=gridsize,byrow=TRUE)
source("plotHexMap.R", local = TRUE) #assuming this file is actually local for you
plot1 <- plotHexMap(dataplot,title,gridsize)
}
else{return(NULL)}
})
And then plotOutput in the ui:
plotOutput('myplot')

Conditional reactivity Shiny

Reactive expressions in Shiny propagate changes where they need to go. We can suppress some of this behaviour with isolate, but can we suppress changes being propagated based on our own logical expression?
The example I give is a simple scatterplot, and we draw a crosshair with abline where the user clicks. Unfortunately, Shiny considers the result to be a new plot, and our click value is reset to NULL... which in turn is treated as an update to the value to be propagated as usual. The plot is redrawn, and NULL is passed to both arguments of abline.
My hack (commented out below) is to place a condition in the renderPlot call which updates some non-reactive variables for the plotting coordinates, only when the click values are non-NULL. This works fine for trivial plots, but it actually results in the plot being drawn twice.
What's a better way to do this? Is there a correct way?
Server file:
library(shiny)
shinyServer(function (input, output)
{
xclick <- yclick <- NULL
output$plot <- renderPlot({
#if (!is.null(input$click$x)){
xclick <<- input$click$x
yclick <<- input$click$y
#}
plot(1, 1)
abline(v = xclick, h = yclick)
})
})
UI file:
library(shiny)
shinyUI(
basicPage(
plotOutput("plot", click = "click", height = "400px", width = "400px")
)
)
Winston calls this problem "state" accumulation - you want to display not only the current data, but something generated by the previous plot (the best place to learn about this is at https://www.rstudio.com/resources/videos/coordinated-multiple-views-linked-brushing/)
The basic idea is to create your own set of reactive values, and update them when the user clicks on the plot. They won't be invalidated until the next click, so you don't get circular behaviour.
library(shiny)
shinyApp(
shinyUI(basicPage(plotOutput("plot", click = "click"))),
function(input, output) {
click <- reactiveValues(x = NULL, y = NULL)
observeEvent(input$click, {
click$x <- input$click$x
click$y <- input$click$y
})
output$plot <- renderPlot({
plot(1, 1)
abline(v = click$x, h = click$y)
})
}
)

Resources