R ggplot loop: in a for loop of ggplot histograms, how can you automatically set the y axis scale based on max frequency?

Viewed 541

I have the following loop to produce several histograms based off certain columns (columns 2 to 5) in a larger dataset (df):

loop.vector <- 2:5
for (i in loop.vector){
x <- df[,i]
print(ggplot(df,aes(x=x)) + geom_histogram(binwidth=1)+scale_x_continuous(breaks=seq(0,max((x),1)))
}

I'd like to have my y-axis scale done automatically as I have for the x-axis, where it ranges between zero and whatever the maximum frequency value is, at increments of 1.

I know how to set these values manually if I were to plot, take a look at it, and enter the max y-axis value separately, but i'd like to do this automatically within the loop.

Thanks!

2 Answers

Answering the question: how to access max counts for a histogram plot?

The information you're missing on each plot in order to create your scale_y_continuous command is the maximum number of counts. There is a nice way to access this information once you have created a ggplot object, which is to use the built-in ggplot_build() function from ggplot2. For a given plot, myPlot, the following will give you a list of dataframes that are used for each layer in your plot:

ggplot_build(myPlot)$data

In the case of your example, you can access the count column of the first data frame (since you only have one histogram geom layer). Here's how you can write the function to do what you need it to do. I'll use an example dataset that can show you the results. Note that I've also changed your scale_x_continuous line to be able to accomodate positive and negative numbers by using a combination of min(), max(), and the ceiling() and floor() functions:

set.seed(1234)
df <- data.frame(
  y1=rnorm(100,10,1),
  y2=rnorm(100,12,3),
  y3=rnorm(100,5,4),
  y4=rnorm(100,13,5))
    
for (i in 1:ncol(df)) {
  p <- ggplot(df, aes(df[,i])) +
    geom_histogram(alpha=0.5, color='black', fill='red', binwidth=1) +
    scale_x_continuous(breaks=seq(floor(min(df[,i])),ceiling(max(df[,i])))) +
    ggtitle(names(df)[i])
  
  # get max counts
  max_count <- max(ggplot_build(p)$data[[1]]$count)
  p <- p + scale_y_continuous(breaks=seq(0,max_count,1)) 

  print(p)
}

Is there a better way?

While that gets you what need, it's typically hard to deal with multiple plots output to your graphics device iteratively. I would recommend reformatting the above code as a function and then using lapply() and using something like plot_grid() from cowplot to display the output. This suggested approach is detailed in the code below:

myPlots <- function(data, column, fill_color) {
  # column = character name of column
  p <- ggplot(data, aes_string(x=column)) +
    geom_histogram(fill='red', binwidth=1, alpha=0.5, color='black') +
    scale_x_continuous(breaks=seq(floor(min(data[column])), ceiling(max(data[column])),1)) +
    ggtitle(column)
  
  max_count <- max(ggplot_build(p)$data[[1]]$count)
  p <- p + scale_y_continuous(breaks=seq(0,max_count,1))
  return(p)
}

library(cowplot)

plotList <- lapply(names(df), myPlots, data=df)
plot_grid(plotlist = plotList)

enter image description here

Figured it out - my values are integers, so what ended up working was a variation on Duck's response. See below:

loop.vector <- 2:5
for (i in loop.vector){
x <- df[,i]
print(ggplot(df,aes(x=x)) + geom_histogram(binwidth=1)+scale_x_continuous(breaks=seq(0,max((x),1)))+scale_y_continuous(breaks=seq(0,max(table(x)),1)))
}
Related