Ggplot2: How to force the number of faces with too few sections?

To build 9 histograms on the 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 received:

My question is:

When I have fewer schedules, for example, only one, I would like to keep this formation. However, I get only a large graph as follows (not against legends).

How can I get something like the following? enter image description here

+7
r ggplot2 facet
source share
3 answers

One approach is to create a graph for each non-empty factor level and an empty placeholder for each empty factor level:

Firstly, using the built-in mtcars data mtcars , we set the cut 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 goes through each level of the variable cut and creates a level graph with data or nullGrob (that is, an empty placeholder that displays the graph if there were data for this factor level) and adds it to the pl list.

 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")) 

enter image description here

UPDATE:. The function that I would like to add to my answer is to remove axis labels for graphs that are not in the leftmost column or bottom row (to be more similar to the format in OP). Below is my unsuccessful attempt.

The problem that arises when deleting axial marks and / or labels in some sections is that plot sections in different graphs have different sizes. The reason for this is that all graphs occupy the same physical region, but graphs with axis axes use some part of this region for axis labels, making their sections smaller than graphs without axis labels.

I was hoping I could solve this with plot_grid from the plot_grid package (created by @ClausWilke), but plot_grid does not work with nullGrob s. Then @baptiste added another answer to this question, which he has since deleted, but which remains visible to SO users with a reputation of at least 10,000 users. This answer let me know about its egg package and the set_panel_size function for setting the total panel size in different ggplots.

Below I tried to use set_panel_size to solve the scope area problem. This was not entirely successful, which I will discuss in more detail after showing the code and 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 graphs:

 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 graph below, even if the graphs have the same panel sizes, the fields between them are not constant, presumably due to the fact that grid.arrange processes the interval for zero levels, depending on which positions have actual plots . In addition, since set_panel_size sets the absolute dimensions, I had to manually determine the final result in order to assemble the panels as close to each other as possible, while avoiding overlapping. I hope one of the representatives of the SO residents will be grid and will offer a more efficient approach.

(Also note that with this approach, you can end without a marked graph in a given row or column. In the example below, there are no Y axis labels on the graph β€œE”, and the graph β€œD” is missing, so you need to look in another row, to see what the labels are. If only the graphs β€œB”, β€œC”, β€œE” and β€œF” were present, there would be no labeled graphs in the layout. I don’t know how the OP wants to deal with this situation (one of options would be adding logic to store labels on the "internal" graphs, if the "external" plot is about not present for a given row or column), but I thought it was worth pointing out.)

enter image description here

+2
source share

try it

 d <- subset(mtcars, cyl==4) d$cyl <- factor(d$cyl, levels=unique(mtcars$cyl)) ggplot(d, aes(x=am, y=wt) ) + geom_point() + facet_grid(.~cyl, drop = FALSE) 
+1
source share

a possible solution is to create a list of ggplots and replace some of them with dummy placeholders.

 d <- data.frame(x=rnorm(90), y=rnorm(90), f1 = gl(3, 30) , f2 = rep(gl(3, 10), 3) ) p <- ggplot(d, aes(x, y)) + geom_point() # if you want to keep the facet labels # p <- p + facet_grid(f1~f2) library(plyr) pl <- dlply(d, .(f1, f2), "%+%", e1=p, .drop = FALSE) .dummy_plot <- ggplot() + theme_void() pl[c(3,4,7)] <- rep(list(.dummy_plot), 3, simplify=FALSE) # devtools::install_github("baptiste/egg) library(grid) grid.newpage() grid.draw(egg::ggarrange(plots=pl)) # alternatively # library(cowplot) # plot_grid(plotlist = pl) 

enter image description here

0
source share

All Articles