0

I am currently writing a shiny application. I want to decrease the rendering time of plots (because it takes a long time to initialise a plot). Let's say I want to render a plot dynamically, e.g.

plot(x=1:10)

(plot will not be the function which I will use in the shiny app.)

Now I want to divide the plotting into several parts, here:

plot(x=NA, y=NA, xlim=c(0,10), ylim=c(0,10))
points(x=1:10)

where plot(x=NA, y=NA, xlim=c(0,10), ylim=c(0,10)) will take a very long time in the shiny app to render and points(x=1:10) will take a short time. I need a procedure which will execute plot(x=NA, y=NA, xlim=c(0,10), ylim=c(0,10)) only when loading the app for the first time and then the plot will be build bottom-up (add points, lines, etc. to the plot). Has anybody an idea how to write this into an app? Problem here is that the function I will use in the shiny app to plot will not return anything. The plotting function is based on the base graphics system (not on ggplot2, not on lattice).

Here's a minimal working example for such an app:

library(shiny)
shinyAPP <- function() {
ui <- fluidPage(
    sidebarPanel(),
    mainPanel(
        plotOutput("plotPoints"))
)
server <- function(input, output, session) {
    output$plotPoints <- renderPlot(
        plot(x=1:10)
        ## this needs to be replaced with:
        ##plot(x=NA, y=NA, xlim=c(0,10), ylim=c(0,10))
        ##points(x=1:10)

    )
}

app <- list(ui = ui, server = server)
runApp(app)
}

shinyAPP()

Thank you very much!

tnaake
  • 469
  • 5
  • 17

3 Answers3

2

So maybe try grDevices, like here:

server.R:

library("shiny")
library("grDevices")

data(iris)
plot(x=NA, y=NA, xlim=c(0,10), ylim=c(0,10))
p <- recordPlot()

function(input, output, session) {

    output$plotPoints <- renderPlot({
        replayPlot(p)
        points(1:input$ile)
    })
}

and ui.R:

library(shiny)

fluidPage(
        sidebarPanel(
            sliderInput("ile", min=1, max=10, label="", value=5)
        ),
        mainPanel(
            plotOutput("plotPoints"))
    )
Marta
  • 3,032
  • 3
  • 17
  • 34
0

You said that you won't use plot, but it's important what you're going to use. For example, for ggplot you can do it like here (see reactive):

ui.R:

library(shiny)

fluidPage(
        sidebarPanel(
            sliderInput("ile", min=1, max=10, label="", value=5)
        ),
        mainPanel(
            plotOutput("plotPoints"))
    )

server.R

library("shiny")
library("ggplot2")

data(iris)

function(input, output, session) {

    wyk <- reactive({ggplot(iris)})

    output$plotPoints <- renderPlot(

        wyk() + geom_point(aes(x=Sepal.Length, y=Sepal.Width), col=input$ile)

    )
}
Marta
  • 3,032
  • 3
  • 17
  • 34
  • You are correct in the sense the `ggplot` will react to any changes, but it will essentially be re-rendered. If you use inspect element in chrome for example you will see that `ggpot` is a `png` image. If you are looking to not redraw the plot area but update the data instead you start looking into things like `D3` – amwill04 Dec 11 '15 at 12:41
  • Yes, for `ggplot2` that's the way how it works, however I will use the base `graphics` system, not `ggplot2`. I updated the description of the problem, thanks. – tnaake Dec 11 '15 at 13:19
0

Here is a little trick that should work if I understood your problem. However, it's not R't, just a quick fix.

test/ui.R :

fluidPage(
    sidebarPanel(
        actionButton("trickButton","Useless"),
        sliderInput("something", min=1, max=5, label="Useful", value=5)
    ),
    mainPanel(
        plotOutput("plotPoints")
    )
)

test/server.R :

data(iris)
myData <<- NULL
superHeavyLoad <- function() {
    print("That is super heavy !")
    myData <<- iris
}

function(input, output, session) {
    observe({
        if (!input$trickButton)
            superHeavyLoad()
    })
    output$plotPoints <- renderPlot(
        plot(myData[,1:as.numeric(input$something)])
    )
}

Now, on your R console :

require(shiny)
runApp("test")
Listening on http://127.0.0.1:7175
[1] "That is super heavy !"

And no matter what you do, you will never update the super-heavy part ever again. Now, from what I understood, what you did is to divide your processing between heavy-one-time functions, and reactive things. This is a (not very beautiful) way of doing it ;-)

About how it works : it's all in the button that we add. The observe function will be called each time we interact with the button, plus at server start. The if(!input$trickButton) states that we just run our code at server start (because then the button is not valued). You could also hide this useless button with a renderUI mechanism.

Vongo
  • 1,325
  • 1
  • 17
  • 27
  • Thanks Vongo fo your answer, but this is not what it should do. I think there are very elegant ways to do pure data calculation before starting the server. However, doing this for plotting is cumbersome. I am looking for - metaphorically spoken - a way to create a canvas (time-taking step, only done once; this is what I mean by the initialising process) and then repainting parts of the picture by computationally-cheap functions. – tnaake Dec 11 '15 at 13:24
  • Oh, I think I misunderstood your problem, then. I thought what was difficult was to execute something before, and my mind got tunnelled. So, basically you want to "save" part of your plot, that you compute only once, for later, but with sticking to the base `graphics` system ? If so, the problem is just that this vanilla `graphics` writes directly to the output device. You'll need an external tool to save this output and re-use it later. Have you had a look at http://stackoverflow.com/questions/29583849/r-saving-a-plot-in-an-object ? – Vongo Dec 11 '15 at 13:42