ggplot2:如何强制数量太少的小平面? [英] ggplot2: How to force the number of facets with too few plots?
问题描述
id变量值
1段III | RIM黑莓Pearl | 5.600000
2显示尺寸| RIM黑莓Pearl | 6.500000
3语音/通话质量| RIM黑莓Pearl | 5.600000
即时消息可用性| RIM黑莓Pearl | 7.200000
5媒体质量| RIM黑莓Pearl | 6.100000
6轻松使用键入| RIM黑莓Pearl | 5.700000
7访问电子邮件的速度| RIM黑莓Pearl | 6.400000
8第二部分| RIM黑莓Pearl | 5.545455
9物有所值| RIM黑莓Pearl | 6.000000
10第三部分| Palm Treo 700p | 4.320000
11显示屏尺寸| Palm Treo 700p | 6.500000
12语音/通话质量| Palm Treo 700p | 8.000000
即时通讯可用性| Palm Treo 700p | 5.100000
14媒体质量| Palm Treo 700p | 7.000000
15轻松使用键入| Palm Treo 700p | 6.200000
16访问电子邮件的速度| Palm Treo 700p | 6.500000
17第二部分| Palm Treo 700p | 4.454545
18物有所值| Palm Treo 700p | 5.400000
19第三部分|摩托罗拉Q | 4.680000
20显示尺寸|摩托罗拉Q | 7.400000
21语音/通话质量|摩托罗拉Q | 4.800000
22即时通讯可用性|摩托罗拉Q | 5.300000
23媒体质量|摩托罗拉Q | 6.900000
24轻松使用键入|摩托罗拉Q | 7.400000
25访问电子邮件的速度|摩托罗拉Q | 8.000000
26第二部分|摩托罗拉Q | 3.121212
27物有所值|摩托罗拉Q | 4.200000
28第三部分|诺基亚9300 | 4.360000
29显示尺寸|诺基亚9300 | 6.400000
30语音/通话质量|诺基亚9300 | 7.800000
31即时通讯可用性|诺基亚9300 | 6.700000
32媒体质量|诺基亚9300 | 5.900000
33轻松使用键入|诺基亚9300 | 4.500000
34访问电子邮件的速度|诺基亚9300 | 6.300000
第二部分|诺基亚9300 | 7.181818
36物有所值|诺基亚9300 | 4.600000
37第三部分|索尼爱立信M600i | 4.360000
38显示尺寸|索尼爱立信M600i | 7.300000
39语音/通话质量|索尼爱立信M600i | 8.000000
即时通讯可用性|索尼爱立信M600i | 1.500000
41媒体质量|索尼爱立信M600i | 7.800000
42轻松使用键入|索尼爱立信M600i | 5.000000
43访问电子邮件的速度|索尼爱立信M600i | 8.100000
44第二部分|索尼爱立信M600i | 3.606061
45物有所值|索尼爱立信M600i | 4.000000
第三部分| Sidekick3 | 7.040000
47显示尺寸| Sidekick3 | 7.200000
48语音/通话质量| Sidekick3 | 6.300000
49即时消息可用性| Sidekick3 | 7.200000
50媒体质量| Sidekick3 | 6.400000
51轻松使用键入| Sidekick3 | 6.800000
52访问电子邮件的速度| Sidekick3 | 6.200000
第二部分| Sidekick3 | 3.424242
54物有所值| Sidekick3 | 5.300000
然后我使用下面的代码:
ggplot(data = data_sub,aes(x = variable,y = value))+
geom_bar(stat =identity)+
facet_wrap(〜id ,ncol = 3)+
coord_flip()+
主题(axis.title.x = element_blank(),
axis.title.y = element_blank(),
面板。 grid = element_blank(),
legend.position =none)
我如何得到类似以下的内容?
非空因子水平和每个空因子水平的空白占位符:
首先,使用内置的 mtcars
数据框架中,我们将构面变量设置为9个等级的因子,但只有5个等级具有任何数据:
库(ggplot2)
库(grid)
库(gridExtra)
d = mtcars
set.seed(4193)
d $ cyl = sample(1: 9,nrow(d),replace = TRUE)
d $ cyl < - factor(d $ cyl,levels = sort(unique(d $ cyl)))
d < - 子集(d,cyl %c(1,5,7:9))
#确定没有任何数据的因子水平
blanks = which(table(d $ cyl)== 0)
#初始化列表
pl = list()
for循环下面贯穿每个级别faceting变量并创建一个包含数据或< pl
中。
for(i in 1:length(levels(d $ cyl))){
if (i%in%blanks){
pl [[i]] = nullGrob()
} else {
pl [[i] ] = ggplot(d [d $ cyl%in%levels(d $ cyl)[i],],aes(x = am,y = wt))+
geom_point()+
facet_grid( 。cyl)
}
}
现在,lay出来的地块,并添加他们周围的边界:
do.call(grid.arrange,c(pl,ncol = 3) )
grid.rect(.5,.5,gp = gpar(lwd = 2,fill = NA,col =black))
更新: A功能我想添加到我的答案是删除不在最左边一列或最下面一行的绘图的轴标签(更像是OP中的格式)。以下是我不太成功的尝试。
当您从某些地块删除坐标轴标记和/或标签时,出现的问题是绘图区域在不同的地块中最终会有不同的大小。原因是所有图都占据相同的物理区域,但带有轴标签的图使用轴标签的某些区域,使得它们的绘图区相对于没有轴标签的图较小。
我希望我可以使用 cowplot
包中的 plot_grid
来解决此问题(由@ClausWilke创作),但 plot_grid
不适用于 nullGrob
s。然后@baptiste为这个问题增加了另一个答案,他从那以后被删除了,但对于至少有10,000名声望的SO用户仍然可以看到。这个答案让我意识到他的 egg
包和 set_panel_size
函数,用于设置不同ggplots的通用面板大小。
以下是我尝试使用 set_panel_size
来解决绘图区域问题。这是不是很成功,我会在显示代码和情节后更详细地讨论。
#devtools: :install_github(baptiste / egg)
库(蛋)
#制作barplot的假数据。再一次,我们有9个方面级别,
#但只有5个级别的数据。
set.seed(4193)
d = data.frame(facet = rep(LETTERS [1:9],each = 100),
group = sample(paste(Group,1 :5),900,replace = TRUE))
d < - subset(d,facet%in%LETTERS [c(1,5,7:9)])
#Identify没有任何数据的因素水平
blanks = which(table(d $ facet)== 0)
#初始化列表
pl = list()
for(i in 1:length(levels(d $ facet))){
if(i%in%blanks){
pl [[i]] = nullGrob()
} else {
#创建图,包括所有图中的常见y范围
#(尽管这成为x范围由于coord_flip)
pl [[i]] = ggplot(d [d $ facet%in%levels(d $ facet)[i],],aes(x = group))+
geom_bar ()+
facet_grid(。〜facet)+
coord_flip()+
labs(x =,y =)+
scale_y_continuous(limits = c(0 ,max(table(d $ group,d $ facet))))
#如果面板不在左边缘,删除y轴标签
if(!(i %以%seq(1,9,3 )){
pl [[i]] = pl [[i]] + theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank())
$ b#如果面板不在底部,则删除x轴标签
if(i%in%1:6){
pl [[i ]] = pl [[i]] + theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
}
}
#如果面板是一个绘图(而不是nullGrob),
#删除边距并设置为常用面板大小
if(any(class(pl [[i]])%in %c(ggplot,gtable))){
pl [[i]] = pl [[i]] + theme(plot.margin = unit(rep(-1,4)),lines ))
pl [[i]] = set_panel_size(pl [[i]],width = unit(4,cm),height = unit(3,cm))
}
}
现在布置图:
do.call(grid.arrange,c(pl,ncol = 3))
grid.rect(.5,.5,gp = gpar(lwd = 2,fill = NA,col =black))
请参阅plo即使这些图都具有相同的面板尺寸,它们之间的边距也不是恒定的,可能是由于> grid.arrange
处理null grobs的间距,取决于哪些职位有实际的情节。另外,因为 set_panel_size
设置了绝对尺寸,所以我必须手动调整最终的绘图尺寸以尽可能地将面板尽可能靠近在一起,同时仍然避免重叠。我希望SO的居民 grid
专家会放弃并提出更有效的方法。
(也请注意,使用这种方法,您可以在给定的行或列中没有标记的绘图。如果只有标记B,C,E和F出现,那么就会出现这种情况。而不是布局中任何标记的图表,我不知道OP如何处理这种情况(一种选择是添加逻辑以将标签保持在内部图上,如果外部对于给定的行或列,情节不存在),但我认为值得指出。)
To plot 9 histograms per ggplot graph I used the following data :
id variable value
1 Segment III | RIM BlackBerry Pearl | 5.600000
2 Display size | RIM BlackBerry Pearl | 6.500000
3 Voice/call quality | RIM BlackBerry Pearl | 5.600000
4 Instant messaging availability | RIM BlackBerry Pearl | 7.200000
5 Media quality | RIM BlackBerry Pearl | 6.100000
6 Ease of use for typing | RIM BlackBerry Pearl | 5.700000
7 Speed in accessing email | RIM BlackBerry Pearl | 6.400000
8 Segment II | RIM BlackBerry Pearl | 5.545455
9 Value for money | RIM BlackBerry Pearl | 6.000000
10 Segment III | Palm Treo 700p | 4.320000
11 Display size | Palm Treo 700p | 6.500000
12 Voice/call quality | Palm Treo 700p | 8.000000
13 Instant messaging availability | Palm Treo 700p | 5.100000
14 Media quality | Palm Treo 700p | 7.000000
15 Ease of use for typing | Palm Treo 700p | 6.200000
16 Speed in accessing email | Palm Treo 700p | 6.500000
17 Segment II | Palm Treo 700p | 4.454545
18 Value for money | Palm Treo 700p | 5.400000
19 Segment III | Motorola Q | 4.680000
20 Display size | Motorola Q | 7.400000
21 Voice/call quality | Motorola Q | 4.800000
22 Instant messaging availability | Motorola Q | 5.300000
23 Media quality | Motorola Q | 6.900000
24 Ease of use for typing | Motorola Q | 7.400000
25 Speed in accessing email | Motorola Q | 8.000000
26 Segment II | Motorola Q | 3.121212
27 Value for money | Motorola Q | 4.200000
28 Segment III | Nokia 9300 | 4.360000
29 Display size | Nokia 9300 | 6.400000
30 Voice/call quality | Nokia 9300 | 7.800000
31 Instant messaging availability | Nokia 9300 | 6.700000
32 Media quality | Nokia 9300 | 5.900000
33 Ease of use for typing | Nokia 9300 | 4.500000
34 Speed in accessing email | Nokia 9300 | 6.300000
35 Segment II | Nokia 9300 | 7.181818
36 Value for money | Nokia 9300 | 4.600000
37 Segment III | Sony Ericsson M600i | 4.360000
38 Display size | Sony Ericsson M600i | 7.300000
39 Voice/call quality | Sony Ericsson M600i | 8.000000
40 Instant messaging availability | Sony Ericsson M600i | 1.500000
41 Media quality | Sony Ericsson M600i | 7.800000
42 Ease of use for typing | Sony Ericsson M600i | 5.000000
43 Speed in accessing email | Sony Ericsson M600i | 8.100000
44 Segment II | Sony Ericsson M600i | 3.606061
45 Value for money | Sony Ericsson M600i | 4.000000
46 Segment III | Sidekick3 | 7.040000
47 Display size | Sidekick3 | 7.200000
48 Voice/call quality | Sidekick3 | 6.300000
49 Instant messaging availability | Sidekick3 | 7.200000
50 Media quality | Sidekick3 | 6.400000
51 Ease of use for typing | Sidekick3 | 6.800000
52 Speed in accessing email | Sidekick3 | 6.200000
53 Segment II | Sidekick3 | 3.424242
54 Value for money | Sidekick3 | 5.300000
Then I used the following code :
ggplot(data = data_sub, aes(x = variable, y = value)) +
geom_bar(stat = "identity") +
facet_wrap(~id, ncol = 3) +
coord_flip() +
theme(axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid = element_blank(),
legend.position = "none")
And got :
My question :
When I have fewer graphs, for example only one, I would like to keep that formating. However I get only a big graph like the following (do not mind the legends).
How can I get something like the following ?
One approach is to create a plot for each non-empty factor level and a blank placeholder for each empty factor level:
First, using the built-in mtcars
data frame, we set up the faceting variable as a factor with 9 levels, but only 5 levels with any data:
library(ggplot2)
library(grid)
library(gridExtra)
d = mtcars
set.seed(4193)
d$cyl = sample(1:9, nrow(d), replace=TRUE)
d$cyl <- factor(d$cyl, levels=sort(unique(d$cyl)))
d <- subset(d, cyl %in% c(1,5,7:9))
# Identify factor levels without any data
blanks = which(table(d$cyl)==0)
# Initialize a list
pl = list()
The for loop below runs through each level of the faceting variable and creates a plot of the level has data or a nullGrob
(that is, an empty placeholder where the plot would be if there were data for that factor level) and adds it to the list pl
.
for (i in 1:length(levels(d$cyl))) {
if(i %in% blanks) {
pl[[i]] = nullGrob()
} else {
pl[[i]] = ggplot(d[d$cyl %in% levels(d$cyl)[i], ], aes(x=am, y=wt) ) +
geom_point() +
facet_grid(.~ cyl)
}
}
Now, lay out the plots and add a border around them:
do.call(grid.arrange, c(pl, ncol=3))
grid.rect(.5, .5, gp=gpar(lwd=2, fill=NA, col="black"))
UPDATE: A feature I'd like to add to my answer is removing axis labels for plots that are not on the left-most column or the bottom row (to be more like format in the OP). Below is my not-quite-successful attempt.
The problem that comes up when you remove axis ticks and/or labels from some of the plots is that the plot areas end up being different sizes in different plots. The reason for this is that all the plots take up the same physical area, but the plots with axis labels use some of that area for the axis labels, making their plot areas smaller relative to plots without axis labels.
I had hoped that I could resolve this using plot_grid
from the cowplot
package (authored by @ClausWilke), but plot_grid
doesn't work with nullGrob
s. Then @baptiste added another answer to this question, which he's since deleted, but which is still visible to SO users with at least 10,000 in reputation. That answer made me aware of his egg
package and the set_panel_size
function, for setting a common panel size across different ggplots.
Below is my attempt to use set_panel_size
to solve the plot-area problem. It wasn't quite successful, which I'll discuss in more detail after showing the code and the plot.
# devtools::install_github("baptiste/egg")
library(egg)
# Fake data for making a barplot. Once again we have 9 facet levels,
# but with data for only 5 of the levels.
set.seed(4193)
d = data.frame(facet=rep(LETTERS[1:9],each=100),
group=sample(paste("Group",1:5),900,replace=TRUE))
d <- subset(d, facet %in% LETTERS[c(1,5,7:9)])
# Identify factor levels without any data
blanks = which(table(d$facet)==0)
# Initialize a list
pl = list()
for (i in 1:length(levels(d$facet))) {
if(i %in% blanks) {
pl[[i]] = nullGrob()
} else {
# Create the plot, including a common y-range across all plots
# (though this becomes the x-range due to coord_flip)
pl[[i]] = ggplot(d[d$facet %in% levels(d$facet)[i], ], aes(x=group) ) +
geom_bar() +
facet_grid(. ~ facet) +
coord_flip() +
labs(x="", y="") +
scale_y_continuous(limits=c(0, max(table(d$group, d$facet))))
# If the panel isn't on the left edge, remove y-axis labels
if(!(i %in% seq(1,9,3))) {
pl[[i]] = pl[[i]] + theme(axis.text.y=element_blank(),
axis.ticks.y=element_blank())
}
# If the panel isn't on the bottom, remove x-axis labels
if(i %in% 1:6) {
pl[[i]] = pl[[i]] + theme(axis.text.x=element_blank(),
axis.ticks.x=element_blank())
}
}
# If the panel is a plot (rather than a nullGrob),
# remove margins and set to common panel size
if(any(class(pl[[i]]) %in% c("ggplot","gtable"))) {
pl[[i]] = pl[[i]] + theme(plot.margin=unit(rep(-1,4), "lines"))
pl[[i]] = set_panel_size(pl[[i]], width=unit(4,"cm"), height=unit(3,"cm"))
}
}
Now lay out the plots:
do.call(grid.arrange, c(pl, ncol=3))
grid.rect(.5, .5, gp=gpar(lwd=2, fill=NA, col="black"))
As you can see in the plot below, even though the plots all have the same panel sizes, the margins between them are not constant, presumably due to the way grid.arrange
handles spacing for null grobs, depending on which positions have actual plots. Also, because set_panel_size
sets absolute sizes, I had to size the final plot by hand to get the panels as close to together as possible while still avoiding overlaps. I'm hoping one of SO's resident grid
experts will drop by and suggest a more effective approach.
(Also note that with this approach, you can end up without a labeled plot in a given row or column. In the example below, plot "E" has no y-axis labels and plot "D" is missing, so you have to look in a different row to see what the labels are. If only plots "B", "C","E" and "F" were present, there would not be any labeled plots in the layout. I don't know how the OP wants to deal with this situation (one option would be to add logic to keep labels on "interior" plots if the "outer" plot is absent for a given row or column), but I thought it was worth pointing out.)
这篇关于ggplot2:如何强制数量太少的小平面?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!