dplyr :: mutate添加多个值

时间:2015-04-13 20:50:11

标签: r dplyr

关于dplyr Github repo已经存在一些问题,至少有一个相关的SO问题,但是我认为这些问题都没有完全涵盖我的问题。

这是我的用例:我想计算精确的二项式置信区间

dd <- data.frame(x=c(3,4),n=c(10,11))
get_binCI <- function(x,n) {
    rbind(setNames(c(binom.test(x,n)$conf.int),c("lwr","upr")))
}
with(dd[1,],get_binCI(x,n))
##             lwr       upr
## [1,] 0.06673951 0.6524529

我可以使用do()完成此操作,但我想知道是否有更具表现力的方式(感觉mutate() 可以拥有.n参数as is being discussed for summarise() ...)

library("dplyr")
dd %>% group_by(x,n) %>%
    do(cbind(.,get_binCI(.$x,.$n)))

## Source: local data frame [2 x 4]
## Groups: x, n
## 
##   x  n        lwr       upr
## 1 3 10 0.06673951 0.6524529
## 2 4 11 0.10926344 0.6920953

6 个答案:

答案 0 :(得分:13)

另一种变体,虽然我认为我们都在这里分裂。

> dd <- data.frame(x=c(3,4),n=c(10,11))
> get_binCI <- function(x,n) {
+   as_data_frame(setNames(as.list(binom.test(x,n)$conf.int),c("lwr","upr")))
+ }
> 
> dd %>% 
+   group_by(x,n) %>%
+   do(get_binCI(.$x,.$n))
Source: local data frame [2 x 4]
Groups: x, n

  x  n        lwr       upr
1 3 10 0.06673951 0.6524529
2 4 11 0.10926344 0.6920953

就个人而言,如果我们只是通过可读性,我发现这更可取:

foo  <- function(x,n){
    bi <- binom.test(x,n)$conf.int
    data_frame(lwr = bi[1],
               upr = bi[2])
}

dd %>% 
    group_by(x,n) %>%
    do(foo(.$x,.$n))

...但现在我们真的分裂了头发。

答案 1 :(得分:10)

另一种选择可能是使用purrr::map系列函数。

如果您使用rbind功能中的dplyr::bind_rows替换get_binCI

library(tidyverse)

dd <- data.frame(x = c(3, 4), n = c(10, 11))
get_binCI <- function(x, n) {
  bind_rows(setNames(c(binom.test(x, n)$conf.int), c("lwr", "upr")))
}

您可以将purrr::map2tidyr::unnest一起使用:

dd %>% mutate(result = map2(x, n, get_binCI)) %>% unnest()

#>   x  n        lwr       upr
#> 1 3 10 0.06673951 0.6524529
#> 2 4 11 0.10926344 0.6920953

purrr::map2_dfrdplyr::bind_cols

dd %>% bind_cols(map2_dfr(.$x, .$n, get_binCI))

#>   x  n        lwr       upr
#> 1 3 10 0.06673951 0.6524529
#> 2 4 11 0.10926344 0.6920953

答案 2 :(得分:5)

以下是使用data.table包而非

的快速解决方案

首先,对功能进行一点改动

get_binCI <- function(x,n) as.list(setNames(binom.test(x,n)$conf.int, c("lwr", "upr")))

然后,只需

library(data.table)
setDT(dd)[, get_binCI(x, n), by = .(x, n)]
#    x  n        lwr       upr
# 1: 3 10 0.06673951 0.6524529
# 2: 4 11 0.10926344 0.6920953

答案 3 :(得分:5)

这使用了“标准”dplyr工作流程,但正如@BenBolker在评论中指出的那样,它需要两次调用get_binCI

dd %>% group_by(x,n) %>%
  mutate(lwr=get_binCI(x,n)[1],
         upr=get_binCI(x,n)[2])

  x  n        lwr       upr
1 3 10 0.06673951 0.6524529
2 4 11 0.10926344 0.6920953

答案 4 :(得分:3)

以下是rowwisenesting的一些可能性。

library("dplyr")
library("tidyr")

带有重复x / n组合的数据框,非常有趣

dd <- data.frame(x=c(3, 4, 3), n=c(10, 11, 10))

返回数据框的CI函数版本,如@ Joran的

get_binCI_df <- function(x,n) {
  binom.test(x, n)$conf.int %>% 
    setNames(c("lwr", "upr")) %>% 
    as.list() %>% as.data.frame()
}

