読者です 読者をやめる 読者になる 読者になる

無粋な日々に

日々の技術メモ(データ分析界隈)

R: RのBroadcast

RのBroadcast

RのBroadcast(ベクトル、行列などの型や次元を自動で合わせて演算する)でたまに混乱するのでまとめる。結論だけ書くとRではベクトルを行列にBroadcastするとき、列方向で埋め合わせるという事だけを忘れないようにすればOK

基本原理

デフォルトでは列方向で要素を埋めていく

まず基本原理把握のため、ベクトルから行列を生成してみる。Rではベクトルから行列を生成する際、デフォルトでは列方向(bycol)優先にで要素を埋めていく。 例えば、\( (1, 2, \cdots ,20) \)のベクトルから、5x4行列を生成する過程を考える

# ベクトル生成
(v <- 1:20)

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


# ベクトルから行列を生成 (何も指定しないと、列方向で要素が埋められていく)
(X <- matrix(v, nrow=4, ncol=5))

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

行方向で埋め合わせたい場合はbyrowオプションを入れる

要素を行方向で使う場合にはbyrow=TRUEを指定すれば良い

# ベクトルから行列を生成 (byrowオプションを指定することで要素を行方向で埋めることもできる)
(X <- matrix(v, nrow=4, ncol=5, byrow=TRUE))

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

デフォルトではベクトルから行列を生成する際、列方向に使うという事。この基本原理でBroadcastも行われるのだろうと想像できる

実際にBroadcastしてみる

実際にベクトルと行列を演算してBroadcastしてみる

# 4×5の0行列を生成
(A <- matrix(0, nrow=4, ncol=5))

#>    [,1] [,2] [,3] [,4] [,5]
#>[1,]    0    0    0    0    0
#>[2,]    0    0    0    0    0
#>[3,]    0    0    0    0    0
#>[4,]    0    0    0    0    0

# 行列とベクトルの足し算(element wise)でBroadcastしてみる
(A + v)

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

よくある危険な例(中心化する場合)

よくある処理の例で見ていきます。 行列を各変数(列)ごとに中心化してみましょう。よくやる \( {\bf X} - {\bf E}({\bf X}) \) です。

# 0-50のランダムな数で、5x5行列を生成
set.seed(0)
(X <- matrix(sample(0:50, 25, replace=TRUE), nrow=5, dimnames = list(NULL,paste0('x', as.character(1:5)))))


#>     x1 x2 x3 x4 x5
#>[1,] 45 10  3 39 39
#>[2,] 13 45 10 25 47
#>[3,] 18 48  9 36 10
#>[4,] 29 33 35 50 33
#>[5,] 46 32 19 19  6


# 列の平均値を取る
(X.center <- colMeans(X))

#>   x1   x2   x3   x4   x5 
#> 30.2 33.6 15.2 33.8 27.0 

ダメな例

もとの行列から平均値を引いていくのですが、そのまま引くと意図と異なる値が出ます。以下は間違いです

# 普通に引き算してしまうと列方向でBroadcastされるのでNG
(X - X.center)

#>        x1    x2    x3   x4    x5
#>[1,]  14.8 -20.2 -27.2  8.8   8.8
#>[2,] -20.6  11.4 -23.6 -8.6  13.4
#>[3,]   2.8  32.8  -6.2 20.8  -5.2
#>[4,]  -4.8  -0.8   1.2 16.2  -0.8
#>[5,]  19.0   5.0  -8.0 -8.0 -21.0

正しい例

正しくは行方向でBroadcastしてもらわないとダメです。例えば以下のような方法があります

# 1.明示的に行方向で行列に変換
(X - matrix(X.center, nrow=5, ncol=5, byrow=TRUE))

#>        x1    x2    x3    x4  x5
#>[1,]  14.8 -23.6 -12.2   5.2  12
#>[2,] -17.2  11.4  -5.2  -8.8  20
#>[3,] -12.2  14.4  -6.2   2.2 -17
#>[4,]  -1.2  -0.6  19.8  16.2   6
#>[5,]  15.8  -1.6   3.8 -14.8 -21

# 2. 一度転置(transpose)して、戻す
t(t(X) - X.center)

#>        x1    x2    x3    x4  x5
#>[1,]  14.8 -23.6 -12.2   5.2  12
#>[2,] -17.2  11.4  -5.2  -8.8  20
#>[3,] -12.2  14.4  -6.2   2.2 -17
#>[4,]  -1.2  -0.6  19.8  16.2   6
#>[5,]  15.8  -1.6   3.8 -14.8 -21