为 ggplot 编写自定义函数:添加外部主题时如何避免覆盖 `theme()` 设置
Writing a custom function for ggplot: How to avoid overriding `theme()` settings when adding external 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
但是假设我想添加一个主题
p + theme_bw()
哎呀。这是不希望的。我不想要图例,也不想要 x 轴标题等。
所以一种解决方案是回到plot_from_data
的代码并在theme()
之前添加theme_bw()
。但这不是一个好的解决方案,因为我希望能够在 plot_from_data
的情节之上灵活地应用不同的主题 ,而不必 re-edit 函数的每个地块的代码。
另一个解决方案是将 bw_theme()
和 theme()
都放在 plot_from_data
之外。然后,对于我生成的每个图,我将添加 p
+ theme_bw()
+ theme(...)
。但这也是不希望的,因为 theme()
充满了我不想每次都重复的偏好。
我该如何解决这个问题?我有一个想法,但不知道如何应用,在 theme()
之前的 plot_from_data
中有一个“主题占位符”。类似于:
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()
如果您将参数作为列表输入,您甚至可以在 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")
更进一步,您可以使用相同的技巧将主题作为字符串输入,并设置函数以在适当的地方自动选择相应的音阶:
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")
我想避免重复绘图代码,所以我为 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
但是假设我想添加一个主题
p + theme_bw()
哎呀。这是不希望的。我不想要图例,也不想要 x 轴标题等。
所以一种解决方案是回到plot_from_data
的代码并在theme()
之前添加theme_bw()
。但这不是一个好的解决方案,因为我希望能够在 plot_from_data
的情节之上灵活地应用不同的主题 ,而不必 re-edit 函数的每个地块的代码。
另一个解决方案是将 bw_theme()
和 theme()
都放在 plot_from_data
之外。然后,对于我生成的每个图,我将添加 p
+ theme_bw()
+ theme(...)
。但这也是不希望的,因为 theme()
充满了我不想每次都重复的偏好。
我该如何解决这个问题?我有一个想法,但不知道如何应用,在 theme()
之前的 plot_from_data
中有一个“主题占位符”。类似于:
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()
如果您将参数作为列表输入,您甚至可以在 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")
更进一步,您可以使用相同的技巧将主题作为字符串输入,并设置函数以在适当的地方自动选择相应的音阶:
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")