对矩阵或数据框架的每一行应用函数

假设我有一个 n 乘以2的矩阵和一个以2-向量作为参数的函数。我想把这个函数应用到矩阵的每一行,得到一个 n 向量。如何在 R 中做到这一点?

例如,我想计算二维标准正态分布在三个点上的密度:

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的每一行?

如何传递值的其他参数以外的点的函数在您指定的方式?

301902 次浏览

你只需使用 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()

第一步是创建函数对象,然后应用它。如果你想要一个行数相同的矩阵对象,你可以预先定义它并使用 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)

Application ()也可以用于高维数组,MARGIN 参数可以是一个向量,也可以是一个整数。

如果您想应用常见的函数,比如 sum 或 mean,那么应该使用 rowSumsrowMeans,因为它们比 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

如果希望使用数据集的可变部分而不是单个值,另一种方法是使用 rollapply(data, width, FUN, ...)。使用宽度向量允许您对数据集的可变窗口应用函数。我已经用它构建了一个自适应过滤例程,尽管效率不是很高。

下面是一个将函数应用于矩阵的每一行的简短示例。 (在这里,应用的函数将每一行标准化为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

申请工作做得很好,但是相当缓慢。 Dplyr 的 rowwise 也很有用 让我们看一个示例,说明如何对任何数据帧进行行积运算。

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

请注意,在使用 vapplication/sapplication/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 ()是如何使用的

使用 acrossrowSumsrowMeans的 dplyr 方法。

M <- matrix(1:9, nrow=3, byrow=TRUE)
[,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9


M %>% as_tibble() %>%
rowwise() %>%
mutate(sum = rowSums(across(where(is.numeric)))) %>%
mutate(mean = rowMeans(across(V1:V3))) %>%
mutate(Max = max(V1:V3)) %>%
mutate(Min = min(V1:V3)) %>%
as.matrix()


V1 V2 V3 sum mean Max Min
[1,]  1  2  3   6    2   3   1
[2,]  4  5  6  15    5   6   4
[3,]  7  8  9  24    8   9   7