问题:为什么我不能在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'
)
get中的错误(as.character(FUN),mode =&#34; function&#34;,envir = envir): 对象&#39; expr&#39;模式&#39;功能&#39;没找到
答案 0 :(得分:18)
因此,问题出现的原因是对ggplot2:::strip_dots
的任何美学的递归调用,包括计算的美学&#39;。在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; DR 在此阶段,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并对aes_calculated.r做了两处修改来修复问题。第一个是纠正pairlist
到lapply
strip_dots
而不是expr
的处理,我认为这一定是预期的行为。第二个是对于没有默认值的形式(如此处提供的示例中所示),as.character(as.name(expr))
会抛出错误,因为expr
是一个空名称,虽然这是一个有效的构造,但它是&#39;不能用空字符串创建一个。
https://github.com/NikNakk/ggplot2的ggplot2的分叉版本和提取请求just made。
最后,在所有这些之后,给出的sapply
示例不起作用,因为它返回2行乘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
)。
答案 1 :(得分:7)
玩了一会儿之后,问题似乎是在...中使用..group ..或..count ..的匿名函数:
xy <- data.frame(x=1:10,y=1:10) #data
ggplot(xy, aes(x = x, y = sapply(y, mean))) + geom_line() #sapply is fine
ggplot(xy, aes(x = x, group = y)) +
geom_bar(aes(y = sapply(..group.., mean))) #sapply with ..group.. is fine
ggplot(xy, aes(x = x, group = y)) +
geom_bar(aes(y = sapply(..group.., function(g) {mean(g)})))
#broken, with same error
ggplot(xy, aes(x = x, group = y)) +
geom_bar(aes(y = sapply(y, function(g) {mean(g)})), stat = "identity")
#sapply with anonymous functions works fine!
这似乎是一个非常奇怪的错误,除非我错过了一些愚蠢的东西。