2015-04-13 3 views
21

Есть еще несколько вопросов об этом на dplyr Github repo и, по крайней мере, один связанный вопрос SO, но ни один из них не охватывает мой вопрос - я думаю.dplyr :: mutate добавить несколько значений

  • Adding multiple columns in a dplyr mutate call более или менее то, что я хочу, но есть специальный случай, ответ на этот случай (tidyr::separate), не (я думаю) работает для меня.
  • This issue («суммировать или мутировать с функциями, возвращающими несколько значений/столбцов») говорит «use do()».

Вот мой случай использования: Я хочу, чтобы вычислить точные биномиальных доверительные интервалы

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 
+1

Вы поселились, чтобы сделать это особенно с 'dplyr'? С помощью 'data.table' вы можете быстро выполнить' setDT (dd) [, as.list (get_binCI (x, n)), by =. (X, n)] 'Хотя мои умственные навыки чтения не позволяют мне определите, что именно вы подразумеваете под выражением «* выразительный путь» ... –

+2

Это, безусловно, хорошо. Я * * надеялся на ответ 'dplyr' (хотя я не удивлюсь, если мое решение выше - лучшее, что может сделать ATM). У меня нет ничего против 'data.table', но я предпочитаю' dplyr', и, в основном, я все еще трачу много сил, обнимая его, не хочу добавлять совершенно новый набор синтаксис (и не навязывать его моим ученикам и коллегам) на данный момент. Но если вы ответите так, я продолжу, это полезно. –

+0

Привет всем, надеясь нанести удар; есть ли теперь лучший способ сделать это с помощью гнездования? Я пытаюсь, но еще не получил его. – Aaron

ответ

8

Еще один вариант, хотя я думаю, что мы все раскалываем волосы здесь.

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

... но теперь мы действительно расщепление волос.

+0

tuns out Мне не нужен' data.frame() 'в любом случае (см. правки). –

+0

... и ... в моем реальном случае использования мне нужно сгруппировать что-то другое, кроме' x' и 'n' ... но я мог бы использовать это –

+0

. Я думаю, что ваш второе решение выигрывает, но я немного удержаюсь на принятии. –

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 
+0

вот базовое решение @David Arenburg !! 'dd [, c ('lwr', 'upr')] <- t (mapply (get_binCI, dd [, 1], dd [, 2]))' – rawr

+7

@rawr Я не уверен, почему вы публикуете это как комментарий под моим ответом :) Я предлагаю вам опубликовать это как свое собственное решение (я обещаю увеличить). –

+0

@rawr, является 'Map()' более безопасным (без упрощения)? –

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 
+0

Да, это решение, но уродство в этом заключается в вызове' get_binCI() 'дважды.Похоже, что это лучше или хуже, чем 'do (cbind (., Data.frame (get_binCI (. $ X,. $ N)))' (я мог бы избавиться от данных .frame() ', вставив его внутри' get_binCI') –

+0

Я согласен. Я просто пытался найти что-то с dplyr, который работал бы без вызова 'do'. – eipi10

1

Вот некоторые варианты с rowwise и nesting.

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

кадр данных с повторными х/п комбинаций, для удовольствия

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

Группировка по x и n, как и раньше, удаляет дубликат.

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 сохраняет все строки, но удаляет x и n, если вы поместите их обратно с помощью cbind(. (как Бен делает в 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 означает, что я могу использовать x и n непосредственно вместо .$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 

И, наконец, похоже, что это открытый вопрос (по состоянию на 5 октября 2017 года) для dplyr; см. https://github.com/tidyverse/dplyr/issues/2326; если что-то подобное реализовано, то это будет самый простой способ!

5

Еще одним вариантом может служить семейство функций 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::map2 с 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 

Или purrr::map2_dfr с 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 
+1

Приятно, спасибо, это полезно. – Aaron

Смежные вопросы