5

Although I use a horizontal barplot as an example here, that itself is not the issue. The general issue is how to deal with the white space that is created around ggplots in rmarkdown after using

+coord_fixed(ratio=...)

To reduce the space used by barplots, especially with few factor, I prefer horizontal barplots, where I use coord_fixed(ratio=...) to have a sensible ratio between width and length of the bars.

The plot itself looks the way I want to (see first example below), but it has a lot of white space around it, which I tried to remove by using fig.height = ... in the knitr-header. As the next example shows, this didn't turn out well.

library(ggplot2)
library(ggstance)
library(dplyr)

X <- data.frame(X = sample(c("A", "B"), 30, replace = TRUE))
X <- X %>% group_by(X) %>% summarise(n = n())
ggplot(X, aes(y = X, x = n))+
   geom_barh(stat = "identity")+
   coord_fixed(ratio = 1)+
   ggtitle("blub")

Using this inside rmarkdown where I used x = 3 ; x = 1 ; x=0.5:

```{r,fig.height = x}

# see code above
```

This results in:

fig.height=3

enter image description here

fig.height=1

enter image description here

fig.height=0.5

enter image description here

What I hoped it would do:

  • Crop the white margins
  • while keeping the plot (plot + (axis)titles) area itself constant

The latter clearly doesn't happen as the plot area gets narrower and the titles are moved to the inside of the plot.

I don't know if the solution is to be found inside ggplot or inside knitr. On top of that, an ideal solution should be easily automated for use inside a loop (adapt the solution to changing number of factors).

Dries
  • 470
  • 4
  • 24
  • Not sure if this is your real code, but it is fig.height (you have fig.heigth) so that may not be working as you hope. – sconfluentus May 31 '17 at 11:58
  • @sconfluentus Thanks for noticing, but checking the real code, I can say that I spelled it correctly there. – Dries May 31 '17 at 14:24

2 Answers2

3

You can pass parameters to the fig.width and fig.height chunk options. You just have to calculate them before evaluating that chunk:

---
output: html_document
---

```{r, message=FALSE}
library(ggplot2)
library(ggstance)
library(dplyr)

X <- data.frame(X = sample(c("A", "B"), 30, replace = TRUE))
X <- X %>% group_by(X) %>% summarise(n = n())

hei <- length(X$X) + 1
len <- max(X$n) + 2

```

Text before the plot
```{r, fig.height=hei, fig.width=len, echo=FALSE}

ggplot(X, aes(y = X, x = n))+
  geom_barh(stat = "identity")+
  coord_fixed(ratio = 1)+
  ggtitle("blub") +
  theme(plot.background = element_rect(fill = 'red'))

```

Text after the plot

```{r, message=FALSE}

X <- data.frame(X = sample(c("A", "B", "C"), 30, replace = TRUE))
X <- X %>% group_by(X) %>% summarise(n = n())

hei <- length(X$X) + 1
len <- max(X$n) + 2

```
Another plot
```{r, fig.height=hei, fig.width=len, echo=FALSE}

ggplot(X, aes(y = X, x = n))+
  geom_barh(stat = "identity")+
  coord_fixed(ratio = 1)+
  ggtitle("blub") +
  theme(plot.background = element_rect(fill = 'red'))

```

Result: enter image description here

GGamba
  • 13,140
  • 3
  • 38
  • 47
0

I think, you need to find the good ratio between height and width. But this is sometimes not easy to do.
Some time ago, I wrote a function to remove white margins of a PNG image. If you do not find the good ratio, you can try to use this one. This requires to save your figure as png with function ggsave and then call it in a following chunk with knitr::include_graphics().
The function is the following one:

#' A function to crop white margins of a PNG image
#'
#' @param x path to the PNG image
#' @param pixel number of white pixels lines to keep
#' @export

Rm_WhiteMargins <- function(x, pixel = 2)
{
  # Cut the output image to remove dirty white margins from corrplot
  img <- png::readPNG(x)

  img.test.row <- apply(img, 3, function(layer) {
    apply(layer, 1, function(i) {(sum(i != 1) > 0)})
  }) %>%
    apply(., 1, function(i) {(sum(i) > 0)})
  rowMin <- max(min(which(img.test.row[1:round(length(img.test.row) / 2)])) - (1 + pixel), 1)
  rowMax <- min(max(c(1:length(img.test.row))[
    round(length(img.test.row) / 2) : length(img.test.row)][
      which(img.test.row[(length(img.test.row) / 2) : length(img.test.row)])]) + 1 + pixel,
    length(img.test.row))

  img.test.col <- apply(img, 3, function(layer) {
    apply(layer, 2, function(i) {(sum(i != 1) > 0)})
  }) %>%
    apply(., 1, function(i) {(sum(i) > 0)})
  colMin <- max(min(which(img.test.col[1:round(length(img.test.col) / 2)])) - (1 + pixel), 1)
  colMax <- min(max(c(1:length(img.test.col))[
    round(length(img.test.col) / 2) : length(img.test.col)][
      which(img.test.col[(length(img.test.col) / 2) : length(img.test.col)])]) + 1 + pixel,
    length(img.test.col))

  # Remove rows and cols with white pixels from the original image
  img <- img[rowMin:rowMax, colMin:colMax,]
  png::writePNG(img, target = paste0(gsub(".png", "", x), "_crop.png"))
  rm(img)
}

If this can help...

Sébastien Rochette
  • 6,536
  • 2
  • 22
  • 43
  • Thanks for the help, but I would prefer a solution without the detour of saving and including figures – Dries Jun 06 '17 at 11:12
  • I tried your code and it is saying the following error Sebastien: Error in apply(img, 3, function(layer) { : could not find function "%>%" Calls: Rm_WhiteMargins Execution halted – Process1 Nov 06 '18 at 23:10
  • You need to install and load package dplyr or at least magrittr for this function called pipe %>% – Sébastien Rochette Nov 07 '18 at 06:36