Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

geom_tile : Clean Diagonal Tiles Border

Tags:

r

ggplot2

I have a heatmap created with geom_tile on which the x and y values are the same and ordered the same way. I would like to put black borders around the tiles in the diagonal of the chart.

set.seed(42L)
data <- data.frame(x = rep(letters[1:3], each = 3L), 
                   y = rep(letters[1:3], 3L), 
                   fill = rnorm(9L))

My option is to use the color aes with a variable set to TRUE or NA and using scale_color_manual to remove the grey borders for NA values.

data$diag <- data$x == data$y
data$diag[!data$diag] <- NA

ggplot(data, aes(x = x, y = y, fill = fill)) +
  geom_tile(aes(color = diag), size = 2) +
  scale_color_manual(guide = FALSE, values = c(`TRUE` = "black"))

enter image description here

But the render is not that clean, the border seems a bit overlayed by the "invisible" NA borders.

How can I improve my chart ? Is there another method to do it ? Thank You

like image 332
Julien Navarre Avatar asked Nov 09 '18 15:11

Julien Navarre


2 Answers

It seems that the borders are overlayed not by the "invisible" NA borders, which really don't exist, but rather by the tiles themselves. Meaning that we are dealing with the order of layers. What we can try then is first to add a plain geom_tile and then another geom_tile with borders using only the data for the diagonal tiles. That is,

ggplot(data, aes(x = x, y = y, fill = fill)) + geom_tile() +
  geom_tile(data = data[!is.na(data$diag), ], aes(color = diag), size = 2) +
  scale_color_manual(guide = FALSE, values = c(`TRUE` = "black"))

enter image description here

Your question relates to another one, where all the tiles had a border and the goal was for them not to overlap. Then tile sizes can be adjusted as to create space for borders. In this case, however, by adjusting tile sizes we would create some gaps, which makes things look worse.

ggplot(data, aes(x = x, y = y, fill = fill)) +
  geom_tile(aes(color = diag), width = 0.98, height = 0.98, size = 2) +
  scale_color_manual(guide = FALSE, values = c(`TRUE` = "black"))

enter image description here

On the other hand, it agrees with the idea that there is no way to adjust the order of layers in the same single geom_tile call.

Lastly, another alternative inspired by @utubun's answer.

data$diag <- data$x == data$y
ggplot(data[order(data$diag), ], aes(x = x, y = y, fill = fill)) +
  geom_tile(aes(color = diag), size = 2) +
  scale_color_manual(guide = FALSE, values = c(`TRUE` = "black", `FALSE` = NA))

In this case we sort the data inside the call, but also need to specify that FALSE borders should be invisible.

like image 180
Julius Vainora Avatar answered Oct 24 '22 02:10

Julius Vainora


You can reach the desired result without a second geom_tile() call by changing the ordering of the original data.frame (ensuring that the tiles with borders are drawn the last):

set.seed(42L)

dat <- data.frame(
    x    = rep(letters[1:3], each  = 3L), 
    y    = rep(letters[1:3], times = 3L), 
    fill = rnorm(9L)
  ) |>
  dplyr::mutate(isdiag = (x == y)) |>
  dplyr::arrange(isdiag)

  ggplot(dat, aes(x = x, y = y, fill = fill)) +
      geom_tile(size = 2, colour = dat$isdiag) +
      theme_bw() +
      theme(axis.title = element_blank())

tiles

like image 6
utubun Avatar answered Oct 24 '22 01:10

utubun