Aplique uma função a cada linha de uma matriz ou um quadro de dados
Pergunta
Suponha que eu tenha uma matriz por 2 e uma função que toma um vetor 2 como um de seus argumentos. Eu gostaria de aplicar a função a cada linha da matriz e obter um vetor N. Como fazer isso em r?
Por exemplo, gostaria de calcular a densidade de uma distribuição normal 2D padrão em três pontos:
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))
Como aplicar a função a cada linha de out
?
Como passar valores para os outros argumentos além dos pontos para a função da maneira como você especificar?
Solução
Você simplesmente usa o apply()
função:
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>
Isso leva uma matriz e aplica uma função (boba) a cada linha. Você passa argumentos extras para a função como quarto, quinto, ... argumentos para apply()
.
Outras dicas
Caso você queira aplicar funções comuns, como soma ou média, você deve usar rowSums
ou rowMeans
já que eles são mais rápidos do que apply(data, 1, sum)
abordagem. Caso contrário, fique com apply(data, 1, fun)
. Você pode passar argumentos adicionais após o argumento divertido (como Dirk já sugeriu):
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
Então você pode fazer algo assim:
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
Aqui está um pequeno exemplo de aplicação de uma função a cada linha de uma matriz. (Aqui, a função aplicada normaliza cada linha para 1.)
Observação: O resultado do apply()
Teve que ser transposto usando t()
Para obter o mesmo layout que a 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
A primeira etapa seria fazer o objeto de função e depois aplicá -lo. Se você deseja um objeto Matrix que tenha o mesmo número de linhas, você pode predefini -lo e usar o formulário [] como ilustrado (caso contrário, o valor retornado será simplificado a um vetor):
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
Se você quiser usar outros parâmetros padrão, a chamada deve incluir argumentos nomeados após a função:
bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6)
Aplicar () também pode ser usado em matrizes dimensionais mais altas e o argumento da margem pode ser um vetor e também um número inteiro.
Inscreva -se bem o trabalho, mas é bastante lento. Usar Sapply e Vapply pode ser útil. O RowWise da DPLYR também pode ser útil, vamos ver um exemplo de como fazer o produto de linha de qualquer quadro de dados.
a = data.frame(t(iris[1:10,1:3]))
vapply(a, prod, 0)
sapply(a, prod)
Observe que atribuir à variável antes de usar o VAPPLY/ SAPPLY/ APLICAR é uma boa prática, pois reduz muito o tempo. Vamos ver os resultados da 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))
)
Dê uma olhada cuidadosa de como T () está sendo usado
Outra abordagem se você quiser usar uma parte variável do conjunto de dados em vez de um único valor é usar rollapply(data, width, FUN, ...)
. O uso de um vetor de larguras permite aplicar uma função em uma janela variável do conjunto de dados. Eu usei isso para criar uma rotina de filtragem adaptativa, embora não seja muito eficiente.