Hav11
Hav11

Reputation: 409

R Shiny: Tooltip in ggplot

I want the values of the height or weight to show when I hover over a point in the graph. I already tried to make this work by using the plotly package and the example of this link. But I got all kind of errors and I do not know how to make it work.

I've included my whole code so I hope someone can help me with this problem.

library("shiny")
library("ggplot2")
library('readxl')
library('gridExtra')

ui<- fluidPage(
   titlePanel("Animals"),
   sidebarLayout(
     sidebarPanel(
  helpText("Create graph of height and/or weight animals"),
  selectInput("location", 
              label = "Choose a location",
              choices = list("New York"="New York", "Philadelphia" = "Philadelphia"),
              selected = "New York"),
  uiOutput("animal"),
  checkboxGroupInput("opti", 
              label = "Option",
              choices = c("weight", "height"),
              selected = "weight")
  ),
mainPanel(plotOutput("graph"))
))

server <- function(input, output){
  animal <- read_excel('data/animals.xlsx', sheet =1)
  var <- reactive({
    switch(input$location,
       "New York" = list("Cat1", "Dog2"),
       "Philadelphia"= list("Cat4","Dog3"))
     })

  output$animal <- renderUI({
  checkboxGroupInput("anim", "Choose an animal",
                   var())
  })

output$graph <- renderPlot({
  if (length(input$anim)==1){
    p <- ggplot(subset(animal, Name %in% input$anim & Location %in% input$location), aes(x=date))
    if ("weight" %in% input$opti){
      p <- p + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
    }
    if ("height" %in% input$opti){
      p <- p + geom_line(aes(y=height)) + geom_point(aes(y=height))
    }
    print(p)
  }

  if (length(input$anim)==2){
    p1 <- ggplot(subset(animal, Name %in% input$anim[1] & Location %in% input$location), aes(x=date))
    p2 <- ggplot(subset(animal, Name %in% input$anim[2] & Location %in% input$location), aes(x=date))
    if ("weight" %in% input$opti){
      p1 <- p1 + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
      p2 <- p2 + geom_line(aes(y=weight)) + geom_point(aes(y=weight))
    }
    if ("height" %in% input$opti){
      p1 <- p1 + geom_line(aes(y=height)) + geom_point(aes(y=height))
      p2 <- p2 + geom_line(aes(y=height)) + geom_point(aes(y=height))
    }
    grid.arrange(p1,p2, ncol = 2)
  }
})
}
shinyApp(ui=ui, server= server)

A part of the data:

Location    Name    date    weight  height
New York    Cat1    Mar-16  34,20   22,50
New York    Cat1    Apr-16  35,02   23,02
New York    Cat1    May-16  35,86   23,55
New York    Cat1    Jun-16  36,72   24,09
New York    Dog2    Mar-16  33,55   22,96
New York    Dog2    Apr-16  33,62   23,42
New York    Dog2    May-16  33,68   23,89
New York    Dog2    Jun-16  33,75   24,37
Philadelphia    Cat4    Mar-16  20,33   16,87

Upvotes: 1

Views: 5623

Answers (1)

Michal Majka
Michal Majka

Reputation: 5471

I used this tooltip and customised it a little bit.

Your plots initially don't show up because you don't return any plot. I return an ggplot object p without calling print function.

In general, I heavily modified your code and this is the result:

enter image description here

As the function nearPoints needs the same dataset that you pass to ggplot, I had to create a new reactive, in which I did some subsetting and reshaping of your data.

Instead of grid.arrange to create two seperate plots I used facet_grid (and hence I had to transform the data). I also used colours to differentiate lines.

Everything works fine with the example data you provided.


Full example:

rm(ui)
rm(server)

library("shiny")
library("ggplot2")
library('readxl')
library('gridExtra')
library(reshape) # for "melt"

ui<- fluidPage(
  titlePanel("Animals"),
  sidebarLayout(
    sidebarPanel(
      helpText("Create graph of height and/or weight animals"),
      selectInput("location", 
                  label = "Choose a location",
                  choices = list("New York"="New York", "Philadelphia" = "Philadelphia"),
                  selected = "New York"),
      uiOutput("animal"),
      checkboxGroupInput("opti", 
                         label = "Option",
                         choices = c("weight", "height"),
                         selected = "weight")
    ),
    mainPanel(

      # this is an extra div used ONLY to create positioned ancestor for tooltip
      # we don't change its position
      div(
        style = "position:relative",
        plotOutput("graph", 
                   hover = hoverOpts("plot_hover", delay = 100, delayType = "debounce")),
        uiOutput("hover_info")
      )

    )
  ))

server <- function(input, output){

  animal <- read_excel('data/animals.xlsx', sheet =1)
  #animal <- read_excel("~/Downloads/test2.xlsx")
  var <- reactive({
    switch(input$location,
           "New York" = c("Cat1", "Dog2"),
           "Philadelphia"= c("Cat4","Dog3"))
  })

  output$animal <- renderUI({
    checkboxGroupInput("anim", "Choose an animal",
                       var())
  })


  output$graph <- renderPlot({
    req(input$anim, sub())

    if (length(input$anim) == 1) {
      p <- ggplot(sub(), aes(x = date, colour = variable))
      p <- p + geom_line(aes(y = value)) + 
               geom_point(aes(y = value)) +
        guides(colour = guide_legend(title = NULL))

      return(p) # you have to return the plot
    }

    if (length(input$anim) == 2) {

      p <- ggplot(sub(), aes(x = date, colour = variable)) +
        geom_line(aes(y = value)) + 
        geom_point(aes(y = value)) + 
        facet_grid(~ Name) + 
        guides(colour = guide_legend(title = NULL))

      return(p) # you have to return the plot
    }
  })

  observe({
    print(sub())
  })


  sub <- reactive({
    req(input$anim)

    if (length(input$anim) == 1) {

      df <- animal[animal$Name %in% input$anim & animal$Location %in% input$location, ]
      df <- melt(as.data.frame(df), measure.vars = c("weight", "height"))
      df <- subset(df, df$variable %in% input$opti)
      return(df)
    }

    if (length(input$anim) == 2) {
      df <- animal[animal$Name %in% input$anim & animal$Location %in% input$location, ]
      df$Name <- factor(df$Name)
      df <- melt(as.data.frame(df), measure.vars = c("weight", "height"))
      df <- subset(df, df$variable %in% input$opti)
      return(df)
    }
  })

  output$hover_info <- renderUI({
    hover <- input$plot_hover
    point <- nearPoints(sub(), hover, threshold = 5, maxpoints = 1, addDist = TRUE)

    if (nrow(point) == 0) return(NULL)

    left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left)
    top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom)

    left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left)
    top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top)

    style <- paste0("position:absolute; z-index:100; background-color: rgba(245, 245, 245, 0.85); ",
                    "left:", left_px + 2, "px; top:", top_px + 2, "px;")

    wellPanel(
      style = style,
      p(HTML(paste0("<b>", point$variable, ": </b>", point$value)))
    )
  })


}
shinyApp(ui = ui, server = server)

Upvotes: 6

Related Questions