Add a box for the NA values to the ggplot legend for a continuous map
Asked Answered
S

2

19

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). 
Scrawl answered 21/2, 2017 at 10:59 Comment(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")))Exterminatory
@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.Straddle
S
31

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

Straddle answered 22/2, 2017 at 1:18 Comment(5)
Thanks for your answer, but unfortunately it's not what i wanted. I tried this by myself before but it is important for me to have a gradient in the legend and no groups.Scrawl
@user5514978, I posted another approach that should be pretty close to what you are looking for.Straddle
Hey, that's cool. Thank's a lot for your effort! Especially for your second look, even when you already gave an answer.Scrawl
Hey, that's cool, but when I reproduce your second example the "No data" box sometimes appears below and sometimes above the legend gradient. I have no idea how to control it. Any suggestion?Bodine
@Bodine checkout the order argument of +guide(fill=guide_legend(order =Notogaea
D
0

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?

Dielectric answered 21/2, 2017 at 12:8 Comment(3)
Do you have an example? The warning also appears without guides, because of size = "NA", but when I remove this or change it to a number, my legend doesn't look like it should as before.Scrawl
Sorry for the delay...are you treating your NA as a factor?Dielectric
In addition to the example given above see: r-bloggers.com/custom-legend-in-r/ampDielectric

© 2022 - 2024 — McMap. All rights reserved.