Zastosuj funkcję do każdego wiersza macierzy lub ramki danych


129

Załóżmy, że mam macierz by 2 i funkcję, która jako jeden z argumentów przyjmuje wektor 2. Chciałbym zastosować tę funkcję do każdego wiersza macierzy i uzyskać n-wektor. Jak to zrobić w R?

Na przykład chciałbym obliczyć gęstość standardowego rozkładu normalnego 2D w trzech punktach:

bivariate.density(x = c(0, 0), mu = c(0, 0), sigma = c(1, 1), rho = 0){
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+x[2]^2/sigma[2]^2-2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
}

out <- rbind(c(1, 2), c(3, 4), c(5, 6))

Jak zastosować funkcję do każdego wiersza out?

Jak przekazać wartości dla innych argumentów oprócz punktów do funkcji w sposób określony przez Ciebie?

Odpowiedzi:


180

Wystarczy skorzystać z apply()funkcji:

R> M <- matrix(1:6, nrow=3, byrow=TRUE)
R> M
     [,1] [,2]
[1,]    1    2
[2,]    3    4
[3,]    5    6
R> apply(M, 1, function(x) 2*x[1]+x[2])
[1]  4 10 16
R> 

To pobiera macierz i stosuje (głupią) funkcję do każdego wiersza. Przekazujesz dodatkowe argumenty do funkcji jako czwarty, piąty, ... argument apply().


Dzięki! A co, jeśli wiersze macierzy nie są pierwszym argumentem funkcji? Jak określić, do którego argumentu funkcji przypisany jest każdy wiersz macierzy?
Tim

Przeczytaj pomoc dla apply()- zamiata po wierszu (gdy drugi argument ma wartość 1, w przeciwnym razie po kolumnie), a bieżący wiersz (lub kolumna) jest zawsze pierwszym argumentem. Tak się definiuje.
Dirk Eddelbuettel

@Tim: jeśli używasz wewnętrznej funkcji R, a wiersz nie jest pierwszym argumentem, zrób tak, jak zrobił to Dirk i utwórz własną funkcję niestandardową, w której wiersz jest pierwszym argumentem.
Joris Meys

3
Pakiet plyr zapewnia szeroki wachlarz tego rodzaju funkcji. Zapewnia również większą funkcjonalność, w tym przetwarzanie równoległe.
Paul Hiemstra

6
@ cryptic0 ta odpowiedź jest spóźniona, ale dla pracowników Google drugim argumentem zastosowania jest MARGINargument. Tutaj oznacza zastosowanie funkcji do wierszy (pierwszy wymiar w dim(M)). Gdyby było 2, zastosowałoby funkcję do kolumn.
De Novo

17

Jeśli chcesz zastosować typowe funkcje, takie jak suma lub średnia, powinieneś użyć rowSumslub, rowMeansponieważ są one szybsze niż apply(data, 1, sum)podejście. W przeciwnym razie trzymaj się apply(data, 1, fun). Możesz przekazać dodatkowe argumenty po argumencie FUN (jak już zasugerował Dirk):

set.seed(1)
m <- matrix(round(runif(20, 1, 5)), ncol=4)
diag(m) <- NA
m
     [,1] [,2] [,3] [,4]
[1,]   NA    5    2    3
[2,]    2   NA    2    4
[3,]    3    4   NA    5
[4,]    5    4    3   NA
[5,]    2    1    4    4

Następnie możesz zrobić coś takiego:

apply(m, 1, quantile, probs=c(.25,.5, .75), na.rm=TRUE)
    [,1] [,2] [,3] [,4] [,5]
25%  2.5    2  3.5  3.5 1.75
50%  3.0    2  4.0  4.0 3.00
75%  4.0    3  4.5  4.5 4.00

15

Oto krótki przykład zastosowania funkcji do każdego wiersza macierzy. (Tutaj zastosowana funkcja normalizuje każdy wiersz do 1.)

Uwaga: Wynik z operacji apply()musiał zostać przetransponowany za pomocą, t()aby uzyskać ten sam układ co macierz wejściowa A.

A <- matrix(c(
  0, 1, 1, 2,
  0, 0, 1, 3,
  0, 0, 1, 3
), nrow = 3, byrow = TRUE)

t(apply(A, 1, function(x) x / sum(x) ))

Wynik:

     [,1] [,2] [,3] [,4]
[1,]    0 0.25 0.25 0.50
[2,]    0 0.00 0.25 0.75
[3,]    0 0.00 0.25 0.75

6

Pierwszym krokiem byłoby utworzenie obiektu funkcji, a następnie zastosowanie go. Jeśli potrzebujesz obiektu macierzowego, który ma taką samą liczbę wierszy, możesz go wstępnie zdefiniować i użyć postaci obiektu [], jak pokazano na ilustracji (w przeciwnym razie zwrócona wartość zostanie uproszczona do wektora):

bvnormdens <- function(x=c(0,0),mu=c(0,0), sigma=c(1,1), rho=0){
     exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+
                           x[2]^2/sigma[2]^2-
                           2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 
     1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
     }
 out=rbind(c(1,2),c(3,4),c(5,6));

 bvout<-matrix(NA, ncol=1, nrow=3)
 bvout[] <-apply(out, 1, bvnormdens)
 bvout
             [,1]
[1,] 1.306423e-02
[2,] 5.931153e-07
[3,] 9.033134e-15

Jeśli chcesz użyć innych parametrów niż domyślne, wywołanie powinno zawierać nazwane argumenty po funkcji:

bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6)

Apply () można również stosować w tablicach o wyższych wymiarach, a argument MARGIN może być wektorem, jak również pojedynczą liczbą całkowitą.


4

Aplikuj dobrze, ale działa dość wolno. Używanie sapply i vapply może być przydatne. Dplyr's rowwise również może być przydatny Zobaczmy przykład, jak zrobić iloczyn wierszowy dowolnej ramki danych.

a = data.frame(t(iris[1:10,1:3]))
vapply(a, prod, 0)
sapply(a, prod)

Zauważ, że przypisanie do zmiennej przed użyciem vapply / sapply / apply jest dobrą praktyką, ponieważ bardzo skraca czas. Zobaczmy wyniki microbenchmark

a = data.frame(t(iris[1:10,1:3]))
b = iris[1:10,1:3]
microbenchmark::microbenchmark(
    apply(b, 1 , prod),
    vapply(a, prod, 0),
    sapply(a, prod) , 
    apply(iris[1:10,1:3], 1 , prod),
    vapply(data.frame(t(iris[1:10,1:3])), prod, 0),
    sapply(data.frame(t(iris[1:10,1:3])), prod) ,
    b %>%  rowwise() %>%
        summarise(p = prod(Sepal.Length,Sepal.Width,Petal.Length))
)

Przyjrzyj się uważnie, w jaki sposób używana jest funkcja t ()


Bardziej sprawiedliwe może być porównanie rodziny zastosowania, jeśli użyto b <- t(iris[1:10, 1:3])i apply(b, 2 prod).
DaSpeeg

2

Innym podejściem, jeśli chcesz użyć różnych części zestawu danych zamiast pojedynczej wartości, jest użycie rollapply(data, width, FUN, ...). Użycie wektora szerokości umożliwia zastosowanie funkcji w zmiennym oknie zbioru danych. Użyłem tego do zbudowania procedury filtrowania adaptacyjnego, chociaż nie jest ona zbyt wydajna.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.