knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(scales) library(signs) library(dplyr) library(ggplot2) library(ggrepel)
Using signs
is simple, especially if you're familiar
with functions like number()
, number_format()
,
comma()
, comma_format()
, percent()
, and percent_format()
from the scales
package.
It simply provides two new functions to complement these:
signs()
and signs_format()
.
theme_set(theme_gray()) theme_update( panel.grid.minor = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank() ) p <- ggplot(sleep) + aes(group, extra) + geom_point() + xlab("Drug") + ylab("Extra Sleep (hours)") label_hours <- function(mapping) { geom_text_repel( mapping, nudge_x = -.1, direction = "y", segment.size = .4, segment.color = "grey75", hjust = "right" ) } p + label_hours( aes( label = case_when( group == 1 ~ signs(extra, accuracy = .1), group == 2 ~ number(extra, accuracy = .1) ) ) )
You can use any formatting function with signs::signs()``r "\u2014"
not just
scales::number()
.
Let's assume everyone gets 8 hours of sleep,
so we can label the points as percentages.
p + ylab("Extra Sleep (% over 8 hours)") + label_hours( aes( label = case_when( group == 1 ~ signs(extra / 8, accuracy = .1, format = scales::percent), group == 2 ~ percent(extra / 8, accuracy = .1) ) ) )
Or we can muliply by days in a year and use scales::comma()
.
p + ylab("Extra Sleep (hours / year)") + label_hours( aes( label = case_when( group == 1 ~ signs(extra * 365, format = scales::comma), group == 2 ~ comma(extra * 365) ) ) )
format
can be any function that takes a numeric vector
and returns a character vector.
Note that format
and all other options (see below) come after the dots;
this way you can enjoy the same matching by position
you know from scales
.
For example, if you prefer the simplicity of number(x, 1)
,
you can simply use signs(x, 1)
.
x <- seq(-4, 4) number(x, 1) # first argument is accuracy signs(x, 1) # first argument is accuracy
signs::signs()
offers 3 other arguments for convenience:
add_plusses
trim_leading_zeros
label_at_zero
(#3 is addressed below under Axis Labels.)
Sometimes, as with this dataset,
you want to show change from a baseline.
You might not only want to include a r "\u2212"
in front of negative numbers,
but a + in front of positive numbers as well.
This is as simple as add_plusses = TRUE
.
p + label_hours( aes( label = case_when( group == 1 ~ signs(extra, accuracy = .1, add_plusses = TRUE), group == 2 ~ number(extra, accuracy = .1) ) ) )
If all values are on the interval (r "\u2212"
1, 1),
it can be more compact to remove leading zeros.
Do this with trim_leading_zeros = TRUE
:
p + ylim(-.8, .8) + label_hours( aes( label = case_when( group == 1 ~ signs(extra, accuracy = .1, trim_leading_zeros = TRUE), group == 2 ~ number(extra, accuracy = .1) ) ) ) + theme( axis.text.y = element_blank(), axis.ticks.y = element_blank() )
You can also use Unicode minus signs on an entire axis.
This function is called signs::signs_format()
,
by analogy to scales::number_format()
, scales::percent_format()
,
and the rest of the _format()
functions.
Note that it accepts the same optional arguments as signs::signs()
as well.
theme_update( axis.text.y = element_text(hjust = 1) ) p + scale_y_continuous( limits = c(-.8, .8), breaks = seq(-.8, .8, by = .2), labels = signs_format( accuracy = .1, add_plusses = TRUE, trim_leading_zeros = TRUE ) ) + label_hours( aes( label = case_when( group == 1 ~ signs( extra, accuracy = .1, add_plusses = TRUE, trim_leading_zeros = TRUE ), group == 2 ~ number(extra, accuracy = .1) ) ) )
You may want to treat zero itself differently,
particularly when every other value has either a plus or a minus.
Maybe you'll be extra pedantic about it with label_at_zero = "symbol"
(notice the y-axis labels below, not the data point labels):
p + scale_y_continuous( limits = c(-4, 6), breaks = seq(-4, 6, by = 1), labels = signs_format( add_plusses = TRUE, label_at_zero = "symbol" ) ) + label_hours( aes( label = case_when( group == 1 ~ signs( extra, accuracy = .1, add_plusses = TRUE ), group == 2 ~ number(extra, accuracy = .1) ) ) )
Or, especially if the location of zero is already obvious,
you might want to leave it blank
with label_at_zero = "blank"
:
p + scale_y_continuous( limits = c(-4, 6), breaks = seq(-4, 6, by = 1), labels = signs_format( add_plusses = TRUE, label_at_zero = "blank" ) ) + label_hours( aes( label = case_when( group == 1 ~ signs( extra, accuracy = .1, add_plusses = TRUE ), group == 2 ~ number(extra, accuracy = .1) ) ) )
You can set format
, add_plusses
, trim_leading_zeros
, or label_at_zero
globally for a script with options()
:
options( signs.format = scales::number, signs.add.plusses = TRUE, signs.trim.leading.zeros = TRUE, signs.label.at.zero = "none" ) p + scale_y_continuous( limits = c(-.8, .8), breaks = seq(-.8, .8, by = .2), labels = signs_format(accuracy = .1, label_at_zero = "blank") ) + label_hours( aes( label = case_when( group == 1 ~ signs(extra, accuracy = .1), group == 2 ~ number(extra, accuracy = .1) ) ) )
The defaults are scales::number
, FALSE
, FALSE
, and "none"
, respectively.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.