Застосувати функцію до кожного рядка матриці чи кадру даних


129

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

Як передавати значення для інших аргументів, окрім пунктів функції, у вказаному вами порядку?

Відповіді:


180

Ви просто використовуєте 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().


Дякую! Що робити, якщо рядки матриці не є першим аргументом функції? Як вказати, якому аргументу функції призначений кожен рядок матриці?
Тім

Прочитайте довідку apply()- вона прошивається за рядком (коли другий аргумент - 1, інакше за стовпцем), а поточний рядок (або стовпчик) - це завжди перший аргумент. Саме так визначаються речі.
Дірк Еддельбуеттель

@Tim: якщо ви використовуєте внутрішню функцію R, а рядок не є першим аргументом, зробіть так, як це зробив Дірк і зробіть власну власну функцію, де рядок є першим аргументом.
Йоріс Майс

3
Пакет plyr пропонує широкий спектр цих застосованих функцій. Він також забезпечує більшу функціональність, включаючи паралельну обробку.
Пол Хіемстра

6
@ cryptic0 ця відповідь пізня, але для googlers другий аргумент у застосуванні - це MARGINаргумент. Тут це означає застосувати функцію до рядків (перший вимір у dim(M)). Якби це було 2, він застосував би функцію до стовпців.
De Novo

17

У випадку, якщо ви хочете застосувати загальні функції, такі як сума або середня, ви повинні використовувати 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

15

Ось короткий приклад застосування функції до кожного рядка матриці. (Тут застосована функція нормалізує кожен рядок до 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

Першим кроком було б зробити об’єкт функції, потім застосувати його. Якщо ви хочете, щоб матричний об'єкт мав однакову кількість рядків, ви можете заздалегідь визначити його та використовувати форму 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 може бути як векторним, так і єдиним цілим числом.


4

Застосування виконує роботу добре, але досить повільно. Використання sapply та vapply може бути корисним. roblyise dplyr також може бути корисним. Давайте подивимось приклад того, як зробити досконалий продукт з будь-якого кадру даних.

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

Зауважте, що присвоєння змінної перед використанням vapply / sapply / application є хорошою практикою, оскільки це значно скорочує час. Давайте подивимося результати мікробенчмарки

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


Можливо, було б більш справедливо порівняти сімейство застосувань, якщо ви використовували b <- t(iris[1:10, 1:3])та apply(b, 2 prod).
DaSpeeg

2

Іншим підходом, якщо ви хочете використовувати різну частину набору даних замість одного значення, є використання rollapply(data, width, FUN, ...). Використання вектора ширини дозволяє застосувати функцію до різного вікна набору даних. Я використовував це для побудови адаптивного режиму фільтрації, хоча це не дуже ефективно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.