12
votes

I have some ggplot2 plots with lots of text in them (no way around that), that I am building inside an *.Rmd, like so:

---
title: "SO Test"
output: pdf_document
---

```{r}
library(ggplot2)
df <- data.frame(rep(1:5,5),rep(1:5, each=5),rep("Test",5))
colnames(df) <- c("x", "y", "word")
ggplot(data = df, mapping = aes(x = x, y = y, label = word)) +      geom_text()
```

That works great, giving me something like the below as a PDF.

enter image description here

I would now like to make these labels (Test) as large as possible without overplotting.

My problem is that I now see 2 "levers" which I could pull to change the font size:

  1. "inside" ggplot2, change, add, say, geom_text(size=12)
  2. upon plotting, "inside" knitr (or Rmarkdown?), add a chunk option like `fig.width=15, fig.height=15), which also changes the font size relative to the overall plot size (for reasons I don't really understand).

Obviously, 1. would be the straightforward way, but I then need to adjust geom_text(size=...) always depending on the size of the output, say, when the linewidth in LaTeX changes or the column width of my blog post. That seems inelegant, to say the least.

How do I go about this in an elegant, scalable way that produces a pretty plot?

I'm still a bit fuzzy about this, and where in the toolchain (Rmarkdown?, Knitr?, GGplot2) I should start ...

Ps.: as a bonus, the font size should be arrived at programmatically, because the length of string Text varies from plot to plot in real life.

1
you can evaluate inside of the chunk options {r, fig.height=x, fig.width=y} where x and y were created in a previous chunk, maybe getting the size of blog page or using the data frame dimensions. would that work? Also, I'm not sure I understand how the size of the ticks and text labels are directly affected by the fig.height/fig.width. Seems like you should be messing with the element size or geom_text(size=)rawr
thanks that would help for setting the sizes for the entire document – but it wouldn't solve the label-font-size issue, right? What I'm looking for is a way to automatically blow up (give it a greater canvas) the plot exactly until the labels don't overplot anymore, and then to just rescale it down to whatever place I've got available. Rmarkdown (or knitr? or someone?) seems to actually do the latter automatically, if the object is too large. The ticks were just for cosmetics; I thought it'd be nice if they were in a constant relation to the font size of labels.maxheld
the "canvas" you are referring to is the 10x10 device region? the font size will increase as the canvas increases but only indirectly because you are increasing the overall size of the picture. What I was saying is don't you want to increase the font size in the creation of the ggplot and not the plotting device?rawr
@rawr sorry for being so fuzzy. I edited my question above, hope it's clearer now. My problem is 1) there seem to be two things that influence the relative font size to plot area, 2) I don't know how to automatically scale up font sizes to the possible maximum (short of overplotting) in ggplot2.maxheld

1 Answers

11
votes

I think about the device size as a sheet of paper, 12 pt font is the same size on any size sheet of paper. However, in the pdf_document, the image is scaled to fit the figure region and thus shrinking or stretching the font size in the graphic accordingly.

I would recommend trying tikz graphics. Rmarkdown allows you to incorporate LaTeX commands and options into the .Rmd and render them correctly in the pdf_document. Using tikz graphics allows you to specify specific markup features for each label in the graphic.

First, you'll need to include the correct header information

---
title: SO Example
output: pdf_document
header-includes:
   - \usepackage{lipsum}
   - \usepackage{tikz}
---

The .Rmd file continues

```{r}
# Chunk 1: package load and data set up.
# Note: The `word` column contains labels with LaTeX markups
library(ggplot2)
df <- data.frame(x = rep(1:5,5),
                 y = rep(1:5, each=5),
                 word = c(rep("Test",5), 
                          c("$\\alpha$", "{\\bf bold}", "{\\it italic}",
                            "{\\footnotesize footnotesize}", "$\\beta$"), 
                          rep("test", 15)))
```

```{r, dev = "tikz", fig.width = 6, fig.height = 6}
# A 6 inch by 6 inch figure
ggplot(data = df, mapping = aes(x = x, y = y, label = word)) + geom_text() 
```

and produces the output:

enter image description here

A smaller graphic, where the font size of the labels would need to be edited:

enter image description here

```{r, dev = "tikz", fig.width = 3, fig.height = 3}
# A 3 inch by 3 inch figure
ggplot(data = df, mapping = aes(x = x, y = y, label = word)) + geom_text() 
```

An example of building the font sizes dynamically is done as follows:

# Example of dynamically setting label markups

Some blank text, followed by some lipsum form \LaTeX:

\lipsum[1]

```{r, dev = "tikz"}
library(stringr)
State_Names <- 
  data.frame(x = 1:5, y = rep(10:1, each = 5), state = state.name)

# Set size by lenght of state name
# Less than 8 chars: normalsize
# 8 or more chars: tiny
nchars <- sapply(State_Names$state, nchar)
State_Names$state[nchars >= 8] <- 
  paste0("{\\tiny ", State_Names$state[nchars >= 8], "}")


ggplot(State_Names, aes(x = x, y = y, label = state)) + geom_text() + xlim(0, 6)
```

With the output of:

enter image description here