-->

Rstudio shiny ggvis tooltip on mouse hover

2019-03-11 15:28发布

问题:

In the example below, I have an interactive shiny ggvis plot, but I added a long column that is a long string and for some reason, my hover pop-up shows wt and mpg but does not show long.

Also, if the list of elements in the legend is too long, they are hidden at the bottom right corner of the plot. Is there any way to stack these in several columns in the legend?

Any ideas?

# ui.R
library(ggvis)
shinyUI(pageWithSidebar(
  div(),
  sidebarPanel(
    sliderInput("n", "Number of points", min = 1, max = nrow(mtcars),
                value = 10, step = 1),
    uiOutput("plot_ui")
  ),
  mainPanel(
    ggvisOutput("plot"),
    tableOutput("mtc_table")
  )
))


# server.R
library(shiny)
library(ggvis)
shinyServer(function(input, output, session) {
  # A reactive subset of mtcars
  mtc <- reactive({
      data = mtcars[1:input$n, ]
      data$long = as.character(paste0("A car with ",data$cyl," cylinders and ",data$gear," gears and ",data$carb, " carburators"))
      data
  })
  # A simple visualisation. In shiny apps, need to register observers
  # and tell shiny where to put the controls
  mtc %>%
    ggvis(~wt, ~mpg) %>%
    layer_points(fill = ~factor(long)) %>%
    add_tooltip(function(data){paste0("Wt: ", data$wt, "<br>", "Mpg: ",as.character(data$mpg), "<br>", "String: ", as.character(data$long))}, "hover") %>%
    bind_shiny("plot", "plot_ui")

   output$mtc_table <- renderTable({
     mtc()[, c("wt", "mpg", "long")]
   })
})

回答1:

You need to add long as a key currently data$long is null in the anonymous function supplied to add_tooltip:

library(shiny)
library(ggvis)

runApp(list(ui = pageWithSidebar(
  div(),
  sidebarPanel(
    sliderInput("n", "Number of points", min = 1, max = nrow(mtcars),
                value = 10, step = 1),
    uiOutput("plot_ui")
  ),
  mainPanel(
    ggvisOutput("plot"),
    tableOutput("mtc_table")
  )
)
, server= function(input, output, session) {
  # A reactive subset of mtcars
  mtc <- reactive({
    data = mtcars[1:input$n, ]
    data$long = as.character(paste0("A car with ",data$cyl," cylinders and ",data$gear," gears and ",data$carb, " carburators"))
    data
  })
  # A simple visualisation. In shiny apps, need to register observers
  # and tell shiny where to put the controls
  mtc %>%
    ggvis(~wt, ~mpg, key:= ~long) %>%
    layer_points(fill = ~factor(long)) %>%
    add_tooltip(function(data){
      paste0("Wt: ", data$wt, "<br>", "Mpg: ",as.character(data$mpg), "<br>", "String: ", as.character(data$long))
    }, "hover") %>%
    bind_shiny("plot", "plot_ui")

  output$mtc_table <- renderTable({
    mtc()[, c("wt", "mpg", "long")]
  })
})
)



回答2:

I have been puzzling on this for a while as well. Seems that you can only get TOOLTIP info that is inside the ggvis() data. So if you have ggvis(~wt, ~mp) you can show wt and mp in the tooltip. If you have ggvis(~wt, ~mpg, fill= ~long) you can show wt, mp , long in the tooltip. Or data that is in the of the layer_.....(fill = ~long ,stroke = ~name, strokeWidth := 0). (I created ~name from the row names) You need to suppress the stroke and the legend to avoid a visual impact: hide_legend("stroke") %>%

As far as i can find ggvis limits the amount of info to be shown in the tooltip to what is contains (quite understandable)

If we look at the documentation:

***Usage***
ggvis(data = NULL, ..., env = parent.frame())

***Arguments***
data A data object.

... Property mappings. If not named, the first two mappings are taken to be x and y. 

Common properties are x, y, stroke, fill, opacity, shape

env Environment in which to evaluate properties.

so we can add x, y, stroke, fill, opacity, shape, key (must be unique values), text, font, fontsize etc or whatever is in one of the layer_..... properties.

