计算 R 中给定数量绘图的最佳网格布局尺寸

Calculate the optimal grid layout dimensions for a given amount of plots in R

我在 ggplot 中有 12 个图,我正在用 grid.arrange 排列它们。我手动将网格中的行数设置为 4,将列数设置为 3。由于 3 x 4 = 12,这非常有效。

但是如果我有任意数量的地块怎么办?说 13 ... 我如何以编程方式找到要使用的行数和列数,使整个图成为最 "square-like" 形状?

我想在 R 中执行此操作。

更新 Link 至数据:http://github.com/ngfrey/DataGenii/blob/master/exampleMedicalData.csv

这是我今天早上处理的代码。希望它将提供一个更具说明性的示例。请注意我是如何在函数的 return(list(plots=plots, numrow=4, numcol=3)) 部分设置行数和列数的:

makePlots<- function(fdf){
idx<- which(sapply(fdf, is.numeric))
idx<- data.frame(idx)
names(idx)<- "idx"
idx$names<- rownames(idx)
plots<- list()

for(i in 2:length(idx$idx)) {
  varname<- idx$names[i]
  mydata<- fdf[, idx$names[i]]
  mydata<- data.frame(mydata)
  names(mydata)<- varname
  g<- ggplot(data=mydata, aes_string(x=varname) )
  g<- g + geom_histogram(aes(y=..density..), color="black", fill='skyblue')+ geom_density() + xlab(paste(varname))
  print(g)


  plots<- c(plots, list(g))
}

return(list(plots=plots, numrow=4, numcol=3 ))
}
res<- makePlots(fdf)
do.call(grid.arrange, c(res$plots, nrow=res$numrow, ncol=res$numcol))

实际中能够合理展示的情节有限,美观的布置也很少,大家可以列成表格。

然而,我们能做的是为较大尺寸与较小尺寸的比率指定一个公差。然后我们找到最接近我们目标的两个数字。如果这些在公差范围内,我们就完成了。否则,我们会给目标增加浪费。这终止于较早找到合适的对或下一个最大的正方形。 (公差应 > 1)。

最接近给定因子的两个数中的最小值 n

fact<-function(n) {
  k<-floor(sqrt(n));
  for(i in k:1) {if (n%%i == 0) return(i)}
}

在公差范围内搜索附近的正方形

nearsq<-function(n,tol=5/3+0.001) {
  m<-ceiling(sqrt(n))^2;
  for(i in n:m) {
    a<-fact(i);
    b<-i/a;
    if(b/a < tol) return(c(a,b))
  }
}

例子

#probably too many plots
nearsq(83)
#> [1]  8 11

#a more reasonable range of plots, tabulated
cbind(12:36,t(Vectorize(nearsq)(12:36)))
      [,1] [,2] [,3]
 [1,]   12    3    4
 [2,]   13    3    5
 [3,]   14    3    5
 [4,]   15    3    5
 [5,]   16    4    4
 [6,]   17    4    5
 [7,]   18    4    5
 [8,]   19    4    5
 [9,]   20    4    5
[10,]   21    4    6
[11,]   22    4    6
[12,]   23    4    6
[13,]   24    4    6
[14,]   25    5    5
[15,]   26    5    6
[16,]   27    5    6
[17,]   28    5    6
[18,]   29    5    6
[19,]   30    5    6
[20,]   31    5    7
[21,]   32    5    7
[22,]   33    5    7
[23,]   34    5    7
[24,]   35    5    7
[25,]   36    6    6

下面是我如何让这个坏男孩工作的: (我仍然可以收紧轴标签,并可能压缩 makePlots() 函数中的前 2 个 if 语句,这样它会 运行 更快,但我稍后会解决这个问题 date/post )

library(gmp)
library(ggplot2)
library(gridExtra)

############
factors <- function(n)
{
   if(length(n) > 1) 
   {
      lapply(as.list(n), factors)
   } else
   {
      one.to.n <- seq_len(n)
      one.to.n[(n %% one.to.n) == 0]
   }
}


###########
makePlots<- function(fdf){
idx<- which(sapply(fdf, is.numeric))
idx<- data.frame(idx)
names(idx)<- "idx"
idx$names<- rownames(idx)
plots<- list()

for(i in 2:length(idx$idx)) {
  varname<- idx$names[i]
  mydata<- fdf[, idx$names[i]]
  mydata<- data.frame(mydata)
  names(mydata)<- varname
  g<- ggplot(data=mydata, aes_string(x=varname) )
  g<- g + geom_histogram(aes(y=..density..), color="black", fill='skyblue')+ geom_density() + xlab(paste(varname))
  print(g)


  plots<- c(plots, list(g))
}

numplots<- 0
#Note: The reason I put in length(idx$idx)-1 is because the first column is the row indicies, which are usually numeric ;)
#isprime returns 0 for non-prime numbers, 2 for prime numbers
if(length(idx$idx) == 2){
  numplots<- length(idx$idx)
  ncolx<- 1
  nrowx<- 2
} else if(length(idx$idx)==3){
  numplots<- length(idx$idx)
  ncolx<- 1
  nrowx<- 3
} else if(isprime((length(idx$idx)-1)) !=0){ 
  numplots<- length(idx$idx)
  facts<- factors(numplots)
  ncolx<- facts[length(facts)/2]
  nrowx<- facts[(length(facts)/2) + 1]

} else{numplots<- (length(idx$idx)-1)
  facts<- factors(numplots)
  ncolx<- facts[length(facts)/2]
  nrowx<- facts[(length(facts)/2) + 1]}

if(abs(nrowx-ncolx)>2){
  ncolx<- ncolx+1
  nrowx<- ceiling(numplots/ncolx)
}


return(list(plots=plots, numrow=nrowx, numcol=ncolx ))
}
res<- makePlots(fdf)
do.call(grid.arrange, c(res$plots, nrow=res$numrow, ncol=res$numcol))

?n2mfrow 为您找到默认布局;事实上,如果缺少 nrowncol,它已经被 grid.arrange 使用

grid.arrange(grobs = replicate(7, rectGrob(), simplify=FALSE))