使用“with”和qplot(是的,这是错误的),出乎意料的结果 - 真的很深奥

时间:2015-04-13 16:08:46

标签: r ggplot2 environment

好的,我正在上这堂课,其中一位学生发布了一个关于密谋使用的问题。事实证明,她并不需要为她的情节而烦恼,但我和你一起玩,看看你如何使用它。

所以使用with base plotting很有意思,我可以得到很酷的情节。

然后我决定尝试将它与ggplot一起使用。特别是qplot。现在我知道这不是这样做的方式,但我很想知道会发生什么。

所以我这样做了:

我创建了一个小数据框 - mydf

> mydf
    x  y year
1   6 24 2004
2  22 28 2004
3  44 16 2004
4  40 47 2003
5  50 23 2002
6  49 48 2003
7   2 21 2004
8   3 12 2002
9  19 11 2003
10 29 25 2002

所以正确的方法是:

qplot(x,y,data = mydf)

显然它很有效。但我很想知道发生了什么,所以我做了这个

with(mydf,qplot(x,y))

我真的没想到它会起作用,但事实确实如此。凉爽

然后我尝试了方面。所以这样做是正确的。

qplot(x,y, facets = .~ year,data = mydf)

但这会引发错误。

> with(mydf,qplot(x,y, facets = .~ year))
Error in FUN("year"[[1L]], ...) : object 'year' not found

我发现奇怪的是,qplot可以访问mydf $ x和mydf $ y,而尝试访问face in face则会抛出错误。

我认为这是与环境有关的问题。例如,我可以这样做:

the_years <- mydf$year
with(mydf,qplot(x,y, facets = .~ the_years))

它很好。因此,如果我在全局环境中有一个变量,那么就可以访问它。

但是这个

with(mydf,qplot(x,y, facets = .~ mydf$year))
Error in FUN(c("mydf", "year")[[2L]], ...) : object 'year' not found

会抛出错误,即使这也是在全局环境中。这种错误类似于将mydf和year拆分为传递给函数的两个不同参数。

据我了解,创建一个环境然后在该环境中评估表达式

所以这是有效的

> with(mydf,{new_years <- year;print(new_years)})
 [1] 2004 2004 2004 2003 2002 2003 2004 2002 2003 2002
Levels: 2002 2003 2004

我有点希望这会奏效。在with环境中创建一个新变量。

> with(mydf,{new_years <- year; qplot(x,y, facets = .~ new_years)})
Error in FUN("new_years"[[1L]], ...) : object 'new_years' not found

但qplot仍然没有看到它

所以我希望比我聪明的人能告诉我发生了什么。

我知道这是有史以来最奇怪的问题之一。正确答案是:&#34;不要这样做&#34;。 :)

如果qplot在我以这种方式使用x和y时遭到轰炸,我将非常高兴。但是基本情节部分中的qplot可以访问mydf $ x和mydf $ y,这似乎很奇怪。但是mtf $ year并没有在facets电话中找到。

欢迎任何想法。

1 个答案:

答案 0 :(得分:2)

和自己说话。对此感到抱歉,但这真让我烦恼。 :)

以下是我的想法。这是来自github的qplot的代码。 https://github.com/hadley/ggplot2/blob/master/R/quick-plot.r

if (missing(data)) {
    # If data not explicitly specified, will be pulled from workspace
    data <- data.frame()

    # Faceting variables must be in a data frame, so pull those out
    facetvars <- all.vars(facets)
    facetvars <- facetvars[facetvars != "."]
    names(facetvars) <- facetvars
    facetsdf <- as.data.frame(lapply(facetvars, get))
    if (nrow(facetsdf)) data <- facetsdf
}

我的想法是,如果没有指定数据框,那么代码会尝试从构面中获取信息。

如果没有创建数据框中传递的数据 - 称为数据

facetvars是从all.vars(facets)创建的。

all.vars返回一个字符向量,其中包含表达式或调用中出现的所有名称。 http://www.inside-r.org/r-doc/base/all.vars

这里有一些例子