按照xn分组,删除重复项。

dd %>% group_by(x,n) %>% do(get_binCI_df(.$x,.$n))
# # A tibble: 2 x 4
# # Groups:   x, n [2]
#       x     n       lwr       upr
#   <dbl> <dbl>     <dbl>     <dbl>
# 1     3    10 0.1181172 0.8818828
# 2     4    11 0.1092634 0.6920953

使用rowwise会保留所有行,但会删除xn,除非您使用cbind(.将其放回去(就像Ben在他的OP中所做的那样)。

dd %>% rowwise() %>% do(cbind(., get_binCI_df(.$x,.$n)))
# Source: local data frame [3 x 4]
# Groups: <by row>
#   
# # A tibble: 3 x 4
#       x     n        lwr       upr
# * <dbl> <dbl>      <dbl>     <dbl>
# 1     3    10 0.06673951 0.6524529
# 2     4    11 0.10926344 0.6920953
# 3     3    10 0.06673951 0.6524529

感觉嵌套可以更干净地工作,但这是我能得到的好。使用mutate表示我可以直接使用xn代替.$x.$n,但mutate需要单个值,因此需要将其包含在内list

dd %>% rowwise() %>% mutate(ci=list(get_binCI_df(x, n))) %>% unnest()
# # A tibble: 3 x 4
#       x     n        lwr       upr
#   <dbl> <dbl>      <dbl>     <dbl>
# 1     3    10 0.06673951 0.6524529
# 2     4    11 0.10926344 0.6920953
# 3     3    10 0.06673951 0.6524529

最后,对于dplyr来说,这样的事情是一个公开的问题(截至2017年10月5日);见https://github.com/tidyverse/dplyr/issues/2326;如果实现了类似的东西那么这将是最简单的方法!

答案 5 :(得分:2)

古老的问题(有很多好的答案),但这是tidyverse的broom package的一个很好的用例,它处理来自测试和建模对象(例如binom.test,{{1 }}等。

它比其他方法更冗长,但我认为它符合您对更具表现力的方法的渴望。

过程是:

  1. 定义将在lm上运行的组(在这种情况下,这些组由binom.testx定义)和n,以创建单独的数据每个.frame(在完整的data.frame中)
  2. nest对每个组的mapbinom.test值的x调用
  3. n每个组的tidy输出(这是扫帚进入的位置)
  4. binom.test整理好的测试输出data.frames到完整data.frame

现在剩下的是一个data.frame,其中每行包含unnestx值,以及来自相应n的所有输出,并分别用整齐的格式进行格式化输出信息的每一位(点估计,上/下conf,p值等)的列。

binom.test

在这里,您只需进行一点点操作即可选择所需的确切格式,选择所需的输出变量,然后重命名它们:

library(tidyverse)
library(broom)
dd <- data.frame(x=c(3,4),n=c(10,11))
dd %>%
  group_by(x, n) %>%
  nest() %>%
  mutate(test = map(data, ~tidy(binom.test(x, n)))) %>%
  unnest(test)
#> # A tibble: 2 x 11
#> # Groups:   x, n [2]
#>       x     n data  estimate statistic p.value parameter conf.low conf.high
#>   <dbl> <dbl> <lis>    <dbl>     <dbl>   <dbl>     <dbl>    <dbl>     <dbl>
#> 1     3    10 <tib…    0.3           3   0.344        10   0.0667     0.652
#> 2     4    11 <tib…    0.364         4   0.549        11   0.109      0.692
#> # … with 2 more variables: method <chr>, alternative <chr>

如前所述,它很冗长。比(例如)@joran简洁优美

dd %>%
  group_by(x, n) %>%
  nest() %>%
  mutate(test = map(data, ~tidy(binom.test(x, n)))) %>%
  unnest(test) %>%
  rename(lwr = conf.low, upr = conf.high) %>%
  select(x, n, lwr, upr)
#> # A tibble: 2 x 4
#> # Groups:   x, n [2]
#>       x     n    lwr   upr
#>   <dbl> <dbl>  <dbl> <dbl>
#> 1     3    10 0.0667 0.652
#> 2     4    11 0.109  0.692

但是,扫帚方法的好处是您不需要定义函数dd %>% group_by(x,n) %>% do(foo(.$x,.$n)) (或foo)。它是完全独立的,我认为它更具表现力和灵活性。