Jak obsłużyć notatki R CMD „brak widocznego wiązania dla zmiennej globalnej”, gdy moja składnia ggplot2 jest rozsądna?

180

EDYCJA: Hadley Wickham zwraca uwagę, że popełniłem błąd. R Kontrola CMD rzuca UWAGI, a nie Ostrzeżenia. Bardzo mi przykro z powodu zamieszania. To był mój niedopatrzenie.

Krótka wersja

R CMD checkrzuca tę notatkę za każdym razem, gdy używam rozsądnej składni tworzenia fabuły w ggplot2:

no visible binding for global variable [variable name]

Rozumiem, dlaczego R CMD to robi, ale wydaje się, że kryminalizacja całej żyły skądinąd sensownej składni. Nie jestem pewien, jakie kroki podjąć, aby moja paczka została przekazana R CMD checki przyjęta do CRAN.

Tło

Sascha Epskamp poprzednio pisał na zasadniczo ten sam problem . Myślę, że różnica polega na tym, że subset()strona ta mówi, że została zaprojektowana do użytku interaktywnego .

W moim przypadku kwestia ta nie jest zakończona, subset()ale dotyczy podstawowej cechy ggplot2: data =argumentu.

Przykład kodu, który piszę, który generuje te notatki

Oto podfunkcja w moim pakiecie, która dodaje punkty do wykresu:

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, podczas analizowania tego kodu, powie

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

Dlaczego sprawdzenie R CMD jest prawidłowe

Sprawdzenie jest technicznie prawidłowe. x.valuesiy.values

  • Nie są zdefiniowane lokalnie w funkcji JitteredResponsesByContrast()
  • Nie są wstępnie zdefiniowane x.values <- [something]ani w formie globalnej, ani w dzwoniącym.

Zamiast tego są zmiennymi w ramce danych, która została wcześniej zdefiniowana i przekazana do funkcji JitteredResponsesByContrast().

Dlaczego ggplot2 utrudnia złagodzenie kontroli R CMD

ggplot2 wydaje się zachęcać do użycia dataargumentu. Argument data prawdopodobnie przypuszczalnie powoduje wykonanie tego kodu

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

ale ten kod wygeneruje błąd „nie znaleziono obiektu”:

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

Dwa obejścia i dlaczego nie jestem zadowolony z żadnego

Strategia NULLing out

Matthew Dowle zaleca najpierw ustawienie problematycznych zmiennych na NULL, co w moim przypadku wyglądałoby tak:

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))
    )
  )
}

Doceniam to rozwiązanie, ale nie podoba mi się to z trzech powodów.

  1. nie służy żadnemu dodatkowemu celowi poza uspokajaniem R CMD check.
  2. nie odzwierciedla intencji. Rodzi to oczekiwanie, że aes()wywołanie zobaczy nasze zmienne NULL (nie zrobi tego), jednocześnie zaciemniając prawdziwy cel (uświadomienie R CMD zmiennych, o których inaczej nie wiedziałby, że byłyby związane)
  3. Problemy 1 i 2 mnożą się, ponieważ za każdym razem, gdy piszesz funkcję zwracającą element wydruku, musisz dodawać mylącą instrukcję NULLing

Strategia with ()

Możesz użyć with()do jawnego zasygnalizowania, że ​​zmienne, o których mowa, można znaleźć w większym środowisku. W moim przypadku użycie with()wygląda następująco:

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

To rozwiązanie działa. Ale nie podoba mi się to rozwiązanie, ponieważ nie działa tak, jakbym tego oczekiwał. Jeśli with()naprawdę rozwiązywania problemu wskazując tłumacza do miejsca, gdzie są zmienne, to nie powinien nawet trzeba ten data =argument. Ale with()to nie działa w ten sposób:

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

I znów myślę, że to rozwiązanie ma podobne wady do strategii NULLing:

  1. Nadal muszę przejść przez każdą funkcję elementu wykresu i zawrzeć logikę w with()wywołaniu
  2. with()Rozmowa jest mylące. Nadal muszę przedstawić data =argument; wszystko co with()robi jest uspokajające R CMD check.

Wniosek

Z mojego punktu widzenia mogę wziąć trzy opcje:

  1. Zachęcaj CRAN do zignorowania notatek, argumentując, że są „fałszywe” (zgodnie z zasadami CRAN ) i rób to za każdym razem, gdy przesyłam paczkę
  2. Napraw mój kod za pomocą jednej z dwóch niepożądanych strategii (NULLing lub with()bloki)
  3. Bucz się naprawdę głośno i mam nadzieję, że problem zniknie

Żadna z tych trzech rzeczy mnie nie uszczęśliwia i zastanawiam się, co ludzie sugerują, że ja (i inni programiści pakietów chcący skorzystać z ggplot2) powinienem zrobić. Dzięki wszystkim z góry. Naprawdę doceniam twoje nawet przeczytanie tego :-)

Briandk
źródło
20
Lubię # 1 i # 3.
Ben Bolker
8
@BenBolker to również moje techniki.
hadley,
6
Istnieje czwarta opcja: zmodyfikuj „R CMD check” i prześlij łatkę do r-devel do rozważenia. Podejrzewam, że stwierdzenie, że fałszywe, a które nie, jest dość trudne (i być może niemożliwe). Jeśli ktoś wymyśliłby do tego kawałek kodu, to ...
Matt Dowle
6
Inną strategią jest użycieaes_string
hadley
2
To wydaje się być problemem transform i subsetzbyt (nie 100% pewien, ale to ma sens).
BrodieG

