Hot questions for Using Ggplot2 in ggiraph

Question:

Here is my code:

library(shiny)
library(ggplot2)
library(ggiraph)

df <- data.frame(achseX = LETTERS[1:24], achseY = 1:24, facetX = as.factor(rep(1:4, each = 6)))

server <- function(input, output) {
  output$ggplot <- renderPlot({
    ggplot(data = df) + geom_bar_interactive(aes(tooltip = achseY, x = achseX, y = achseY), stat = "identity") +
      theme_minimal() + facet_grid(.~ facetX, scales = "free_x")
  })


  output$plot <- renderggiraph({
    gg <- ggplot(data = df) + geom_bar_interactive(aes(tooltip = achseY, x = achseX, y = achseY), stat = "identity") +
      theme_minimal() + facet_grid(.~ facetX, scales = "free_x")
    return(ggiraph(code = print(gg), selection_type = "multiple", zoom_max = 4,
                   hover_css = "fill:#FF3333;stroke:black;cursor:pointer;",
                   selected_css = "fill:#FF3333;stroke:black;"))
  })
}

ui <- fluidPage(
  "GGPLOT2:",
  plotOutput("ggplot"),
  "GGIRAPH:",
  ggiraphOutput("plot", width = "500px", height = "1000px")
)

shinyApp(ui = ui, server = server)

The result looks like:

As you can see in the code, the first barplot is a ggplot that works the way it should. It is responsive to the site and has a rectangular format. The ggiraph stays in a square format instead and doesn't fit the page.

How can I get the ggiraph to look like the ggplot?

I tried several combinations of the width and height argument, also including width = "auto" and height = "auto". This made the ggiraph fit the page, but still in a square format.


Answer:

You can make the ui responsive with some amount of js code in it. Something along the lines of this answer.

The difference is that the ggiraph function wants the input in inches so we will need to convert pixels into inches. The formula for that is inches = pixels/dpi. So, the js code in the ui passes the window height and with along with the dpi of the screen from which we can calculate the length in inches which can be then passed to the ggiraph function and hence make the plot responsive to the ui.

I have modified your example to do exactly that. Hope it helps!

 library(shiny)
 library(ggplot2)
 library(ggiraph)

 df <- data.frame(achseX = LETTERS[1:24], achseY = 1:24, facetX = as.factor(rep(1:4, each = 6)))

 server <- function(input, output, session) {
   output$ggplot <- renderPlot({
     ggplot(data = df) + geom_bar_interactive(aes(tooltip = achseY, x = achseX, y = achseY), stat = "identity") +
       theme_minimal() + facet_grid(.~ facetX, scales = "free_x")
   })


   output$plot <- renderggiraph({
     gg <- ggplot(data = df) + geom_bar_interactive(aes(tooltip = achseY, x = achseX, y = achseY), stat = "identity") +
       theme_minimal() + facet_grid(.~ facetX, scales = "free_x")
     return(ggiraph(code = print(gg), selection_type = "multiple", zoom_max = 4,
                    hover_css = "fill:#FF3333;stroke:black;cursor:pointer;",
                    selected_css = "fill:#FF3333;stroke:black;", 
                    width_svg = (0.8*input$pltChange$width/input$pltChange$dpi),
                    height_svg = (0.5*input$pltChange$height/input$pltChange$dpi)
                    ))
   })
 }

 ui <- fluidPage(

   tags$body(tags$div(id="ppitest", style="width:1in;visible:hidden;padding:0px")),

   tags$script('$(document).on("shiny:connected", function(e) {
                                    var w = window.innerWidth;
                                    var h = window.innerHeight;
                                    var d =  document.getElementById("ppitest").offsetWidth;
                                    var obj = {width: w, height: h, dpi: d};
                                    Shiny.onInputChange("pltChange", obj);
                                });
                                $(window).resize(function(e) {
                                    var w = $(this).width();
                                    var h = $(this).height();
                                    var d =  document.getElementById("ppitest").offsetWidth;
                                    var obj = {width: w, height: h, dpi: d};
                                    Shiny.onInputChange("pltChange", obj);
                                });
                            '),


   "GGPLOT2:",
   plotOutput("ggplot"),
   "GGIRAPH:",
   ggiraphOutput("plot")
 )

 shinyApp(ui = ui, server = server) 

Question:

The new ggiraph package is really great for making ggplot2 reactive to users' mouse, no problems.

