Ошибка объекта не найден с ddply внутри функции

Это действительно поставило под сомнение мою способность отлаживать код R.

Я хочу использовать ddply() для применения одних и тех же функций к разным столбцам с последовательными именами; например. а, б, в. Для этого я намерен многократно передавать имя столбца в виде строки и использовать eval(parse(text=ColName)), чтобы функция могла ссылаться на него. Я взял эту технику из другого ответа.

И это хорошо работает, пока я не вставлю ddply() в другую функцию. Вот пример кода:

# Required packages:
library(plyr)

myFunction <- function(x, y){
    NewColName = "a"
    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

a = c(1,2,3,4)
b = c(0,0,1,1)
c = c(5,6,7,8)
df = data.frame(a,b,c)
sv = c("b")

#This works.
ColName = "a"
ddply(df, sv, summarize,
        Ave = mean(eval(parse(text=ColName)), na.rm=TRUE)
)

#This doesn't work
#Produces error: "Error in parse(text = NewColName) : object 'NewColName' not found"
myFunction(df,sv)

#Output in both cases should be
#  b Ave
#1 0 1.5
#2 1 3.5

Любые идеи? NewColName даже определяется внутри функции!

Я подумал, что ответ на этот вопрос: циклы-для-создания-новых-переменных- in-ddply, может мне помочь, но на сегодня я уже намотал себе голову, и пора поднять руку и попросить о помощи.


person Look Left    schedule 05.08.2011    source источник


Ответы (5)


Вы можете сделать это с помощью комбинации do.call и call, чтобы создать вызов в среде, где NewColName все еще виден:

myFunction <- function(x,y){
NewColName <- "a"
z <- do.call("ddply",list(x, y, summarize, Ave = call("mean",as.symbol(NewColName),na.rm=TRUE)))
return(z)
}

myFunction(d.f,sv)
  b Ave
1 0 1.5
2 1 3.5
person James    schedule 05.08.2011

Сегодняшнее решение этого вопроса - превратить summarize в here(summarize). например

myFunction <- function(x, y){
    NewColName = "a"
    z = ddply(x, y, here(summarize),
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

here(f), добавленный в plyr в декабре 2012 года, фиксирует текущий контекст.

person Peter O    schedule 27.06.2013
comment
Блестяще! При совместном использовании lubridate и plyr убедитесь, что вы специально ссылаетесь на plyr :: here () (поскольку lubridate, к сожалению, переопределяет здесь ()). - person Pierre D; 03.01.2015

Я иногда сталкиваюсь с подобными проблемами при сочетании ddply с summarize или transform или чем-то еще, и, будучи недостаточно умен, чтобы предугадывать тонкости навигации в различных средах, я стараюсь обойти проблему, просто не используя summarize и вместо этого использую свои собственные анонимная функция:

myFunction <- function(x, y){
    NewColName <- "a"
    z <- ddply(x, y, .fun = function(xx,col){
                             c(Ave = mean(xx[,col],na.rm=TRUE))}, 
               NewColName)
    return(z)
}

myFunction(df,sv)

Очевидно, что выполнение этого «вручную» требует определенных затрат, но это часто позволяет избежать головной боли, связанной с проблемами оценки, возникающими при объединении ddply и summarize. Это, конечно, не означает, что Хэдли не придет с решением ...

person joran    schedule 05.08.2011
comment
Пока я не исправлю ошибку, это мой рекомендуемый обходной путь. Обратите внимание, что вы можете использовать transform и т. Д. Внутри своей анонимной функции. - person hadley; 07.08.2011
comment
@joran я реализовал ваше решение, и оно сработало для меня. Мне просто интересно, почему в ddply есть эта проблема с областью видимости? это потому, что summarize создает новый фрейм данных, и у него нет доступа к этому colName? - person joel.wilson; 03.11.2016
comment
@ user3801801 Это связано с нестандартной оценкой аргументов функции. Мне пришлось бы просмотреть исходный код, чтобы напомнить себе о конкретной проблеме, но в основном это связано с тем, как R знает, где оценивать аргументы (т.е. в контексте текущего приложения, в глобальной среде, где-то в между). - person joran; 03.11.2016
comment
@joran Я понял вашу точку зрения! ddply () использует with () внутри, я думаю, как data.tables, и поэтому вектор, в котором хранится имя столбца, здесь, я думаю, не имеет области видимости. - person joel.wilson; 03.11.2016

Проблема заключается в коде самого пакета plyr. В функции суммирования есть строка eval(substitute(...),.data,parent.frame()). Хорошо известно, что parent.frame () может делать довольно необычные и неожиданные вещи. Т

Решение @James - очень хороший обходной путь, но, если я правильно помню, сам @Hadley ранее сказал, что пакет plyr не предназначен для использования в функциях.

Извини, здесь я ошибся. Однако известно, что на данный момент пакет plyr дает проблемы в этих ситуациях.

Поэтому я предлагаю вам базовое решение проблемы:

myFunction <- function(x, y){
    NewColName = "a"
    z = aggregate(x[NewColName],x[y],mean,na.rm=TRUE)
    return(z)
}
> myFunction(df,sv)
  b   a
1 0 1.5
2 1 3.5
person Joris Meys    schedule 05.08.2011
comment
+1 За то, что я принял решение «избежать summarize» и дал реальное объяснение проблемы. ;) - person joran; 05.08.2011
comment
+1 определенно за то, что нашли время объяснить проблему с parent.frame (). Кажется странным, что функцию нельзя использовать внутри другой функции, потому что это заставляет вас писать непрерывный код. Может быть, @Hadley мог бы прокомментировать. - person Look Left; 06.08.2011
comment
Я, конечно, никогда не утверждал, что plyr не предназначен для использования в функциях - я всегда говорил, что это ошибка, которую мне в настоящее время не хватает понимания, чтобы исправить :( - person hadley; 07.08.2011

Похоже, у вас проблемы с окружающей средой. Глобальное задание решает проблему, но ценой души:

library(plyr)

a = c(1,2,3,4)
b = c(0,0,1,1)
c = c(5,6,7,8)
d.f = data.frame(a,b,c)
sv = c("b")

ColName = "a"
ddply(d.f, sv, summarize,
        Ave = mean(eval(parse(text=ColName)), na.rm=TRUE)
)

myFunction <- function(x, y){
    NewColName <<- "a"
    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}

myFunction(x=d.f,y=sv)

eval ищет в parent.frame (1). Поэтому, если вы вместо этого определите NewColName вне MyFunction, он должен работать:

rm(NewColName)
NewColName <- "a"
myFunction <- function(x, y){

    z = ddply(x, y, summarize,
            Ave = mean(eval(parse(text=NewColName)), na.rm=TRUE)
    )
    return(z)
}
myFunction(x=d.f,y=sv)

Используя get для извлечения my.parse из более ранней среды, мы можем подойти намного ближе, но по-прежнему должны передать curenv как глобальный:

myFunction <- function(x, y){
    NewColName <- "a"
    my.parse <- parse(text=NewColName)
    print(my.parse)
    curenv <<- environment()
    print(curenv)

    z = ddply(x, y, summarize,
            Ave = mean( eval( get("my.parse" , envir=curenv ) ), na.rm=TRUE)
    )
    return(z)
}

> myFunction(x=d.f,y=sv)
expression(a)
<environment: 0x0275a9b4>
  b Ave
1 0 1.5
2 1 3.5

Я подозреваю, что ddply уже оценивается в .GlobalEnv, поэтому все стратегии parent.frame() и sys.frame(), которые я пробовал, потерпели неудачу.

person Ari B. Friedman    schedule 05.08.2011
comment
Я подозреваю, что для решения этой проблемы может потребоваться функция @Hadley :-) - person Ari B. Friedman; 05.08.2011
comment
Огромное усилие, чтобы попробовать все это. Групповой хедбанг всегда приветствуется ... Я сохраню свою душу - person Look Left; 07.08.2011