Force Facet_Wrap to Fill Bottom Row (And Leave Any "Gaps" in the Top Row)

Force facet_wrap to fill bottom row (and leave any gaps in the top row)

Would this fix suffice?

library(ggplot2)
n <- 1000
df <- data.frame(x = runif(n), y=rnorm(n), label = sample(letters[1:7],
size = n, replace = TRUE), stringsAsFactors=TRUE)
# following @Didzis' suggestion (with some minor changes)
df$label.new <- factor(df$label, levels=sort(c(""," ",levels(df$label))))
p <- ggplot(df, aes(x=x, y=y)) + geom_point() +
facet_wrap(~ label.new, ncol=3,drop=FALSE)

Sample Image


EDIT (from @baptiste):

Starting from the last solution, it's easier to remove the grobs from the gtable,

g = ggplotGrob(p)
## remove empty panels
g$grobs[names(g$grobs) %in% c("panel1", "panel2", "strip_t.1", "strip_t.2")] = NULL
## remove them from the layout
g$layout = g$layout[!(g$layout$name %in% c("panel-1", "panel-2", 
                                                "strip_t-1", "strip_t-2")),]
## move axis closer to panel
g$layout[g$layout$name == "axis_l-1", c("l", "r")] = c(9,9)
grid.newpage()
grid.draw(g)

Sample Image

How to force facet_wrap to draw last panel in right bottom corner and leave the white gap

