定义一个将在后续所有几何对象中使用的ggplot() 参数
编辑与要点摘要:
(我的使用场景在下文详细描述)
我在寻找一种在ggplot组件之间共享参数的方法,形式如下
ggplot(my_arg="foo") +
geom_my(my_arg)
或者也许
ggplot() +
some_wrapper(my_arg = "foo") +
geom_my(my_arg)
从回复中看来(感谢!),这似乎是不可行。
在地球化学,地质学的一个分支,习惯使用被称作“蜘蛛图”或“蜘蛛坐标图”或“蜘蛛图谱”的图形(自行搜索)。它们属于平行坐标图的一种(是的,我知道,PCP存在不少概念性问题,但规矩我来定……)。
它们的做法是将(选定的)元素按常规定序排列,并将每个元素的浓度除以一个参考值——例如,可以用La/0.310、Ce/0.808、Nd/0.600等来绘制图表。
ggplot书(第18章)为绘制PCP提供了一个极好的基础 (https://ggplot2-book.org/programming.html#sec-functions)。我对代码做了些微调整,以允许用户定义要使用的归一化方案:
spider_data<-function(df,norm){
elt <- names(norm)
df %>%
mutate(across(elt,
~ .x / norm[cur_column()]
)
) %>%
# Add row identifier
rowid_to_column(var = ".row") %>%
# To long format
pivot_longer(cols = all_of(elt),
names_to = "Element", values_to = "Normalized") %>%
{.} -> df_out
return(df_out)
}
gg_spider <- function(df=NULL){
ggplot(df, aes(x=Element, y=Normalized, group = .row))+
scale_x_discrete()
}
这使得工作按计划完成:
## Define mock data
df1 <- tibble(a=c(1,2),b=c(2,3),d=c(4,2))
df2 <- tibble(a=c(2,1),b=c(4,1),d=c(4,3))
## A normaization scheme
mynorm <- c(a=1,b=2,d=3)
## Use it!
spider_data(df1,mynorm) %>%
gg_spider()+
geom_line()
gg_spider()+
geom_line(data=spider_data(df2,mynorm),col="red")
spider_data(df1,mynorm) %>%
gg_spider()+
geom_line()+
geom_line(data=spider_data(df2,mynorm),col="red")
gg_spider()+
geom_line(data=spider_data(df2,mynorm),col="red")+
geom_point(data=spider_data(df1,mynorm))
这一切都运行良好,且如预期地工作(是的!)。不过我现在在寻找改进工作流的方法。逻辑上,归一化方案的定义应该属于绘图本身的定义的一部分(对不同数据集使用不同的归一化没有意义),在这个画布上你可以按需添加数据。对用户而言,我期望的代码应该是这样的(从用户的角度):
# Not work
gg_spider(norm=mynorm)+
geom_line(data=df2,col="red")+
geom_line(data=df1)
当然,这样做当然会不起作用。原因之一当然是我没有使用norm参数。但更深层的原因,是我还没有找到任何办法(我所知道的)让ggplot() 将其提供给下游的geom_* 函数。ggspider函数应该是这样的:
# Not work
gg_spider <- function(df=NULL, norm){
ggplot(df, aes(x=Element, y=Normalized, group = .row), norm=norm)+
scale_x_discrete()
}
# Errors upon calling : Problematic argument: norm (...)
然后我会得到一个看起来像这样的函数geom_my_line()
# Not work
geom_my_line <- function(df=NULL){
geom_line(spider_data(df))
}
显然,由于spider_data() 需要一个norm参数,我需要一种方法来“知道”传给ggplot() 的到底是什么!
那么,是否有办法为ggplot() 调用定义一个通用的、自定义的参数?
解决方案
我不知道你是否在找类似如下的东西。
函数 gg_spider 调用 spider_data,并将结果通过管道传给 ggplot。因此,当你添加美学属性,如 geom_line 或 geom_point 时,它将使用这次调用所得的归一化数据。
但如果你更换数据集,你需要独立地调用 spider_data。
另外,我还把 mutate(across(elt)) 改为使用 all_of(elt),已不再接受发布的形式。
suppressPackageStartupMessages(
library(tidyverse)
)
spider_data<-function(df, norm){
elt <- names(norm)
df %>%
mutate(across(all_of(elt),
~ .x / norm[cur_column()]
)
) %>%
# Add row identifier
rowid_to_column(var = ".row") %>%
# To long format
pivot_longer(cols = all_of(elt),
names_to = "Element", values_to = "Normalized") %>%
{.} -> df_out
return(df_out)
}
gg_spider <- function(df = NULL, norm){
spider_data(df, norm) %>%
ggplot(aes(x = Element, y= Normalized, group = .row))+
scale_x_discrete()
}
## Define mock data
df1 <- tibble(a=c(1,2),b=c(2,3),d=c(4,2))
df2 <- tibble(a=c(2,1),b=c(4,1),d=c(4,3))
## A normaization scheme
mynorm <- c(a=1,b=2,d=3)
gg_spider(df1, mynorm) +
geom_line() +
geom_line(data = spider_data(df2, mynorm), col="red")
gg_spider(df1, mynorm) +
geom_line() +
# still uses df1
geom_point() +
# switch to df2
geom_line(data = spider_data(df2, mynorm), col="red")
Created on 2026-05-10 with reprex v2.1.1