2017-01-25 33 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