Odpowiedzi:

45

Próbowałeś aes_stringzamiast aes? To powinno działać, chociaż nie próbowałem:

aes_string(x = 'x.values', y = 'y.values')
Harlan
źródło
4
tylko ostrzeżenie: aesrobi while aes_stringnie definiuje parametrów pozycyjnych xi y.
topchef
6
Kolejne ostrzeżenie. aes_string nie pozwala na używanie funkcji do manipulowania wartościami xiy. Powiedz, że chcesz zapisać transformację y, w którym to przypadku aes_string (x = 'x.values', y = 'log (y.values)') oczywiście nie działa. Często używam tego rodzaju transformacji, więc aes_string nie zawsze jest dla mnie opcją.
Dr Mike
Być może ta odpowiedź (i ta z największą liczbą głosów) powinna zostać zaktualizowana, ponieważ dokumentacja aes_stringmówi: „Wszystkie te funkcje są przestarzałe. Zamiast tego użyj schludnych idiomów oceny (zobacz sekcję quasiquotation w dokumentacji aes ()).” (ggplot2 wersja 3.2.1). To prawdopodobnie rlang::.datanajlepszy kandydat do wyciszenia tych notatek.
Vandenman
86

Masz dwa rozwiązania:

  • Przepisz kod, aby uniknąć niestandardowej oceny. W przypadku ggplot2 oznacza to użycie aes_string()zamiast aes()(zgodnie z opisem Harlana)

  • Dodaj połączenie do globalVariables(c("x.values", "y.values"))dowolnego miejsca na najwyższym poziomie pakietu.

Podczas przesyłania do CRAN powinieneś dążyć do 0 UWAG w swoim pakiecie, nawet jeśli musisz zrobić coś nieco zhackowanego. Ułatwia to życie CRAN i jest łatwiejsze dla Ciebie.

(Zaktualizowano 31.12.2014, aby odzwierciedlić moje najnowsze przemyślenia na ten temat)

Hadley
źródło
26
globalVariablesto ohydny hack i nigdy go nie użyję.
hadley,
10
Co jest warte, moje przesyłanie pakietów zostało odrzucone z powodu tych notatek i powiedziano mi, aby korzystało z funkcji utils :: globalVariables. Ponieważ nie jestem w stanie się kłócić, właśnie to zrobiłem.
jbryer
9
Zgadzam się, że najlepiej byłoby je zignorować, ale mój kod używa dużo ggploti data.table, a zatem ma mnóstwo tych ostrzeżeń, które trzymało mnie z zauważając inne ważniejsze ostrzeżenia, które naprawdę były problemy Musiałem naprawić.
Ken Williams
108
@ Hadley nie powinieneś mówić, że nigdy nie będziesz używać rzeczy, gdy tylko dwa lata później uważasz, że jest w porządku
hadley
10
postanowienie noworoczne? Będę mieć oczy otwarte ggplot::scale_dualAxis.sqrti wykresy kołowe 3D z wzorami wypełnień.
baptiste
29

To pytanie zostało zadane i udzielono odpowiedzi jakiś czas temu, ale tylko dla informacji, ponieważ od wersji 2.1.0 istnieje inny sposób na obejście notatek:aes_(x=~x.values,y=~y.values).

stefan.schroedl
źródło
12

Jeśli

getRversion() >= "3.1.0"

Możesz dodać połączenie na najwyższym poziomie pakietu:

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

z:

help("suppressForeignCheck")
Bastiaan Quast
źródło
3
To uczciwe rozwiązanie. Dzięki! Rozważyłem to, ale problem polega na tym, że mam wiele zmiennych, takich jak x.valuesi y.values, więc musiałbym zarejestrować WSZYSTKIE z nich.
briandk,
4
To nie suppressForeignCheckjest używane
Hadley
10
Gdzie właściwie jest najwyższy poziom ? W jakim pliku mam dodać to polecenie?
drmariod
9
Domyślnie jest to umieszczane w zzz.Rpliku ./R/. Na przykład github.com/HughParsonage/grattan/blob/master/R/zzz.R
Hugh
6
@ Hadley, do czego służy? wydaje się, że help („suppressForeignCheck”) oznacza „natywny symbol obliczany w czasie wykonywania”, ale co to do cholery jest?
pdb
8

W 2019 roku najlepszym sposobem na obejście tego jest użycie .dataprefiksu z rlangpakietu. To każe R traktować x.valuesi y.valuesjako kolumny w data.frame(więc nie będzie narzekać na niezdefiniowane zmienne).

Uwaga: Działa to najlepiej, jeśli masz predefiniowane nazwy kolumn, o których wiesz, że będą istnieć w danych wejściowych

#' @importFrom rlang .data
my_func <- function(data) {
    ggplot(data, aes(x = .data$x, y = .data$y))
}
Paul Wildenhain
źródło
3

Dodaj ten wiersz kodu do pliku, w którym dostarczasz dokumentację na poziomie pakietu:

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

Przykład tutaj

stevec
źródło