3

I've noticed an unexpected behavior in ggplot2's geom_text() geom. If the attributes hjust and vjust are specified as strings, R returns coercion errors, though the plots seem to come out OK. The problem came up in a ggplot2-based package I'm developing. For simplicity, I've created stripped-down examples that still produce the error.

First, I tried it with qplot()

##qplot version library(ggplot2) p <- qplot(cty, hwy, label = drv, hjust = "right", geom = "text", data = mpg ) print(p) 

And I got this error:

Warning message: In validDetails.text(x) : NAs introduced by coercion 

Then I tried it with ggplot():

##ggplot version library(ggplot2) p <- ggplot( aes(x = cty, y = hwy ), data = mpg ) p <- p + geom_text( aes(label = drv), hjust = "right" ) print(p) 

and got an identical plot, and an identical error:

Warning message: In validDetails.text(x) : NAs introduced by coercion 

I then tried setting both hjust and vjust:

library(ggplot2) p <- ggplot( aes(x = cty, y = hwy ), data = mpg ) p <- p + geom_text( aes(label = drv), hjust = "right", vjust = "top" ) print(p) 

With both parameters set using strings, R returns two coercion errors:

Warning messages: 1: In validDetails.text(x) : NAs introduced by coercion 2: In validDetails.text(x) : NAs introduced by coercion 

But, when the parameters are numbers, R returns no coercion errors:

## Using numbers instead of strings library(ggplot2) p <- ggplot( aes(x = cty, y = hwy ), data = mpg ) p <- p + geom_text( aes(label = drv), hjust = 0, vjust = 0, data = mpg ) print(p) 

I'm not quite sure why this happens, or whether it's significant, but I didn't expect it.

ggplot2 documentations don't agree

Hadley's book(p. 196) says hjust and vjust can accept string arguments:

Justification of a string (or legend) defines the location within the string that is placed at the given position. There are two values for horizontal and vertical justification. The values can be:

  • A string: "left", "right", "centre", "center", "bottom", and "top".
  • A number between 0 and 1, giving the position within the string (from bottom-left corner).

But the man file for geom_text() in version 0.8.9 says hjust and vjust are numeric, though it doesn't say they can only be numeric:

Aesthetics

The following aesthetics can be used with geom_text. Aesthetics are mapped to variables in the data with the aes function: geom_text(aes(x = var))

  • x: x position (required)
  • y: y position (required)
  • label: text label (required)
  • colour: border colour
  • size: size
  • angle: angle
  • hjust: horizontal justification, between 0 and 1
  • vjust: vertical justification, between 0 and 1
  • alpha: transparency
1
  • To be very particular, ggplot2 the book on page 196 does NOT say that hust and vjust can have those values... it only says (roughly) that "justification" can be one of... See my full "answer" below. Commented Feb 22, 2011 at 2:28

2 Answers 2

5

So, I don't know much about WHAT CODE defines or consumes hjust/vjust, but using TextMate's "Find in project" (in the ggplot2/R/ directory) for hjust, I don't see any lines that look like they are the definition of or the implementation of hjust... just places where it's listed as a valid aes and where it gets passed along.

That makes me want to go read grid...

http://stat.ethz.ch/R-manual/R-patched/library/grid/html/grid.text.html

which leads me to want to know more about how grid.text is defined

R> grid.text function (label, x = unit(0.5, "npc"), y = unit(0.5, "npc"), just = "centre", hjust = NULL, vjust = NULL, rot = 0, check.overlap = FALSE, default.units = "npc", name = NULL, gp = gpar(), draw = TRUE, vp = NULL) { tg <- textGrob(label = label, x = x, y = y, just = just, hjust = hjust, vjust = vjust, rot = rot, check.overlap = check.overlap, default.units = default.units, name = name, gp = gp, vp = vp) if (draw) grid.draw(tg) invisible(tg) } <environment: namespace:grid> 

so, it's a textGrob, and just, hjust, and vjust are simply being passed into it... off to textGrob

R> textGrob function (label, x = unit(0.5, "npc"), y = unit(0.5, "npc"), just = "centre", hjust = NULL, vjust = NULL, rot = 0, check.overlap = FALSE, default.units = "npc", name = NULL, gp = gpar(), vp = NULL) { if (!is.unit(x)) x <- unit(x, default.units) if (!is.unit(y)) y <- unit(y, default.units) grob(label = label, x = x, y = y, just = just, hjust = hjust, vjust = vjust, rot = rot, check.overlap = check.overlap, name = name, gp = gp, vp = vp, cl = "text") } <environment: namespace:grid> 

so, it's a grob........... off to grob......

R> grob function (..., name = NULL, gp = NULL, vp = NULL, cl = NULL) { g <- list(..., name = name, gp = gp, vp = vp) if (!is.null(cl) && !is.character(cl)) stop("Invalid 'grob' class") class(g) <- c(cl, "grob", "gDesc") validGrob(g) } <environment: namespace:grid> 

Nothing too helpful there, so I Google

R grid hjust vjust

and after overriding Google's autocorrect of my search, I find

