S'inspirant de la discussion sur évaluation dplyr conditionnelle , j'aimerais exécuter de manière conditionnelle une étape du pipeline selon que la colonne de référence existe ou non dans la trame de données transmise.
Les résultats générés par1)
et2)
doivent être identiques.
# 1)
mtcars %>%
filter(am == 1) %>%
filter(cyl == 4)
# 2)
mtcars %>%
filter(am == 1) %>%
{
if("cyl" %in% names(.)) filter(cyl == 4) else .
}
# 1)
mtcars %>%
filter(am == 1)
# 2)
mtcars %>%
filter(am == 1) %>%
{
if("absent_column" %in% names(.)) filter(absent_column == 4) else .
}
Pour la colonne disponible, l'objet transmis ne correspond pas au bloc de données initial. Le code d'origine renvoie le message d'erreur:
Erreur dans
filter(cyl == 4)
: objet'cyl'
non trouvé
J'ai essayé la syntaxe alternative (sans chance):
>> mtcars %>%
... filter(am == 1) %>%
... {
... if("cyl" %in% names(.)) filter(.$cyl == 4) else .
... }
Show Traceback
Rerun with Debug
Error in UseMethod("filter_") :
no applicable method for 'filter_' applied to an object of class "logical"
Je voulais développer cette question qui tiendrait compte de l’évaluation à droite du==
infilter
call. Par exemple, la syntaxe ci-dessous tente de filtrer sur la première valeur disponible. mtcars%>%
filter({
if ("does_not_ex" %in% names(.))
does_not_ex
else
NULL
} == {
if ("does_not_ex" %in% names(.))
unique(.[['does_not_ex']])
else
NULL
})
De manière attendue, l'appel se traduit par un message d'erreur:
Erreur dans
filter_impl(.data, quo)
: le résultat doit avoir une longueur de 32, pas 0
Lorsqu'il est appliqué à une colonne existante:
mtcars %>%
filter({
if ("mpg" %in% names(.))
mpg
else
NULL
} == {
if ("mpg" %in% names(.))
unique(.[['mpg']])
else
NULL
})
Cela fonctionne avec un message d'avertissement:
mpg cyl disp hp drat wt qsec vs am gear carb
1 21 6 160 110 3.9 2.62 16.46 0 1 4 4
Message d'avertissement: Dans
{
: la longueur d'un objet plus long n'est pas un multiple de La longueur d'un objet plus court
Existe-t-il un moyen judicieux de développer la syntaxe existante afin d'obtenir une évaluation conditionnelle du côté droit de l'appel filter
, en restant idéalement dans le flux de travaux dplyr?
En raison de la manière dont les portées fonctionnent ici, vous ne pouvez pas accéder à la structure de données à partir de votre instruction if
. Heureusement, vous n'en avez pas besoin.
Essayer:
mtcars %>%
filter(am == 1) %>%
filter({if("cyl" %in% names(.)) cyl else NULL} == 4)
Ici, vous pouvez utiliser l'objet '.
' dans la condition afin de vérifier si la colonne existe et, si elle existe, vous pouvez renvoyer la colonne à la fonction filter
.
EDIT: selon le commentaire de docendo discimus sur la question, vous pouvez accéder au dataframe mais pas implicitement - c’est-à-dire que vous devez le référencer spécifiquement avec .
Je sais que je suis en retard à la fête, mais voici une réponse un peu plus conforme à ce que vous pensiez à l’origine:
mtcars %>%
filter(am == 1) %>%
{
if("cyl" %in% names(.)) filter(., cyl == 4) else .
}
Fondamentalement, il manquait le .
dans filter
. Notez que ceci est dû au fait que le pipeline n'ajoute pas .
à filter(expr)
puisqu'il se trouve dans une expression entourée de {}
.
Edit: Malheureusement, c'était trop beau pour être vrai
Je pourrais être un peu en retard à la fête. Mais est
mtcars %>%
filter(am == 1) %>%
try(filter(absent_column== 4))
une solution?