dplyr :: mutate per aggiungere più valori

Ci sono un paio di problemi a riguardo sul dplyr Github repo già, e almeno una domanda SO correlata, ma nessuno di loro copre la mia domanda – penso.

  • Aggiungere più colonne in una chiamata muting dplyr è più o meno quello che voglio, ma c’è una risposta caso speciale per quel caso ( tidyr::separate ) che non (penso) funzioni per me.
  • Questo problema (“riepilogare o mutare con funzioni che restituiscono più valori / colonne”) dice “use do() “.

Ecco il mio caso d’uso: voglio calcolare intervalli di confidenza binomiali esatti

 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 

Posso farlo con do() ma mi chiedo se c’è un modo più espressivo per farlo (sembra che mutate() possa avere un argomento .n come viene discusso per riassumere () …)

 library("dplyr") dd %>% group_by(x,n) %>% do(cbind(.,get_binCI(.$x,.$n))) ## Source: local data frame [2 x 4] ## Groups: x, n ## ## xn lwr upr ## 1 3 10 0.06673951 0.6524529 ## 2 4 11 0.10926344 0.6920953 

Ancora un’altra variante, anche se penso che stiamo tutti dividendo i capelli qui.

 > 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 xn lwr upr 1 3 10 0.06673951 0.6524529 2 4 11 0.10926344 0.6920953 

Personalmente, se stiamo semplicemente leggendo, trovo questo preferibile:

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

… ma ora stiamo davvero dividendo i capelli.

Un’altra opzione potrebbe essere quella di utilizzare la famiglia di funzioni purrr::map .

Se si sostituisce rbind con dplyr::bind_rows nella funzione 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"))) } 

Puoi usare purrr::map2 con tidyr::unnest :

 dd %>% mutate(result = map2(x, n, get_binCI)) %>% unnest() #> xn lwr upr #> 1 3 10 0.06673951 0.6524529 #> 2 4 11 0.10926344 0.6920953 

Oppure purrr::map2_dfr con dplyr::bind_cols :

 dd %>% bind_cols(map2_dfr(.$x, .$n, get_binCI)) #> xn lwr upr #> 1 3 10 0.06673951 0.6524529 #> 2 4 11 0.10926344 0.6920953 

Ecco una soluzione rapida utilizzando invece il pacchetto data.table

Innanzitutto, un piccolo cambiamento alla funzione

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

Quindi, semplicemente

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

Questo utilizza un stream di lavoro dplyr “standard”, ma come note di @BenBolker nei commenti, richiede di chiamare get_binCI due volte:

 dd %>% group_by(x,n) %>% mutate(lwr=get_binCI(x,n)[1], upr=get_binCI(x,n)[2]) xn lwr upr 1 3 10 0.06673951 0.6524529 2 4 11 0.10926344 0.6920953 

Ecco alcune possibilità con rowwise e nesting .

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

frame dati con combinazioni x / n ripetute, per divertimento

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

una versione della funzione CI che restituisce un frame di dati, come @ Joran

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

Raggruppando per x e n come prima, rimuove il duplicato.

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

Usare rowwise mantiene tutte le righe ma rimuove x e n meno che non le cbind(. usando cbind(. (Come fa Ben nel suo OP).

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

Sembra che la nidificazione possa funzionare in modo più pulito, ma questo è il massimo che posso ottenere. Usare mutate significa che posso usare x e n direttamente invece di .$x e .$n , ma mutare si aspetta un singolo valore, quindi deve essere incluso nella list .

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

Infine, sembra che qualcosa del genere sia un problema aperto (al 5 ottobre 2017) per dplyr; vedere https://github.com/tidyverse/dplyr/issues/2326 ; se qualcosa del genere è implementato, allora quello sarà il modo più semplice!