R:在矩阵上应用函数并保持矩阵尺寸

Pau*_*und 29 r

所以我想在R中的矩阵上应用一个函数.这对于简单函数非常直观:

> (function(x)x*x)(matrix(1:10, nrow=2))
 [,1] [,2] [,3] [,4] [,5]
[1,]    1    9   25   49   81
[2,]    4   16   36   64  100
Run Code Online (Sandbox Code Playgroud)

......但显然我不了解它的所有工作原理:

> m = (matrix(1:10, nrow=2))
> (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) })(m)
     [,1] [,2] [,3] [,4] [,5]
[1,]    2    4    6    8   10
[2,]    3    5    7    9   11
Warning message:
In if (x == 3) { :
  the condition has length > 1 and only the first element will be used
Run Code Online (Sandbox Code Playgroud)

我读了这篇文章,发现了Vectorize和sapply,它们看起来都很棒,就像我想要的那样,除了它们都将我的矩阵转换成一个列表:

> y = (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) })
> sapply(m, y)
 [1]  2  3 NA  5  6 NA  8  9 NA 11
> Vectorize(y)(m)
 [1]  2  3 NA  5  6 NA  8  9 NA 11
Run Code Online (Sandbox Code Playgroud)

...而我想把它保持在当前尺寸的矩阵中.我怎么能这样做?谢谢!

Tom*_*mmy 20

@Joshua Ulrich(和Dason)有一个很好的答案.直接在没有功能的情况下完成它y是最好的解决方案.但是如果你真的需要调用一个函数,你可以更快地使用它vapply.它生成一个没有维度的矢量(sapply但是更快),但是你可以使用structure以下方法将它们添加回来:

# Your function (optimized)
y = function(x) if (x %% 3) x+1 else NA

m <- matrix(1:1e6,1e3)
system.time( r1 <- apply(m,1:2,y) ) # 4.89 secs
system.time( r2 <- structure(sapply(m, y), dim=dim(m)) ) # 2.89 secs
system.time( r3 <- structure(vapply(m, y, numeric(1)), dim=dim(m)) ) # 1.66 secs
identical(r1, r2) # TRUE
identical(r1, r3) # TRUE
Run Code Online (Sandbox Code Playgroud)

......正如您所看到的,该vapply方法比apply... 快约3倍.原因vapply比必须分析结果更快sapply,sapply以确定它可以简化为数字向量.有了vapply,你指定了结果类型(numeric(1)),所以它不必猜...

更新我想出了另一种(更短的)保留矩阵结构的方法:

m <- matrix(1:10, nrow=2)
m[] <- vapply(m, y, numeric(1))
Run Code Online (Sandbox Code Playgroud)

您只需使用将新值分配给对象m[] <-.然后,所有其他属性将被保留(如dim,dimnames,class等).


Jos*_*ich 15

一种方法是apply在行和列上使用:

apply(m,1:2,y)
     [,1] [,2] [,3] [,4] [,5]
[1,]    2   NA    6    8   NA
[2,]    3    5   NA    9   11
Run Code Online (Sandbox Code Playgroud)

您也可以使用下标来执行此操作,因为==它已经过矢量化:

m[m %% 3 == 0] <- NA
m <- m+1
m
     [,1] [,2] [,3] [,4] [,5]
[1,]    2   NA    6    8   NA
[2,]    3    5   NA    9   11
Run Code Online (Sandbox Code Playgroud)


Das*_*son 8

对于这个具体的例子,你可以做这样的事情

> # Create some fake data
> mat <- matrix(1:16, 4, 4)
> # Set all elements divisible by 3 to NA
> mat[mat %% 3 == 0] <- NA
> # Add 1 to all non NA elements
> mat <- mat + 1
> mat
     [,1] [,2] [,3] [,4]
[1,]    2    6   NA   14
[2,]    3   NA   11   15
[3,]   NA    8   12   NA
[4,]    5    9   NA   17
Run Code Online (Sandbox Code Playgroud)

  • 一位同事向我指出了这种方法。它确实满足了我的需求,我很欣赏它,但看起来确实应该有某种方法可以在矩阵上应用预先存在的函数。 (2认同)

Ric*_*ton 7

使用Dason和Josh的解决方案略有改进ifelse.

mat <- matrix(1:16, 4, 4)
ifelse(mat %% 3 == 0, NA, mat + 1)
     [,1] [,2] [,3] [,4]
[1,]    2    6   NA   14
[2,]    3   NA   11   15
[3,]   NA    8   12   NA
[4,]    5    9   NA   17
Run Code Online (Sandbox Code Playgroud)