问:为什么打不通sapply
里面 aes()
?
下图的目标:创建显示死亡/活着的比例的直方图,以便每个组/类型组合的比例总和为 1(示例受先前 post 启发)。
我知道你可以通过在 ggplot
之外进行总结来得出这个数字。但问题实际上是关于为什么该函数在 aes
内部不起作用.
## Data
set.seed(999)
dat <- data.frame(group=factor(rep(1:2, 25)),
type=factor(sample(1:2, 50, rep=T)),
died=factor(sample(0:1, 50, rep=T)))
## Setup the figure
p <- ggplot(dat, aes(x=died, group=interaction(group, type), fill=group, alpha=type)) +
theme_bw() +
scale_alpha_discrete(range=c(0.5, 1)) +
ylab("Proportion")
## Proportions, all groups/types together sum to 1 (not wanted)
p + geom_histogram(aes(y=..count../sum(..count..)), position=position_dodge())
## Look at groups
stuff <- ggplot_build(p)
stuff$data[[1]]
## The long way works: proportions by group/type
p + geom_histogram(
aes(y=c(..count..[..group..==1] / sum(..count..[..group..==1]),
..count..[..group..==2] / sum(..count..[..group..==2]),
..count..[..group..==3] / sum(..count..[..group..==3]),
..count..[..group..==4] / sum(..count..[..group..==4]))),
position='dodge'
)
## Why can't I call sapply there?
p + geom_histogram(
aes(y=sapply(unique(..group..), function(g)
..count..[..group..==g] / sum(..count..[..group..==g]))),
position='dodge'
)
Error in get(as.character(FUN), mode = "function", envir = envir) : object 'expr' of mode 'function' was not found
最佳答案
因此,问题是由于对 ggplot2:::strip_dots
的递归调用而出现的。对于包括“计算美学”在内的任何美学。 this SO question and answer 中有一些关于计算美学的讨论。 . layer.r中的相关代码在这儿:
new <- strip_dots(aesthetics[is_calculated_aes(aesthetics)])
即
strip_dots
仅当存在计算美学时才调用,使用正则表达式 "\\.\\.([a-zA-z._]+)\\.\\."
定义.strip_dots
in 采用递归方法,向下遍历嵌套调用并去除点。代码是这样的:function (expr)
{
if (is.atomic(expr)) {
expr
}
else if (is.name(expr)) {
as.name(gsub(match_calculated_aes, "\\1", as.character(expr)))
}
else if (is.call(expr)) {
expr[-1] <- lapply(expr[-1], strip_dots)
expr
}
else if (is.pairlist(expr)) {
as.pairlist(lapply(expr, expr))
}
else if (is.list(expr)) {
lapply(expr, strip_dots)
}
else {
stop("Unknown input:", class(expr)[1])
}
}
如果我们提供一个匿名函数,则代码如下:
anon <- as.call(quote(function(g) mean(g)))
ggplot2:::strip_dots(anon)
我们重现错误:
#Error in get(as.character(FUN), mode = "function", envir = envir) :
# object 'expr' of mode 'function' was not found
通过这个,我们可以看到 anon 是
call
.对于 call
年代,strip_dots
将使用 lapply
调用strip_dots
关于 call
的第二个和第三个元素.对于这样的匿名函数,第二个元素是 formals
的功能。如果我们查看 formals
的 anon
使用 dput(formals(eval(anon)))
或 dput(anon[[2]])
我们看到这个:#pairlist(g = )
对于
pairlist
年代,strip_dots
尝试 lapply
它自己。我不确定为什么有这段代码,但在这种情况下肯定会导致错误:expr <- anon[[2]]
lapply(expr, expr)
# Error in get(as.character(FUN), mode = "function", envir = envir) :
# object 'expr' of mode 'function' was not found
TL;博士 在这个阶段,
ggplot2
不支持在 aes
中使用匿名函数其中使用了经过计算的美学(例如 ..count..
)。无论如何,使用
dplyr
可以达到预期的最终结果。 ;一般来说,我认为它可以使代码更具可读性,从而将数据摘要与绘图分开:newDat <- dat %>%
group_by(died, type, group) %>%
summarise(count = n()) %>%
group_by(type, group) %>%
mutate(Proportion = count / sum(count))
p <- ggplot(newDat, aes(x = died, y = Proportion, group = interaction(group, type), fill=group, alpha=type)) +
theme_bw() +
scale_alpha_discrete(range=c(0.5, 1)) +
geom_bar(stat = "identity", position = "dodge")
ggplot2 修复
我已经 fork 了 ggplot2 并对 aes_calculated.r 进行了两项更改,从而解决了这个问题。首先是更正
pairlist
的处理。转至 lapply
strip_dots
而不是 expr
,我认为这一定是预期的行为。第二个是对于没有默认值的形式(如此处提供的示例),as.character(as.name(expr))
引发错误,因为 expr
是一个空名称,虽然这是一个有效的构造,但不可能从空字符串创建一个。ggplot2 的 fork 版本,位于 https://github.com/NikNakk/ggplot2并拉取请求 just made .
最后,毕竟,
sapply
给出的示例不起作用,因为它返回 2 行 x 4 列矩阵而不是 8 长度向量。修正后的版本是这样的:p + geom_histogram(
aes(y=unlist(lapply(unique(..group..), function(g)
..count..[..group..==g] / sum(..count..[..group..==g])))),
position='dodge'
)
这给出了与
dplyr
相同的输出。上面的解决方案。另一件需要注意的是,这个
lapply
代码假定该阶段的数据按组排序。我认为情况总是如此,但如果由于某种原因不是这样,你最终会得到 y 数据乱序。保留计算数据中行顺序的替代方法是:p + geom_histogram(
aes(y={grp_total <- tapply(..count.., ..group.., sum);
..count.. / grp_total[as.character(..group..)]
}),
position='dodge'
)
还值得注意的是,这些表达式在
baseenv()
中计算。 ,基础包的命名空间。这意味着来自其他软件包的任何功能,甚至是标准的,如 stats
和 utils
, 需要与 ::
一起使用运算符(例如 stats::rnorm
)。
关于r - aes里面的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31212054/