12

Let's say I have a data.frame like this:

molten <- data.frame(
  Var1 = factor(
    rep(c("A", "B", "C", "D"), 5),
    levels = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J")
  ),
  Var2 = rep(6:10, each = 4L),
  value = c(
    -0.468920099229389, 0.996105987531978, -0.527496444770932, -0.767851702991822,
    -0.36077954422072, -0.145335912847538, 0.114951323188032, 0.644232124274217,
    0.971443502096584, 0.774515290180507, -0.436252398260595, -0.111174676975868,
    1.16095688943808, 0.44677656465583, -0.708779168274131, 0.460296447139761,
    -0.475304748445917, -0.481548436194392, -1.66560630161765, -2.06055347675196
  ),
  na = rep(c(FALSE, TRUE, FALSE, TRUE, FALSE), c(8L, 1L, 7L, 1L, 3L)),
  row.names = c(
    51L, 52L, 53L, 54L, 61L, 62L, 63L, 64L, 71L, 72L, 73L, 74L,
    81L, 82L, 83L, 84L, 91L, 92L, 93L, 94L
  )
)

head(molten)

  Var1 Var2      value    na
1    A    1 -0.2413015 FALSE
2    B    1  1.5077282 FALSE
3    C    1 -1.0798806 TRUE
4    D    1  2.0723791 FALSE

Now, I want to plot a tile (or raster) plot using ggplot and mark those tiles which have na=TRUE. Currently I plot the marks as points:

g <- ggplot( molten ) +
  geom_raster( aes( x = Var1, y = Var2, fill = value )  ) + 
  scale_fill_gradient2( low = "blue", high = "red", na.value="black", name = "" ) +
  geom_point( aes( x = Var1, y = Var2, size= as.numeric(na) ) )

tiles with points

However, I don't like this plot very much for two reasons:

  1. There is still a point drawn even if molten$na = FALSE. Sure I could specify data=molten[ molten$na, ], but actually this should be possible without specifying another data set.
  2. I don't like the points, but would rather like to have frames around or stripes through the tiles. But I have no idea how to achieve this. If I would use geom_segment() for stripes, how would I specify yendand xend?
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
Beasterfield
  • 7,023
  • 2
  • 38
  • 47
  • "but actually this should be possible without specifying another data set" Why? Specifying separate data sets for different layers is a huge feature of ggplot, and is precisely how one is supposed to do what you want (for (1) at least). – joran Nov 06 '12 at 20:07
  • How about `size = ifelse(na, 1, NA)` instead of `size = as.numeric(na)`? – bdemarest Nov 06 '12 at 20:17
  • @bdemarest because it simply doesn't work: `geom_point( aes( x = Var1, y = Var2, size = ifelse( na, 1, NA ) ) )` gives me a point in each tile. – Beasterfield Nov 06 '12 at 20:32
  • @joran if you say that this is how it is supposed to work thats fine, I was just expecting to get it work without subsetting and thought I have missed something. – Beasterfield Nov 06 '12 at 20:35
  • @Beasterfield: Sorry! I didn't test it. I would be happy to try a few other things if you could make your example reproducible. Maybe you could give us the output of `dput(subset(molten, Var2 > 5 & Var1 %in% c("A", "B", "C", "D")))`? – bdemarest Nov 06 '12 at 20:47
  • @bdemarest I thoght my example was without `dput`reproducible. Anyways, see my edit. – Beasterfield Nov 06 '12 at 20:57

2 Answers2

18

Here are two possible approaches:

In Example 1, I used ifelse and scale_size_manual to control whether a point is plotted in each cell.

In Example 2, I created a small auxiliary data.frame and used geom_rect to plot a rectangle instead of a dot. For convenience, I converted Var2 to factor. In ggplot2, each step along a discrete/factor axis is length 1.0. This allows easy computation of the values for geom_rect.

# Using ggplot2 version 0.9.2.1
library(ggplot2)

# Test dataset from original post has been assigned to 'molten'.

molten$Var2 = factor(molten$Var2)

# Example 1.
p1 = ggplot(data=molten, aes(x=Var1, y=Var2, fill=value)) +
     geom_raster() +
     scale_fill_gradient2(low="blue", high="red", na.value="black", name="") +
     geom_point(aes(size=ifelse(na, "dot", "no_dot"))) +
     scale_size_manual(values=c(dot=6, no_dot=NA), guide="none") +
     labs(title="Example 1")

ggsave(plot=p1, filename="plot_1.png", height=3, width=3.5) 

enter image description here

# Example 2.
# Create auxiliary data.frame.
frames = molten[molten$na, c("Var1", "Var2")]
frames$Var1 = as.integer(frames$Var1)
frames$Var2 = as.integer(frames$Var2)

p2 = ggplot(data=molten) +
     geom_raster(aes(x=Var1, y=Var2, fill=value)) +
     scale_fill_gradient2(low="blue", high="red", na.value="black", name="") +
     geom_rect(data=frames, size=1, fill=NA, colour="black",
       aes(xmin=Var1 - 0.5, xmax=Var1 + 0.5, ymin=Var2 - 0.5, ymax=Var2 + 0.5)) +
     labs(title="Example 2")

ggsave(plot=p2, filename="plot_2.png", height=3, width=3.5) 

enter image description here

bdemarest
  • 14,397
  • 3
  • 53
  • 56
  • The second answer is what I was actually looking for and which should work with `geom_segement()` for stripes as well. Thanks for that. Unfortunately, the solution doesn't work for me, since group of columns are faceted by `facet_wrap( ..., scale= "free_x" )`. Nevermind, the question was not detailed enough, so I accept your answer. – Beasterfield Nov 07 '12 at 11:58
  • @Beasterfield How do you change the colour of the point, to something else than black? – user2300940 May 27 '21 at 08:03
6

As @joran suggested in the comments, you can pass a subset of the data to a particular layer.

Using your example data

g <- ggplot( molten ) +
  geom_raster( aes( x = Var1, y = Var2, fill = value )  ) + 
  scale_fill_gradient2( low = "blue", high = "red", na.value="black", name = "" ) +
  geom_point(data = molten[molten$na,], aes( x = Var1, y = Var2, size= as.numeric(na) ) )


g

enter image description here

If you wanted the legend to say something about what the dots signify

 g <- ggplot( molten ) +
  geom_raster( aes( x = Var1, y = Var2, fill = value )  ) + 
  scale_fill_gradient2( low = "blue", high = "red", na.value="black", name = "" ) +
  geom_point(data = molten[molten$na,], aes( x = Var1, y = Var2, colour = 'black' )) +
  scale_colour_manual(name = 'Ooh look', values = 'black', labels = 'Something cool')

enter image description here

mnel
  • 113,303
  • 27
  • 265
  • 254