2012-02-17 17 views
31

Jedną z cech R, która jest związana z wrodzoną naturą wektoryzacji, jest reguła recyklingu opisana w An Introduction to R w sekcji 2.2.Wdrożenie standardowych zasad recyklingu

Wektory występujące w tym samym wyrażeniu nie muszą mieć tej samej długości. Jeśli nie, wartość wyrażenia jest wektorem o tej samej długości, co najdłuższy wektor występujący w wyrażeniu. Krótsze wektory w wyrażeniu są poddawane recyklingowi tak często, jak to jest konieczne (być może ułamkowo), aż dopasują długość najdłuższego wektora. W szczególności stała jest po prostu powtarzana.

Większość standardowych funkcji korzysta z tego, ale kod, który to robi, jest ukryty w ukrytym kodzie C.

Czy istnieje kanoniczny sposób wdrożenia standardowych zasad recyklingu dla funkcji całkowicie w kodzie R? Oznacza to, że biorąc pod uwagę funkcję jak

mock <- function(a, b, c) { 
    # turn a, b, and c into appropriate recycled versions 

    # do something with recycled a, b, and c in some appropriately vectorized way 
} 

gdzie a, b, a c są wektorami, ewentualnie o różnych długościach i nieznanych typów/klas, czy istnieje kanoniczny sposób, aby uzyskać nowy zestaw wektorów, które są poddawane recyklingowi zgodnie do standardowych zasad recyklingu? W szczególności nie mogę zakładać, że krok "zrób coś" sam dokona właściwego recyklingu, więc muszę to zrobić sam wcześniej.

Odpowiedz

20

Używałem tego w przeszłości,

expand_args <- function(...){ 
    dots <- list(...) 
    max_length <- max(sapply(dots, length)) 
    lapply(dots, rep, length.out = max_length) 
} 
+0

+1 Nasze idee są zasadniczo identyczne, ale wykonujesz lepszą robotę sprowadzając je do sedna. (Patrząc na twoją odpowiedź, teraz zdaję sobie sprawę, że Trochę mnie to poruszyło przy konstruowaniu tego przykładu danych.frame!) –

+0

kiedy napisałem twoją oryginalną odpowiedź była całkiem inna, są teraz praktycznie identyczne oprócz nazw. – baptiste

+0

Tak, widzę, że zasadniczo podobne wersje trafiają w Kilka minut w stosunku do siebie, ale zwartość Twojej funkcji sprawia, że ​​jest ona bardziej przenośna dwójka. –

7

Najprawdopodobniej użyję argumentu length.out z rep(), aby wykonać większość prawdziwej pracy.

Oto przykład, który tworzy funkcję better.data.frame() (powinna ona nazywać się "better".data.frame()), która nie nakłada żadnych ograniczeń na długości wektorów przekazywanych jako argumenty. W tym przypadku oddaję wszystkie wektory do długości najdłuższego, ale oczywiście możesz to dostosować, aby zaspokoić własne potrzeby recyklingu!

better.data.frame <- function(...) { 
    cols <- list(...) 
    names(cols) <- sapply(as.list(match.call()), deparse)[-1] 

    # Find the length of the longest vector 
    # and then recycle all columns to that length. 
    n <- max(sapply(cols, length)) 
    cols <- lapply(cols, rep, length.out = n) 

    as.data.frame(cols) 
} 

# Try it out 
a <- Sys.Date() + 0:9 
b <- 1:3 
c <- letters[1:4] 

data.frame(a,b,c) 
# Error in data.frame(a, b, c) : 
# arguments imply differing number of rows: 10, 3, 4 

better.data.frame(a,b,c) 
#    a b c 
# 1 2012-02-17 1 a 
# 2 2012-02-18 2 b 
# 3 2012-02-19 3 c 
# 4 2012-02-20 1 d 
# 5 2012-02-21 2 a 
# 6 2012-02-22 3 b 
# 7 2012-02-23 1 c 
# 8 2012-02-24 2 d 
# 9 2012-02-25 3 a 
# 10 2012-02-26 1 b 
+0

Możesz dodać 'n <-' do obsługi przypadku, w którym najdłuższy argument nie jest pierwszy. 'n <- max (unlist (lapply (lista (a, b, c), długość), max)' i dodaj 'a <- rep (a, length.out = n)'. – Justin

+0

@Justin - Podziękowania Zrobiłem to, a także uogólniłem funkcję, by wziąć dowolną liczbę argumentów.Jeśli ty (lub ktokolwiek inny) widzisz lepszy sposób dla mnie, aby wyłuskać nazwy dostarczonych argumentów z wewnątrz funkcji, proszę daj mi znać, właśnie to wymyśliłem w locie ... –

0

Jeden krótki i-brudna droga do argumentów liczbowych ma polegać na automatycznym recyklingu cbind użytkownika. Na przykład:

f.abc <- function(a,b,c) { 

    df.abc <- as.data.frame(suppressWarnings(cbind(a=a, b=b, c=c))) 

    #Then use, for example, with() to use a, b and c inside the data frame, 
    #or apply(df.abc,1, ...) 
} 

W dużym stopniu polega na braku innych uzasadnionych przyczyn ostrzeżeń.