Nikola Knezevic
Nikola Knezevic

Reputation: 799

How to programmatically assign a good width to a graph in ggplot2

I'm generating a lot of faceted graphs with discrete x-axis inside my R scripts. Sometimes, I get overlapping labels in x-axis, and I would like to avoid it:

ggsave overlaps

Since I don't know in advance how big the graph is going to be (number of facets and breaks), I can't assign it manually to ggsave.

Any tips or best practices in this case?

Upvotes: 4

Views: 287

Answers (2)

shadow
shadow

Reputation: 22313

You can extract the number of facets of your plot with the following function. I have tried to be as general as possible and it should be applicable for both facet_grid and facet_wrap faceting. It is however possible that I have overlooked some possibilities. If so, please leave a comment.

nfacetcols <- function(p) {
  if (is.null(p$facet$cols) & is.null(p$facet$facets)) return(1)
  # facet_grid - version
  if (! is.null(p$facet$cols)) {
    if (length(p$facet$cols)==0) return(1)
    dat <- p$data[,as.character(p$facet$cols)]
    if (is.factor(dat)) return(nlevels(dat)) 
    if (is.data.frame(dat)) return(nrow(unique(dat)))
    if (is.numeric(dat)) return(length(unique(dat)))
  }
  # facet_wrap - version
  if (! is.null(p$facet$facets)){
    if (!is.null(p$facet$ncol)) return(p$facet$ncol)
    dat <- p$data[,as.character(p$facet$facets)]
    tot <- ifelse(is.factor(dat), nlevels(dat), nrow(unique(dat)))
    if (!is.null(p$facet$nrow)) return(ceiling(tot/p$facet$nrow))
    return(ceiling(sqrt(tot)))
  }
}

With this function, you can extract the number of columns. The number of levels of your x-factor can easily be extracted using

xlvls <- nlevels(p$data[,p$labels$x])

Then using the suggestion of @agstudy, you can use the following code to ggsave your plot:

# create data set
set.seed(123)
N <- 20
df <- data.frame(a = sample(LETTERS[1:10], N, replace=TRUE), 
                 b = sample(letters[1:5], N, replace=TRUE), 
                 yax = rnorm(N), 
                 xax = factor(sample(letters[16:20], N, replace=TRUE)))
# plot
p <- ggplot(df, aes(xax, yax)) + geom_point() + facet_grid(b~a) 
ggsave("eg.plot.png", 
       p + theme(axis.text.x = element_text(angle = 90, hjust = 1)), 
       width=xlvls*nfacetcols(p)/5)

Upvotes: 1

agstudy
agstudy

Reputation: 121578

Not an exact solution but a practice that usually works well: You can rotate the axis labels.

data(diamonds)
diamonds$cut <- paste("Super",as.character(diamonds$cut))
q <- qplot(cut,carat,data=diamonds,geom="boxplot") +
     facet_grid(~color)
library(gridExtra)

grid.arrange(q,
q + theme(axis.text.x = element_text(angle = 90, hjust = 1)))

enter image description here

second solution

Use facet_wrap with one column ( 1 or few columns)

qplot(cut,carat,data=diamonds,geom="boxplot") +
    facet_wrap(~color,ncol=1)

enter image description here

Upvotes: 2

Related Questions