One of my apps is displaying a ggplot via uiOutput('plot.ui') and plot.ui is rendered through renderUI().
output$plot.ui=renderUI({
plotOutput('plot', width=a function(), height=a function())
})
The code works, but it is very laggy. It seems that this is a two-step process. In my app, it first renders 'plot' (which is a ggplot rendered by renderPlot), then it resizes the plot according the specified width and height. The lag between the two steps is significant (about 3 seconds). I checked it by wrapping a withProgress() around plotOutput(), and the problem still exists. I am wondering why this problem exists and if there is any way to solve it.
A small example is attached to illustrate this problem.
library(shiny)
shinyApp(
ui=shinyUI(
pageWithSidebar(
titlePanel('test'),
sidebarPanel(
sliderInput('width','Width: ', min=0,max=1000,value=100),
sliderInput('height','Height: ', min=0,max=1000,value=100)
),
mainPanel(uiOutput('plot.ui'))
)
),
server=function(input,output){
output$plot.ui=renderUI({
plotOutput('plot',width=input$width,height=input$height)
})
output$plot=renderPlot({
plot(runif(100000,1,100),runif(100000,1,100))
})
}
)
Thank you very much for your help!
I had the similar issue, so if you had fixed the problem in another way, let me know. What I attempted to to was adjust the size of plotOutput depending on what I was plotting (with certain inputs, I had horizontal bar plot with 1 bar or 10 bars.. needed to adjust the height accordingly.
Solution 1) Adjust the height of renderplot()
As explained by jcheng5 here. See if this solves the issue
Solution 2) Define a plot function, use isolate()
# Define a function that returns a plot
plot_function <- function(){
plot(runif(100000,1,100),runif(100000,1,100)
}
# reactive UI and adjust the height here
output$plot.ui=renderUI({
plotOutput("plot", height = -------------)
})
# call plot_function but use isolate()
output$plot <- renderPlot({
isolate(plot_function())
}
This works for me. See if this solves the issue.
Related
I'm attempting to design a Shiny App where the user changes several inputs, then presses a "Plot" button to generate new visualizations. Currently, I have the a data object being generated in an eventReactive tied to the "Plot" button, with that data then getting fed into the renderPlot functions.
The framework seems to work, except that the plots will change whenever the inputs are changed. This often leads to errors in the plots, as different inputs load in different data. Pressing the "Plot" button after changing the inputs will cause the correct plots to render, but I'm trying to find a way to ensure that the plots don't change ever until that button is hit.
I believe the solution is a use of the "isolation" function, and I've tried that just about everywhere. However, none of these attempts have been successful. Below is a (simplified) setup of my code.
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
# several selectInput options
actionButton('plot', label = 'Plot')
)
mainPanel(
plotOutput('outputPlot', height = '3in'),
)
)
)
server <- function(input, output) {
plotData <- eventReactive(input$plot, {
# load various data and organize into a list
return(data.list)
})
outPutPlot <- renderPlot({
plot.data <- plotData()
# manipulate data based on the specific plot, then generate
return(plot)
)
}
You’re right that you’d need to isolate() all reactive dependencies other
than plotData(). Without having a complete runnable example, it’s not
possible to point out where this might have gone wrong. However, it may be
easier to wrap the renderPlot() call in bindEvent(), which will isolate
everything. You just pass the expressions you want to depend on in other
arguments.
So try something like this:
bindEvent(renderPlot({ ... }), plotData())
I am using shinycssloaders to show loading animation. There are multiple inputs on the page which are loaded from the server. These inputs are also dependent on each other.
In the below example I have used a reactive object to create such dependency. First the table is displayed and only when the calculation of table is completed (rv$a <- 1) plot can be completed.
library(shiny)
library(shinycssloaders)
ui <- fluidPage(
withSpinner(tableOutput('data')),
withSpinner(plotOutput('plot'))
)
server <- function(input, output) {
rv <- reactiveValues(a = NULL)
output$data <- renderTable({
#Some long calculation here, using Sys.sleep for simplicity
Sys.sleep(2)
rv$a <- 1
head(mtcars)
})
output$plot <- renderPlot({
req(rv$a)
#Some long calculation here, using Sys.sleep for simplicity
Sys.sleep(2)
plot(rnorm(100), rnorm(100))
})
}
shinyApp(ui, server)
This works fine but it shows 2 loaders, one for table and other one for plot. I want to combine these 2 loaders and show only 1 loading animation which covers the entire page combining table and plot. Also loading should end only after all the calculation is done i.e after plot calculation.
I have tried putting table and plot in a div and use spinner on div but it did not work and gave a blank page.
ui <- fluidPage(
withSpinner(div(
tableOutput('data'),
plotOutput('plot')
))
)
Does anybody have a solution to this? Is this possible using some different package?
You can use the tagList() function, which creates a list of the tags allowing the shinycssloaders package to wrap all the inputs within it in one go.
So the ui will look like the following:
ui <- fluidPage(
withSpinner(tagList(tableOutput('data'),
plotOutput('plot')))
)
Just some extra information for the animation. You can all change the style of the animation such as the the type, color, size , etc by adding these as arguments to the withSpinner(). Check the withSpinner() RDocumentation.
I'm building a shiny app that is supposed to show a bupaR process_map, which is kind of working.
Sadly the process_map()-function returns a dgr_graph-object, which can't be rendered with the DiagrammeR::renderGrViz() or DiagrammeR::renderDiagrammeR() functions. I found a way to convert it to a grViz / htmlwidget-object with render_graph(), but this way the graph is not nicely scaled. This is especially bad since I'm dealing with very long linear graphs.
Here is a MWE:
# server.R
library(DiagrammeR)
library(bupaR)
shinyServer(
function(input, output) {
plot <- patients %>%
process_map(rankdir = "TB",
render = FALSE)
output$diagram <- renderGrViz({
render_graph(plot)
})
}
)
# ui.R
library(DiagrammeR)
shinyUI(fluidPage(
titlePanel("DiagrammeR + shiny"),
grVizOutput(outputId = "diagram")
))
Is there a better way of displaying a dgr_graph-object in shiny, that fills out the whole width of the app and not just a post-stamp size in the middle? Ideal would be something adaptive, without the need for a fixed width in pixels.
As it turns out the problem is not the render_graph-function on the Server, but rather the default setting of the renderGrViz-function in the UI. When you set it to grVizOutput(outputId = "diagram", height = "100%") it works just fine.
This question is a follow-up to How can put multiple plots side-by-side in shiny r? and R Shiny layout - side by side chart and/or table within a tab.
Is it possible to embed two side-by-side plots in a tab panel when there are other outputs present besides the two plots I want to embed side-by-side? I have tried every suggestion offered in the answers to the previous two questions and nothing works. No matter what I try I end up breaking my program so it refuses to display any out put at all.
mainPanel(
width = 10,
tabsetPanel(
tabPanel("Dashboard", textOutput("text30"), textOutput("text31"), textOutput("text28"), textOutput("text29"),
column(6, plotOutput("plot1st"), column(6, plotOutput("plot2nd")), #what do I put here to make this work?
plotOutput("plot3rd"), plotOutput("plot9th"), plotOutput("plot4th"), plotOutput("plot8th"), plotOutput("plot7th"), plotOutput("plot6th")),
tabPanel("Graph Switcher", plotOutput("selected_graph"))
))))
Is there anything I can do to make this work?
Update: Thanks to Florian's answer I was able to make the app look the way I wanted. However, as can be seen in the screenshot below, a small number 12 appears in between the plots, and the plots are not perfectly horizontally aligned. I'm wondering if it's possible to fix this?
You can put multiple column elements in one fluidRow:
Hope this helps!
library(shiny)
ui <- fluidPage(
tabsetPanel(
tabPanel("Dashboard",
fluidRow(12,
column(6,plotOutput('plot1')),
column(6,plotOutput('plot2'))
),
fluidRow(12,
column(4,plotOutput('plot3')),
column(4,plotOutput('plot4')),
column(4,plotOutput('plot5'))
),
fluidRow(12,
column(3,plotOutput('plot6')),
column(3,plotOutput('plot7')),
column(3,plotOutput('plot8')),
column(3,plotOutput('plot9'))
)
)))
server <- function(input,output)
{
lapply(seq(10),function(x)
{
output[[paste0('plot',x)]] = renderPlot({hist(runif(1:20))})
})
}
shinyApp(ui,server)
I'm looking at implementing 3D interactive plots in my shiny App, and so far I've been using plotly. However, plotly has one major disadvantage, it's extremely slow when rendering.
I've done checks, and the whole creation of updated outplot$plot <- renderPlotly ({.....}) and plotlyOutput("plot") takes less than 0.5 seconds, despite the large data set involved. This is a know issue for years but still seems to be current.
Hence, I'm looking to use a package called car, also because it has many options, some which I particularly want that are not available in other packages. Info on the car package is here: http://www.sthda.com/english/wiki/amazing-interactive-3d-scatter-plots-r-software-and-data-visualization
The problem is that it renders in a separate popup window rather than inside the shiny app, and I want to have it inside it, or even better, add a button that allow the user to have it as a popup, but only when asked. However, i can't figure out how to jam the bugger into the actual shiny page.
Here is my minimal example with a single text element and the graph code that (in my case) keeps appearing in a separate window rather than in the app.
install.packages(c("rgl", "car", "shiny"))
library("rgl")
library("car")
library(shiny)
cars$time <- cars$dist/cars$speed
ui <- fluidPage(
hr("how do we get the plot inside this app window rather than in a popup?"),
plotOutput("plot", width = 800, height = 600)
)
server <- (function(input, output) {
output$plot <- renderPlot({
scatter3d(x=cars$speed, y=cars$dist, z=cars$time, surface=FALSE, ellipsoid = TRUE)
})
})
shinyApp(ui = ui, server = server)
There is also this package, scatterplot3d but that's not interactive
http://www.sthda.com/english/wiki/scatterplot3d-3d-graphics-r-software-and-data-visualization
And there are some RGL packages but they have the same issue (seperate window) and don't offer the options I am lookign for.
You need to use an rglwidget which takes the last rgl plot and puts in in an htmlwidget. It used to be in a separate package, but it has recently been integrated into `rgl.
Here is the code to do this:
library(rgl)
library(car)
library(shiny)
cars$time <- cars$dist/cars$speed
ui <- fluidPage(
hr("how do we get the plot inside this app window rather than in a popup?"),
rglwidgetOutput("plot", width = 800, height = 600)
)
server <- (function(input, output) {
output$plot <- renderRglwidget({
rgl.open(useNULL=T)
scatter3d(x=cars$speed, y=cars$dist, z=cars$time, surface=FALSE, ellipsoid = TRUE)
rglwidget()
})
})
shinyApp(ui = ui, server = server)
Yielding this: