Использование ggplot() в рамках другой функции в R

Я пытаюсь написать простую функцию графика, используя библиотеку ggplot2. Но вызов ggplot не находит аргумент функции.

Рассмотрим a data.frame, называемый means, который хранит два условия и два средних значения, которые я хочу построить (условие появится на оси X, значит, на Y).

library(ggplot2)
m <- c(13.8, 14.8)
cond <- c(1, 2)
means <- data.frame(means=m, condition=cond)
means
# The output should be:
#     means    condition
#   1 13.8     1
#   2 14.8     2

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, aes(fill=meansdf$condition, y=meansdf$means, x = meansdf$condition))
  p + geom_bar(position="dodge", stat="identity")
}

testplot(means)
# This will output the following error:
# Error in eval(expr, envir, enclos) : object 'meansdf' not found

Итак, кажется, что ggplot вызывает eval, который не может найти аргумент meansdf. Кто-нибудь знает, как я могу успешно передать аргумент функции ggplot?

(Примечание. Да, я могу просто вызвать функцию ggplot напрямую, но в конце я надеюсь, что моя функция графика сделает более сложные вещи!:))

Ответ 1

Как правильно ответили Joris и Chase, стандартная передовая практика - просто опустить часть meansdf$ и напрямую обращаться к столбцам фрейма данных.

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means,
                  x = condition))
  p + geom_bar(position = "dodge", stat = "identity")
}

Это работает, потому что переменные, упомянутые в aes, рассматриваются либо в глобальной среде, либо в кадре данных, переданном в ggplot. Это также причина, почему ваш примерный код - с помощью meansdf$condition и т.д. - не работал: meansdf не доступен в глобальной среде и не доступен в кадре данных, переданном в ggplot, который равен meansdf сам.


Тот факт, что переменные ищутся в глобальной среде, а не в вызывающей среде, - это фактически известная ошибка в ggplot2, которую Хэдли не считает фиксированной на момент. Это приводит к проблемам, если вы хотите использовать локальную переменную, скажем, scale, чтобы влиять на данные, используемые для графика:

testplot <- function(meansdf)
{
  scale <- 0.5
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means * scale,   # does not work, since scale is not found
                  x = condition))
  p + geom_bar(position = "dodge", stat = "identity")
}

Очень хороший обходной путь для этого случая предоставляется Уинстоном Чангом в заданной проблеме GitHub: Явно устанавливаю параметр environment для текущей среды во время вызова ggplot. Вот как это выглядело бы для приведенного выше примера:

testplot <- function(meansdf)
{
  scale <- 0.5
  p <- ggplot(meansdf, 
              aes(fill = condition,
                  y = means * scale,
                  x = condition),
              environment = environment())   # This is the only line changed / added
  p + geom_bar(position = "dodge", stat = "identity")
}

## Now, the following works
testplot(means)

Ответ 2

"Правильный" способ использовать ggplot программно - использовать aes_string() вместо aes() и использовать имена столбцов как символы, а не как объекты:

Для более программных применений, например, если вы хотите, чтобы пользователи могли указывать имена столбцов для различных эстетических объектов в качестве аргументов или если эта функция идет в пакете, который должен пройти R CMD CHECK без предупреждений об именах переменных без определений, вы можете использовать aes_string(), с столбцами, необходимыми в качестве символов.

testplot <- function(meansdf, xvar = "condition", yvar = "means",
                     fillvar = "condition") {
    p <- ggplot(meansdf,
                aes_string(x = xvar, y= yvar, fill = fillvar)) +
             geom_bar(position="dodge", stat="identity")
}

Ответ 3

Вот простой трюк, который я использую для определения моих переменных в моей среде функций (вторая строка):

FUN <- function(fun.data, fun.y) {
    fun.data$fun.y <- fun.data[, fun.y]
    ggplot(fun.data, aes(x, fun.y)) + 
        geom_point() + 
        scale_y_continuous(fun.y)    
}

datas <- data.frame(x = rnorm(100, 0, 1),
                    y = x + rnorm(100, 2, 2),
                    z = x + rnorm(100, 5, 10))
FUN(datas, "y")
FUN(datas, "z")

Обратите внимание, что метка оси Y также изменяется, когда используются разные переменные или наборы данных.

Ответ 4

Я не думаю, что вам нужно включить часть meansdf$ в свой вызов функции. Кажется, это работает на моей машине:

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(meansdf)
{
p <- ggplot(meansdf, aes(fill=condition, y=means, x = condition))
p + geom_bar(position="dodge", stat="identity")
}


testplot(meansdf)

для создания:

enter image description here

Ответ 5

Это пример проблемы, обсуждавшейся ранее . В основном, это сводится к тому, что ggplot2 кодируется для использования в глобальной среде в основном. В вызове aes() переменные ищутся либо в глобальной среде, либо в пределах указанного фрейма данных.

