Приложете функция към всеки ред от матрица или рамка от данни

Да предположим, че имам матрица n на 2 и функция, която приема 2-вектор като един от своите аргументи. Бих искал да приложа функцията към всеки ред от матрицата и да получа n-вектор. Как да направя това в R?

Например, бих искал да изчисля плътността на 2D стандартно нормално разпределение на три точки:

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))

Как да приложа функцията към всеки ред от out?

Как да предам стойности за другите аргументи освен точките към функцията по указания от вас начин?


person Tim    schedule 21.11.2010    source източник


Отговори (6)


Просто използвате функцията apply():

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> 

Това взема матрица и прилага (глупава) функция към всеки ред. Подавате допълнителни аргументи към функцията като четвърти, пети, ... аргументи към apply().

person Dirk Eddelbuettel    schedule 21.11.2010
comment
Благодаря! Ами ако редовете на матрицата не са първият аргумент на функцията? Как да укажа на кой аргумент на функцията е присвоен всеки ред от матрицата? - person Tim; 21.11.2010
comment
Прочетете помощта за apply() -- той измита по ред (когато вторият аргумент е 1, иначе по колона), а текущият ред (или колона) винаги е първият аргумент. Така се определят нещата. - person Dirk Eddelbuettel; 21.11.2010
comment
@Tim : ако използвате вътрешна R функция и редът не е първият аргумент, направете като Dirk и създайте своя собствена персонализирана функция, където ред е първият аргумент. - person Joris Meys; 22.11.2010
comment
Пакетът plyr предоставя широка гама от тези приложни видове функции. Освен това предоставя повече функционалност, включително паралелна обработка. - person Paul Hiemstra; 30.11.2011
comment
Можете ли да обясните какво означава 1 в apply(M, 1...)? - person cryptic0; 08.11.2017
comment
@cryptic0 този отговор е закъснял, но за потребителите на Google вторият аргумент в apply е аргументът MARGIN. Тук това означава прилагане на функцията към редовете (първото измерение в dim(M)). Ако беше 2, щеше да приложи функцията към колоните. - person De Novo; 05.03.2018

В случай, че искате да приложите общи функции като сума или средна стойност, трябва да използвате rowSums или rowMeans, тъй като те са по-бързи от подхода apply(data, 1, sum). В противен случай се придържайте към apply(data, 1, fun). Можете да подадете допълнителни аргументи след аргумента FUN (както 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

След това можете да направите нещо подобно:

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
person aL3xa    schedule 21.11.2010

Ето кратък пример за прилагане на функция към всеки ред от матрица. (Тук приложената функция нормализира всеки ред до 1.)

Забележка: Резултатът от apply() трябваше да бъде транспониран с помощта на t(), за да се получи същото оформление като входната матрица 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) ))

Резултат:

     [,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
person Viliam Simko    schedule 04.11.2014

Първата стъпка би била създаването на функционален обект, след което прилагането му. Ако искате матричен обект, който има същия брой редове, можете да го дефинирате предварително и да използвате формата object[], както е илюстрирано (в противен случай върнатата стойност ще бъде опростена до вектор):

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

Ако искате да използвате различни от вашите параметри по подразбиране, тогава извикването трябва да включва именувани аргументи след функцията:

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

apply() може да се използва и върху по-високомерни масиви и аргументът MARGIN може да бъде вектор, както и едно цяло число.

person IRTFM    schedule 21.11.2010

Приложението върши добре работата, но е доста бавно. Използването на sapply и vaply може да бъде полезно. rowwise на dplyr също може да бъде полезен. Нека да видим пример за това как да направите произведение по редове на всеки кадър с данни.

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

Имайте предвид, че присвояването на променлива преди използването на vaply/sapply/ apply е добра практика, тъй като намалява много времето. Нека да видим резултатите от микробенчмарка

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))
)

Погледнете внимателно как се използва t().

person Pratham    schedule 29.05.2017
comment
Може да е по-справедливо да сравните семейството на приложения, ако сте използвали b <- t(iris[1:10, 1:3]) и apply(b, 2 prod). - person DaSpeeg; 13.12.2018

Друг подход, ако искате да използвате различна част от набора от данни вместо една стойност, е да използвате rollapply(data, width, FUN, ...). Използването на вектор от ширини ви позволява да приложите функция върху различен прозорец на набора от данни. Използвах това, за да изградя адаптивна рутина за филтриране, въпреки че не е много ефективна.

person DWAHL    schedule 21.09.2011