Fix typography in axis labels
Asked Answered
I

5

20

Preamble: I want to create publication-grade graphics from R without postprocessing. Other researchers at my institute always perform postprocessing in a graphics software (such as Adobe Illustrator). I am hoping to avoid this.

My gripe is that R doesn’t use the correct minus sign for negative numbers (especially in plot axes):

plot(-20:-1, rnorm(20) + 1 : 20)

plot with negative numbers

(I’ve circled the offenders for your consideration.)

As somewhat of a typography nerd (it’s true! Check my Careers CV!) this is unacceptable. I need to use the correct Unicode character ᴍɪɴᴜꜱ ꜱɪɢɴ (U+2212, “−”) here. A friend of mine achieves this by replacing all minus signs in Adobe Illustrator prior to publication but I can’t help but think that there must be a better way – from within R – to accomplish this; and one that doesn’t force me to manually replace all axis labels.

(I’m not currently using ggplot2 but if there’s a solution which only works with ggplot2 I’ll gladly take it.)

Isle answered 8/2, 2013 at 20:31 Comment(6)
-1 for not using XKCD-style graphs.Steatopygia
Hi Konrad, do you know about the no-longer-on-CRAN (not sure why) package tikzDevice? See this for an example.Irreformable
@Dirk Oooh. Very interesting, I’ll definitely install that. However, just looking over the example briefly it doesn’t seem that the TikZ device automatically puts numbers into a maths environment so my particular issue isn’t fixed by that.Isle
See perhaps my answer to this related question (although looking back at it now, it seems to me like the cairo_pdf device places the minus signs lower than it ought to).Degreeday
@Josh Interesting but obviously that’s kind of a hack since it replaces all – even legitimate – hyphens. Oh, and cairo_pdf apparently crashes my R. ;-) Well, no loss.Isle
it does feel like something using the TikZ device (especially in combination with a LaTeX document) would give you the nicest typesetting solutions overall (e.g. fonts matching the document)Echoechoic
S
3

The new package signs doing exactly what the OP asks for. Here is an example from the website:

library(dplyr)
library(ggplot2)
library(ggrepel)

theme_set(theme_gray())
theme_update(panel.grid.minor = 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(
    mapping = aes(
      label = case_when(
        group == 1 ~ signs(extra, accuracy = .1), # Unicode minuses
        group == 2 ~ number(extra, accuracy = .1) # ASCII minuses
      )
    )
  ) +
  scale_y_continuous(
    limits = c(-4, 6),
    breaks = seq(-4, 6),
    labels = signs_format(accuracy = .1) # Unicode, analogous to number_format() 
  )

Here is the plot on the website.

Setsukosett answered 3/4, 2020 at 13:21 Comment(0)
C
7

Perhaps draw the axis and labels manually, rather than accepting the defaults?

plot(-20:-1, rnorm(20) + 1 : 20, xaxt="n")
Axis(-20:-1, at=seq(-20,-5,5), side=1,
  labels=paste("\U2212",seq(20,5,-5),sep=""))

enter image description here

Cornstarch answered 8/2, 2013 at 20:55 Comment(1)
In fact that’s exactly what I want to avoid but I’ll take it if there’s no better solution. +1 either way.Isle
M
5

Another way which is almost the same as the one provided by Joshua Ulrich, except that you can let R compute the axis ticks :

plot(-20:-1, rnorm(20) + 1 : 20, xaxt="n")
at <- axTicks(1, usr=par("usr")[1:2])
labs <- gsub("-", "\U2212", print.default(at))
axis(1, at=at, labels=labs)

enter image description here

Marielamariele answered 8/2, 2013 at 21:34 Comment(4)
Hmm, in fact I could write my own version of plot which does this internally. The only problem is that it’s slightly tricky to correctly catch all edge cases (such as when the axis should be printed on another side, nor not at all …)Isle
@Marielamariele -- Could you let me know which OS you're using? (This doesn't for me without explicitly setting Encoding(lab) <- "UTF-8", and I'd like to pinpoint whether that's because I'm using Windows. Oddly, when the Unicode character is paste'ed on (as in Joshua's solution), R for Windows does figure out that the resultant string is now Unicode)).Degreeday
@JoshO'Brien I'm using Linux.Marielamariele
@Marielamariele -- And I'm jealous. Thanks for the confirmation.Degreeday
O
5

Here is how to do it with ggplot. The pdf device doesn't render the unicode symbols, so use cairo_pdf instead.

unicode_minus <- function(x) sub('^-', '\U2212', format(x))

# change the default scales
scale_x_continuous <- function(..., labels=unicode_minus)
                        ggplot2::scale_x_continuous(..., labels=labels)
scale_y_continuous <- function(..., labels=unicode_minus)
                        ggplot2::scale_y_continuous(..., labels=labels)

qplot(-20:-1, rnorm(20) + 1:20)
Ophthalmic answered 23/1, 2014 at 16:30 Comment(0)
S
3

Here's a hack that fixes the axis labels of lattice plots. The idea is to insert code in the low-level function panel.axis() which is called by most/all of the higher level plotting functions.

Here (with a couple of lines of context above and below) is the code you would insert "by hand" following a call to library(lattice); trace("panel.axis", edit=TRUE):

if (draw.labels && !is.null(labels)) {
    {
        labels <- gsub("-", "\U2212", labels)  ## <- My addition
        Encoding(labels) <- "UTF-8"            ## <- My addition
        just <- if (outside) 
            switch(side, bottom = if (rot[1] == 0) c("centre", 

A better option (once you've divined the correct value of the at= argument (and see here for several ways to do that)) is to run this:

library(lattice)

trace(what = "panel.axis",
      tracer = quote({labels <- gsub("-", "\U2212", labels)
                      Encoding(labels) <- "UTF-8"}),
      at = list(c(30,3,2,2)))

Following which proper minus signs in tick labels are printed on any of the many screen or file graphics devices that I've checked

To compare on your own screen device, run the code block directly above and then:

xyplot(1:10 ~ -1:-10)
untrace("panel.axis")
windows()
xyplot(1:10 ~ -1:-10)
Spellbinder answered 9/2, 2013 at 3:44 Comment(0)
S
3

The new package signs doing exactly what the OP asks for. Here is an example from the website:

library(dplyr)
library(ggplot2)
library(ggrepel)

theme_set(theme_gray())
theme_update(panel.grid.minor = 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(
    mapping = aes(
      label = case_when(
        group == 1 ~ signs(extra, accuracy = .1), # Unicode minuses
        group == 2 ~ number(extra, accuracy = .1) # ASCII minuses
      )
    )
  ) +
  scale_y_continuous(
    limits = c(-4, 6),
    breaks = seq(-4, 6),
    labels = signs_format(accuracy = .1) # Unicode, analogous to number_format() 
  )

Here is the plot on the website.

Setsukosett answered 3/4, 2020 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.