r - aes里面的函数

标签 r ggplot2

问:为什么打不通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())

enter image description here
## 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'
)

enter image description here
## 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的功能。如果我们查看 formalsanon使用 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")

Final output

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() 中计算。 ,基础包的命名空间。这意味着来自其他软件包的任何功能,甚至是标准的,如 statsutils , 需要与 :: 一起使用运算符(例如 stats::rnorm )。

关于r - aes里面的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31212054/

相关文章:

r - ggplot 中的换行符用 LateX 表达式进行注释

r - 连接具有相同后缀的变量对

r - 使用 purrr::pwalk 从 tibble 创建多个 Shiny 的 observeEvents

r - ggplot图例标题顶部中心

r - 组合/合并两个 ggplot 美学

r - 有没有办法在使用因子变量的填充选项的 ggplot 条形图上创建误差线?

R - 线性区域中的 ggplot2 外推回归线

r - 绘制方决策树

r - 将值的数据帧转换为二进制数据帧,其中每个唯一值都是一列

r - 如何将 map 投影从太平洋中心更改为大西洋中心?