最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

r - How to ensure a shared x- and y-axis across multiple ggplot2 plots in patchwork - Stack Overflow

programmeradmin5浏览0评论

I am creating a 2x2 matrix of raster plots using ggplot2 and patchwork, where each plot is generated from a different dataset. I want to ensure that the x- and y-axis of these plots, which are equal, are only shown ones (like the example in this post or this post).

Here is my reproducible example:

library(ggplot2)
library(patchwork)

# Create different datasets for each plot
df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50))

df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50))

df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50))

df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50))

# Compute global min and max for z-values across all datasets
min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)
max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)

# Create individual plots with a common color scale
p1 <- ggplot(df1, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(y = "Excitation Wavelength / nm") +
  theme(axis.title.x = element_blank())

p2 <- ggplot(df2, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  theme(axis.title = element_blank())

p3 <- ggplot(df3, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(x = "Emission Wavelength / nm", y = "Excitation Wavelength / nm")

p4 <- ggplot(df4, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(x = "Emission Wavelength / nm") +
  theme(axis.title.y = element_blank())

# Combine plots in a 2x2 grid with shared axis titles and legend
plot_combined <- (p1 + p2) / (p3 + p4) +
  plot_layout(axis_titles = "collect", guides = "collect") +
  plot_annotation(
    title = "Emission-Excitation-Matrix",
    subtitle = "Rayleigh Filtered Data"
  )

# Show plot
print(plot_combined)

resulting in the following plot

Despite using plot_layout(axis_titles = "collect", guides = "collect") from the patchwork package, the axis are not shared for the combined plot.

Is there a way to ensure Only one shared X- and Y-axis title instead of repeated axis labels using the patchwork package? What should be changed in the example?

I am creating a 2x2 matrix of raster plots using ggplot2 and patchwork, where each plot is generated from a different dataset. I want to ensure that the x- and y-axis of these plots, which are equal, are only shown ones (like the example in this post or this post).

Here is my reproducible example:

library(ggplot2)
library(patchwork)

# Create different datasets for each plot
df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50))

df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50))

df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50))

df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50))

# Compute global min and max for z-values across all datasets
min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)
max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)

# Create individual plots with a common color scale
p1 <- ggplot(df1, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(y = "Excitation Wavelength / nm") +
  theme(axis.title.x = element_blank())

p2 <- ggplot(df2, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  theme(axis.title = element_blank())

p3 <- ggplot(df3, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(x = "Emission Wavelength / nm", y = "Excitation Wavelength / nm")

p4 <- ggplot(df4, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(x = "Emission Wavelength / nm") +
  theme(axis.title.y = element_blank())

# Combine plots in a 2x2 grid with shared axis titles and legend
plot_combined <- (p1 + p2) / (p3 + p4) +
  plot_layout(axis_titles = "collect", guides = "collect") +
  plot_annotation(
    title = "Emission-Excitation-Matrix",
    subtitle = "Rayleigh Filtered Data"
  )

# Show plot
print(plot_combined)

resulting in the following plot

Despite using plot_layout(axis_titles = "collect", guides = "collect") from the patchwork package, the axis are not shared for the combined plot.

Is there a way to ensure Only one shared X- and Y-axis title instead of repeated axis labels using the patchwork package? What should be changed in the example?

Share Improve this question edited Mar 21 at 5:45 Jan 9,9156 gold badges20 silver badges33 bronze badges asked Mar 20 at 23:00 ExcelsiorExcelsior 2671 silver badge5 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 4

By using:

plot_combined <- (p1 + p2) / (p3 + p4) +

I believe you are explicitly defining rows and columns as independent so labels are being collected for each () object. It seems that having non-identical labels may also affect what gets"collect"ed.

You can simplify your workflow by creating a list of basic plots to supply to wrap_plots(), and then customising plot elements by piping any modifications using &.

In response to your comment regarding spacing, I have provided two options informed by Edward Tufte's canonical advice regarding the "data-ink ratio". The patchwork documentation discusses many other ways to control the plot layout.

library(ggplot2)
library(patchwork)

# Generate basic ggplot() objects as a list
plots <- lapply(list(df1, df2, df3, df4), function(df) {
  
  ggplot(df, aes(x, y, fill = z)) +
    geom_raster()
  
})

Now generate plot with expand = FALSE to reduce visual spacing, and add annotation, label, and colour elements:

wrap_plots(plots) +
  plot_layout(
    nrow = 2,
    ncol = 2,
    axis_titles = "collect",
    guides = "collect"
    ) +
  plot_annotation(
    title = "Emission-Excitation-Matrix",
    subtitle = "Rayleigh Filtered Data"
    ) &
  labs(
    x = "Emission Wavelength / nm",
    y = "Excitation Wavelength / nm"
    ) &
  scale_fill_viridis_c(limits = c(min_z, max_z)) &
  coord_cartesian(expand = FALSE)

An improvement, but IMHO also applying axes = "collect" creates a much less cluttered result:

wrap_plots(plots) +
  plot_layout(
    nrow = 2,
    ncol = 2,
    axis_titles = "collect",
    axes = "collect",
    guides = "collect"
    ) +
  plot_annotation(
    title = "Emission-Excitation-Matrix",
    subtitle = "Rayleigh Filtered Data"
    ) &
  labs(
    x = "Emission Wavelength / nm",
    y = "Excitation Wavelength / nm"
    ) &
  scale_fill_viridis_c(limits = c(min_z, max_z)) &
  coord_cartesian(expand = FALSE)

In this case, it will default to a 2x2 grid so nrow = 2 and ncol = 2 are not needed but I have included them for completeness.

Slightly different than what you asked for, but have you tried to combine all dfs and doing a faceted plot?

library(ggplot2)
library(patchwork)

# Create different datasets for each plot
df1 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df1$z <- with(df1, dnorm(x, mean = 500, sd = 50) * dnorm(y, mean = 400, sd = 50))

df2 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df2$z <- with(df2, dnorm(x, mean = 600, sd = 50) * dnorm(y, mean = 450, sd = 50))

df3 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df3$z <- with(df3, dnorm(x, mean = 550, sd = 50) * dnorm(y, mean = 500, sd = 50))

df4 <- expand.grid(x = seq(300, 800, length.out = 50), y = seq(300, 600, length.out = 50))
df4$z <- with(df4, dnorm(x, mean = 650, sd = 50) * dnorm(y, mean = 350, sd = 50))

# Compute global min and max for z-values across all datasets
min_z <- min(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)
max_z <- max(c(df1$z, df2$z, df3$z, df4$z), na.rm = TRUE)

df.grouped <- dplyr::bind_rows(list(df1=df1, df2=df2, df3=df3, df4=df4), .id = 'source')

head(df.grouped)

ggplot(df.grouped, aes(x, y, fill = z)) +
  geom_raster() +
  scale_fill_viridis_c(limits = c(min_z, max_z)) + 
  labs(y = "Excitation Wavelength / nm",
       x = "Emission Wavelength / nm") +
  facet_wrap(~source, scales = "free")+
  theme_classic()+
  theme(strip.text = element_blank())

发布评论

评论列表(0)

  1. 暂无评论