I don't know a way to do this naturally with ggplot2 or extension packages. If there is a more straightforward way to do this, I'll gladly hear it. However, you can edit the gtable that your plot produces to get the result you want. For brevity (and because I couldn't run your code without errors), I simplified the example a bit, but it would probably work similarly for your plot. The example also assumes fixed x-axes.

First, we convert the plot to a gtable. We can inspect the row/column numbers with gtable::gtable_show_layout().

library(ggplot2)
library(grid)

df <- data.frame(
x = 1:7, y = 1:7,
facet = LETTERS[1:7]
)

g <- ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(~ facet)

gt <- ggplotGrob(g)
gtable::gtable_show_layout(gt)

Sample Image

In the layout above we need to figure out which cells we want to swap. We want to swap the 7th panel with the 9th (empty) panel. So, we find the cells that belong to these panels.

panel_7 <- which(gt$layout$l %in% 4:5 & gt$layout$t %in% 14:19)
panel_9 <- which(gt$layout$l %in% 12:13 & gt$layout$t %in% 14:19)

Then, we adjust the left (l) and right (r) positions of the grobs in these cells so that they are being swapped.

gt$layout$l[panel_7] <- gt$layout$l[panel_7] + 8
gt$layout$r[panel_7] <- gt$layout$r[panel_7] + 8
gt$layout$l[panel_9] <- gt$layout$l[panel_9] - 8
gt$layout$r[panel_9] <- gt$layout$r[panel_9] - 8

grid.newpage(); grid.draw(gt)

Sample Image

Created on 2021-04-01 by the reprex package (v1.0.0)

In facet_wrap, how to align the second row to the right?

As per this answer you can manipulate the plot as a grid object.

library(ggplot2)
library(dplyr)
library(grid)
# inject blank level into factor
d1 <- mutate(mtcars,
carb = factor(replace(carb, carb == 8, 6),
levels = c(1:3, 0, 4, 6),
labels = c("Replay-Say", "Replay-Project", "Replay-Proj-Say",
"", "Project", "Project-Say")))
p1 <- ggplot(data = d1,
mapping = aes(x = factor(cyl))) +
geom_bar() + xlab("") + ylab("") + facet_wrap(~carb, drop = FALSE)
p1

facetted bar chart with blank panel
Depending on the layout of the plot you will need to identify the name of the panel and tick marks you wish to remove.

g1 <- ggplotGrob(p1)
g1$layout$name
# [1] "background" "panel-1-1" "panel-3-1" "panel-1-2" "panel-2-2"
# [6] "panel-3-2" "axis-t-1-2" "axis-t-2-2" "axis-t-3-2" "axis-t-1-1"
# [11] "axis-t-2-1" "axis-t-3-1" "axis-b-1-2" "axis-b-2-2" "axis-b-3-2"
# ...

You can then remove these from the plot. The letter assignations appear to be top ("t"), bottom ("b"), left ("l"), right ("r").

blank_panel_grobs <- c("panel-2-1", "strip-t-1-2", "axis-b-1-2", "axis-l-2-1")
blank_panel_index <- g1$layout$name %in% blank_panel_grobs
g1$layout <- lapply(g1$layout, function(x) x[!blank_panel_index])
g1$grobs <- g1$grobs[!blank_panel_index]
grid.draw(g1)

facetted bar chart with removed panel

Align gridArranged facetted ggplots

Sample ImageHere's a solution with some guidance from this question.

library(ggplot2)
library(gridExtra)

ncol = 3
df <- data.frame(x=rep(seq(0.05,1,by=0.05),times=40),
y=factor(sample(c('A','B'),20*40,replace=TRUE), levels = c("A", "B")),
id=rep(1:40,each=20),
group=c(rep(1,20*12),rep(2,20*12),rep(3,20*16)))

max_cases <- max(table(unique(df[,c("id", "group")])$group))

# create phantom plots for everything in the containing rectangle to standardize labels
rect_dim <- ceiling(max_cases / ncol) * ncol

plots <- lapply(X=unique(df$group), FUN= function(i){

df_case <- subset(df, subset= group == i)
tot_case <- nrow(unique(df_case[,c("id", "group")]))
# create fill levels to pad the plots
fill_levels <- unlist(lapply(X=1:(rect_dim - tot_case), function(y){paste0(rep(x=" ", times=y), collapse="")}))
df_case$id.label <- ordered(df_case$id, levels = c(unique(df_case$id), fill_levels))

g_case <- ggplot(df_case,aes(x,y,group=id.label)) +
geom_line() +
facet_wrap(~id.label, ncol = ncol, drop=FALSE)

# whiteout the inner y axis elements to clean it up a bit
if(i != 1){
g_case <- g_case + theme(axis.text.y = element_text(color = "white"),
axis.title.y = element_text(color = "white"),
axis.ticks.y = element_line(color = "white"))
}

g_case <- ggplotGrob(g_case)
rm_me <- (tot_case:rect_dim)[-1]
# remove empty panels and layout
g_case$grobs[names(g_case$grobs) %in% c(paste0("panel", rm_me), paste0("strip_t.", rm_me))] <- NULL
g_case$layout <- g_case$layout[!(g_case$layout$name %in% c(paste0("panel-", rm_me), paste0("strip_t-", rm_me))),]
g_case
})

plots$nrow = 1
do.call("grid.arrange", plots)

make a figure in R with two panels in the top row and three in the bottom row

plot.mat = matrix(c(1, 1, 1, 2, 2, 2,
3, 3, 4, 4, 5, 5),
nrow = 2, byrow = T)

layout(plot.mat)
layout.show(n = 5)
# looks good
for (i in 1:5) plot(rnorm(10), rnorm(10))

The ?layout help is well-written and has plenty of examples.

Order the panels from right to left in ggplot

Here's a solution that's somewhat hacky, because it requires manual positioning. We create a plotting function and then plot the first row of plots (rows 1 to 3 of df and the second row of plots (rows 4 and 5 of df) separately. We lay them out using grid.arrange, but we add a blank plot with just the y-axis at the left end of the second row to create the blank space.

The manual adjustment of the widths argument is necessary to get the blank plot to take up just the right amount of space so that the other two plots line up vertically with the plots above.

library(gridExtra)
library(grid)

pf = function(data, xrng=range(df$x), yrng=range(df$y)) {
ggplot(data, aes(x,y)) +
geom_point(size=3) +
facet_wrap(~ factor(group, rev(group))) +
scale_y_continuous(limits=yrng) +
scale_x_continuous(limits=xrng)
}

grid.arrange(pf(df[1:3,]),
arrangeGrob(pf(data.frame(x=-10,y=-10, group="x")) +
theme(panel.border=element_blank(),
panel.background=element_blank(),
strip.background=element_rect(colour=NA, fill=NA),
strip.text=element_text(colour=NA),
axis.text.x=element_text(colour=NA),
axis.title.x=element_text(colour=NA),
axis.ticks.x=element_blank(),
axis.title.y=element_text(angle=90, vjust=0.5),
axis.text.y=element_text(angle=0),
axis.ticks.y=element_line()),
pf(df[4:5,]) + theme(axis.text.y=element_blank(),
axis.title.y=element_blank(),
axis.ticks.y=element_blank()) ,
widths=c(1.12,2)),
ncol=1)

Sample Image

Error in dividing the column values into Top 20% and Bottom 80%

Like this?

library(dplyr)

Vec <- data.frame(Vec = c(70.0600, 8.5100, 5.8600, 399.9800, 9.0600, 78.8200, 71.4600))

Vec %>%
mutate(up = quantile(Vec, .8),
part = ifelse(Vec > up, "Top_20", "Bottom_80"))

Vec up part
1 70.06 77.348 Bottom_80
2 8.51 77.348 Bottom_80
3 5.86 77.348 Bottom_80
4 399.98 77.348 Top_20
5 9.06 77.348 Bottom_80
6 78.82 77.348 Top_20
7 71.46 77.348 Bottom_80

Create a heatmaps with average values on the very right column and bottom row

You could make use of an ifelse to replace the values mapped on fill to NA for your average column and row like so. The value to be used for the NA value could then be set via the na.value argument of scale_fill_xxx where I chose NA or transparent:

library(ggplot2)

ggplot(mapping = aes(ind, hour)) +
geom_tile(aes(fill = ifelse(!(ind == "Average" | hour == 1), values, NA)), subset(dat2, hour != "Average" & ind != "Sum")) +
geom_text(aes(label = round(values, 1)), dat2) +
scale_y_discrete(limits = c("Average", 24:1)) +
scale_x_discrete(limits = c("E","T","K","N","R","L","P", "Average"), position = "top") +
viridis::scale_fill_viridis(na.value = NA) +
theme_minimal() + theme(axis.title = element_blank()) +
labs(fill = "values")

Sample Image



Related Topics



Leave a reply



Submit