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)
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)
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)
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)
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
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)
Align gridArranged facetted ggplots
Here'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)
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")
Related Topics
Truncate Decimal to Specified Places
Counting Occurrence of Particular Letter in Vector of Words in R
Match Two Columns with Two Other Columns
Convert Table into Matrix by Column Names
Unexpected Symbol Error in Parse(Text = Str) with Hyphen After a Digit
Why Are the Colors Wrong on This Ggplot
Converting Yearmon Column to Last Date of the Month in R
Override Horizontal Positioning with Ggrepel
Removing One Table from Another in R
Graph Flow Chart of Transition from States
Cast String Directly to Idatetime
How to Increase Smoothness of Spheres3D in Rgl
R Ggplot Ordering Bars in "Barplot-Like " Plot
Converting to Date in a Character Column That Contains Two Date Formats
Write Different Data Frame in One .CSV File with R
Scraping Leaderboard Table on Golf Website in R