Comment puis-je gérer les notes de vérification R CMD «pas de liaison visible pour la variable globale» lorsque ma syntaxe ggplot2 est raisonnable?

180

EDIT: Hadley Wickham souligne que je me suis mal exprimé. Le contrôle R CMD lance des NOTES, pas des avertissements. Je suis terriblement désolé pour la confusion. C'était ma faute.

La version courte

R CMD checkjette cette note chaque fois que j'utilise une syntaxe de création de tracé sensible dans ggplot2:

no visible binding for global variable [variable name]

Je comprends pourquoi R CMD check fait cela, mais cela semble criminaliser toute une veine de syntaxe autrement sensée. Je ne sais pas quelles mesures prendre pour que mon colis soit accepté R CMD checket admis au CRAN.

L'arrière-plan

Sascha Epskamp a déjà publié essentiellement sur le même problème . La différence, je pense, est que subset()la page de manuel dit qu'elle est conçue pour une utilisation interactive .

Dans mon cas, la question n'est pas terminée, subset()mais sur une caractéristique essentielle de ggplot2: l' data =argument.

Un exemple de code que j'écris qui génère ces notes

Voici une sous-fonction dans mon package qui ajoute des points à un tracé:

JitteredResponsesByContrast <- function (data) {
  return(
    geom_point(
             aes(
               x = x.values, 
               y = y.values
             ),
             data     = data,
             position = position_jitter(height = 0, width = GetDegreeOfJitter(jj))
    )
  )
}

R CMD check, en analysant ce code, dira

granovagg.contr : JitteredResponsesByContrast: no visible binding for
  global variable 'x.values'
granovagg.contr : JitteredResponsesByContrast: no visible binding for
  global variable 'y.values'

Pourquoi le contrôle R CMD est correct

Le contrôle est techniquement correct. x.valuesety.values

  • Ne sont pas définis localement dans la fonction JitteredResponsesByContrast()
  • Ne sont pas prédéfinis dans le formulaire x.values <- [something]ni globalement ni dans l'appelant.

Au lieu de cela, ce sont des variables dans une trame de données qui est définie plus tôt et transmise à la fonction JitteredResponsesByContrast().

Pourquoi ggplot2 rend difficile la vérification de R CMD

ggplot2 semble encourager l'utilisation d'un dataargument. L'argument data est probablement la raison pour laquelle ce code s'exécutera

library(ggplot2)
p <- ggplot(aes(x = hwy, y = cty), data = mpg)
p + geom_point()

mais ce code produira une erreur d'objet introuvable:

library(ggplot2)
hwy # a variable in the mpg dataset

Deux solutions de contournement et pourquoi je ne suis satisfait ni de l'un ni de l'autre

La stratégie NULLing out

Matthew Dowle recommande de définir d'abord les variables problématiques sur NULL, ce qui dans mon cas ressemblerait à ceci:

JitteredResponsesByContrast <- function (data) {
  x.values <- y.values <- NULL # Setting the variables to NULL first
  return(
    geom_point(
             aes(
               x = x.values, 
               y = y.values
             ),
             data     = data,
             position = position_jitter(height = 0, width = GetDegreeOfJitter(jj))
    )
  )
}

J'apprécie cette solution, mais je ne l'aime pas pour trois raisons.

  1. il ne sert à rien d'autre que l'apaisement R CMD check.
  2. cela ne reflète pas l'intention. Cela laisse espérer que l' aes()appel verra nos variables maintenant NULL (ce ne sera pas le cas), tout en obscurcissant le but réel (rendant la vérification R CMD consciente des variables qu'il ne saurait apparemment pas autrement être liées)
  3. Les problèmes de 1 et 2 se multiplient car chaque fois que vous écrivez une fonction qui renvoie un élément de tracé, vous devez ajouter une instruction NULL déroutante

La stratégie with ()

Vous pouvez utiliser with()pour signaler explicitement que les variables en question peuvent être trouvées dans un environnement plus large. Dans mon cas, l'utilisation with()ressemble à ceci:

JitteredResponsesByContrast <- function (data) {
  with(data, {
      geom_point(
               aes(
                 x = x.values, 
                 y = y.values
               ),
               data     = data,
               position = position_jitter(height = 0, width = GetDegreeOfJitter(jj))
      )
    }
  )
}

Cette solution fonctionne. Mais je n'aime pas cette solution car elle ne fonctionne même pas comme je m'y attendais. Si with()je résolvais vraiment le problème de pointer l'interpréteur vers où se trouvent les variables, alors je ne devrais même pas avoir besoin de l' data =argument. Mais with()cela ne fonctionne pas de cette façon:

library(ggplot2)
p <- ggplot()
p <- p + with(mpg, geom_point(aes(x = hwy, y = cty)))
p # will generate an error saying `hwy` is not found

Donc, encore une fois, je pense que cette solution a des défauts similaires à la stratégie NULLing:

  1. Je dois encore parcourir chaque fonction d'élément de tracé et envelopper la logique dans un with()appel
  2. L' with()appel est trompeur. J'ai encore besoin de fournir un data =argument; tout with()est apaisant R CMD check.

Conclusion

D'après moi, il y a trois options que je pourrais prendre:

  1. Faites pression sur CRAN pour qu'il ignore les notes en faisant valoir qu'elles sont "fausses" (conformément à la politique CRAN ), et faites-le chaque fois que je soumets un colis
  2. Corrigez mon code avec l'une des deux stratégies indésirables (NULLing ou with()blocs)
  3. Hum très fort et j'espère que le problème disparaîtra

Aucun des trois ne me rend heureux, et je me demande ce que les gens suggèrent que je (et les autres développeurs de paquets souhaitant exploiter ggplot2) devrais faire. Merci à tous d'avance. J'apprécie vraiment votre lecture même de ceci :-)

