Rendering ggvis controls in shiny

扶醉桌前 提交于 2019-12-05 13:39:48

A nice example demonstrating how you can customize the X/Y-axis variables using a selectizeInput can be found here in this Movie Explorer example.

However, wrapping a ggvis() function inside a reactive environment has prominent drawback (or a bug), that once you change the input$xInp$ or input$yInp, the layer_smooths() stop reacting to your slider input.

Another potential issue with your code is that the data is not visible to ui.R. You probably want to create a global.R file which contains your data object.

Below I present two approaches on how you can interact with your ggvis plot by selecting X/Y variables. You can find both of them in server.R.

global.R

data <- data.frame(var1=rnorm(30,5,2.3),
                 var2=rbeta(30,1.5,.8),
                 var3=rnorm(30,10,2.5))

ui.R

library(shiny)

shinyUI(fluidPage(

    title="PCA Explorer", 
    h2("Principal Component Explorer"),

    fluidRow(
        column(6,
            ggvisOutput("scores"),
            uiOutput("scores_ui")),
        column(6,
            ggvisOutput("loadings"),
            uiOutput("loadings_ui"))
    ),

    br(),

    fluidRow(
        column(6,
            h3("Component Selection"),
            selectInput('xInp',"X Variable", choices=names(data),
                        selected=names(data)[[1]]),
            selectInput('yInp',"Y Variable", choices=names(data), 
                        selected=names(data)[[2]])
        ),
        column(6,
            h3("Summary of Selected Data Points"),
            verbatimTextOutput("diagn"))
    )
))

server.R

library(shiny)
library(ggvis)

shinyServer(function(input, output,session) {
    # Approach 1: regenerate a compdat object once the input changes
    # rename the X/Y variables to fixed names.
    compdat <- reactive({
        x <- data[, c(input$xInp, input$yInp)]
        names(x) <- c("x", "y")
        x
    })

    # NOTE that you use compdat here instead of compdat()
    compdat %>% ggvis(x=~x, y=~y) %>%
        layer_points(fill:="red") %>%
        layer_smooths(span=input_slider(.1,1)) %>% 
        bind_shiny("scores", controls_id="scores_ui")

    # Approach 2: wrap ggvis in a reactive environment
    # This however, would stop to react to slider input 
    # once input$xInp or input$yInp changes.
    vis2 <- reactive({ 
        xvar <- prop("x", as.symbol(input$xInp))
        yvar <- prop("y", as.symbol(input$yInp))

        data %>% ggvis(x=xvar, y=yvar) %>% 
        layer_points(fill:="red") %>% 
        layer_smooths(span=input_slider(.1,1))
    })

    vis2 %>% bind_shiny("loadings", controls_id="loadings_ui")
})

Both approaches should work (well, almost). But wait, you may see that the smoothing layer stops responding to your slider changes once you change the X/Y variables.

To fix this issue, consider the solution below.


Fixing the problem with approach 2

The bug that I mentioned earlier can be fixed by creating a sliderInput in ui.R.

ui.R

library(shiny)

shinyUI(fluidPage(

    title="PCA Explorer", 
    h2("Principal Component Explorer"),

    fluidRow(
        column(6,
            ggvisOutput("scores"),
            uiOutput("scores_ui")
        ),
        column(6,
            ggvisOutput("loadings"),
            uiOutput("loadings_ui"),
            # Create a slider by Shiny, instead of by ggvis.
            sliderInput('smooth_span', 
                        h5("Smoothing span for plot 2"), 
                        .1, 1, value=0.5)
        )
    ),

    br(),

    fluidRow(
        column(6,
            h3("Component Selection"),
            selectInput('xInp',"X Variable", choices=names(data),
                        selected=names(data)[[1]]),
            selectInput('yInp',"Y Variable", choices=names(data), 
                        selected=names(data)[[2]])
        ),
        column(6,
            h3("Summary of Selected Data Points"),
            verbatimTextOutput("diagn"))
    )
))

server.R

library(shiny)
library(ggvis)

shinyServer(function(input, output,session) {
    # Approach 1: regenerate a compdat object once the input changes
    # rename the X/Y variables to fixed names.
    compdat <- reactive({
        x <- data[, c(input$xInp, input$yInp)]
        names(x) <- c("x", "y")
        x
    })

    # NOTE that you use compdat here instead of compdat()
    compdat %>% ggvis(x=~x, y=~y) %>%
        layer_points(fill:="red") %>%
        layer_smooths(span=input_slider(.1,1)) %>% 
        bind_shiny("scores", controls_id="scores_ui")

    # Approach 2: wrap ggvis in a reactive environment
    # This however, would stop to react to slider input 
    # once input$xInp or input$yInp changes.
    vis2 <- reactive({ 
        xvar <- prop("x", as.symbol(input$xInp))
        yvar <- prop("y", as.symbol(input$yInp))
        smooth.span <- input$smooth_span

        data %>% ggvis(x=xvar, y=yvar) %>% 
        layer_points(fill:="red") %>% 
        # FIXED: use the value from the input object, instead of a input_slider
        layer_smooths(span=smooth.span)
    })

    vis2 %>% bind_shiny("loadings", controls_id="loadings_ui")
})

Final words

Wrapping ggvis into a reactive environment has a few drawbacks:

  • Reduced performance: because the entire graph needs to be redrawn once dependent input variables change.
  • Lack of transitioning animation: because the entire graph is redrawn, you don't see the nice transitioning effect that you would see in Approach 1.

However, it does have a few advantages:

  • Higher flexibility: wrapping ggvis in a reactive environment is, AFAIK, currently the ONLY way if you want to change the X-Y axis label dynamically (say, depend on your input$xInp). Because ggvis only computes and binds data to the ggvis object once, changes made to the axis labels won't be reflected in real time. However, because wrapping ggvis in reactive environment causes the entire graph to redraw, somehow the labels are updated in the redraw as well.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!