r - 为 ggplot : How to avoid overriding `theme()` settings when adding external themes 编写自定义函数

标签 r function ggplot2 themes

我想避免重复绘图代码,所以我为 ggplot 编写了一个简单的函数。我使用 theme() 指定了几个首选项。但是,如果我想应用外部主题(例如 theme_bw()),它会覆盖我特定的 theme() 首选项。 如果我一直在而不是用户定义的函数中编写代码,解决方案会很简单:重新排列ggplot代码的结构所以 theme_bw() 出现在 theme() 之前。但我不知道如何在自定义函数的情况下解决这个问题。

数据

df <- 
  data.frame(
    x_names = c("asia", "europe", "america", "africa", "australia"),
    y_values = 1:5
  )

##     x_names y_values
## 1      asia        1
## 2    europe        2
## 3   america        3
## 4    africa        4
## 5 australia        5

绘图函数

library(ggplot2)

plot_from_data <- function(data_input, x_col, y_col) {
  
  p_barplot <- 
    ggplot(data = data_input, aes(x = {{ x_col }}, y = {{ y_col }}, fill = as_factor({{ x_col}} ))) +
    geom_bar(stat = "identity") +
    labs(caption = "caption blah") +
    theme(plot.title = element_text(hjust = 0.5, size = 14),    ## I have a bunch
          axis.text.x=element_text(angle = -60, hjust = 0),     ## of preferences
          axis.title.x = element_blank(),                       ## I set up using
          legend.title = element_blank(),                       ## `theme()`
          panel.grid  = element_blank(),
          panel.grid.major=element_blank(),
          plot.caption = element_text(hjust = 0, size = 8),
          legend.position = "none")
  
  return(p_barplot)
  
}

使用函数作图

p <- plot_from_data(data_input = df,
               x_col = x_names,
               y_col = y_values)

p

just_p

但是假设我想添加一个主题

p + theme_bw()

p_w_theme_bw

哎呀。这是不希望的。我不想要图例,也不想要 x 轴标题等。 所以一种解决方案是返回到plot_from_data的代码并添加theme_bw() before theme ()。但这不是一个好的解决方案,因为我希望能够灵活地应用不同的主题 plot_from_data 的情节上,而不必重新编辑函数的代码每个地 block 。

另一个解决方案是将 bw_theme()theme() 都放在 plot_from_data 之外。然后,对于我生成的每个图,我将添加 p + theme_bw() + theme(...)。但这也是不希望的,因为 theme() 充满了我不想每次都重复的偏好。

我该如何解决这个问题?我有一个想法,但不知道如何应用,是在 plot_from_data 中,在 theme() 之前有一个“主题占位符”。像这样的东西:

plot_from_data <- function(data_input, x_col, y_col) {
  
  p_barplot <- 
    ggplot(data = data_input, aes(x = {{ x_col }}, y = {{ y_col }}, fill = as_factor({{ x_col}} ))) +
    geom_bar(stat = "identity") +
    labs(caption = "caption blah") +
    ***theme_placeholder*** + ##################################  <----------------------- here
    theme(plot.title = element_text(hjust = 0.5, size = 14),
          axis.text.x=element_text(angle = -60, hjust = 0),
          axis.title.x = element_blank(),
          legend.title = element_blank(),
          panel.grid  = element_blank(),
          panel.grid.major=element_blank(),
          plot.caption = element_text(hjust = 0, size = 8),
          legend.position = "none")
  
  return(p_barplot)
  
}

但是,我不知道如何让它足够灵活。我能立即想到的一个问题是 ggthemes 库,有时一个主题需要两个组件(例如,theme_economist()scale_color_economist() ).

有什么想法吗?

最佳答案

您可以添加一个可选主题作为函数的参数。如果您需要添加色标,可以在函数运行后添加,因为色标函数不​​会覆盖任何 theme 元素。

library(tidyverse)
library(ggthemes)

plot_from_data <- function(data_input, x_col, y_col, my_theme=NULL) {
  
  my_theme = list(my_theme)
  
  p_barplot <- ggplot(data = data_input, aes(x = {{ x_col }}, y = {{ y_col }}, fill = as_factor({{ x_col}} ))) +
    geom_col() +
    labs(caption = "caption blah") +
    my_theme +
    theme(plot.title = element_text(hjust = 0.5, size = 14),
          axis.text.x=element_text(angle = -60, hjust = 0),
          axis.title.x = element_blank(),
          legend.title = element_blank(),
          panel.grid  = element_blank(),
          panel.grid.major=element_blank(),
          plot.caption = element_text(hjust = 0, size = 8),
          legend.position = "none")
  
  return(p_barplot)
  
}

