2011-12-05 42 views
5

我想寫一個自定義函數的包裝,它需要一些向量作爲輸入(如:mtcars$hp,mtcars$am等)將輸入作爲數據幀名稱(如data參數,例如:mtcars)和變量名稱(如:hpam),如大多數標準函數一樣。`With`使用內部函數(包裝)

但我有一些問題,我提出了「演示」功能(大約mean的包裝不起作用

代碼:

f <- function(x, data=NULL) { 
    if (!missing(data)) { 
     with(data, mean(x)) 
    } else { 
     mean(x) 
    } 
} 

運行鍼對課程的載體作品:

> f(mtcars$hp) 
[1] 146.69 

with不幸失敗:

> f(hp, mtcars) 
Error in with(d, mean(x)) : object 'hp' not found 

雖然在全球環境/沒有我的自定義函數作品的權利:

> with(mtcars, mean(hp)) 
[1] 146.69 

我試圖做一些實驗substitutedeparse和其他人,但沒有成功。任何暗示都會受到歡迎!

+5

看看@Hadley Wickham的wiki文章:https://github.com/hadley/devtools/wiki /評估 – Andrie

+1

它不應該是'f(hp,mtcars)'? – James

+1

您可能想要探索的另一個選項是使用公式,因此您可以稍微區別它 - foo(〜hp,mtcars) - 然後使用像model.frame這樣的東西來獲取值。 – Spacedman

回答

10

這裏的謎題的關鍵部分:

f <- function(x,data=NULL) { 
    eval(match.call()$x,data) # this is mtcars$hp, so just take the mean of it or whatever 
} 

> f(hp,mtcars) 
[1] 110 110 93 110 175 105 245 62 95 123 123 180 180 180 205 215 230 66 52 65 97 150 150 245 175 66 
[27] 91 113 264 175 335 109 

# it even works without a data.frame specified: 
> f(seq(10)) 
[1] 1 2 3 4 5 6 7 8 9 10 

見@ Andrie的鏈接@哈德利的文檔,爲什麼它的工作原理的解釋。請參閱@ Hadley的注意事項:f()不能從另一個函數內部運行。

基本上R使用懶惰評估(例如,它不會評估的東西,直到他們實際使用)。所以你可以通過它hp逃脫,因爲它仍然是一個未評估的符號,直到它出現在某個地方。由於match.call抓住它作爲一個符號,並等待評估它,一切都很好。

然後eval在指定的環境中對其進行評估。根據?eval,第二個參數表示:

要評估expr的環境。也可以是NULL,一個 列表,一個數據幀,一個pairlist或一個指定給sys.call的整數。

因此,您的狀態良好,可以是NULL(如果不傳遞data.frame)或data.frame。

證明懶惰的評價是,這不會返回一個錯誤(因爲x是從來沒有在函數中使用):

> g <- function(x) { 
+ 0 
+ } 
> g(hp) 
[1] 0 
+0

多麼優雅的成語。感謝你(和哈德利)。 –

+0

大部分是哈德利。我記得它,但不足以在沒有引用他(真棒)wiki的情況下將它從帽子裏拿出來。 –

+0

確實非常整齊。總而言之,eveRy pRoblem是RT(F)M相關的... – aL3xa

-1

試試這個:

f <- function(x, data = NULL) { 
    if (is.null(data)) { 
     mean(x) 
    } else { 
     attach(data) 
     mean(x) 
     detach(data) 
    } 
} 

另外,在你的榜樣,你輸入的數據集而不是列。 你的榜樣應當與F(HP,mtcars)

+0

不錯的努力,但缺少一個括號,我不確定我更喜歡用'attach'來'',而且'f(hp,mtcars)'仍然會以「找不到hp」的方式失敗。 –

3
f <- function(x, data=NULL) { 
    if (!missing(data)) { colname=deparse(substitute(x)) 
     mean(data[[colname]]) 
    } else { 
     mean(x) 
    } 
} 

f(hp, mtcars) 
[1] 146.6875 

(誠然不一樣緊湊@ GSK的,我想我會嘗試記住他的方法,並感謝Josh O'Brien指出現在已經修復的錯誤。)

+0

謝謝@DWin這個黑客! – daroczig