You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@hadley mentioned the idea of magic scale defaults, particularly for medium-sized numbers that look nice with scales::comma(). This is a just reprex to play with this idea (seemed more suited to here than an issue in ggplot2).
library(ggplot2)
library(scales)
library(rlang)
conditional_labeller<-function(..., .env= caller_env()) {
dots<- quos(...)
force(.env)
function(breaks) {
eval_tidy(
quo(dplyr::case_when(!!!dots)),
data=list(.=breaks), env=.env
)
}
}
label_magic<- conditional_labeller(
# range with big but not too big numbers
any(abs(.) >=1e3) && all(abs(.) <=1e7) ~ comma(.),
# range with really big numbers
any(abs(.) >1e7) ~ scientific(.),
# range with really small numbers
any(abs(.) <1e-3& abs(.) !=0) ~ scientific(.),
# all other rangesTRUE~ format(., trim=TRUE)
)
ggrange<-function(low=0, high=1, labels=label_magic) {
ggplot() +
geom_blank(aes(x= c(low, high), y= c(low, high))) +
scale_x_continuous(labels=labels) +
scale_y_continuous(labels=labels)
}
ggrange(0, 1e7)
(I think using min() and max() instead of any() and all() makes it a bit easier to reason about, but the fact that I didn't use max() makes me suspicious that I haven't correctly captured your algorithm)
Is there any harm in using comma() where you currently use format()?
I worry about about too many digits — I think you might also need to test some small ranges, like c(0, 0.000001) (and just showing the results of the function as character vectors would be adequate, I think)
It may be easier to understand if all the labels had the same exponent (except in the case of transformed scales). Some plotting programs (matlab does this I think) do scientific notation by default with pretty superscripts (i.e. expression(1.0 %.% 10^6)).
This new-and-improved version also does zero-length input (which was previously very noisy), and uses max_magnitude to detect numbers that should be comma-ed (as in c(999, 1000)). I also dropped the cutoff for scientific notation to 1e6 to help with too many digits. I wonder if this will be annoying for the common case of years on the x-axis (when 1999 should probably not be 1,999).
Here's one way that might look...it's slightly different for huge and tiny numbers since we might want to pick between commas and scientific for the big ones, and format() with scientific = TRUE and scientific = FALSE for the tiny ones.
@dpseidel can you think of other cases that we need to test for this? All I remember is that I usually forget some important edge case of format(), and we might've encountered this last year.
The only thing that comes to mind immediately is that I know we debated the proper handling of NA, NULL, & Inf in regards to format functions last summer. As it stands this latest iteration of label_auto errors if passed an NA.
@paleolimbot do you have any more thoughts on this function? Do you think it's fine to add to scales, at least as a first attempt at solving the problem?
I haven't thought about this in a long time, but I think it's ready for scales, perhaps marked as experimental. The only concern I have based on the above output is that it's super common to have year on an axis, and label_auto() would make those labels be "2,019".
Is this a PR you were hoping I would do? I would be happy to do it (or not) but it might take me a week or more to get to it.
I think so - all the other ones are *_format() with a corresponding (*()) function (e.g., (comma_format() and comma()). Having an auto() function seems sub-optimal though.
Activity
[-]Magic scales[/-][+]Magic scale labeller[/+]hadley commentedon May 15, 2019
Could you rewrite with a little less indirection? I think your solution is mostly equivalent to this:
(I think using
min()andmax()instead ofany()andall()makes it a bit easier to reason about, but the fact that I didn't usemax()makes me suspicious that I haven't correctly captured your algorithm)paleolimbot commentedon May 15, 2019
Thanks, it's much clearer that way. I ended up having to use
max()to make sure that breaks that start small and go huge don't usecomma().Created on 2019-05-15 by the reprex package (v0.2.1)
hadley commentedon May 15, 2019
Is there any harm in using
comma()where you currently useformat()?I worry about about too many digits — I think you might also need to test some small ranges, like
c(0, 0.000001)(and just showing the results of the function as character vectors would be adequate, I think)paleolimbot commentedon May 15, 2019
A few more thoughts:
comma()rounds by default, which makes it hard to use generally for small ish numbers:scientific()also has a similar rounding issue for large numbers that are very close together:It may be easier to understand if all the labels had the same exponent (except in the case of transformed scales). Some plotting programs (matlab does this I think) do scientific notation by default with pretty superscripts (i.e.
expression(1.0 %.% 10^6)).This new-and-improved version also does zero-length input (which was previously very noisy), and uses
max_magnitudeto detect numbers that should be comma-ed (as inc(999, 1000)). I also dropped the cutoff for scientific notation to1e6to help with too many digits. I wonder if this will be annoying for the common case of years on the x-axis (when 1999 should probably not be 1,999).paleolimbot commentedon May 16, 2019
pillar does some cool stuff here too:
hadley commentedon May 16, 2019
For
label_auto(c(999999, 1000000, 1000001))maybe we should callformat()withscientific = TRUEandscientific = FALSEand pick the shortest?The constraints are a little different than for pillar since I think we always want to show the same number of decimal places.
paleolimbot commentedon May 20, 2019
Here's one way that might look...it's slightly different for huge and tiny numbers since we might want to pick between commas and scientific for the big ones, and
format()withscientific = TRUEandscientific = FALSEfor the tiny ones.Created on 2019-05-20 by the reprex package (v0.2.1)
hadley commentedon May 20, 2019
@dpseidel can you think of other cases that we need to test for this? All I remember is that I usually forget some important edge case of
format(), and we might've encountered this last year.dpseidel commentedon May 20, 2019
The only thing that comes to mind immediately is that I know we debated the proper handling of
NA,NULL, &Infin regards to format functions last summer. As it stands this latest iteration oflabel_autoerrors if passed anNA.paleolimbot commentedon May 21, 2019
Good call! Here's one go at handling
NA,NULL, andInf:Created on 2019-05-21 by the reprex package (v0.2.1)
hadley commentedon Oct 25, 2019
@paleolimbot do you have any more thoughts on this function? Do you think it's fine to add to scales, at least as a first attempt at solving the problem?
paleolimbot commentedon Oct 25, 2019
I haven't thought about this in a long time, but I think it's ready for scales, perhaps marked as experimental. The only concern I have based on the above output is that it's super common to have
yearon an axis, andlabel_auto()would make those labels be"2,019".Is this a PR you were hoping I would do? I would be happy to do it (or not) but it might take me a week or more to get to it.
hadley commentedon Oct 25, 2019
I'm happy to do the PR.
I guess it should be called
auto_format()?paleolimbot commentedon Oct 25, 2019
I think so - all the other ones are
*_format()with a corresponding (*()) function (e.g., (comma_format()andcomma()). Having anauto()function seems sub-optimal though.hadley commentedon Oct 25, 2019
We could also do
number_auto()to match the newernumber_si().paleolimbot commentedon Oct 26, 2019
Looks great! Thanks!