2

Problem description

I'm writing a function that outputs a line plot ggplot2 object. I would like to have an argument that can control whether to add labels at the end of each line. A visual example is found here. The difficulty lies in the variable length of line labels. Ideally, the function will be smart enough to figure out the proper extra space to expand for the labels on the right.

In base R, there is a function graphics::strwidth that computes how many inches needed for the passed in string. I was wondering if there is a way that can do one step further, i.e. maps the string length to that with respect to the data scale. A dummy example is provided below for better explanation.

A dummy example

library(directlabels)
library(reshape2)
library(ggplot2)
dts <- cbind(`my long group 1` = mdeaths, `my long group 2` = fdeaths, time = 1:length(mdeaths))
ddf <- melt(as.data.frame(dts), id = "time")
names(ddf) <- c("time", "group", "deaths")

plot_wo_label <- ggplot(ddf, aes(x = time, y = deaths, group = group)) + geom_line()
plot_with_label <- plot_wo_label + geom_dl(aes(label = group), method = list(dl.combine('last.points')))
Plot with line label

enter image description here As we can see above, the long line labels ('my long group 1' and 'my long group 2') get truncated due to the margin space. An ad hoc solution is to use xlim to expand the right edge of x-axis by trial and error. But that certainly is not an option in my case.

I know there are posted solutions by turning off clipping (like here), however, I imagine that some of the lines may end early and having a label at the canvas edge far away from the line end may cause difficulty to associate the labels with its corresponding lines.

So if there is a way to figure out how much space a string of arbitrary length will occupy on the x-axis (in the dummy example, the "duration" of label "my long group 1" in the "time" axis), that would be very helpful. But this is just one possible direction in my mind, other solutions are welcome and greatly appreciated!

Thanks!

statechular
  • 273
  • 3
  • 12

1 Answers1

1

The difficulty is that the absolute sizes of the text labels stay the same when you change the rendered output size of the plot. As a result, when you make the rendered size of the plot larger, the text labels span a smaller fraction of the plot area, and vice versa.

There's probably a way to generate the plot, dig into the grob structure of the plot to get the label width in plot coordinates, and then use scale_x_continuous to adjust the plot's x limits to include all of the label text. Unfortunately, I'm not sure how to do that, but hopefully someone else will come along who does.

For now, here's a demonstration of the issue. (I've switched to geom_text to place the labels, as I don't think directlabels is necessary here.):

library(tidyverse)

ggplot(ddf, aes(x = time, y = deaths, group = group)) + 
  geom_line() +
  geom_text(data=ddf %>% group_by(group) %>% filter(time==max(time)),
            aes(label=group), hjust=0, position=position_nudge(x=0.5)) +
  scale_x_continuous(limits=c(0,1.15*max(ddf$time)))

Here's a screenshot of two saved versions of the plot, one version saved as a 700x350 pixel png file and the other saved as a 500x250 pixel png file. You can see that the absolute font sizes are the same, even though the sizes of the plots are different.

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285