2012-07-04 8 views
9

Czy można łączyć funkcje w R?Metoda łańcuchowa z R

dane próbki:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2) 

Na przykład, chciałbym wymienić następujące instrukcje poniżej:

step1 <- mean(m) 
step2 <- sum(step1) 
res <- step2 

Albo

res <- sum(mean(m)) 

Z czymś takim:

res <- [email protected]()@sum() 

W niektórych przypadkach może to znacznie wyjaśnić mój kod.

EDIT1 To jest fałszywy przykład. Wybrałem losowo "sumę" i "średnią".

Ben dał pierwszy kawałek odpowiedź używając% @% jednak to uniemożliwia korzystanie z dodatkowych argumentów wewnątrz funkcji:

m %@% function1(arg1, arg2) %@% function2(arg1, arg2) 

Jak mogę obejść to?

EDIT2 Dodawanie przykład

require(xts) 
require(PerformanceAnalytics) 
xts.ts <- xts(rnorm(231),as.Date(13514:13744,origin="1970-01-01")) 
plot(na.omit(lag(rollapply(xts.ts, width=rolling.per-1, FUN= function(x){sqrt(var(x))*sqrt(252)}), k=1)), main = "Dummy Example") 

Przykład ten wydaje się działać prawidłowo roztworem Charles:

`%@%` <- function(x, f) eval.parent(as.call(append(as.list(substitute(f)), list(x), 1))) 
xts.ts %@% rollapply(width = rolling.per-1, FUN= function(x) x%@%var%@%sqrt * sqrt(252)) %@% lag(k=1) %@% na.omit %@% plot(main = "Dummy Example") 

mniej ważne do mojego przypadku, ale wart wymieniając następujące statment nie roztworem Karola :

xts.ts %@% names <- 'ts name' 
+2

Co jest nie tak z 'res <- sum (mean (m))? –

+0

Nic, ale nie ma większego sensu wzięcie "sumy" wektora długości 1 (co jest zwracane przez "średnie" na macierzy). – Henrik

+0

Chociaż zniknie "wkrótce", nadal istnieje funkcja 'mean.data.frame', która zwraca wektor. –

Odpowiedz

4

W podobnym duchu, aby odpowiedzieć Bena, ale pozwalające argumenty:

`%@%` <- function(x, f) eval.parent(as.call(append(as.list(substitute(f)), list(x), 1))) 

x %@% mean %@% sqr # => 6.25 
c(1, 2, NA, 3, 4) %@% mean(na.rm=T) %@% sqr # => 6.25 
m %@% colMeans() %@% sum() # => 21 
+0

Aby ostatni działał, musisz użyć 'names <-' lub' setNames', np. 'Xts.ts% @% setNames ('ts name')', ponieważ '<-' ma specjalną obsługę dla wywołania funkcji lhs, które tutaj nie zadziała. – Charles

+0

Nie widziałem tego wcześniej. Jest super-sprytny. Byłbym bardzo boi się użyć go w kodzie produkcyjnym z powodu szans, że byłby on delikatny ... –

+0

Nie chciałbym używać tego w kodzie - produkcji lub w inny sposób! Niezależnie od tego, czy jest to dobry pomysł, czy nie, to, co podoba mi się w R, jest takie, że tego rodzaju rzeczy są możliwe. Mogę zobaczyć, jak byłoby to bardziej znane dla kogoś, kto jest przyzwyczajony do wspólnego obiektu OO 'object.verb()' zamiast '' czasownik (obiekt) R '. – Charles

9

Sortowanie, ale I th tusz jest nie-idiomatyczny i może kruchy/niezbyt dobry pomysł. (To jest dorozumiany, jak sądzę, przez użytkownika @ RichieCotton komentarzu powyżej.)

Od http://cran.r-project.org/doc/manuals/R-lang.html:

10.3.4 operatorów Specjalne

R pozwala operatorom Infix zdefiniowane przez użytkownika. Mają one postać ciągu znaków ograniczonego przez znak "%". Ciąg znaków może zawierać dowolny znak drukowalny oprócz "%". Sekwencje specjalne dla ciągów znaków nie mają tutaj zastosowania.

Należy pamiętać, że następujące podmioty są predefiniowane

%% %*% %/% %in% %o% %x% 
"%@%" <- function(x,f) { 
    f(x) 
} 

sqr <- function(x) x^2 
x <- 1:4 

x %@% mean ## 2.5 
x %@% mean %@% sqr ## 6.25 
x %@% (mean %@% sqr) ## fails 

Biorąc m jak zdefiniowano powyżej - może być to, co miał na myśli?

m %@% colMeans %@% sum ## 21 

