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.)

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 3
    -1 for not using [XKCD-style graphs](http://stackoverflow.com/questions/12675147/how-can-we-make-xkcd-style-graphs-in-r). – Mysticial Feb 08 '13 at 20:33
  • 2
    Hi Konrad, do you know about the no-longer-on-CRAN (not sure why) package tikzDevice? See [this](http://www.texample.net/tikz/examples/tikzdevice-demo/) for an example. – Dirk Eddelbuettel Feb 08 '13 at 20:45
  • @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. – Konrad Rudolph Feb 08 '13 at 20:52
  • See perhaps my answer to [this related question](http://stackoverflow.com/questions/10438398/any-way-to-disable-the-minus-hack-in-pdf-poscript-output/10440614#10440614) (although looking back at it now, it seems to me like the `cairo_pdf` device places the minus signs *lower* than it ought to). – Josh O'Brien Feb 08 '13 at 20:52
  • @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. – Konrad Rudolph Feb 08 '13 at 21:02
  • 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) – Ben Bolker Feb 08 '13 at 22:30

5 Answers5

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

Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
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

juba
  • 47,631
  • 14
  • 113
  • 118
  • 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 …) – Konrad Rudolph Feb 08 '13 at 21:50
  • @juba -- 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)). – Josh O'Brien Feb 10 '13 at 20:44
  • @JoshO'Brien I'm using Linux. – juba Feb 10 '13 at 20:50
  • @juba -- And I'm jealous. Thanks for the confirmation. – Josh O'Brien Feb 10 '13 at 20:56
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)
Rosen Matev
  • 1,828
  • 19
  • 20
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)
Community
  • 1
  • 1
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
nmc
  • 46
  • 1