web-dev-qa-db-ja.com

各行列要素のインデックスに関数を適用する方法

マトリックスの各要素に関数を適用するRの組み込み関数があるかどうか疑問に思っています(もちろん、関数はマトリックスインデックスに基づいて計算する必要があります)。同等のものは次のようになります。

matrix_apply <- function(m, f) {
  m2 <- m
  for (r in seq(nrow(m2)))
    for (c in seq(ncol(m2)))
      m2[[r, c]] <- f(r, c)
  return(m2)
}

そのような組み込み関数がない場合、マトリックスインデックスをパラメーターとして持つ任意の関数を計算することで取得した値を含むようにマトリックスを初期化する最良の方法は何ですか?

48
eold

outerが欲しいと思う:

> mat <- matrix(NA, nrow=5, ncol=3)

> outer(1:nrow(mat), 1:ncol(mat) , FUN="*")
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    4    6
[3,]    3    6    9
[4,]    4    8   12
[5,]    5   10   15

> outer(1:nrow(mat), 1:ncol(mat) , FUN=function(r,c) log(r+c) )
          [,1]     [,2]     [,3]
[1,] 0.6931472 1.098612 1.386294
[2,] 1.0986123 1.386294 1.609438
[3,] 1.3862944 1.609438 1.791759
[4,] 1.6094379 1.791759 1.945910
[5,] 1.7917595 1.945910 2.079442

これにより、ニースのコンパクトな出力が生成されます。しかし、mapplyは他の状況で役立つ可能性があります。 mapplyを、このページの他のユーザーがVectorizeを使用しているのと同じ操作を行う別の方法と考えると便利です。 mapplyは、Vectorizeが「プリミティブ」関数を使用できないため、より一般的です。

data.frame(mrow=c(row(mat)),   # straightens out the arguments
           mcol=c(col(mat)), 
           m.f.res= mapply(function(r,c) log(r+c), row(mat), col(mat)  ) )
#   mrow mcol   m.f.res
1     1    1 0.6931472
2     2    1 1.0986123
3     3    1 1.3862944
4     4    1 1.6094379
5     5    1 1.7917595
6     1    2 1.0986123
7     2    2 1.3862944
8     3    2 1.6094379
9     4    2 1.7917595
10    5    2 1.9459101
11    1    3 1.3862944
12    2    3 1.6094379
13    3    3 1.7917595
14    4    3 1.9459101
15    5    3 2.0794415

おそらく、関数にrow()およびcol()関数が返すものを提供するつもりはなかったでしょう。これにより、15(やや冗長な)3 x 5行列の配列が生成されます。

> outer(row(mat), col(mat) , FUN=function(r,c) log(r+c) )
29
42-

最も単純なアプローチisは、マトリックスの要素に直接適用できるf()を使用するだけです。たとえば、@ adamleerich's Answerのm行列を使用します

_m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)
_

apply()の例の場合、as.character()を使用する理由はありません。代わりに、mの要素をベクトルであるかのように操作し(実際は1)、in-placeを置き換えることができます。

_> m[] <- as.character(m)
> m
     [,1] [,2] [,3] [,4]
[1,] "1"  "3"  "5"  "7" 
[2,] "2"  "4"  "6"  "8"
_

このブロックの最初の部分がここのキーです。 _m[]_は、mの要素を、mを文字のベクトルで上書きするのではなく、as.character()からの出力に強制的に置き換えます。

つまり、isは、行列の各要素に関数を適用する一般的な解決策です。

行と列のインデックスで動作するf()を本当に使用する必要がある場合は、f()row()を使用してcol()を記述します。 :

_> m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)
> row(m)
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    2    2    2    2
> col(m)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    1    2    3    4
> row(m) * col(m) ## `*`(row(m), col(m)) to see this is just f()
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    2    4    6    8
_

または、他の人が示したようにouter()を使用するもの。 f()がベクトル化されていない場合、可能な限り自分の戦略を再考しますi)おそらく真にベクトル化されたバージョンを記述する方法であり、ii)ベクトル化されていない関数非常にうまくスケーリングするつもりはありません。

18
Gavin Simpson