Would be nice if it was possible to add dataframe data to the ggplot that is not used in the plot but is avialible in the TOOLTIP only. If i find a way i'll post it here too

I added invisible NAME info only visible in the tooltip in this way(based on the previous example):

mtc <- reactive({
  if(length(input$n)>0){
    data = mtcars[1:input$n, ]
    data$long = as.character(paste0("A car with ",data$cyl," cylinders and     ",data$gear," gears and ",data$carb, " carburators"))
    data$name <- rownames(data) 

  data %>%
    ggvis(~wt, ~mpg) %>%
    layer_points(fill = ~long ,stroke = ~name, strokeWidth := 0) %>%
    hide_legend("stroke") %>%
    add_tooltip(function(dataT){
      paste0("Car: ",dataT$name, "<br>", "Wt: ", dataT$wt, "<br>",
      "Mpg: ",as.character(dataT$mpg), "<br>", "String: ", as.character(dataT$long))
    }, "hover") %>%
    bind_shiny("plot", "plot_ui")

  data
  }
})

Suggestions for a better and more stable solution are more than welcome ! ;^)


OK, i found a way to show all data in the tooltip that is not in the ggvis plot: (see also: Add data to ggvis tooltip that's contained in the input dataset but not directly in the vis )

The key is a unique !!! link to another data set and a function outside the ggvis() that returns the data and info to show in the TOOLTIP.

I changed my code to:

mtc <- reactive({
  if(length(input$n)>0){
    data = mtcars[1:input$n, ]
    data$long = as.character(paste0("A car with ",data$cyl," cylinders and ",data$gear," gears and ",data$carb, " carburators"))
    data$name <- rownames(data) 


  all_values <- function(x) {
    if(is.null(x)) return(NULL)
    row <- data[data$name == x$name, ]
    paste0(names(row), ": ", format(row), collapse = "<br />")
  }

  data %>%
    ggvis(~wt, ~mpg, key := ~name) %>%
    layer_points(fill = ~long) %>%

    add_tooltip(all_values, "hover")  %>%
#       add_tooltip(function(dataT){
#       paste0("Car: ",dataT$name, "<br>", "Wt: ", dataT$wt, "<br>", "Mpg: ",as.character(dataT$mpg), "<br>", "String: ", as.character(dataT$long))
#       }, "hover") %>%
    bind_shiny("plot", "plot_ui")

  data
  }
})

This way you can change the tooltip info in every way you want !


AND to TOP things if you can combine the two (Hover -> summary info && Click -> ALL info) in this way:

mtc <- reactive({
  if(length(input$n)>0){
    data = mtcars[1:input$n, ]
    data$long = as.character(paste0("A car with ",data$cyl," cylinders and ",data$gear," gears and ",data$carb, " carburators"))
    data$name <- rownames(data) 


  all_values <- function(x) {
    if(is.null(x)) return(NULL)
    row <- data[data$name == x$name, ]
    paste0(names(row), ": ", format(row), collapse = "<br />")
  }

  data %>%
    ggvis(~wt, ~mpg, key := ~name) %>%
    layer_points(fill = ~long) %>%

    add_tooltip(all_values, "click")  %>%
    add_tooltip(function(dataT){
      paste0("Car: ",dataT$name, "<br>", "Wt: ", dataT$wt, "<br>", "Mpg: ",as.character(dataT$mpg), "<br>", "String: ", as.character(dataT$long))
      }, "hover") %>%
    bind_shiny("plot", "plot_ui")

  data
  }
})

Just to be complete !

To be even completer -> the tooltip is just HTML code as string -> so in a way you can you can create whatever HTML page you want to show up. You just can not point at it because as soon as you leave the point it will disapear ! (but you can have a click action at the same point to supplement (for instance redirect the page) the hover action.)

just a last short example of a image in a tooltip:

  add_tooltip(function(img){'<img src="pic_mountain.jpg" alt="Mountain View" style="width:100px;height:100px;">'}, "hover") %>%

(sorry for the long answer)



标签: r shiny ggvis