2017-05-26 20 views
5

Próbuję napisać proste opakowanie do dowolnych zmiennych summarise() przez dowolne grupy i zrobiłem postęp teraz Mam correct library version loaded, ale jestem zdezorientowany (ponownie), jak odliczyć argumenty z wieloma wartości.programowanie w trybie dplyr-0.6.0 z pominięciem

Obecnie mam następujące funkcje ...

table_summary <- function(df  = ., 
          id  = individual_id, 
          select = c(), 
          group = site, 
          ...){ 
    ## Quote all arguments (see http://dplyr.tidyverse.org/articles/programming.html) 
    quo_id  <- enquo(id) 
    quo_select <- enquo(select) 
    quo_group <- enquo(group) 
    ## Subset the data 
    df <- df %>% 
      dplyr::select(!!quo_id, !!quo_select, !!quo_group) %>% 
      unique() 
    ## gather() data, just in case there is > 1 variable selected to be summarised 
    df <- df %>% 
      gather(key = variable, value = value, !!quo_select) 
    ## Summarise selected variables by specified groups 
    results <- df %>% 
      group_by(!!quo_group, variable) %>% 
      summarise(n = n(), 
        mean = mean(value, na.rm = TRUE)) 
    return(results) 
} 

które dostaje większość drogi tam i działa, jeśli mogę określić jedną zmienną grupowania ...

> table_summary(df = mtcars, id = model, select = c(mpg), group = gear) 
# A tibble: 3 x 4 
# Groups: c(gear) [?] 
     gear variable  n  mean 
     <dbl> <chr> <int> <dbl> 
1   3  mpg 15 16.10667 
2   4  mpg 12 24.53333 
3   5  mpg  5 21.38000 

... ale nie w group_by(!!quo_group, variable) kiedy podać więcej niż jeden group = c(gear, hp) ...

> mtcars$model <- rownames(mtcars) 
> table_summary(df = mtcars, id = model, select = c(mpg), group = c(gear, hp)) 
Error in mutate_impl(.data, dots) : 
    Column `c(gear, hp)` must be length 32 (the group size) or one, not 64 

Wróciłem i ponownie przeczytać programming dplyr documentation i czytałem, że można capture multiple variables użyciu quos() zamiast enquo() a następnie unquote-splice them with !!!, więc próbował ...

table_summary <- function(df  = ., 
          id  = individual_id, 
          select = c(), 
          group = c(), 
          digits = 3, 
          ...){ 
    ## Quote all arguments (see http://dplyr.tidyverse.org/articles/programming.html) 
    quo_id  <- enquo(id) 
    quo_select <- enquo(select) 
    quo_group <- quos(group) ## Use quos() rather than enquo() 
    UQS(quo_group) %>% print() ## Check to see what quo_group holds 
    ## Subset the data 
    df <- df %>% 
      dplyr::select(!!quo_id, !!quo_select, !!!quo_group)) %>% 
      unique() 
    ## gather() data, just in case there is > 1 variable selected to be summarised 
    df <- df %>% 
      gather(key = variable, value = value, !!quo_select) 
    ## Summarise selected variables by specified groups 
    results <- df %>% 
       group_by(!!!quo_group, variable) %>% 
       summarise(n = n(), 
         mean = mean(value, na.rm = TRUE)) 
    return(results) 
} 

