web-dev-qa-db-fra.com

dplyr :: mutate pour ajouter plusieurs valeurs

Il y a quelques problèmes à ce sujet sur le dplyr Github repo déjà, et au moins une question connexe SO, mais aucun d'eux ne couvre tout à fait ma question - je pense.

Voici mon cas d'utilisation: je veux calculer les intervalles de confiance binomiaux exacts

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

Je peux faire ça avec do() mais je me demande s'il y a une manière plus expressive de faire ça (on dirait que mutate() pourrait ont un argument .ncomme cela est discuté pour resume () ...)

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
31
Ben Bolker

Encore une autre variante, même si je pense que nous partageons tous des cheveux ici.

> 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

Personnellement, si nous allons juste par lisibilité, je trouve cela préférable:

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))

... mais maintenant nous sommes vraiment coupant les cheveux.

13
joran

Une autre option pourrait être d'utiliser le purrr::map famille de fonctions.

Si vous remplacez rbind par dplyr::bind_rows dans le get_binCI fonction:

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")))
}

Vous pouvez utiliser purrr::map2 avec tidyr::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

Ou purrr::map2_dfr avec dplyr::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
10
markdly

Voici une solution rapide en utilisant data.table package à la place

Tout d'abord, un petit changement dans la fonction

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

Ensuite, simplement

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
6
David Arenburg

Cela utilise un flux de travail dplyr "standard", mais comme @BenBolker le note dans les commentaires, il nécessite d'appeler get_binCI deux fois:

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
5
eipi10

Voici quelques possibilités avec rowwise et nesting.

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

trame de données avec des combinaisons répétées x/n, pour le plaisir

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

une version de la fonction CI qui renvoie une trame de données, comme celle de @ Joran

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

Le regroupement par x et n comme précédemment, supprime le doublon.

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

L'utilisation de rowwise conserve toutes les lignes mais supprime x et n à moins que vous ne les remettiez à l'aide de cbind(. (comme Ben le fait dans son 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

J'ai l'impression que la nidification pourrait fonctionner plus proprement, mais c'est aussi bon que possible. L'utilisation de mutate signifie que je peux utiliser x et n directement au lieu de .$x et .$n, mais mutate attend une valeur unique, elle doit donc être enveloppée dans 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

Enfin, il semble que quelque chose comme ceci soit un problème ouvert (au 5 octobre 2017) pour dplyr; voir https://github.com/tidyverse/dplyr/issues/2326 ; si quelque chose comme ça est implémenté, ce sera le moyen le plus simple!

3
Aaron