Ggplot with different ylims characteristics

I want to create a shape ggplot

with six panels in R. The first five faces should represent five different subsets of the data in the histograms, and the end face should represent integer data. I also want to have a fixed Y-axis scale on the first five faces, but a different scale on the last aspect. I know it is currently not possible to specify separate ylims for each face in the ggplot functionality ( https://github.com/hadley/ggplot2/issues/187 ), but I'm wondering, I can do something similar using packages grid

and perhaps gtable

none of which I know at the moment.

Here is my attempt. I am replacing the last facet with a facet in another drawing.

library("ggplot2")
library("dplyr")
library("grid")

# create data
set.seed(1)
d1 <- data_frame(
  value = rnorm(3 * 5, mean = 30, sd = 10),
  f = rep(LETTERS[1:3], 5),
  p = rep(paste("Panel", 1:5), each = 3)
)
d2 <- d1 %>%
  mutate(p = "Total") %>%
  rbind(d1)

# make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p) +
  coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p, scales = "free_y")

# extract their grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)

# replace the final facet of plot1 with the final facet of plot2
g1[["grobs"]][[7]] <- g2[["grobs"]][[7]]
g1[["grobs"]][[19]] <- g2[["grobs"]][[19]]
g1[["grobs"]][[25]] <- g2[["grobs"]][[25]]

# draw the figure
grid.newpage()
grid.draw(g1)

      

And this is what I get. enter image description here

As you can see, however, the y-axis label of the end facet overlaps with the previous face. Does anyone know a way to avoid overlapping, for example by shrinking the end face?

+3


source to share


1 answer


One approach is to extract the "total" plot from "g2" and then insert it into "g1", but remove the "Total" plot from "g1" first. But you will notice that the x-axis label mark labels are not aligned to the edges.

# Load packages
library(ggplot2)
library(dplyr)
library(gtable)
library(grid)

# create data
set.seed(1)
d1 <- data.frame(
  value = rnorm(3 * 5, mean = 30, sd = 10),
  f = rep(LETTERS[1:3], 5),
  p = rep(paste("Panel", 1:5), each = 3)
)
d2 <- d1 %>%
  mutate(p = "Total") %>%
  rbind(d1)

# make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p) +
  coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p, scales = "free_y")

# Get the ggplot grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)

# Extract "Total" plot from g2
keep = g2$layout$name %in% c("panel-3-2", "axis-b-3-2", "axis-l-2-3", "strip-t-3-2")
pos = subset(g2$layout, keep, c(t,l,b,r))
g2 = g2[c(min(pos$t):max(pos$b)), c(min(pos$l):max(pos$r))]

# Remove "Total" plot from g1
keep = !g1$layout$name %in% c("panel-3-2", "axis-b-3-2", "strip-t-3-2")
pos = subset(g1$layout, !keep, c(t,l,b,r))
g1$grobs <- g1$grobs[keep]
g1$layout <- g1$layout[keep, ]

# Insert g2 into g1
g1 = gtable_add_grob(g1, g2, t=min(pos$t), b=max(pos$b), l=min(pos$l), r=max(pos$r))

# Draw it
grid.newpage()
grid.draw(g1)

      

enter image description here



Another approach is to extract the "Total" of the plot from "g2" as before, but to move its y-axis to the right side of the plot (using the code borrowed from here (I changed your "plot2" so that the label labels are better aligned to Thus, the Total panel takes up the same amount of space as the other panels, and therefore the x-labels of the axis label are aligned, but the y-axis for the Total panel sticks out to the right.

# Make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p) +
  coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ p, scales = "free_y") +
  theme(axis.text.y = element_text(hjust = 0))    ## For better formatting of labels

# extract their grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)

# Extract "Total" plot from g2
keep = g2$layout$name %in% c("panel-3-2", "axis-b-3-2", "axis-l-2-3", "strip-t-3-2")
pos = subset(g2$layout, keep, c(t,l,b,r))
g2 = g2[c(min(pos$t):max(pos$b)), c(min(pos$l):max(pos$r))]


# Get the position of the panel in the layout
panel <- c(subset(g2$layout, grepl("panel", g2$layout$name), se = t:r))

# Get the row number of the y-axis in the layout
rn <- which(grepl("axis-l", g2$layout$name))

# Extract the axis (tick marks and axis text from the gtable)
axis.grob <- g2$grobs[[rn]]
axisl <- axis.grob$children[[2]]  # Two children - get the second
axisl  # Note: two grobs - tick marks and text

# Reverse the grobs and the widths
axisl$widths <- rev(axisl$widths)
axisl$grobs <- rev(axisl$grobs)

axisl$grobs[[1]]$x <- axisl$grobs[[1]]$x - unit(1, "npc") + unit(2.75, "pt")

axisl$grobs[[2]]$children[[1]]$x = unit(.15, "npc")    

# Remove the column containing the left axis
g2 <- g2[, -(panel$r-1)]

## remove empty panels
keep = !g1$layout$name %in% c("panel-3-2", "axis-b-3-2", "strip-t-3-2")
pos = subset(g1$layout, !keep, c(t,l,b,r))
g1$grobs <- g1$grobs[keep]
g1$layout <- g1$layout[keep, ]

# Insert g2 into g1
g1 = gtable_add_grob(g1, g2, t = min(pos$t), b = max(pos$b), l = min(pos$l), r = max(pos$r))

# Add a new column to g1, and add the revised axisl grob to the new column.
pos = subset(g1$layout, grepl("panel", g1$layout$name), c(t,l,b,r)) # position of bottom right panel
g1 <- gtable_add_cols(g1, axisl$widths, max(pos$r))
g1 <- gtable_add_grob(g1, axisl, t = max(pos$b), l = max(pos$r)+1,  r = max(pos$r)+2)

# Draw it
grid.newpage()
grid.draw(g1)

      

enter image description here

+1


source







All Articles