... co teraz zawiedzie przy pierwszym nawiązaniu do !!!quo_group``within dplyr :: select() regardless of how many variables are specified under grupa = `...

> table_summary(df = mtcars, id = model, select = c(mpg), group = c(gear)) 
[[1]] 
<quosure: frame> 
~group 

attr(,"class") 
[1] "quosures" 
Error in overscope_eval_next(overscope, expr) : object 'gear' not found 
> traceback() 
17: .Call(rlang_eval, f_rhs(quo), overscope) 
16: overscope_eval_next(overscope, expr) 
15: FUN(X[[i]], ...) 
14: lapply(.x, .f, ...) 
13: map(.x[matches], .f, ...) 
12: map_if(ind_list, !is_helper, eval_tidy, data = names_list) 
11: select_vars(names(.data), !(!(!quos(...)))) 
10: select.data.frame(., !(!quo_id), !(!quo_select), !(!(!quo_group))) 
9: dplyr::select(., !(!quo_id), !(!quo_select), !(!(!quo_group))) 
8: function_list[[i]](value) 
7: freduce(value, `_function_list`) 
6: `_fseq`(`_lhs`) 
5: eval(quote(`_fseq`(`_lhs`)), env, env) 
4: eval(quote(`_fseq`(`_lhs`)), env, env) 
3: withVisible(eval(quote(`_fseq`(`_lhs`)), env, env)) 
2: df %>% dplyr::select(!(!quo_id), !(!quo_select), !(!(!quo_group))) %>% 
     unique() 
1: table_summary(df = mtcars, id = model, select = c(mpg), group = c(gear)) 

Co wydaje się dziwne i myślę, że jest źródłem problemu jest to, że !!!quo_group (tj UQS(quo_group)) wypisuje ~gear zamiast listę quosures jak dodanie print() do przepracowanych przykładów pokazuje stanie ...

> my_summarise <- function(df, ...) { 
    group_by <- quos(...) 
    UQS(group_by) %>% print() 
    df %>% 
    group_by(!!!group_by) %>% 
    summarise(a = mean(a)) 
    } 
> df <- tibble(
    g1 = c(1, 1, 2, 2, 2), 
    g2 = c(1, 2, 1, 2, 1), 
    a = sample(5), 
    b = sample(5) 
) 
> my_summarise(df, g1, g2) 
[[1]] 
<quosure: global> 
~g1 

[[2]] 
<quosure: global> 
~g2 

attr(,"class") 
[1] "quosures" 
# A tibble: 4 x 3 
# Groups: g1 [?] 
    g1 g2  a 
    <dbl> <dbl> <dbl> 
1  1  1 1.0 
2  1  2 5.0 
3  2  1 2.5 
4  2  2 4.0 

Chciałbym wyraźnie dostarczyć zmienne pragnę przez grupę jako parametr do mojej Argument, ale to działa, jeśli mogę określić je jako ... ale postanowiłem sprawdzić, czy moja funkcja działa dostarczając zmienne grupujące jak ...

table_summary <- function(df  = ., 
          id  = individual_id, 
          select = c(), 
          group = c(), 
          digits = 3, 
          ...){ 
    ## Quote all arguments (see http://dplyr.tidyverse.org/articles/programming.html) 
    quo_id  <- enquo(id) 
    quo_select <- enquo(select) 
    ## quo_group <- quos(group) 
    quo_group <- quos(...) 
    UQS(quo_group) %>% print() 
    ## Subset the data 
    df <- df %>% 
      dplyr::select(!!quo_id, !!quo_select, !!!quo_group) %>% 
      unique() 
    ## gather() data, just in case there is > 1 variable selected to be summarised 
    df <- df %>% 
      gather(key = variable, value = value, !!quo_select) 
    ## Summarise selected variables by specified groups 
    results <- df %>% 
       group_by(!!!quo_group, variable) %>% 
       summarise(n = n(), 
         mean = mean(value, na.rm = TRUE)) 
    return(results) 
} 

... ale nie, quos() ponownie cytatu-spawów do NULL więc var iables są ani wybrane ani pogrupowane według ...

> table_summary(df = mtcars, id = model, select = c(mpg), gear, hp) 
NULL 
# A tibble: 1 x 3 
    variable  n  mean 
    <chr> <int> <dbl> 
1  mpg 32 20.09062 
> table_summary(df = mtcars, id = model, select = c(mpg), gear) 
NULL 
# A tibble: 1 x 3 
    variable  n  mean 
    <chr> <int> <dbl> 
1  mpg 32 20.09062 

Poszedłem przez ten cykl kilka razy sprawdzanie każdej metody z użyciem enquo() i quos() ale nie może zobaczyć, gdzie idę źle i mimo zapoznania się z programowaniem Dokumentacja dplyr kilka razy.

Odpowiedz

4

IIUC twój wpis, chcesz dostarczyć c(col1, col2) do group_by(). To nie jest obsługiwana przez tego czasownika:

group_by(mtcars, c(cyl, am)) 
#> Error in mutate_impl(.data, dots) : 
#> Column `c(cyl, am)` must be length 32 (the number of rows) or one, not 64 

To dlatego group_by() ma mutować semantykę, nie wybrać semantykę. Oznacza to, że wyrażenia dostarczane do group_by() są wyrażeniami transformacyjnymi. Jest to zaskakująca, ale całkiem przydatna funkcja.Na przykład można grupować według disp cięcia w trzech przedziałach tak:

group_by(mtcars, cut3 = cut(disp, 3)) 

Oznacza to również, że jeśli dostarczy c(cyl, am) będzie złączyć dwie kolumny razem i zwrot wektora o długości 64, podczas gdy została ona spodziewa się długość 32 (liczba rzędów).

Twój problem polega na tym, że chcesz otoki do group_by(), która ma semantykę wyboru. Łatwo to zrobić za pomocą dplyr::select_vars(), które wkrótce zostaną wyodrębnione do nowego pakietu tidyselect:

library("dplyr") 

group_wrapper <- function(df, groups = rlang::chr()) { 
    groups <- select_vars(tbl_vars(df), !! enquo(groups)) 
    group_by(df, !!! rlang::syms(groups)) 
} 

Alternatywnie można zawinąć nową group_by_at() czasownik, który ma wybierz semantyki:

group_wrapper <- function(df, groups = rlang::chr()) { 
    group_by_at(df, vars(!! enquo(groups))) 
} 

Spróbujmy it out:

group_wrapper(mtcars, c(disp, am)) 
#> # A tibble: 32 x 11 
#> # Groups: disp, am [27] 
#>  mpg cyl disp hp drat wt qsec vs am gear carb 
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 
#> 1 21.0  6 160 110 3.90 2.62 16.5  0  1  4  4 
#> # ... with 22 more rows 

interfejs ten ma tę zaletę, wspieranie wszystkich select() operacji, aby wybrać kolumny do grupy przez.

Zauważ, że używam rlang::chr() jako domyślny argument, ponieważ c() powraca NULL który nie jest obsługiwany przez wybranie funkcji (może chcemy to zmienić w przyszłości). chr() wywołany bez argumentów zwraca wektor znaków o długości 0.