28

I want the show the y-value when I hold my mouse on a point in the graph. The code for my plot looks like this:

output$graph <- renderPlot({
  p1 <- ggplot(data, aes(x= date)) + 
  geom_line(aes(y=Height, colour = "Height"), size=1) + 
  geom_point(aes(y=Height, colour = "Height", text = paste("Weight/Height:", Height)))
  plot(p1)
})

I did some research and I thought that the text = paste("Weight/Height:", Height) part in aes would make sure that the text would appear. Unfortunately nothing appears. Does anyone know what I did wrong?

Hav11
  • 409
  • 1
  • 5
  • 14
  • I've tried plotly but it have so much bugs that we give up, we have alternative Open Source project that we created at work https://github.com/Roche/ggtips it use same ggplot data but use svg and tooltips are in JS. Also plots are scaling not like plotly. – jcubic May 09 '19 at 13:07

1 Answers1

57

Unfortunately ggplot is not interactive but it can be easily "fixed" with plotly package. You only need to replace plotOutput with plotlyOutput and then render a plot on with renderPlotly.

Example 1: plotly

library(shiny)
library(ggplot2)
library(plotly)

ui <- fluidPage(
    plotlyOutput("distPlot")
)

server <- function(input, output) {
   output$distPlot <- renderPlotly({
      ggplot(iris, aes(Sepal.Width, Petal.Width)) + 
       geom_line() + 
       geom_point()
   })
}

shinyApp(ui = ui, server = server)


Example 2: plotOutput(..., hoverOpts(id = "plot_hover", delay = 50)):

We don't have to use any special package to introduce the interactivity to our graphs though. All we need is our lovely shiny shiny! We can just play with plotOutput options as for instance click, hover or dblclick to make the plot interactive. (See more examples in shiny gallery)

In the example below we add "hovering" by the parameterhover and then customise delay, which is set by default 300ms.

plotOutput("distPlot", hover = hoverOpts(id = "plot_hover", delay = 50))

We then can access values via input$plot_hover and use a function nearPoints to show values that are near the points.

ui <- fluidPage(
  selectInput("var_y", "Y-Axis", choices = names(iris)),

# plotOutput("distPlot", hover = "plot_hover", hoverDelay = 50), # UPDATED
# plotOutput in shiny 1.7.2 doesn't have the hoverDelay argument. One needs to use hoverOpts()
plotOutput("distPlot", hover = hoverOpts(id = "plot_hover", delay = 50)),
  uiOutput("dynamic")
  
)

server <- function(input, output) {
  
  output$distPlot <- renderPlot({
    req(input$var_y)
    ggplot(iris, aes_string("Sepal.Width", input$var_y)) + 
      geom_point()
  })
  
  output$dynamic <- renderUI({
    req(input$plot_hover) 
    verbatimTextOutput("vals")
  })
  
  output$vals <- renderPrint({
    hover <- input$plot_hover 
    # print(str(hover)) # list
    y <- nearPoints(iris, input$plot_hover)[input$var_y]
    req(nrow(y) != 0)
    y
  })
  
}
shinyApp(ui = ui, server = server)

Example 3: Custom ggplot2 tooltip:

