2010-11-21 54 views
85

Supongamos que tengo una matriz n por 2 y una función que toma un 2-vector como uno de sus argumentos. Me gustaría aplicar la función a cada fila de la matriz y obtener un n-vector. ¿Cómo hacer esto en R?Aplicar una función a cada fila de una matriz o un marco de datos

Por ejemplo, me gustaría para calcular la densidad de una distribución normal estándar 2D en tres puntos:

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

Cómo aplicar la función a cada fila de out?

¿Cómo pasar valores para los otros argumentos además de los puntos a la función en la forma que especifique?

Respuesta

136

sólo tiene que utilizar la función 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> 

Esto toma una matriz y se aplica una función (tonto) para cada fila. Usted pasa argumentos adicionales a la función como cuarto, quinto, ... argumentos al apply().

+0

Gracias! ¿Qué sucede si las filas de la matriz no son el primer arg de la función? ¿Cómo especificar a qué arg de la función se asigna cada fila de la matriz? – Tim

+0

Lea la ayuda para 'apply()' - barre por fila (cuando el segundo arg es 1, sino por columna), y la fila actual (o col) es siempre el primer argumento. Así es como se definen las cosas. –

+0

@Tim: si usa una función R interna y la fila no es la primera arg, haga lo que hizo Dirk y haga su propia función personalizada donde la fila ** es ** la primera arg. –

6

El primer paso sería crear el objeto de función y luego aplicarlo. Si desea que un objeto de matriz que tiene el mismo número de filas, puede predefinir y utilizar la forma de objeto [] como se ilustra (en caso contrario el valor devuelto será simplificado a un vector):

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 

Si quería para usos distintos de los parámetros por defecto entonces la llamada debe incluir argumentos el nombre de la función:

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

aplicar() también se puede utilizar en matrices de dimensiones superiores y el argumento margen puede ser un vector, así como un único entero .

14

En caso de que desee aplicar funciones comunes como suma o media, debe usar rowSums o rowMeans ya que son más rápidas que apply(data, 1, sum) aproximación. De lo contrario, quédese con apply(data, 1, fun). Puede pasar argumentos adicionales después argumento divertido (como Dirk ya se ha sugerido):

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 

entonces usted puede hacer algo como esto:

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 
2

Otro enfoque si desea utilizar una porción variable del conjunto de datos en lugar de un solo valor es usar rollapply(data, width, FUN, ...). El uso de un vector de anchuras le permite aplicar una función en una ventana variable del conjunto de datos. Lo he usado para crear una rutina de filtrado adaptativo, aunque no es muy eficiente.

11

Aquí hay un breve ejemplo de cómo aplicar una función a cada fila de una matriz. (Aquí, la función aplicada normaliza cada fila a 1.)

Nota: El resultado de la apply() tenía que ser transpuesta usando t() para obtener el mismo diseño que la matriz de entrada 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))) 

Resultado:

 [,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 
3

Aplicar hace el trabajo bien, pero es bastante lento. Usar sapply y vapply podría ser útil. También puede ser útil el archivo de dplyr. Veamos un ejemplo de cómo hacer filas producto de cualquier marco de datos.

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

Tenga en cuenta que la asignación a la variable antes de usar vapply/sapply/aplicar una buena práctica, ya que reduce el tiempo mucho. Vamos a ver los resultados de microanálisis

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

Tener una mirada cuidadosa a cómo t() se está utilizando

Cuestiones relacionadas