However I'm having trouble getting this to work properly with ggmap() for interactive/reactive longitude and latitude points.

The interactivity works fine, I can get tooltip response when the mouse is hovered a point, but there seems to be a problem with the scale or axis when using it with ggmap().

The code chunks below provide a reproducible example of the problem that I am trying to solve, and there are also some images linked to illustrate what I mean.

First install required packages, then make up small example data set, and then download required map using get_map() function:

#Install required_packages:
required_packages <- c("ggmap", "ggplot2", "ggiraph")
new.packages <- required_packages[!(required_packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
#Load required_packages:
lapply(required_packages, require, character.only = TRUE)

#Make small example data set:
suburb<-c("BURNLEY","COLLINGWOOD","MALVERN","PRAHRAN","RICHMOND","SOUTH YARRA","ST KILDA","ST KILDA WEST","TOORAK","WINDSOR")
lon<-c(145.0176466,144.98815,145.036,144.998,144.998,144.989,144.982,144.9732,145.018,144.988)
lat<-c(-37.8299258, -37.8019,-37.857,-37.852,-37.823,-37.84,-37.864,-37.8604,-37.841,-37.854)
`Change 2005-2015 (%)`<-c(112, 120, 136, 127, 122, 115, 110, 146, 120, 128)
df<-data.frame(suburb, lon, lat, `Change 2005-2015 (%)`)

#Download map from google maps
SOUTH_YARRA <- get_map(location = 'South Yarra, Australia', zoom = 13, maptype="terrain")

Now I have no problem using the code below to create the static map:

ggmap(SOUTH_YARRA) + 
    geom_point(data = df, 
               aes(x =lon, y= lat, size=`Change 2005-2015 (%)`, colour = `Change 2005-2015 (%)`),
               alpha=0.75) +  
    scale_colour_gradientn(colours=rainbow(5)) +
    scale_radius (range = c(6, 12), trans = "identity", guide = "legend") +
    ggtitle("Total change in Median \n House Price (%) from 2005-2015 \n")

here is the static map produced by the code above - no problem

However, when I use ggiraph's geom_point_interactive() to make the points in the map react to the user's mouse hover, a problem with the scale or axis occurs:

#Try add reactivity using ggiraph's geom_point_interactive() instead of geom_point()
interactive_map<-ggmap(SOUTH_YARRA) + 
    geom_point_interactive(data = df, 
                           aes(x =lon, y= lat, size=`Change 2005-2015 (%)`, colour = `Change 2005-2015 (%)`, tooltip=suburb, data_id = suburb),
                           alpha=0.75) +  
    scale_colour_gradientn(colours=rainbow(5)) +
    scale_radius (range = c(6, 12), trans = "identity", guide = "legend") +
    ggtitle("Total change in Median Melbourne \n House Price (%) from 2005-2015 \n")

ggiraph(code = {print(interactive_map)}, zoom_max = 5,
        tooltip_offx = 20, tooltip_offy = -10, 
        hover_css = "fill:black;",
        tooltip_opacity = 0.7)

here is a still image of my scale/axis problem produced by the code above. Note the the code above makes the tooltip reactivity works fine for mouse hover, it's just this scale problem that I need to resolve

I've tried changing the maprange, extent, and base_layer arguments to the ggmap() function, e.g.:

ggmap(SOUTH_YARRA, maprange=TRUE, extent = "panel", base_layer = ggplot(data = df, aes(x =lon, y= lat)))

However this hase not helped. ggiraph is an awesome package imho, since it is new there are not yet much on stackoverflow etc for it. Any help would be really appreciated!


Answer:

This was a bug in the ggiraph package, and the package maintainer has just fixed it, see the github bug report here for more details: https://github.com/davidgohel/ggiraph/issues/32#issuecomment-257869888

Question:

I wanted to add tooltip to ggsurvplot. I am using ggiraph to display the plot. Since ggiraph does not have geom_step_interactive, I am manipulating my the data output of ggsurvplot to get the step function values and using geom_line_interactive to overlay a line on my ggsurvplot and add a tooltip. Here is the code that is working correctly:

library(ggiraph)
library(survminer)
library(survival)

  #Function to get the step function points
  step_func <- function(data, direction="hv", x , y) {
    direction <- match.arg(direction, c("hv", "vh"))
    data <- as.data.frame(data)[order(data[, x]), ]
    n <- nrow(data)

    if (n <= 1) {
      # Need at least one observation
      return(data[0, , drop = FALSE])
    }

    if (direction == "vh") {
      xs <- rep(1:n, each = 2)[-2*n]
      ys <- c(1, rep(2:n, each = 2))
    } else {
      ys <- rep(1:n, each = 2)[-2*n]
      xs <- c(1, rep(2:n, each = 2))
    }

    return(data.frame(
      x = data[,x][xs],
      y = data[,y][ys],
      data[xs, setdiff(names(data), c(x, y))]
    ))
  }



  fit<- survfit(Surv(time, status) ~ sex, data = lung )
  g <- ggsurvplot(fit, data = lung, risk.table = TRUE,
                  risk.table.pos= "out", risk.table.y.text = TRUE)

  dat1 <- plyr::ddply(g$plot$data, "strata", step_func, "hv", "time", "surv")
  dat1$tooltip <- paste0("Time:", dat1$x)
  gg<- g$plot+geom_line_interactive(data = dat1, aes(x= x, y=y, colour = strata, tooltip= tooltip ), size = .75)
  ggiraph(code = print(gg))

With the above code I get the following plot with tooltip as displayed in the image:

I want the tooltip not only show the time but also the survival probability, for that I change the code slightly as follows:

dat1$tooltip <- paste0("Time:", dat1$x, "Surv:", dat1$y )

This causes the tooltip to disappear.

What am I doing wrong?


Answer:

It seems the geom_line_interactive tooltips vanish when they get too long. You can truncate the values result to keep them short. The following works:

dat1$tooltip <- paste0("Time:", dat1$x, " Surv:", round(dat1$y,2) )

but setting the value to 3 causes them to vanish. I am not completely sure of this because the tooltips are extremely fiddly and seemingly only appear when the the mouse is in exactly the right place. It could be that all of them over a certain length are suppressed.

I did notice however that the whole tooltip thing works much better if you use geom_point_interactive instead of geom_line_interactve and with larger size parameter values. That is probably what you want to do.

Could be that geom_line_interactive is ignoring the size parameter.

This figure is with geom_point_interactive, the round parameter above set to 3, and the size set to 1.75, and it works much better.

Question:

I am trying to add tooltip to a ggsurvplot. I am using ggiraph to display the plot. I have added a minimal example of what I am trying to do below. If I don't add any xlim in ggsurvplot my code seems to work. The following is the code that works:

library(survminer)
library(survival)
library(ggiraph)

fit<- survfit(Surv(time, status) ~ sex, data = lung )
g <- ggsurvplot(fit, data = lung)   
tooltip <- paste0("Time:", g$plot$data$time, "<br/>", "Survival Prob:", g$plot$data$surv)
x <- g$plot+geom_point_interactive(aes(tooltip = tooltip))
ggiraph(print(x), zoom_max = 5)

Now I want to limit the x-axis values so I add the parameter xlim = c(0, 800) to ggsurvplot as shown below:

library(survminer)
library(survival)
library(ggiraph)

fit<- survfit(Surv(time, status) ~ sex, data = lung )
g <- ggsurvplot(fit, data = lung, xlim = c(0,800))  
tooltip <- paste0("Time:", g$plot$data$time, "<br/>", "Survival Prob:", g$plot$data$surv)
x <- g$plot+geom_point_interactive(aes(tooltip = tooltip))
ggiraph(print(x), zoom_max = 5)

This gives me the following error:

Error: length(ids) == length(str) is not TRUE

How can I fix this error?


Answer:

That's an issue with ggiraph. Adding an xlim is triggering clipping, some points will not be drawn, but the tooltip variable is not clipped; I will try to solve that.

The workaround is to filter the data before sending them to geom_point_interactive:

library(survminer)
library(survival)
library(ggiraph)


fit<- survfit(Surv(time, status) ~ sex, data = lung )
g <- ggsurvplot(fit, data = lung, xlim = c(0,800))  

data_ <- g$plot$data
data_ <- base::transform(data_, tooltip = sprintf("Time: %.0f<br/>Survival Prob: %.3f", time, surv) )
data_ <- data_[data_$time < 800, ]

x <- g$plot + geom_point_interactive(data = data_, aes(time, surv, tooltip = tooltip))
ggiraph(print(x), zoom_max = 5)