http://rwiki.sciviews.org/doku.php?id=tips:graphics-grid:hvjust

Looking back at Hadley's book, I notice that the p.196 reference doesn't actually MENTION hjust or vjust... simply justification.

Reading the documentation for

R> ?textGrob 

I see that

just The justification of the text relative to its (x, y) location. If there are two values, the first value specifies horizontal justification and the second value specifies vertical justification. Possible string values are: "left", "right", "centre", "center", "bottom", and "top". For numeric values, 0 means left alignment and 1 means right alignment. hjust A numeric vector specifying horizontal justification. If specified, overrides the just setting. vjust A numeric vector specifying vertical justification. If specified, overrides the just setting. 

So, here's my thinking.

  • the just parameter can be string or numeric
  • hjust and vjust are numeric only and can override just
  • if you try to use strings for them, it may "work", but throw warnings

So, lets look at the grid.text demo code and in particular the draw.text function where they use just and seem to do so successfully with string values:

grid.newpage() x <- stats::runif(20) y <- stats::runif(20) rot <- stats::runif(20, 0, 360) grid.text("SOMETHING NICE AND BIG", x=x, y=y, rot=rot, gp=gpar(fontsize=20, col="grey")) grid.text("SOMETHING NICE AND BIG", x=x, y=y, rot=rot, gp=gpar(fontsize=20), check=TRUE) grid.newpage() draw.text <- function(just, i, j) { grid.text("ABCD", x=x[j], y=y[i], just=just) grid.text(deparse(substitute(just)), x=x[j], y=y[i] + unit(2, "lines"), gp=gpar(col="grey", fontsize=8)) } x <- unit(1:4/5, "npc") y <- unit(1:4/5, "npc") grid.grill(h=y, v=x, gp=gpar(col="grey")) draw.text(c("bottom"), 1, 1) draw.text(c("left", "bottom"), 2, 1) draw.text(c("right", "bottom"), 3, 1) draw.text(c("centre", "bottom"), 4, 1) draw.text(c("centre"), 1, 2) draw.text(c("left", "centre"), 2, 2) draw.text(c("right", "centre"), 3, 2) draw.text(c("centre", "centre"), 4, 2) draw.text(c("top"), 1, 3) draw.text(c("left", "top"), 2, 3) draw.text(c("right", "top"), 3, 3) draw.text(c("centre", "top"), 4, 3) draw.text(c(), 1, 4) draw.text(c("left"), 2, 4) draw.text(c("right"), 3, 4) draw.text(c("centre"), 4, 4) 

Now notice the difference if I change draw.text to use hjust and vjust AS STRINGS

grid.newpage() x <- stats::runif(20) y <- stats::runif(20) rot <- stats::runif(20, 0, 360) grid.text("SOMETHING NICE AND BIG", x=x, y=y, rot=rot, gp=gpar(fontsize=20, col="grey")) grid.text("SOMETHING NICE AND BIG", x=x, y=y, rot=rot, gp=gpar(fontsize=20), check=TRUE) grid.newpage() draw.text <- function(just, i, j) { grid.text("ABCD", x=x[j], y=y[i], hjust=just[1], vjust=just[2]) grid.text(deparse(substitute(just)), x=x[j], y=y[i] + unit(2, "lines"), gp=gpar(col="grey", fontsize=8)) } x <- unit(1:4/5, "npc") y <- unit(1:4/5, "npc") grid.grill(h=y, v=x, gp=gpar(col="grey")) draw.text(c("bottom"), 1, 1) draw.text(c("left", "bottom"), 2, 1) draw.text(c("right", "bottom"), 3, 1) draw.text(c("centre", "bottom"), 4, 1) draw.text(c("centre"), 1, 2) draw.text(c("left", "centre"), 2, 2) draw.text(c("right", "centre"), 3, 2) draw.text(c("centre", "centre"), 4, 2) draw.text(c("top"), 1, 3) draw.text(c("left", "top"), 2, 3) draw.text(c("right", "top"), 3, 3) draw.text(c("centre", "top"), 4, 3) draw.text(c(), 1, 4) draw.text(c("left"), 2, 4) draw.text(c("right"), 3, 4) draw.text(c("centre"), 4, 4) 

Long story-short: I think when you use hjust or vjust as a string, you're violating the documentation (it's value should be numeric 0 <= x <= 1), and that if you want to use strings, you have to use the just parameter....

Sign up to request clarification or add additional context in comments.

2 Comments

In looking through the documentation for textGrob, I guess my real question is: Why can't ggplot2's theme_text() just accept and pass along the just parameter so users can specify justification vectors like just=c("bottom", "centre")?
It could... it just doesn't. Perhaps this suggests a pull request for ggplot2?
1

hjust and vjust should be numbers, check the manual (?geom_text):

  • hjust’: horizontal justification, between 0 and 1
  • ‘vjust’: vertical justification, between 0 and 1

2 Comments

I agree the manual says that hjust and vjust should be numbers. I've edited the question to reflect the fact that Hadley's book seems to pretty clearly specify that they can be strings as well. It's not clear to me which documentation supercedes which.
Code supersedes all documentation; documentation lies. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.