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.