假设我们有以下数据框
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)
但是,如果我们有更多的&#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
。因此,图例将变为颜色条,并且不包含不同的列名。我的下一个猜测是混合aes
和aes_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
法术?
答案 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()
答案 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_string
和aes_q
采用与您相同的变通办法。我发现它们非常不令人满意,因为它们(1)完全与aes
行为一致,(2)特别干净。哦,学习NSE的乐趣;)
您可以在此处找到+
和aes
运算符的源代码:
ggplot2:::`+.gg`
ggplot2:::aes
ggplot2:::aes_q
ggplot2:::aes_string