Dokumentacja mówi
vapply
jest podobny dosapply
, ale ma wstępnie określony typ wartości zwracanej, więc może być [...] bezpieczniejszy w użyciu.
Czy mógłbyś wyjaśnić, dlaczego jest to ogólnie bezpieczniejsze, może podając przykłady?
PS: Znam odpowiedź i staram się już tego unikać sapply
. Chciałbym tylko, żeby była tu miła odpowiedź, więc mogę wskazać ją moim współpracownikom. Prosimy o brak odpowiedzi „przeczytaj instrukcję”.
Odpowiedzi:
Jak już wspomniano,
vapply
robi dwie rzeczy:Druga kwestia to większa zaleta, ponieważ pomaga wychwycić błędy, zanim się pojawią, i prowadzi do bardziej niezawodnego kodu. To sprawdzanie wartości zwracanej można przeprowadzić osobno, używając,
sapply
a następnie,stopifnot
aby upewnić się, że wartości zwracane są zgodne z oczekiwanymi, alevapply
jest trochę łatwiejsze (jeśli jest bardziej ograniczone, ponieważ niestandardowy kod sprawdzania błędów może sprawdzać wartości w granicach itp.) ).Oto przykład
vapply
upewnienia się, że wynik jest zgodny z oczekiwaniami. Jest to analogia do czegoś, nad czym właśnie pracowałem podczas skrobania plików PDF, gdziefindD
użyłbym plikuwyrażenie regularneaby dopasować wzorzec w nieprzetworzonych danych tekstowych (np. miałbym listęsplit
według encji i wyrażenie regularne do dopasowania adresów w każdej encji. Czasami plik PDF był konwertowany poza kolejnością i były dwa adresy dla podmiot, który spowodował zło).> input1 <- list( letters[1:5], letters[3:12], letters[c(5,2,4,7,1)] ) > input2 <- list( letters[1:5], letters[3:12], letters[c(2,5,4,7,15,4)] ) > findD <- function(x) x[x=="d"] > sapply(input1, findD ) [1] "d" "d" "d" > sapply(input2, findD ) [[1]] [1] "d" [[2]] [1] "d" [[3]] [1] "d" "d" > vapply(input1, findD, "" ) [1] "d" "d" "d" > vapply(input2, findD, "" ) Error in vapply(input2, findD, "") : values must be length 1, but FUN(X[[3]]) result is length 2
Jak mówię moim studentom, częścią stania się programistą jest zmiana sposobu myślenia z „błędy są irytujące” na „błędy są moim przyjacielem”.
Wejścia o zerowej długości
Jedną z powiązanych kwestii jest to, że jeśli długość danych wejściowych wynosi zero,
sapply
zawsze zwróci pustą listę, niezależnie od typu wejścia. Porównać:sapply(1:5, identity) ## [1] 1 2 3 4 5 sapply(integer(), identity) ## list() vapply(1:5, identity) ## [1] 1 2 3 4 5 vapply(integer(), identity) ## integer(0)
Dzięki temu
vapply
masz gwarancję określonego typu danych wyjściowych, więc nie musisz pisać dodatkowych sprawdzeń dla danych wejściowych o zerowej długości.Benchmarki
vapply
może być nieco szybszy, ponieważ już wie, w jakim formacie powinien oczekiwać wyników.input1.long <- rep(input1,10000) library(microbenchmark) m <- microbenchmark( sapply(input1.long, findD ), vapply(input1.long, findD, "" ) ) library(ggplot2) library(taRifx) # autoplot.microbenchmark is moving to the microbenchmark package in the next release so this should be unnecessary soon autoplot(m)
źródło
Dodatkowe naciśnięcia klawiszy
vapply
mogą zaoszczędzić czas późniejszego debugowania mylących wyników. Jeśli funkcja, którą wywołujesz, może zwracać różne typy danych, zvapply
pewnością należy jej użyć.Jednym z przykładów, które przychodzi na myśl byłoby
sqlQuery
wRODBC
opakowaniu. Jeśli podczas wykonywania zapytania wystąpi błąd, ta funkcja zwracacharacter
wektor z komunikatem. Załóżmy na przykład, że próbujesz wykonać iterację po wektorze nazw tabeltnames
i wybierz maksymalną wartość z kolumny numerycznej „NumCol” w każdej tabeli za pomocą:sapply(tnames, function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]])
Jeśli wszystkie nazwy tabel są prawidłowe, dałoby to
numeric
wektor. Ale jeśli zdarzy się, że jedna z nazw tabel zmieni się w bazie danych i zapytanie nie powiedzie się, wyniki zostaną przekształcone w trybcharacter
. Jednak użycievapply
withFUN.VALUE=numeric(1)
zatrzyma błąd w tym miejscu i zapobiegnie jego pojawieniu się gdzieś w dół - lub, co gorsza, wcale.źródło
Jeśli zawsze chcesz, aby wynik był czymś szczególnym ... np. Wektorem logicznym.
vapply
upewnia się, że tak się dzieje, alesapply
niekoniecznie to robi.a<-vapply(NULL, is.factor, FUN.VALUE=logical(1)) b<-sapply(NULL, is.factor) is.logical(a) is.logical(b)
źródło
logical(1)
w tym przypadku, ponieważ FALSE wygląda na to, że ustawia opcję na „OFF” zamiast określać typ