Примените функцию к каждой строке матрицы или фрейма данных - PullRequest
112 голосов
/ 21 ноября 2010

Предположим, у меня есть матрица 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?

Как передать значения для других аргументов, кроме точек, в функцию указанным вами способом?

Ответы [ 6 ]

166 голосов
/ 21 ноября 2010

Вы просто используете функцию 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().

15 голосов
/ 21 ноября 2010

Если вы хотите применить общие функции, такие как сумма или среднее, вы должны использовать rowSums или rowMeans, поскольку они работают быстрее, чем apply(data, 1, sum). В противном случае придерживайтесь apply(data, 1, fun). Вы можете передать дополнительные аргументы после аргумента FUN (как уже предложил Дирк):

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
13 голосов
/ 04 ноября 2014

Вот краткий пример применения функции к каждой строке матрицы.(Здесь применяемая функция нормализует каждую строку до 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
6 голосов
/ 21 ноября 2010

Первым шагом будет создание объекта функции, а затем его применение. Если вы хотите, чтобы матричный объект имел такое же количество строк, вы можете предварительно определить его и использовать форму объекта [], как показано (в противном случае возвращаемое значение будет упрощено до вектора):

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 может быть вектором, а также одним целым числом.

4 голосов
/ 29 мая 2017

Apply делает свою работу хорошо, но довольно медленно.Использование sapply и vapply может быть полезным.Строка dplyr также может быть полезна. Давайте рассмотрим пример того, как сделать построчный продукт для любого фрейма данных.

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

Обратите внимание, что присвоение переменной перед использованием vapply / 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 ()

2 голосов
/ 21 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...