ggplot2:如何强制数量太少的小平面? [英] ggplot2: How to force the number of facets with too few plots?

查看:222
本文介绍了ggplot2:如何强制数量太少的小平面?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  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变量并创建一个包含数据或< nullGrob (即,如果存在该因子级别的数据的情况下为空的占位符)的图表,将它添加到列表 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 nullGrobs. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