briandk
la source
20
J'aime les n ° 1 et 3.
Ben Bolker
8
@BenBolker ce sont aussi mes techniques de prédilection.
hadley
6
Il existe une 4ème option: modifier 'R CMD check' et soumettre un patch à r-devel pour examen. Je suppose que vous trouverez qu'il est assez difficile (et peut-être impossible) de détecter ceux qui sont faux et ceux qui ne le sont pas. Si quelqu'un a trouvé un morceau de code pour le faire, alors ...
Matt Dowle
6
Une autre stratégie consiste à utiliseraes_string
hadley
2
Cela semble être un problème avec transformet subsetaussi (pas sûr à 100%, mais cela a du sens).
BrodieG

Réponses:

45

Avez-vous essayé avec aes_stringau lieu de aes? Cela devrait fonctionner, même si je ne l'ai pas essayé:

aes_string(x = 'x.values', y = 'y.values')
Harlan
la source
4
juste un avertissement: aesfait while aes_stringne définit pas les paramètres de position xet y.
topchef
6
Juste un autre avertissement. aes_string ne vous permet pas d'utiliser des fonctions pour manipuler les valeurs x et y. Dites que vous souhaitez enregistrer la transformation y, auquel cas aes_string (x = 'x.values', y = 'log (y.values)') ne fonctionne bien sûr pas. J'utilise beaucoup ce genre de transformations moi-même donc aes_string n'est pas toujours une option pour moi.
Dr Mike
Peut-être que cette réponse (et celle avec le plus de votes) devrait être mise à jour puisque la documentation de aes_stringdit: "Toutes ces fonctions sont dépréciées. Veuillez plutôt utiliser des idiomes d'évaluation ordonnés (voir la section de quasiquotation dans la documentation aes ())." (ggplot2 version 3.2.1). Cela fait probablement rlang::.datale meilleur candidat pour faire taire ces notes.
Vandenman
86

Vous avez deux solutions:

  • Réécrivez votre code pour éviter une évaluation non standard. Pour ggplot2, cela signifie utiliser aes_string()au lieu de aes()(comme décrit par Harlan)

  • Ajoutez un appel à globalVariables(c("x.values", "y.values"))quelque part dans le niveau supérieur de votre package.

Vous devriez vous efforcer d'obtenir 0 NOTES dans votre paquet lors de la soumission à CRAN, même si vous devez faire quelque chose de légèrement piraté. Cela rend la vie plus facile pour CRAN et plus facile pour vous.

(Mis à jour le 31/12/2014 pour refléter mes dernières réflexions à ce sujet)

hadley
la source
26
globalVariablesest un hack hideux et je ne l'utiliserai jamais.
hadley
10
Pour ce qui vaut la peine, ma soumission de paquet a été rejetée à cause de ces notes et on m'a dit d'utiliser la fonction utils :: globalVariables. Puisque je ne suis pas en mesure d'argumenter, c'est ce que j'ai fait.
jbryer
9
Je conviens qu'il serait préférable de les ignorer, mais mon code utilise beaucoup de ggplotet data.table, et contient donc des tonnes de ces avertissements, qui m'ont empêché de remarquer d'autres avertissements plus importants qui étaient vraiment des problèmes que je devais résoudre.
Ken Williams
108
@hadley, vous ne devriez pas dire que vous n'utiliserez jamais les choses quand seulement deux ans plus tard vous pensez que ça va
hadley
10
résolution de nouvelle année? Je vais garder les yeux ouverts pour ggplot::scale_dualAxis.sqrtles graphiques à secteurs 3D avec des motifs de remplissage.
baptiste
29

Cette question a été posée et répondue il y a quelque temps mais juste pour votre information, depuis la version 2.1.0, il existe un autre moyen de contourner les notes:aes_(x=~x.values,y=~y.values).

stefan.schroedl
la source
12

Si

getRversion() >= "3.1.0"

Vous pouvez ajouter un appel au niveau supérieur du package:

utils::suppressForeignCheck(c("x.values", "y.values"))

de:

help("suppressForeignCheck")
Bastiaan Quast
la source
3
C'est une solution juste. Merci! J'avais envisagé cela, mais le problème est que j'ai un grand nombre de variables comme x.valueset y.values, donc je devrais les enregistrer TOUTES.
briandk
4
Ce n'est pas ce qui suppressForeignCheckest utilisé pour
hadley
10
Où est réellement le niveau supérieur ? Dans quel fichier dois-je ajouter cette commande?
drmariod le
9
Par coutume, cela est placé dans un zzz.Rfichier au format ./R/. Par exemple, github.com/HughParsonage/grattan/blob/master/R/zzz.R
Hugh
6
@hadley, à quoi ça sert? help ("suppressForeignCheck") semble impliquer que c'est pour un "symbole natif calculé à l'exécution", mais qu'est-ce que c'est que ça?
pdb
8

En 2019, le meilleur moyen de contourner ce problème est d'utiliser le .datapréfixe du rlangpackage. Cela indique à R de traiter x.valueset y.valuescomme des colonnes dans a data.frame(il ne se plaindra donc pas des variables non définies).

Remarque: cela fonctionne mieux si vous avez des noms de colonnes prédéfinis dont vous savez qu'ils existeront dans votre entrée de données

#' @importFrom rlang .data
my_func <- function(data) {
    ggplot(data, aes(x = .data$x, y = .data$y))
}
Paul Wildenhain
la source
3

Ajoutez cette ligne de code au fichier dans lequel vous fournissez la documentation au niveau du package:

if(getRversion() >= "2.15.1")  utils::globalVariables(c("."))

Exemple ici

stevec
la source