计算 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
为您找到默认布局;事实上,如果缺少 nrow
和 ncol
,它已经被 grid.arrange
使用
grid.arrange(grobs = replicate(7, rectGrob(), simplify=FALSE))
我在 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
为您找到默认布局;事实上,如果缺少 nrow
和 ncol
,它已经被 grid.arrange
使用
grid.arrange(grobs = replicate(7, rectGrob(), simplify=FALSE))