2017-09-22 3 views
2

J'ai un simple cadre de données avec 3 colonnes: nom, but et réelle. Comme il s'agit d'une simplification d'une base de données beaucoup plus grande, je veux utiliser dplyr pour calculer le nombre de fois qu'un objectif a été atteint par chaque personne.Comment passer une fonction anonyme à dplyr récapituler

df <- data.frame(name = c(rep('Fred', 3), rep('Sally', 4)), 
       goal = c(4,6,5,7,3,8,5), actual=c(4,5,5,3,3,6,4)) 

enter image description here

Le résultat devrait ressembler à ceci:

enter image description here

je devrais être en mesure de passer une fonction anonyme similaire à ce qui est indiqué ci-dessous, mais ne pas la syntaxe tout à fait à droite:

library(dplyr) 
g <- group_by(df, name) 
summ <- summarise(g, met_goal = sum((function(x,y) { 
             if(x>y){return(0)} 
             else{return(1)} 
            })(goal, actual) 
            ) 
       ) 

Quand je lance le code ci-dessus, je vois 3 de ces erreurs:

Messages d'avertissement: 1: Dans le cas (x == y) {: la condition a une longueur> 1 et seul le premier élément sera être utilisé

Répondre

3

Nous avons des vecteurs de longueur égale à goal et actual, de sorte que les opérateurs relationnels sont appropriés à utiliser ici. Cependant, lorsque nous les utilisons dans une simple instruction if(), nous pouvons obtenir des résultats inattendus car if() attend des vecteurs de longueur 1. Puisque nous avons des vecteurs de longueur égale et que nous avons besoin d'un résultat binaire, prendre la somme du vecteur logique est la meilleure approche, comme suit.

group_by(df, name) %>% 
    summarise(met_goal = sum(goal <= actual)) 
# A tibble: 2 x 2 
    name met_goal 
    <fctr> <int> 
1 Fred  2 
2 Sally  1 

L'opérateur est mis à <= parce que vous voulez 0 pour goal > actual et 1 autrement. Notez que peut utiliser une fonction anonyme. C'était la déclaration if() qui vous jetait. Par exemple, l'utilisation de

sum((function(x, y) x <= y)(goal, actual)) 

fonctionnerait comme vous le demandez.

+0

Cela répond bien à la question. J'ai compliqué intentionnellement ma tentative parce que je voulais voir comment une fonction anonyme plus complexe/générale pouvait être transmise. –

+1

@MichaelSzczepaniak - Notez que vous pouvez * utiliser * une fonction anonyme. C'était la déclaration 'if()' qui vous lançait. Par exemple, 'sum ((fonction (x, y) x <= y) (objectif, réel))' fonctionnerait. –

+1

C'était exactement ce que je cherchais. Merci d'avoir expliqué ceci (deux fois ;-). –

2

solution en utilisant data.table:

vous avez demandé la solution dplyr, mais en tant que données réelles est que vous pouvez utiliser beaucoup plus data.table. foo est la fonction que vous souhaitez appliquer.

foo <- function(x, y) { 
    res <- 0 
    if (x <= y) { 
     res <- 1 
    } 
    return(res) 
} 

library(data.table) 
setDT(df) 
setkey(df, name)[, foo(goal, actual), .(name, 1:nrow(df))][, sum(V1), name] 

Si vous préférez les tuyaux, vous pouvez utiliser ceci:

library(magrittr) 
setDT(df) %>% 
    setkey(name) %>% 
    .[, foo(goal, actual), .(name, 1:nrow(.))] %>% 
    .[, .(met_goal = sum(V1)), name] 

    name met_goal 
1: Fred  2 
2: Sally  1 
+0

Je n'ai pas beaucoup travaillé avec les tables de données, mais je suis conscient de leurs avantages, donc je suis sûr que je vais utiliser quelque chose comme ça dans le futur. –