各要素にどのような関数を適用するのか教えてくれませんでしたが、他の回答の例が機能する唯一の理由は、関数が既にベクトル化されているためだと思います。本当に各要素に関数を適用したい場合、outerは、関数がまだ提供していない特別なものを提供しません。答えがouterにマトリックスを渡さなかったことに気付くでしょう。

@Chaseのコメントをフォローしてapplyを使用してみてください。

たとえば、私は行列を持っています

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)

(例として)要素ごとに文字マトリックスに変換したい場合は、これを行うことができます

apply(m, c(1,2), as.character)

もちろん、 as.characterはすでにベクトル化されていますが、私の特別な関数my.special.functionは違います。 1つの引数、要素のみを取ります。 outerを操作するための簡単な方法はありません。しかし、これは動作します

apply(m, c(1,2), my.special.function)
13
adamleerich

outerを考えているかもしれません:

rows <- 1:10
cols <- 1:10

outer(rows,cols,"+")

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    2    3    4    5    6    7    8    9   10    11
 [2,]    3    4    5    6    7    8    9   10   11    12
 [3,]    4    5    6    7    8    9   10   11   12    13
 [4,]    5    6    7    8    9   10   11   12   13    14
 [5,]    6    7    8    9   10   11   12   13   14    15
 [6,]    7    8    9   10   11   12   13   14   15    16
 [7,]    8    9   10   11   12   13   14   15   16    17
 [8,]    9   10   11   12   13   14   15   16   17    18
 [9,]   10   11   12   13   14   15   16   17   18    19
[10,]   11   12   13   14   15   16   17   18   19    20

これは明らかにかなり単純なサンプル関数ですが、独自のカスタム関数を提供することもできます。見る ?outer

編集

以下のコメントに反して、outerを非ベクトル化関数で使用することもできます。

m <- matrix(1:16,4,4)

#A non-vectorized function 
myFun <- function(x,y,M){
     M[x,y] + (x*y)
}

#Oh noes! 
outer(1:4,1:4,myFun,m)
Error in dim(robj) <- c(dX, dY) : 
  dims [product 16] do not match the length of object [256]

#Oh ho! Vectorize()! 
myVecFun <- Vectorize(myFun,vectorize.args = c('x','y'))

#Voila! 
outer(1:4,1:4,myVecFun,m)
     [,1] [,2] [,3] [,4]
[1,]    2    7   12   17
[2,]    4   10   16   22
[3,]    6   13   20   27
[4,]    8   16   24   32
8
joran

これはあなたの質問に正確に答えるわけではありませんが、似たような質問を見つけようとしているときに見つけたので、何かお見せします。

1つの部分だけが必要な行列の各要素に適用する関数があるとします。

mydouble <- function(x) {
   return(x+x)
}

そして、行列Xがあるとします。

> x=c(1,-2,-3,4)
> X=matrix(x,2,2)
> X
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4

あなたはこれをします:

res=mydouble(X)

次に、各値の要素ごとの倍精度を実行します。

ただし、以下のような関数でロジックを実行すると、パラメーター化されておらず、期待どおりに動作しないという警告が表示されます。

myabs <- function(x) {
  if (x<0) {
      return (-x)
  } else {
      return (x)
  }
}

> myabs(X)
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4
Warning message:
In if (x < 0) { :
  the condition has length > 1 and only the first element will be used

ただし、apply()関数を使用する場合は使用できます。

例えば:

> apply(X,c(1,2),myabs)
     [,1] [,2]
[1,]    1    3
[2,]    2    4

素晴らしいですねさて、2つ以上のパラメーターを持つ関数がある場合、それは故障します。たとえば、これがあるとします:

mymath <- function(x,y) {
    if(x<0) {
        return(-x*y)
    } else {
        return(x*y)
    }
}

この場合、apply()関数を使用します。ただし、マトリックスは失われますが、結果は正しく計算されます。あなたがそんなに傾いているなら、彼らは改革することができます。

> mapply(mymath,X,X)
[1]  1 -4 -9 16
> mapply(mymath,X,2)
[1] 2 4 6 8
> matrix(mapply(mymath,X,2),c(2,2))
     [,1] [,2]
[1,]    2    6
[2,]    4    8
0
netskink