Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control ggplot2 facet height independently from number of row facets

Tags:

r

ggplot2

knitr

Problem

ggplot2/knitr forces plots (in LaTeX) to have the default aspect ratio. Two Plots which have different number of facets result in plots with facet heights being different. This is not nice.

Example for problem

I was looking for a solution which controls the ggplot2 facet height independently from the number of row facets.

Edited Sept. 9th: The solution should work within one chunk as both plots are in one chunk (as in the example code). So tweaking knitr's figure related chunk options is not viable, as the chunk option applies to both plots equally.

Solution - but having problems

Putting each plot into a gridExtra box and scaling it's height with the number of facets, results in facet heights which are no longer different: nicer but not perfect.

  • Plots appear on the page with too much margin, even there's remaining text below so plots shall move more closer.
  • Seems plots manipulated by this get cropped.

Is there a smarter and problem-free solution which controls the ggplot2 facet height independently from the number of row facets?

\documentclass[a4paper]{article}
\usepackage[margin=1in]{geometry}

\begin{document}

<<setup, results='asis', message=FALSE, echo=FALSE>>=
require(ggplot2)
require(gridExtra)

### Generate two data frames

# Data frame with 2 classes
db.small = data.frame (
  class = as.factor(c(rep("A", 12), rep("B", 12))),
  month = as.factor(rep(1:12, 2)),
  value = runif(2*12, 0, 100)
  )

# Data frame with 5 classes
db.large = data.frame (
  class = as.factor(c(rep("A", 12), rep("B", 12), rep("C", 12), rep("C", 12), rep("D", 12))),
  month = as.factor(rep(1:12, 5)),
  value = runif(5*12, 0, 100)
)

# Generate plots
plot1 = ggplot(db.small, aes(month, value)) + geom_bar(stat="identity") + facet_grid(class ~ .) + ylim(c(0, 100))
plot2 = ggplot(db.large, aes(month, value)) + geom_bar(stat="identity") + facet_grid(class ~ .) + ylim(c(0, 100))
@

\section{Before: Native plots without modification}
Ggplot2/knitr forces both plots to have the default aspect ratio. As both plots have different number of facets the result is that the facet heights are different. This is not nice.

<<before, results='asis', message=FALSE, echo=FALSE, out.width="0.6\\linewidth">>=
print(plot1)
print(plot2)
@

\pagebreak
\section{After: Plots with modification}
Putting each plot into a gridExtra box and scaling it's height with the number of facets, results in facet heights which are no longer different: nicer but not perfect, see problems below.

<<After, results='asis', message=FALSE, echo=FALSE, out.width="0.6\\linewidth">>=
# Calculate number of facets for the two plots
db.small.numberfacets = length(unique(db.small$class))
db.large.numberfacets = length(unique(db.large$class))

# Define heights for modification of plots
facet.height = 4
other.height = 2

plot1 = plot1 + theme( plot.margin = unit(c(0, 0, 0, 0), "cm"))
plot2 = plot2 + theme( plot.margin = unit(c(0, 0, 0, 0), "cm"))

# Put plots inside a gridExtra box and change height plots
grid.arrange(arrangeGrob(plot1, heights=unit(db.small.numberfacets*facet.height+other.height, "cm")), nrow=1)
grid.arrange(arrangeGrob(plot2, heights=unit(db.large.numberfacets*facet.height+other.height, "cm")), nrow=1)
@


2 Problems:
\begin{itemize}
\item Plots appear on the page with too much margin, even there's remaining text below so plots shall move more closer.
\item Seems the second plot is cropped, see the x-axis label.
\end{itemize}

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

\end{document}

pdf Output: Page 1Page 2

like image 484
user2030503 Avatar asked Sep 08 '13 11:09

user2030503


People also ask

What does Facet_wrap do in Ggplot?

facet_wrap() makes a long ribbon of panels (generated by any number of variables) and wraps it into 2d. This is useful if you have a single variable with many levels and want to arrange the plots in a more space efficient manner.

What is Facet_wrap and Facet_grid?

The facet_grid() function will produce a grid of plots for each combination of variables that you specify, even if some plots are empty. The facet_wrap() function will only produce plots for the combinations of variables that have values, which means it won't produce any empty plots.

What does the Facet_grid function do in R?

facet_grid() forms a matrix of panels defined by row and column faceting variables. It is most useful when you have two discrete variables, and all combinations of the variables exist in the data. If you have only one variable with many levels, try facet_wrap() .

How do I reorder facets in R?

To reorder the facets accordingly of the given ggplot2 plot, the user needs to reorder the levels of our grouping variable accordingly with the help of the levels function and required parameter passed into it, further it will lead to the reordering of the facets accordingly in the R programming language.


1 Answers

Do you know that knitr chunk options can take arbitrary R expressions? The example 40 shows how you can calculate fig.width and fig.height dynamically.

For your case, you can assign a value to fig.height according to the number of levels in the factor variable for faceting. For example,

\documentclass{article}

\begin{document}
<<setup>>=
library(ggplot2)
calc_height = function(f) length(levels(f))
p = ggplot(diamonds, aes(color)) + geom_bar()
@

<<test-a, fig.width=7, fig.height=calc_height(diamonds$cut)>>==
p + facet_grid(cut ~ .)
@

<<test-b, fig.width=7, fig.height=calc_height(diamonds$clarity)>>==
p + facet_grid(clarity ~ .)
@

\end{document}

Output:

like image 199
Yihui Xie Avatar answered Oct 04 '22 02:10

Yihui Xie