10

I am trying to convert a geom_tile plot built with ggplot to ggplotly. However, the tiles are distorted in plotly. The same issues takes place with geom_raster.

Showcase:

library(ggplot2)
library(plotly)
set.seed(1)
n <- 10
X <- data.frame(xcoord = sample(1:10, n, replace = TRUE),
                ycoord = sample(1:10, n, replace = TRUE),
                value = runif(n))
gg <- ggplot(X) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg)

enter image description here

2

1 Answer 1

8
+50

Looking at the plotly code here (excerpt below), it seems that the raster is only defined for any values of x and y available in the dataset - and whatever happens in between is up the the rest of the plotly code.

geom2trace.GeomTile <- function(data, params, p) {
  x <- sort(unique(data[["x"]]))
  y <- sort(unique(data[["y"]]))
  # make sure we're dealing with a complete grid
  g <- expand.grid(x = x, y = y)
  g$order <- seq_len(nrow(g))
  g <- merge(g, data, by = c("x", "y"), all.x = TRUE)
  g <- g[order(g$order), ]
...

For the example data this code generates the following data, where gray areas are NA, and blank areas are simply undefined. And all the distortions/stretching happens in the undefined areas.

ggplot(g, aes(x = x, y = y, fill = value)) + geom_tile()

enter image description here

With this, one possible workaround (outside the plotly package) would be to manually ensure there are (NA) data available across the whole x/y range, so expand.grid generates a sufficiently dense grid when the plot is translated to plotly.

set.seed(1)
X1 <- data.frame(xcoord = c(sample(1:10, n, replace = TRUE), 1:10),
                ycoord = c(sample(1:10, n, replace = TRUE), 1:10),
                value = c(runif(n), rep(NA, 10)))
gg1 <- ggplot(X1) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg1)

enter image description here

Update

While the example above demonstrates it is sufficient to have a single value for any x and y in the dataset, I'll also add an arguably cleaner solution as suggested by Waldi in the comments. By (automatically) generating the full grid in advance, there is less reliance on quirks from the plotly translation. For grid spacing different than 1, the sequence of course needs to be adjusted.

# X: original dataframe as defined in the question
X2 <- tidyr::expand_grid(
  xcoord = seq(min(X$xcoord), max(X$xcoord)), 
  ycoord = seq(min(X$ycoord),max(X$ycoord))
  ) %>%    
  dplyr::left_join(X, by=c('xcoord','ycoord'))
gg2 <- ggplot(X2) + geom_tile(aes(x = xcoord, y = ycoord, fill = value))
ggplotly(gg2)

enter image description here

2
  • 1
    automatic grid generation, as described here : X <- tidyr::expand_grid(xcoord = seq(min(X$xcoord),max(X$xcoord)), ycoord = seq(min(X$ycoord),max(X$ycoord))) %>% dplyr::left_join(X,by=c('xcoord','ycoord'))
    – Waldi
    Commented Feb 22, 2022 at 7:44
  • Thanks @Waldi, I've added this to the answer.
    – pholzm
    Commented Feb 22, 2022 at 17:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.