6

Рассмотрите это использование ggplot(...) внутри функции.Определение переменных в aes (...) внутри функции в ggplot

x <- seq(1,10,by=0.1) 
df <- data.frame(x,y1=x, y2=cos(2*x)/(1+x)) 

library(ggplot2) 
gg.fun <- function(){ 
    i=2 
    plot(ggplot(df,aes(x=x,y=df[,i]))+geom_line()) 
} 

if(exists("i")) remove(i) 
gg.fun() 
# Error in `[.data.frame`(df, , i) : object 'i' not found 
i=3 
gg.fun() # plots df[,3] vs. x 

Похоже, ggplot не распознает переменную i определенную внутри функции, но делает признать i, если она определена в глобальной среде. Почему это?

Обратите внимание, что это дает ожидаемый результат.

gg.new <- function(){ 
    i=2 
    plot(ggplot(data.frame(x=df$x,y=df[,i]),aes(x,y)) + geom_line()) 
} 
if(exists("i")) remove(i) 
gg.new() # plots df[,2] vs. x 
i=3 
gg.new() # also plots df[,2] vs. x 
+0

Вы можете использовать 'aes_string' передать аргументы? –

+0

@ RomanLuštrik - Спасибо, но я никогда не буду использовать 'aes (...)' like this; Я бы сделал это вторым способом. Я пытаюсь понять, что происходит. Это ошибка? Или есть веская причина такого поведения? Или я просто делаю что-то неправильно? – jlhoward

+0

Это может быть связано с моей проблемой? http://stackoverflow.com/questions/23482036/ggplot-inside-function-treating-subsetdf-differently-resulting-geom-step-is-w Я попробовал ваш подход там, но не решил его. – smci

ответ

5

Давайте возвращать не рендерящийся ggplot объект, чтобы увидеть, что происходит:

gg.str <- function() { 
    i=2 
    str(ggplot(df,aes(x=x,y=df[,i]))+geom_line()) 
} 

gg.str() 
List of 9 
$ data  :'data.frame': 91 obs. of 3 variables: 
    ..$ x : num [1:91] 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 ... 
    ..$ y1: num [1:91] 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 ... 
    ..$ y2: num [1:91] -0.208 -0.28 -0.335 -0.373 -0.393 ... 
$ layers  :List of 1 
    ..$ :Classes 'proto', 'environment' <environment: 0x0000000009886ca0> 
$ scales  :Reference class 'Scales' [package "ggplot2"] with 1 fields 
    ..$ scales: list() 
    ..and 21 methods, of which 9 are possibly relevant: 
    .. add, clone, find, get_scales, has_scale, initialize, input, n, non_position_scales 
$ mapping :List of 2 
    ..$ x: symbol x 
    ..$ y: language df[, i] 
$ theme  : list() 
$ coordinates:List of 1 
    ..$ limits:List of 2 
    .. ..$ x: NULL 
    .. ..$ y: NULL 
    ..- attr(*, "class")= chr [1:2] "cartesian" "coord" 
$ facet  :List of 1 
    ..$ shrink: logi TRUE 
    ..- attr(*, "class")= chr [1:2] "null" "facet" 
$ plot_env :<environment: R_GlobalEnv> 
$ labels  :List of 2 
    ..$ x: chr "x" 
    ..$ y: chr "df[, i]" 
- attr(*, "class")= chr [1:2] "gg" "ggplot" 

Как мы можем видеть, mapping для y это просто невычисленного выражение. Теперь, когда мы просим сделать фактический график, выражение оценивается в пределах plot_env, что является глобальным. Я не знаю, почему это делается; Я считаю, что для этого есть причины.

Вот демо, которое может изменить это поведение:

gg.envir <- function(envir=environment()) { 
    i=2 
    p <- ggplot(df,aes(x=x,y=df[,i]))+geom_line() 
    p$plot_env <- envir 
    plot(p) 
} 
# evaluation in local environment; ok 
gg.envir() 
# evaluation in global environment (same as default); fails if no i 
gg.envir(environment())