The second solution works great but yes...we want to do it better! And yes...we can do it better! (...If we use some javaScript but pssssss don't tell anyone!).

library(shiny)
library(ggplot2)

ui <- fluidPage(
  
  tags$head(tags$style('
     #my_tooltip {
      position: absolute;
      width: 300px;
      z-index: 100;
      padding: 0;
     }
  ')),
  
  tags$script('
    $(document).ready(function() {
      // id of the plot
      $("#distPlot").mousemove(function(e) { 
        
        // ID of uiOutput
        $("#my_tooltip").show();         
        $("#my_tooltip").css({             
          top: (e.pageY + 5) + "px",             
          left: (e.pageX + 5) + "px"         
        });     
      });     
    });
  '),
  
  selectInput("var_y", "Y-Axis", choices = names(iris)),
  plotOutput("distPlot", hover = hoverOpts(id = "plot_hover", delay = 50)),
  uiOutput("my_tooltip")
  
  
)

server <- function(input, output) {

  
  output$distPlot <- renderPlot({
    req(input$var_y)
    ggplot(iris, aes_string("Sepal.Width", input$var_y)) + 
      geom_point()
  })
  
  output$my_tooltip <- renderUI({
    hover <- input$plot_hover 
    y <- nearPoints(iris, input$plot_hover)[input$var_y]
    req(nrow(y) != 0)
    verbatimTextOutput("vals")
  })
  
  output$vals <- renderPrint({
    hover <- input$plot_hover 
    y <- nearPoints(iris, input$plot_hover)[input$var_y]
    req(nrow(y) != 0)
    y
  })  
}
shinyApp(ui = ui, server = server)


Example 4: ggvis and add_tooltip:

We can also use ggvis package. This package is great, however, not enough mature yet.

Update: ggvis is currently dormant: https://github.com/rstudio/ggvis#status

library(ggvis)

ui <- fluidPage(
  ggvisOutput("plot")
)

server <- function(input, output) {
  
  iris %>%
    ggvis(~Sepal.Width, ~Petal.Width) %>%
    layer_points() %>%
    layer_lines() %>% 
    add_tooltip(function(df) { paste0("Petal.Width: ", df$Petal.Width) }) %>%
    bind_shiny("plot")
}

shinyApp(ui = ui, server = server)

EDITED


Example 5:

After this post I searched internet to see whether it could be done more nicely than example 3. I found this wonderful custom tooltip for ggplot and I believe it can hardly be done better than that.

Updated: it can happen that the shiny function p gets masked by some other function and the shiny code doesn't work properly. In order to avoid the issue, once can run the following command before sourcing the full shiny code:

p <- shiny::p


EDITED 2


The four first examples have been updated as of 03.09.2022 such that they are working with the newest shiny version 1.7.2.

Michal Majka
  • 5,332
  • 2
  • 29
  • 40
  • Thank you for your very detailed explanation! I am a few days out of town, but I will try this when I get back. – Hav11 Aug 13 '16 at 10:46
  • I took a look at the link at Example 5, and the code looks really great. Unfortunately I get the error: `nearPoints: not able to automatically infer 'xvar' from coordinfo`. From a different Stackoverflow post I learned that it could have something to do with the `print` function in my code. However, I think I need to have some kind of print function, mostly because I am working with for-loops and `grid.arrange()` for multiple plots. I will try to find some answers to solve this problem – Hav11 Aug 16 '16 at 12:36
  • Yes, the problem is caused by `p <- ggplot(...) ; print(p)`. If you print the plot without `print` function, so `p <- ggplot(...) ; p` the code works fine. You could try to specify `xval` and `yval` in `nearPoints`. I don't think `grid.arrange` needs explicitly `print(p)`. It works fine with `p` as well.. I would run the code without the function `print` first to see if everything works – Michal Majka Aug 16 '16 at 12:59
  • If nothing works then you can create a plot within a separate `myplot <- `reactive(...)` and return it without using `print` function. After that, pass it to the `renderPlot` and in other reactive parts of your code where you, for instance save the plots in PDF, use it in this way: `print(myplot())`. – Michal Majka Aug 16 '16 at 13:41
  • If I run the code without `print(p)` nothing appears in my Shiny app. If I run the app with `myplot <- reactive(p<- ggplot(..), print(p))` and pass that to `renderPlot`, the plots get printed, but I get the same error as I got before. – Hav11 Aug 16 '16 at 14:31
  • `myplot` reactive should be without `print` like this: `reactive({p<- ggplot(..) ; p})` and anywhere where you would need `print(p)` you use `print(mydata())` – Michal Majka Aug 16 '16 at 14:34
  • Nothing appears in my Shiny app when I change my code like that (with `print(myplot())` – Hav11 Aug 16 '16 at 14:48
  • 1
    Thank you for your help! I will try to make a reproducible example (maybe in a new post) – Hav11 Aug 16 '16 at 14:53
  • 1
    Plotly is fantastic. Thanks for including that in your response. – awwsmm Jul 26 '18 at 07:57
  • Hey Michal, I'm trying to use your answer 3 here. The answer 5 in my case only displays the hover message for half a second it seems. Are you still active on SO, because I would like to ask a small follow up question about solution 3 concerning multiple plots – Mark Apr 28 '19 at 18:37
  • Hello @MichalMajka, great explanation on shiny, just a question, how could it be written for flexdashboard? – Manu Feb 20 '20 at 19:22
  • how does this work across multiple shiny sessions with different users accessing the same app? – Brian D Aug 01 '22 at 20:12
  • Hi Brian, Unfortunately I don't know it. Never tried it in that context. I guess, everyone has his own session and a tooltip. :) – Michal Majka Sep 03 '22 at 18:05