在ggplot找不到的函数内创建的对象

Bar*_*art 8 for-loop r function ggplot2

我为我生成ggplots的许多站点提供了一系列时间序列数据,显示了使用changepoint包的方法的变化.我编写了一个函数,它接受csv,执行一些计算以获得方法然后遍历站点,为每个站点生成一个图.我的问题是找不到在for循环中创建的对象.

下面是一个非常简单的示例,但会产生相同的错误:

df1 <- data.frame(date = seq(as.Date("2015-01-01"), as.Date("2015-01-10"),
                         by = "day"),
              site1 = runif(10),
              site2 = runif(10),
              site3 = runif(10))

example <- function(df1){

    sname <- names(df1)[-1]

    for (i in 1:length(sname)){
            df2 <- df1[,c(1, 1+i)]
            df2$label <- factor(rep("ts", by=length(df2[,1])))

            plot <- ggplot()+
                    geom_point(data=df2, aes(x=date, y=df2[,2]))+
                    geom_line(data=df2, aes(x=date, y=df2[,2]))
            sname.i<-sname[i]
            filename<-paste0(sname.i, "-test-plot.pdf")
            ggsave(file=filename, plot)
    }
}

example(df1)
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:"eval中的错误(expr,envir,enclos):找不到对象'df2'"

我不太确定问题是什么,因为我创建了类似的循环,这些循环在过去有效.如果我为i分配一个值并循环遍历循环中的代码,它可以正常工作.我在想环境问题?还是ggsave在做什么呢?感激地收到任何帮助/指针.谢谢.

Jor*_*eys 4

你的问题不在于你的代码,而在于 ggplot2 包的实现。该包使用非标准评估,这可能会严重扰乱您的结果。

看一下本文末尾的示例代码。我在全局环境中创建一个df2具有不同值的数据框。如果我现在运行你的代码,你会得到如下所示的图:

在此输入图像描述

df2请注意,在 X 轴上,它使用正确的日期,但 Y 轴上的值是来自全局环境中的数据帧的值!因此该函数aes()在两个不同的位置查找数据。如果将变量名称指定为符号 ( date),则函数首先在函数调用中指定的数据帧中查找。df2[,2]但是,在数据框中找不到类似的表达式,因为没有具有该名称的变量。由于ggplot2包的构造方式,R 将在全局环境而不是调用环境中查找该包。

根据wici的评论:您最好的选择可能是使用该函数aes_string(),因为这允许您以字符形式传递 aes,并且该函数在正确的环境中计算表达式:

plot <- ggplot()+
      geom_point(data=df2, aes_string(x="date", y=sname[i]))+
      geom_line(data=df2, aes_string(x="date", y=sname[i]))
Run Code Online (Sandbox Code Playgroud)

eval()或者,您可以通过使用和来解决parse()这个问题:

example <- function(df1){

  sname <- names(df1)[-1]

  for (i in 1:length(sname)){
    df2 <- df1[,c(1, 1+i)]
    df2$label <- factor(rep("ts", by=length(df2[,1])))

    aesy <- sname[i]
    command <- paste("plot <- ggplot()+
      geom_point(data=df2, aes(x=date, y=",aesy,"))+
      geom_line(data=df2, aes(x=date, y=",aesy,"))")

    eval(parse(text=command))                     
    sname.i<-sname[i]
    print(plot)
  }
Run Code Online (Sandbox Code Playgroud)

如果您使用下面的示例脚本进行尝试,您会发现这次显示了正确的值。请注意,这是一个次优解决方案,因为大多数解决方案都涉及eval(). 我会去aes_string()这里。


示例脚本

df1 <- data.frame(date = seq(as.Date("2015-01-01"), as.Date("2015-01-10"),
                             by = "day"),
                  site1 = runif(10),
                  site2 = runif(10),
                  site3 = runif(10))

df2 <- data.frame(date = seq(as.Date("2014-10-01"), as.Date("2014-10-10"),
                             by = "day"),
                  site1 = runif(10,10,20),
                  site2 = runif(10,10,20),
                  site3 = runif(10,10,20))

example <- function(df1){

  sname <- names(df1)[-1]

  for (i in 1:length(sname)){
    df2 <- df1[,c(1, 1+i)]
    df2$label <- factor(rep("ts", by=length(df2[,1])))

    plot <- ggplot()+
      geom_point(data=df2, aes(x=date, y=df2[,2]))+
      geom_line(data=df2, aes(x=date, y=df2[,2]))

    sname.i<-sname[i]
    print(plot)
  }
}

example(df1)
Run Code Online (Sandbox Code Playgroud)

  • 你当然是对的,但是ggplot2还提供了定期评估美观的功能。只需使用 `aes_string(x="date", y=aesy)` 作为您的美学生成器。 (2认同)