Uwagi:

  • Twój przykład jest trochę zabawne, bo mean(x) zawsze zwraca skalar (czyli długość-1 wektor), więc sum(mean(x)) zawsze będzie taka sama jak mean(x)
  • Operatory infiksów muszą być otoczone przez %, więc nie można mieć niczego tak zwięzłego jak pojedynczy symbol (i już zostało pobrane %%).
  • ten rodzaj łączenia nie jest skojarzony, co mnie martwi - wydaje się, że powyższe przykłady działają, więc R (najwyraźniej) ocenia to od lewej do prawej, ale nie wiem, że to gwarantuje ...

edycja: teraz pytanie, w jaki sposób można włączyć dodatkowe argumenty. Nie sądzę, aby sugerowana składnia (x %@% fun1(arg1) %@% fun2(arg2)) zadziała bez poważnej magii. Jest to najbliższy moment, w którym mogę uzyskać - tworzenie funkcji otoki, która tworzy zmodyfikowaną wersję oryginalnej funkcji.

F <- function(f,...) { 
    function(x) { 
     f(x,...) 
    } 
} 

Testowanie:

pow <- function(x,b=2) { x^b } 
sqr <- function(x) x^2 
x <- 1:4 

x %@% F(mean,na.rm=TRUE) ## 2.5 
x %@% F(mean,na.rm=TRUE) %@% F(pow,3) ## 16.25 

(Zauważ, że użyłem F jako funkcję tutaj, co może być ryzykowne w niektórych sytuacjach, ponieważ zastępuje skrót F==FALSE)

+0

Wydaje się być w pracy, ale może po prostu załatwić sprawę dobrze! Jak zasugerowałbyś zmodyfikowanie operatora% @%, aby móc dodawać argumenty? Na przykład: m% @% funkcja1 (arg1, arg2)% funkcja2 (arg1, arg2) – Sam

+1

Co to jest '"% @% "<- funkcja (f, ...) f (...)'? Dzięki temu wszystkie argumenty powinny zostać przekazane do 'f', nawet nazwane argumenty. (uwaga: nietestowane) – Henrik

+0

@Sam, czy możesz podać mi konkretny przykład tego, co chcesz zrobić i jakie wyniki powinien on przynieść? Twoja składnia nie będzie działać tak, jak napisano, ponieważ 'funkcja1 (arg1, arg2)' nie jest w ogóle funkcją. Myślę, że pomysł @ Henrika może nie działać, ponieważ musimy uważać, aby utrzymać argumenty we właściwej kolejności ... 'f' powinno być argumentem * drugim *, aby operacja była prawidłowa od lewej do prawej zamów ... –

11

Wypróbuj pakiet funkcjonalny :

library(functional) 
squared <- function(x)x*x 
Compose(sum, squared)(m) 
## [1] 44100 
squared(sum(m)) 
## [1] 44100 

EDIT:

Odnosząc się do pytania w komentarzach innej odpowiedzi na temat argumentów, tutaj znajduje się przykład komponowania z argumentami. Curry jest z opakowania funkcjonalne:

addn <- function(n, x) x + n 
Compose(Curry(addn, 1), squared)(10) 
## [1] 121 
squared(addn(1, 10)) 
## [1] 121 

EDIT 2:

chodzi pytanie debugowania debug działa, gdy funkcja jest curry. Jeśli jej nie już curry następnie owinąć go w Curry:

# this works since addn is curried 
debug(addn) 
Compose(Curry(addn, 1), squared)(10) 

# to debug squared put it in a Curry -- so this works: 
debug(squared) 
Compose(Curry(addn, 1), Curry(squared))(10) 
+0

Myślałem, że pakiet gsubfn mógłby zrobić coś takiego, przynajmniej składnia przyrostków env @ fn? –

+0

@DWin, dobry punkt. W gsubfn możemy to zrobić: 'Compose (fn $ identity (~ addn (1, x)), squared) (10)'. Oczywiście nawet bez gsubfn możemy to zrobić: 'Compose (funkcja (x) addn (1, x), kwadrat) (10)' –

+0

@ G.Grothendieck, działa, ale nie sądzę, że daje to zwykłe zalety płynny interfejs (http://en.wikipedia.org/wiki/Fluent_interface), podobnie jak inne języki obiektowe. Ponadto uważam, że nie ułatwia to debugowania części zapisu. – Sam

2

użyłbym pakiet magrittr. Ma „rura” operatora, który zaczyna się na podstawie jednej funkcji i przekazuje je jako pierwszy argument do następnego:

m <- matrix(c(1:10, 11:20), nrow = 10, ncol = 2) 

m %>% mean %>% sum 

Ceci n'est pas rury un!