16
votes

I have got a map with a legend gradient and I would like to add a box for the NA values. My question is really similar to this one and this one. Also I have read this topic, but I can't find a "nice" solution somewhere or maybe there isn't any?

Here is an reproducible example:

library(ggplot2)
map <- map_data("world")
map$value <- setNames(sample(-50:50, length(unique(map$region)), TRUE), 
                      unique(map$region))[map$region]
map[map$region == "Russia", "value"] <- NA
ggplot() +
  geom_polygon(data = map,
               aes(long, lat, group = group, fill = value)) +
  scale_fill_gradient2(low = "brown3", mid = "cornsilk1", high = "turquoise4",
                       limits = c(-50, 50),
                       na.value = "black")

So I would like to add a black box for the NA value for Russia. I know, I can replace the NA's by a number, so it will appear in the gradient and I think, I can write a workaround like the following, but all this workarounds do not seem like a pretty solution for me and also I would like to avoid "senseless" warnings:

ggplot() +
  geom_polygon(data = map,
               aes(long, lat, group = group, fill = value)) +
  scale_fill_gradient2(low = "brown3", mid = "cornsilk1", high = "turquoise4",
                       limits = c(-50, 50),
                       na.value = "black") +
  geom_point(aes(x = -100, y = -50, size = "NA"), shape = NA, colour = "black") +
  guides(size = guide_legend("NA", override.aes = list(shape = 15, size = 10)))
Warning messages:
1: Using size for a discrete variable is not advised. 
2: Removed 1 rows containing missing values (geom_point). 
2
I unfortunately miss reputation points to comment posted answers, but I think there is a little mistake in the top answer from @bdemarest: for the "Another solution", in the code line guides(colour=guide_legend("No data", override.aes=list(colour="black"))). in override.aes, colour should not be used. It should be fill otherwise the color of the box will be black by default I guess, and with colouryou just change the colorbox contour to black. So I suggest a line replacement in the answer as followed: guides(colour=guide_legend("No data", override.aes=list(fill="black")))Yoann Pageaud
@YoannPageaud, Yes, good point. I did some experimenting while testing your proposed change. I found that the entire line guides(...) is not really needed. The same plot is created after removing that line and changing the previous line to scale_colour_manual(values=NA, name="No data"). It's possible that I was just mistaken, or that changes to ggplot2 in the last 2 years are responsible.bdemarest

2 Answers

25
votes

One approach is to split your value variable into a discrete scale. I have done this using cut(). You can then use a discrete color scale where "NA" is one of the distinct colors labels. I have used scale_fill_brewer(), but there are other ways to do this.

map$discrete_value = cut(map$value, breaks=seq(from=-50, to=50, length.out=8))

p = ggplot() +
    geom_polygon(data=map, aes(long, lat, group=group, fill=discrete_value)) +
    scale_fill_brewer(palette="RdYlBu", na.value="black") +
    coord_quickmap()

ggsave("map.png", plot=p, width=10, height=5, dpi=150)   

enter image description here

Another solution

Because the original poster said they need to retain the color gradient scale and the colorbar-style legend, I am posting another possible solution. It has 3 components:

  1. We need to trick ggplot into drawing a separate color scale by using aes() to map something to color. I mapped a column of empty strings using aes(colour="").
  2. To ensure that we do not draw a colored boundary around each polygon, I specified a manual color scale with a single possible value, NA.
  3. Finally, guides() along with override.aes is used to ensure the new color legend is drawn as the correct color.

p2 = ggplot() +
     geom_polygon(data=map, aes(long, lat, group=group, fill=value, colour="")) +
     scale_fill_gradient2(low="brown3", mid="cornsilk1", high="turquoise4",
                     limits=c(-50, 50), na.value="black") +
     scale_colour_manual(values=NA) +              
     guides(colour=guide_legend("No data", override.aes=list(colour="black")))

ggsave("map2.png", plot=p2, width=10, height=5, dpi=150)   

enter image description here

0
votes

It's possible, but I did it years ago. You can't use guides. You have to set individually the continuous scale for the values as well as the discrete scale for the NAs. This is what the error is telling you and this is how ggplot2 works. Did you try using both scale_continuous and scale_discrete since your set up is rather awkward, instead of simply using guides which is basically used for simple plot designs?