2017-07-31 1 views
1

Je voudrais faire des tableaux pour la publication qui donnent le nombre d'observations, groupées par deux variables. Le code pour cela fonctionne bien. Cependant, j'ai rencontré des problèmes en essayant de transformer cela en fonction.Écrire sa propre fonction en utilisant dplyr et group_by - comment continuer avec les noms de colonne changés

J'utilise dplyr_0.7.2

Exemple d'utilisation: mtcars

code pour table à l'extérieur de la fonction: cela fonctionne

library(tidyverse) 

tab1 <- mtcars %>% count(cyl) %>% rename(Total = n) 

tab2 <- mtcars %>% 
    group_by(cyl, gear) %>% count %>% 
    spread(gear, n) 

tab <- full_join(tab1, tab2, by = "cyl") 
tab 


# This is the output (which is what I want) 

A tibble: 3 x 5 
cyl Total `3` `4` `5` 
<dbl> <int> <int> <int> <int> 
1  4 11  1  8  2 
2  6  7  2  4  1 
3  8 14 12 NA  2 

Essayer de mettre cela en fonction

Fonction pour tab1: cela fonctionne

count_by_two_groups_A <- function(df, var1){ 
    var1 <- enquo(var1) 
    tab1 <- df %>% count(!!var1) %>% rename(Total = n) 
    tab1 
} 

count_by_two_groups_A(mtcars, cyl) 

A tibble: 3 x 2 
cyl Total 
<dbl> <int> 
1  4 11 
2  6  7 
3  8 14 

Fonction pour la première partie de tab2: il fonctionne jusqu'à ce point, mais ...

count_by_two_groups_B <- function(df, var1, var2){ 

    var1 <- enquo(var1) 
    var2 <- enquo(var2) 

    tab2 <- df %>% group_by((!!var1), (!!var2)) %>% count 
    tab2 
} 

count_by_two_groups_B(mtcars, cyl, gear) 

A tibble: 8 x 3 
Groups: (cyl), (gear) [8] 
`(cyl)` `(gear)`  n 
<dbl> <dbl> <int> 
1  4  3  1 
2  4  4  8 
3  4  5  2 
4  6  3  2 
5  6  4  4 
6  6  5  1 
7  8  3 12 
8  8  5  2 

Les noms de colonnes ont changé (CYL) et (vitesse). Je n'arrive pas à comprendre comment continuer avec spread() et full_join() (ou n'importe quoi d'autre en utilisant les nouveaux noms de colonnes) maintenant que les noms des colonnes ont changé. C'est à dire. Je n'arrive pas à comprendre comment spécifier les nouveaux noms de colonnes de manière typhique, pour pouvoir continuer. J'ai essayé différentes choses, sans succès.

Répondre

2

La manière habituelle de la définition des noms dans un tidyev Le contexte est d'utiliser l'opérateur de définition :=. Il ressemblerait à ceci:

df %>% 
    group_by(
    !! nm1 := !! var1, 
    !! nm2 := !! var2 
) %>% 
    count() 

Pour cela, vous devez extraire nm1 de var1. Malheureusement, je n'ai pas encore trouvé un moyen facile de supprimer les parenthèses fermantes. Je pense qu'il serait logique de le faire dans la prochaine fonction ensym() (il capture des symboles au lieu de quosures et d'émettre une erreur si vous fournissez un appel). J'ai soumis un ticket ici: https://github.com/tidyverse/rlang/issues/223

Heureusement, nous avons deux solutions faciles ici.Notez d'abord que vous n'avez pas besoin des parenthèses englobantes. Ils ne sont nécessaires que lorsque d'autres opérateurs sont impliqués dans l'expression capturée. Par exemple. dans ces situations:

(!! var)/avg 
(!! var) < value 

Dans ce cas, si vous omettez entre parenthèses, !! essaieraient de CITATION l'ensemble des expressions au lieu de simplement l'un symbole. D'autre part dans votre fonction il n'y a pas d'opérateur afin que vous puissiez en toute sécurité sans enfermer unquote:

count_by_two_groups_B <- function(df, var1, var2) { 
    var1 <- enquo(var1) 
    var2 <- enquo(var2) 

    df %>% 
    group_by(!! var1, !! var2) %>% 
    count() 
} 

Enfin, vous pouvez rendre votre fonction plus générale en permettant à un nombre variable d'arguments. Ceci est encore plus facile à implémenter parce que les points sont transférés donc il n'y a pas besoin de capturer et de décocher. Il suffit de les passer à group_by():

count_by <- function(df, ...) { 
    df %>% 
    group_by(...) %>% 
    count() 
} 
2

Je peux le faire fonctionner avec NSE (évaluation non standard). Je n'ai pas pu le faire avec tidyverse car je n'avais pas installé ça et je n'ai pas pris la peine d'installer.

Voici un code de travail:

library(dplyr) 
library(tidyr) 

count_by_two_groups_B <- function(df, var1, var2){ 

# var1 <- enquo(var1) 
# var2 <- enquo(var2) 

    tab2 <- df %>% group_by_(var1, var2) %>% summarise(n = n()) %>%spread(gear, n) 

    tab2 
} 

count_by_two_groups_B(mtcars, 'cyl', 'gear') 

Résultat:

# A tibble: 3 x 4 
# Groups: cyl [3] 
    cyl `3` `4` `5` 
* <dbl> <int> <int> <int> 
1  4  1  8  2 
2  6  2  4  1 
3  8 12 NA  2 
0

C'est l'une de ces situations où pour atteindre dplyr ou tidyverse semble excessive. Il y a des fonctions de base pour le faire ... table et de rendre les résultats sous forme de long, as.dataframe:

as.data.frame(with(mtcars, table(cyl,gear)) , responseName="Total") 
#-------- 
    cyl gear Total 
1 4 3  1 
2 6 3  2 
3 8 3 12 
4 4 4  8 
5 6 4  4 
6 8 4  0 
7 4 5  2 
8 6 5  1 
9 8 5  2 

Ce serait une approche dplyr:

mtcars %>% group_by(cyl,gear) %>% summarise(Total=n()) 
#---- 
# A tibble: 8 x 3 
# Groups: cyl [?] 
    cyl gear Total 
    <dbl> <dbl> <int> 
1  4  3  1 
2  4  4  8 
3  4  5  2 
4  6  3  2 
5  6  4  4 
6  6  5  1 
7  8  3 12 
8  8  5  2 

Et si la question était de savoir comment obtenir cela comme un objet de table (pensée qui aurait pu être votre objectif avec spread puis juste:

with(mtcars, table(cyl,gear)) 
+0

Je pense qu'il est utile de conserver un style uniforme dans un script. Aussi, si vous essayez de faire fonctionner Tidyeval, il est préférable de commencer avec de petites fonctions. Pour ces raisons, il est logique de répondre à la question initiale. – lionel

+0

Une partie de mon problème était le manque de clarté de la question initiale. –