plot_from_data(iris, Species, Petal.Width)
plot_from_data(iris, Species, Petal.Width, theme_bw())
plot_from_data(iris, Species, Petal.Width, theme_economist()) + 
  scale_fill_economist()

enter image description here

如果您将参数作为列表输入,您甚至可以在 my_theme 参数中添加比例函数:

plot_from_data(iris, Species, Petal.Width, 
               list(theme_economist(), scale_fill_economist())

另一个选项(可能有点矫枉过正)是允许用户有选择地添加填充比例的名称作为字符串。例如:

  plot_from_data <- function(data_input, x_col, y_col, my_theme=NULL, my_fill_scale=NULL) {

  
  my_theme = list(my_theme)
  
  p_barplot <- ggplot(data = data_input, aes(x = {{ x_col }}, y = {{ y_col }}, fill = as_factor({{ x_col}} ))) +
    geom_col() +
    labs(caption = "caption blah") +
    my_theme +
    theme(plot.title = element_text(hjust = 0.5, size = 14),
          axis.text.x=element_text(angle = -60, hjust = 0),
          axis.title.x = element_blank(),
          legend.title = element_blank(),
          panel.grid  = element_blank(),
          panel.grid.major=element_blank(),
          plot.caption = element_text(hjust = 0, size = 8),
          legend.position = "none")
  
  if(!is.null(my_fill_scale)) {
    p_barplot = p_barplot + 
      match.fun(paste0("scale_fill_", my_fill_scale))()
  }
  
  return(p_barplot)
  
}

plot_from_data(iris, Species, Petal.Width, theme_economist())
plot_from_data(iris, Species, Petal.Width, theme_economist(), "economist")
plot_from_data(iris, Species, Petal.Width, theme_economist(), "fivethirtyeight") 

enter image description here

更进一步,您可以使用相同的技巧将主题作为字符串输入,并设置函数以在适当的情况下自动选择相应的音阶:

plot_from_data <- function(data_input, x_col, y_col, my_theme=NULL, my_fill_scale=NULL) {
  
  p <- ggplot(data = data_input, aes(x ={{ x_col }}, y={{ y_col }}, fill=as_factor({{ x_col}} ))) +
    geom_bar(stat = "identity") +
    labs(caption = "caption blah")
  
  if(!is.null(my_theme)) {
    p = p + match.fun(paste0("theme_", my_theme))()
  }
  
  p = p + theme(plot.title = element_text(hjust = 0.5, size = 14),
                axis.text.x=element_text(angle = -60, hjust = 0),
                axis.title.x = element_blank(),
                legend.title = element_blank(),
                panel.grid  = element_blank(),
                panel.grid.major=element_blank(),
                plot.caption = element_text(hjust = 0, size = 8),
                legend.position = "none")
  
  if(!is.null(my_fill_scale)) {
    p = p + 
      match.fun(paste0("scale_fill_", my_fill_scale))()
  }
  
  if(!is.null(my_theme)) {
    if(my_theme %in% c("economist","fivethirtyeight","few") & 
       is.null(my_fill_scale)) {
      p = p + match.fun(paste0("scale_fill_", my_theme))()
    }
  }
  
  return(p)
}

plot_from_data(iris, Species, Petal.Width, "economist")
plot_from_data(iris, Species, Petal.Width, "economist", "viridis_d") 
plot_from_data(iris, Species, Petal.Width, "economist", "few") 
plot_from_data(iris, Species, Petal.Width, "fivethirtyeight") 

enter image description here

关于r - 为 ggplot : How to avoid overriding `theme()` settings when adding external themes 编写自定义函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64159263/

相关文章:

Jquery "reusable"函数

r - 使用 ggplot 的条形图和折线图的多个 y 轴

r - 在已编译的 pdf 中在文档文本和 knitr 代码块之间添加空格

r - 如何将列范围和行范围内的所有值相乘?

注册的 doParallel 集群不适用于 train/caret parRF 模型

javascript - 增强函数原型(prototype)以在执行之前调用给定函数

r - 如何在 r 中创建类似对比的表格

jquery - 迭代无序列表时跳过第一个列表项。 jQuery

r - 将带有美元符号 ($) 的变量与 facet_grid() 或 facet_wrap() 组合传递给 aes() 时出现问题

r - 使用 ggplot2 中子图的自定义 x,y 位置创建子图(小平面)