library(ggplot2)
means <- data.frame(means=c(13.8,14.8),condition=1:2)

testplot <- function(meansdf)
{
  p <- ggplot(meansdf, aes(fill=condition, 
          y=means, x = condition))
  p + geom_bar(position="dodge", stat="identity")
}

EDIT:

update: после просмотра другого ответа и обновления пакета ggplot2, приведенный выше код работает. Разумеется, как поясняется в комментариях, ggplot будет искать переменные в aes в глобальной среде (когда фрейм данных специально добавляется как meandf $...) или в пределах указанной среды.

Для этого убедитесь, что вы работаете с последней версией ggplot2.

Ответ 6

Это разочаровало меня в течение некоторого времени. Я хотел отправить разные кадры данных с разными именами переменных, и мне нужна возможность отображать разные столбцы из фрейма данных. Я, наконец, получил работу, создав некоторые фиктивные (глобальные) переменные, чтобы обрабатывать построение и принудительное назначение внутри функции

plotgraph function(df,df.x,df.y) {

dummy.df <<- df
dummy.x <<- df.x
dummy.y <<- df.y

p = ggplot(dummy.df,aes(x=dummy.x,y=dummy.y,.....)
print(p)

}

то в главном коде я могу просто вызвать функцию

plotgraph(data,data$time,data$Y1)
plotgraph(data,data$time,data$Y2)

Ответ 7

Короткий ответ: используйте qplot

Длинный ответ: В сущности, вы хотите что-то вроде этого:

my.barplot <- function(x=this.is.a.data.frame.typically) {
   # R code doing the magic comes here
   ...
}

Но у этого нет гибкости, потому что вы должны придерживаться последовательного названия столбцов, чтобы избежать раздражающих особенностей области R. Конечно, следующий логический шаг:

my.barplot <- function(data=data.frame(), x=..., y....) {
   # R code doing something really really magical here
   ...
}

Но тогда это начинает подозрительно выглядеть, как вызов qplot(), правильно?

qplot(data=my.data.frame, x=some.column, y=some.other column,
      geom="bar", stat="identity",...)

Конечно, теперь вы хотели бы изменить такие вещи, как названия шкал, но для этого функция удобна... Хорошей новостью является то, что проблемы с охватом часто больше не проходят.

my.plot <- qplot(data=my.data.frame, x=some.column, y=some.other column,...)
set.scales(p, xscale=scale_X_continuous, xtitle=NULL,
           yscale=scale_y_continuous(), title=NULL) {
  return(p + xscale(title=xtitle) + yscale(title=ytitle))
}
my.plot.prettier <- set.scale(my.plot, scale_x_discrete, 'Days',
                              scale_y_discrete, 'Count')

Ответ 8

Другим обходным решением является определение aes (...) как переменной вашей функции:

func<-function(meansdf, aes(...)){}

Это просто сработало для меня по аналогичной теме

Ответ 9

Вам ничего не нужно. Даже не фиктивные переменные. Вам нужно только добавить функцию print() внутри вашей функции, как с помощью cat(), когда вы хотите, чтобы что-то отображалось в консоли.

myplot < - ggplot (......) + Все, что вы хотите здесь print (myplot)

Он работал у меня более одного раза внутри одной и той же функции

Ответ 10

Я просто генерирую новые переменные фрейма данных с нужными именами внутри функции:

testplot <- function(df, xVar, yVar, fillVar) {
    df$xVar = df[,which(names(df)==xVar)]
    df$yVar = df[,which(names(df)==yVar)]
    df$fillVar = df[,which(names(df)==fillVar)]
    p <- ggplot(df,
                aes(x=xvar, y=yvar, fill=fillvar)) +
             geom_bar(position="dodge", stat="identity")
    }

Ответ 11

Если важно передать переменные (имена столбцов) в пользовательскую функцию построения без кавычек, в то время как в функции используются разные имена переменных, то другой обходной путь, который я пробовал, заключался в использовании match.call() и eval (как здесь также):

library(ggplot2)

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(df, x, y) {
  arg <- match.call()
  scale <- 0.5
  p <- ggplot(df, aes(x = eval(arg$x),
                      y = eval(arg$y) * scale,
                      fill = eval(arg$x)))
  p + geom_bar(position = "dodge", stat = "identity")
}

testplot(meansdf, condition, means)

Создано в 2019-01-10 пакетом представлением (v0.2.1)

Другой обходной путь, но с передачей заключенных в кавычки переменных в пользовательскую функцию построения графиков, использует get():

meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)

testplot <- function(df, x, y) {
  scale <- 0.5
  p <- ggplot(df, aes(x = get(x),
                      y = get(y) * scale,
                      fill = get(x)))
  p + geom_bar(position = "dodge", stat = "identity")
}

testplot(meansdf, "condition", "means")

Создано в 2019-01-10 пакетом представлением (v0.2.1)