Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I keep 0 values in ggplot's legend after log transformation?

Tags:

r

ggplot2

How can I add on the graph legend an NA or "0" label if I used trans="log" in the scale_fill_viridis or another continuous scale

> d1
       persona num.de.puntos puntos
    1       p1             1      3
    2       p1             2      4
    3       p1             3      0
    4       p1             4      4
    5       p1             5      2
    6       p2             1      2
    7       p2             2      3
    8       p2             3      0
    9       p2             4      0
    10      p2             5      4
    11      p3             1      0
    12      p3             2      1
    13      p3             3      0
    14      p3             4      5
    15      p3             5      8

p <- ggplot(d1, aes(persona, num.de.puntos, fill = puntos)) + 
  scale_fill_viridis(trans="log", option ="D", direction=1, na.value = "gray50", 
                     breaks=c(0,1,5,8),
                     name="Number of people",
                     guide=guide_legend(label.position = "bottom", 
                                        title.position = 'top', 
                                        nrow=1,
                                        label.theme = element_text(size = 6, 
                                                                   face = "bold", 
                                                                   color = "black"),
                                        title.theme = element_text(size = 6, 
                                                                   face = "bold", 
                                                                   color = "black"))) +
  geom_tile(colour="grey", show.legend = TRUE)

p

enter image description here

I want

enter image description here

like image 236
Lizbeth Montes Avatar asked Mar 04 '19 16:03

Lizbeth Montes


People also ask

How do you deal with zeros in a log plot?

The logarithm of zero is not defined -- its mathematically impossible to plot zero on a log scale. Instead of entering zero, you can enter a low value (say -10 on the log scale), and then use custom ticks to label the graph correctly (so it is labeled "0" rather than "-10".

What does %>% do in Ggplot?

%>% is a pipe operator reexported from the magrittr package. Start by reading the vignette. Adding things to a ggplot changes the object that gets created. The print method of ggplot draws an appropriate plot depending upon the contents of the variable.

How do I add a figure legend in ggplot2?

You can place the legend literally anywhere. To put it around the chart, use the legend. position option and specify top , right , bottom , or left . To put it inside the plot area, specify a vector of length 2, both values going between 0 and 1 and giving the x and y coordinates.

How do you increase the size of points in Legend of ggplot2?

To change the Size of Legend, we have to add guides() and guide_legend() functions to the geom_point() function.


1 Answers

Note: Code below is run in R 3.5.1 and ggplot2 3.1.0. You might be using an older version of the ggplot2 package, since your code uses scale_fill_viridis instead of scale_fill_viridis_c.

TL;DR solution:

I'm pretty sure this is not orthodox, but assuming your plot is named p, setting:

p$scales$scales[[1]]$is_discrete <- function() TRUE

will get the NA value in your legend without changing any of the existing fill aesthetic mappings.

Demonstration:

p <- ggplot(d1, aes(persona, num.de.puntos, fill = puntos)) + 
  scale_fill_viridis_c(trans="log", option ="D", direction=1, 
                       na.value = "gray50",                   # optional, change NA colour here
                       breaks = c(0, 1, 5, 8),
                       labels = c("NA label", "1", "5", "8"), # optional, change NA label here
                       name="Number of people",
                       guide=guide_legend(label.position = "bottom", 
                                          title.position = 'top', 
                                          nrow=1,
                                          label.theme = element_text(size = 6, 
                                                                     face = "bold", 
                                                                     color = "black"),
                                          title.theme = element_text(size = 6, 
                                                                     face = "bold", 
                                                                     color = "black"))) +
  geom_tile(colour = "grey", show.legend = TRUE)

p # no NA mapping

p$scales$scales[[1]]$is_discrete <- function() TRUE

p # has NA mapping

without NA mapping

with NA mapping

Explanation:

I dug deep into the plotting mechanics by converting p into a grob object (via ggplotGrob), & running debug() on functions that affect the legend generation part of the plotting process.

After debugging through ggplot2:::ggplot_gtable.ggplot_built, ggplot2:::build_guides, and ggplot2:::guides_train, I got to ggplot2:::guide_train.legend, an un-exported function from the ggplot2 package:

> ggplot2:::guide_train.legend
function (guide, scale, aesthetic = NULL) 
{
    breaks <- scale$get_breaks()
    if (length(breaks) == 0 || all(is.na(breaks))) {
        return()
    }
    key <- as.data.frame(setNames(list(scale$map(breaks)), aesthetic %||% 
        scale$aesthetics[1]), stringsAsFactors = FALSE)
    key$.label <- scale$get_labels(breaks)
    if (!scale$is_discrete()) {
        limits <- scale$get_limits()
        noob <- !is.na(breaks) & limits[1] <= breaks & breaks <= 
            limits[2]
        key <- key[noob, , drop = FALSE]
    }
    if (guide$reverse) 
        key <- key[nrow(key):1, ]
    guide$key <- key
    guide$hash <- with(guide, digest::digest(list(title, key$.label, 
        direction, name)))
    guide
}

I saw that up until the key$.label <- scale$get_labels(breaks) step, key looks like this:

> key
     fill   .label
1  gray50 NA label
2 #440154        1
3 #72CC59        5
4 #FDE725        8

But scale$is_discrete() is FALSE, so !scale$is_discrete() is TRUE, and the row in key corresponding to the NA value gets dropped in the next step, since there is an NA value in the fill scale's breaks after log transformation:

> scale$get_breaks()
[1]       NA 0.000000 1.609438 2.079442

Thus, if we can have scale$is_discrete() evaluate to TRUE instead of FALSE, this step would be skipped, and we end up with the full legend including the NA value.

like image 165
Z.Lin Avatar answered Nov 14 '22 17:11

Z.Lin