ggplot:aes vs aes_string,或者如何以编程方式指定列名?

时间:2014-11-12 20:49:16

标签: r plot ggplot2

假设我们有以下数据框

data <- data.frame(time=1:10, y1=runif(10), y2=runif(10), y3=runif(10))

我们想创建一个这样的情节:

p <- ggplot(data, aes(x=time))
p <- p + geom_line(aes(y=y1, colour="y1"))
p <- p + geom_line(aes(y=y2, colour="y2"))
p <- p + geom_line(aes(y=y3, colour="y3"))
plot(p)

enter image description here

但是,如果我们有更多的&#34; y&#34;列,我们不知道他们的确切名称。这提出了一个问题:我们如何以编程方式迭代所有列,并将它们添加到绘图中?基本上目标是:

otherFeatures <- names(data)[-1]
for (f in otherFeatures) {
  # what goes here?
}

尝试失败

到目前为止,我发现许多方法都行不通。例如(以下所有示例仅显示上面for循环中的代码行):

我的第一次尝试只是使用aes_string代替aes,以便通过循环变量f指定列名:

p <- p + geom_line(aes_string(y=f, colour=f))

但这并没有给出相同的结果,因为现在colour不是每行的固定颜色(aes_string将在数据框环境中解释f。因此,图例将变为颜色条,并且不包含不同的列名。我的下一个猜测是混合aesaes_string,尝试将colour设置为固定字符串:

p <- p + geom_line(aes_string(y=f), aes(colour=f))

但这导致Error: ggplot2 doesn't know how to deal with data of class uneval。我的下一次尝试是使用colour&#34;绝对&#34; (不在aes之内)像这样:

p <- p + geom_line(aes_string(y=f), colour=f)

但这会给Error: invalid color name 'y1'(我也不想手动选择一些正确的颜色名称)。接下来的尝试只是回到aes,复制手动方法:

p <- p + geom_line(aes(y=data[[f]], colour=f))

这不会给出错误,但只会绘制最后一列。这是有道理的,因为aes可能会调用substitute,并且在调用{{1}之前,将始终使用循环中的f的最后一个值rm f来计算表达式。出现错误,表示评估在循环后发生)。

重新解释这个问题:在for循环中复制上面的简单代码需要什么样的plot(p) / substitute / eval法术?

3 个答案:

答案 0 :(得分:4)

现在已经过时了但是如果有其他人遇到它,我有一个非常类似的问题让我发疯。我找到的解决方案是使用aes_q()选项将geom_line()传递给as.name()。您可以在aes_q() here找到详细信息。下面是我解决这个问题的方法,尽管同样的原则应该在循环中起作用。请注意,我在此处添加了多个带有geom_line()的变量作为列表,这更好地概括了(包括一个变量)。

varnames <- c("y1", "y2", "y3")
add_lines <- lapply(varnames, function(i) geom_line(aes_q(y = as.name(i), colour = i)))

p <- ggplot(data, aes(x = time))
p <- p + add_lines
plot(p)

希望有所帮助!

答案 1 :(得分:3)

您可以melt(感谢您提醒我这个函数,rawr)将您的所有数据分成几列。例如,它可能如下所示:

library(reshape2)    
data2 <- melt(data, id = "time")
head(data2)
#    time variable       value
# 1     1       y1 0.353088575
# 2     2       y1 0.621565368
# 3     3       y1 0.696031085
# 4     4       y1 0.507112969
# 5     5       y1 0.009560710
# 6     6       y1 0.158993988
ggplot(data2, aes(x = time, y = value, color = variable)) + geom_line()

enter image description here

答案 2 :(得分:1)

注意:这不是一个真正的答案,只是对可能在正确的轨道上设置的幕后情况的非常局部的解释。我必须承认,我对NSE的理解仍然非常基础。

我一直在努力,并且仍然在努力解决这个问题。我已将问题缩小到NSE。我不熟悉R的原生替代/引用/评估的东西,所以我将演示使用lazyeval包。

library(lazyeval)

a <- lapply(c(1:9,13), function(i) lazy(i))

head(a)
# [[1]]
# <lazy>
#   expr: c(1, 2, 3, 4, 5, 6, 7, 8, 9, 13)[[10L]]
#   env:  <environment: 0x25889a00>
# 
# [[2]]
# <lazy>
#   expr: c(1, 2, 3, 4, 5, 6, 7, 8, 9, 13)[[10L]]
#   env:  <environment: 0x25889a00>
#
# ...........

lazy_eval(a[[1]])
# [1] 13

lazy_eval(a[[2]])
# [1] 13

我认为这是因为lazy(i)绑定了i的承诺。当我们开始评估这些i评估中的任何一个时,i是最后分配给它的任何内容 - 在这种情况下,13。也许这是由于在i函数的所有迭代中共享lapply被评估的环境?

我不得不通过aes_stringaes_q采用与您相同的变通办法。我发现它们非常不令人满意,因为它们(1)完全aes行为一致,(2)特别干净。哦,学习NSE的乐趣;)

您可以在此处找到+aes运算符的源代码:

ggplot2:::`+.gg`
ggplot2:::aes
ggplot2:::aes_q
ggplot2:::aes_string