> all.vars(.~ mydf)
[1] "."    "mydf"
> all.vars(.~ mydf$year)
[1] "."    "mydf" "year"
> all.vars(.~ year)
[1] "."    "year"

下一行摆脱&#39;。&#39;

然后它使用名称来设置名称,然后它尝试创建一个数据框&#34;&#34;得到&#34;来自环境的信息使用get()函数和KABOOM!

以下是我浏览代码时的结果

> facetvars <- all.vars(.~ year)
> facetvars
[1] "."    "year"
> facetvars <- facetvars[facetvars != "."]
> facetvars
[1] "year"
> names(facetvars) <- facetvars
> facetvars
  year 
"year" 
>  facetsdf <- as.data.frame(lapply(facetvars, get))
Error in FUN("year"[[1L]], ...) : object 'year' not found

这是在文件中调用get的唯一地方。所以它正在寻找错误的环境来构建新的facetsdf数据框架,并且没有办法改变环境 - 至少很容易。没有&#34;年&#34;在全球环境中。 &#34;一年&#34;确实存在于&#34;与&#34;环境,但qplot看不到那里。

我不确定qplot的环境是什么样的。我原本认为这将是环境,但似乎并非如此。无论哪种方式,它都会在环境树中查找&#34;年&#34;但从来没有找到它。 &#34;&#34;的环境在另一个分支上。

这是另一个例子。同样的问题 - 没有&#34;年&#34;在qplot正在查找的任何环境中。

> facetvars <- all.vars(.~ mydf$year)
> facetvars
[1] "."    "mydf" "year"
> facetvars <- facetvars[facetvars != "."]
> facetvars
[1] "mydf" "year"
> names(facetvars) <- facetvars
> facetvars
mydf   year 
"mydf" "year" 
>  facetsdf <- as.data.frame(lapply(facetvars, get))
Error in FUN(c("mydf", "year")[[2L]], ...) : object 'year' not found

它起作用的例子

> facetvars <- all.vars(.~ the_years)
> facetvars
[1] "."         "the_years"
> facetvars <- facetvars[facetvars != "."]
> facetvars
[1] "the_years"
> names(facetvars) <- facetvars
> facetvars
  the_years 
"the_years" 
>  facetsdf <- as.data.frame(lapply(facetvars, get))
> facetsdf
   the_years
1       2004
2       2004
3       2004
4       2003
5       2002
6       2003
7       2004
8       2002
9       2003
10      2002

所以现在它可以找到&#34; the_years&#34;在全球环境中。它可以面对这个变量。

另一个例子。

> facetvars <- all.vars(.~ mydf)
> facetvars
[1] "."    "mydf"
> facetvars <- facetvars[facetvars != "."]
> facetvars
[1] "mydf"
> names(facetvars) <- facetvars
> facetvars
  mydf 
"mydf" 
>  facetsdf <- as.data.frame(lapply(facetvars, get))
> facetsdf
   mydf.x mydf.y mydf.year
1       6     24      2004
2      22     28      2004
3      44     16      2004
4      40     47      2003
5      50     23      2002
6      49     48      2003
7       2     21      2004
8       3     12      2002
9      19     11      2003
10     29     25      2002

现在这一次&#34; mydf&#34;找到了 - 但qplot不知道如何处理它。

with(mydf,qplot(x,y, facets = . ~ mydf))
Error in layout_base(data, cols, drop = drop) : 
At least one layer must contain all variables used for faceting

所以,这是我对自己问题的最佳答案。当没有数据传入时,qplot只尝试从不同的环境中获取get(),此时,如果存在facet,那么它会尝试使用facet中的信息来创建数据框,这就是代码炸弹的时候。

稍后编辑以添加: 是的,还在跟自己说话。 :)事实证明,修复此行为需要做的就是对quick-plot.R(qplot的源代码)的源代码进行微小的更改

你所要做的就是搬家

env <- parent.frame()

到代码中的某个位置然后在<-p>中的quick-plot.R中更改第97行

facetsdf <- as.data.frame(lapply(facetvars, get))

facetsdf <- as.data.frame(lapply(facetvars, function(x) {get(x,env)}))

然后将按预期工作。