2017-01-25 32 views
-1

dplyr中,我想對先前選擇的組應用一個函數。但是,該功能始終是針對完整數據計算的。小例子:dplyr中的分組和功能

func_a = function(data_a) { 
    value = mean(data_a$V2) 
    return(value) 
} 

data = as.data.frame(cbind(c("a","a","a","b","b","b"), c(1,2,3,4,5,6))) 
data$V2=as.numeric(data$V2) 
data 
V1 V2 
a 1 
a 2 
a 3 
b 4 
b 5 
b 6 
o = data %>% group_by(V1) %>% dplyr::mutate(test = func_a(.)) 

o$test 
[1] 3.5 3.5 3.5 3.5 3.5 3.5 

我本來期望/所需:

[1] 2 2 2 5 5 5 

平均值功能是一種原始的例子,dplyr::mutate(test = mean(V2))會做的工作顯然, 。然而,還有其他功能不能像那樣使用。

這個問題的主要觀點是如何將一片數據幀轉移到一個函數而不是整個函數。

+0

書面,有不能適用這樣的功能,所以是的,這是必須的。 – MaHo

+3

你寫錯了你的功能。它應該是'func_a = function(x)mean(x)',然後你可以使用'data%>%group_by(V1)%>%mutate(test = func_a(V2))'來調用它,或者如果你想它會遍歷所有列'data%>%group_by(V1)%>%mutate_all(funs(func_a))',除非您希望它在沒有告訴它的情況下在「V2」上運行?在這種情況下,您可能需要使用'lazyval'軟件包。 –

+0

謝謝大衛,我仍然爲此付出了一點努力,但它很有效。很高興,我會接受你的答案作爲解決方案。 – MaHo

回答

1

正如@DavidArenburg所評論的,你的功能的工作方式並不是dplyr設計的工作方式。 .明確表示通過%>%傳遞的完整變量(本例中爲data.frame)。我偶爾使用過這種黑客攻擊,如果我想對整個數據做一些事情,並且對組中的某些內容,例如

data %>% 
    group_by(V1) %>% 
    mutate(eg = mean(V2)/mean(.$V2)) 

 V1 V2  eg 
    <fctr> <dbl>  <dbl> 
1  a  1 0.5714286 
2  a  2 0.5714286 
3  a  3 0.5714286 
4  b  4 1.4285714 
5  b  5 1.4285714 
6  b  6 1.4285714 

因此,最好的解決方案,使mutate應用分組,是通過列名(S)代替,例如,

func_forColumn = function(data_a) { 
    value = mean(data_a) 
    return(value) 
} 

data %>% 
    group_by(V1) %>% 
    mutate(test = func_forColumn(V2)) 

 V1 V2 test 
    <fctr> <dbl> <dbl> 
1  a  1  2 
2  a  2  2 
3  a  3  2 
4  b  4  5 
5  b  5  5 
6  b  6  5 

如果你真的它需要能夠傳入完整的data.frame(例如,您正在使用爲舊範式編寫的函數,並且由於某種原因無法更新它們),您可以使用split/lapply,就像我以前用過的那樣,那麼就bind_rows的結果,就像這樣:

data %>% 
    split(.$V1) %>% 
    lapply(function(x){ 
    x %>% 
     mutate(test = func_a(.)) 
    }) %>% 
    bind_rows() 

這給

V1 V2 test 
1 a 1 2 
2 a 2 2 
3 a 3 2 
4 b 4 5 
5 b 5 5 
6 b 6 5 

,或者您可以使用do允許稍微複雜一些的分組/彙總輸出。這樣設計是爲了讓在data.frames多列的回報,但可以適用於您的方案:

data %>% 
    group_by(V1) %>% 
    do(as.data.frame(func_a(.))) 

 V1 `func_a(.)` 
    <fctr>  <dbl> 
1  a   2 
2  b   5 

注意它只返回每個組一行。因此,如果您希望每個原始條目有一行,您需要使用連接(例如left_join)來處理原始數據。

下面是使用do的一個更典型的例子,它可能與您的函數期望獲得完整data.frame的原因更緊密相關。

mySummary <- function(x){ 
    as.data.frame(rbind(summary(x))) 
} 

data %>% 
    group_by(V1) %>% 
    do(mySummary(.$V2)) 

 V1 Min. `1st Qu.` Median Mean `3rd Qu.` Max. 
    <fctr> <dbl>  <dbl> <dbl> <dbl>  <dbl> <dbl> 
1  a  1  1.5  2  2  2.5  3 
2  b  4  4.5  5  5  5.5  6