R(2)ベクトル

Computation
R
Author

司馬博文

Published

5/07/2021

Modified

6/05/2024

概要
統計言語 R において,ベクトルは極めて基本的なデータ構造であり,行列・配列・リストはいずれも追加の属性を持ったベクトルと理解できる.本稿では,ベクトルの構成法,単項演算,二項演算,indexing などを解説する.

1 概観

1.1 すべてはベクトルである

データ型 logical, integer, double, complex, character, raw は全てベクトルと理解される.1 逆に,全てのベクトルはこの6つのいずれかの atomic data type を持つ.2

4.2, four point two はいずれもデータ型こそ違えど長さ1のベクトルである.

1.2 他のデータ型との関係

全てはベクトルで,行列も配列もリストも,それに特殊な属性を付与したもの.

1.2.1 行列と配列

dim属性を持つベクトルのことである.各次元に名前がつくとdimnames属性も持つ.

1.2.2 リスト

リストは各要素のデータ型が異なっていても良く,“generic vector” というべきものである.

ベクトルは基本横で[1]という行の名前の後に表示される,行列はそのdim属性のために基本縦で[,1]と indexing される.indexing の表示の違いでクラスの違いがわかる.

まずベクトルの時点で定義される演算を紹介し,インデックスを紹介する.全てのオブジェクトはベクトルなので,ベクトルをベクトルでインデックスするという奇怪な状況が発生する.また R では演算のほとんどがベクトル化されていて,これを使いこなすことが高速化の最初の手法になる. 次に行列を定義し,行列特有の演算を見る.

2 ベクトル

スカラーなどの概念はない,全てはベクトル. 文字列も,character modeの長さ1ベクトル.

2.1 構成

  1. 基本的にはベクトルのためのコンストラクタc(arg1,arg2,...)を用いる.実は浮動小数点モードを生成する.
    • 引数もベクトルなので,本質は concatenation
    • データ型が強制される,これが list との違い.
  2. a:ba<bならば増加,a>bならば減少の,step=1のベクトルの生成.実は整数モードを生成する.
  3. seq(from=1,to=1,by=((to - from)/(length.out - 1)))
    • 上の方法だと1:0[1] 1 0を返すので安全でない.
    • seq(NULL)NULLを返すので安全.
  4. rep(y,times=n,each,length.out)
    • times=3n回繰り返す.
    • each=3とすると各要素を3回繰り返す.
    • length.out=3とすると出力の長さを制限
  • 要素ごとに代入したい場合は宣言が必要(ベクトルを指すと判明していないポインタに向かって indexing をするy[2]という書き方は禁忌).y <- vector(length=n)としてポインタを作ってから,y[1]<-5などと代入していく.
    • ベクトルの再割り当ては時間がかかるので,関数定義で返り値のためにベクトルの積み上げをしたい場合は,ret <- vector(length=n)とした方がいい.返す前にret <- ret[1:n]として未使用部分を無くすために再割り当てをして2回で済ませるのがいい.

2.2 単項演算 \(V\to V\)

  • 操作
    • rev(x):反転
    • t(x):転置
  • 表示
    • head(x[, n=6L])
    • tail(x[, n=6L])

2.3 index 付与:自分で index を変えられる.

  • names属性を付与できるが,リストにはならない.
    • names(x) <- :colnames か rownames か,どちらか適切な方.
    • names(x) <- NULL:削除.

2.4 単項演算 \(V\to K\):統計的特徴の抽出

  • 長さ:length(x)
    • 行列などに対しては,属性を外してベクトルと見て演算して,属性を戻すとかそういう感じ.
  • mean(a, na.rm=F):算術平均.na.rm=TNAがあっても適用可能になる.
  • sum(a):総和
  • prod(a):総積
  • max(a)
  • sd(x):standard deviation

2.5 単項演算 \(V\to2\):条件

  • all(expr):expr の結果である logical vector が,全てTかどうかを判定して長さ1ベクトルを返す
  • any(expr):expr の結果である logical vector が,Tが存在するかを判定して長さ1ベクトルを返す

2.6 indexing:ベクトルの構成法3つ:,c,seqに対応した indexing 法がある.

  • y[c(1,3,4)]:scaler は退化したベクトルなのでこれがあるべき姿
  • y[-c(1,3,4)]:除外は負数を用いる.y[-length(y)]は最後の要素の除外.y[-1:-2]は1,2番目の除外.
  • tail(x,[n=]1):右後ろから indexing

2.7 数値演算 \(V^2\to V\)

  • 同次元演算
    • a+b, a-b
    • a*b:Hadamard積,a/b
    • a %*% b:内積
    • %o%:外積
    • %/%:整数除算
    • a^b:冪で,**でも実行できるが非推奨.
    • %%:mod.情報落ちを検出したら警告が出るようになっている.
  • recycle:長さがどちらかの定数倍である時,演算を繰り返す.

2.8 文字列演算

  • paste(“str1”, “str2”):concatenation

  • strsplit(x, split, fixed = FALSE, perl = FALSE, useBytes = FALSE)

  • ベクトルは C の配列のように,連続的に格納されているので,要素の挿入や削除は新しく作るのと等価.実装は C の pointer と同じで,再代入はポインタの指す先を変えることで再割り当てを実現する.

3 行列

dim属性(ncolnrow)のついたベクトルで,縦に並んだベクトルをdim属性を参照しながら横にずらしながら構成される.

3.1 構成

  1. ベクトルへの属性付与による構成
    • ベクトルにdim属性を付与するコンストラクタmatrix([data=]a,[nrow=]m,[ncol=]n)
      • aが scaler の場合m×na-fill する,aがベクトルの場合m×nに fill していく.
      • fill の順番は列ごとで,byrow=TRUEとすると行ごとになる.
      • この逆変換はas.vector(A)
  2. ベクトルの結合による構成
    • rbind(a,b,c):行ベクトルとして結合,あるいは行列の横結合.
    • cbind(a,b,c):列ベクトルとして結合,あるいは行列の縦結合.
    • dim(x) <- c(n,m):ベクトルに次元のデータを加えて,行列に変換する.
  • メモリ確保はmatrix(nrow=a,ncol=b)でなされる.

3.2 操作

  • rownames(), colnames() <- name:名前をつけられる.
  • t(A):転置

3.3 indexing 周り

  • dim(A):返り値は長さ2のベクトル
  • nrow(A)=dim(A)[1]
  • ncol(A)=dim(A)[2]
  • A[行,列]:空欄を渡すと走る,長さ2以上のベクトルを渡すと複数選択
    • [,2]などでも行ベクトルで帰ってくる.これは全てのベクトルは横で表示されるから.
    • [n]だと,列を繋げた順序についての indexing で,要素が返ってくる.
  • 抽出した部分行列に値が部分代入できる.

3.4 一項演算

  • ノルム
    • norm(A [,type=c(“o”, ”I”, ”F”, ”M”, ”2”)])
      • one, infinity, flobenius = Eucleadean as a vector, maximum modulus, 2はspectral norm.
      • \(l^2\)-ノルムはsqrt(a %*% a)::matrixでいける
  • 正方行列
    • det(A)
    • sum(diag(A))\(\operatorname{Tr}\)

3.5 二項演算

  • 同次元演算

    • A+B, A-B
    • A*B, A/B:Hadamard積
    • A %*% B:行列積
  • 逆行列solve(A)

  • 一般化逆行列 (Moore-Penrose pseudoinverse)

    library(MASS)
    ginv(A)

4 generic operator

行列はベクトルなので,ほとんどの関数はベクトルだと思って作用する.属性を外してベクトルと見て演算して,属性を戻すとかそういう感じ.

4.1 ベクトルと行列で挙動が違う generic 単項関数 \(K^n\to V\)

  • diag(x):引数がベクトルなら対角行列の作成.diag(rep(1,n))=En.行列ならベクトルを返す.

4.2 ベクトルも行列も同等に扱う二項演算

  • +, -:長さが整数倍ならば,repしてから足す.
  • Hadamard積 *:scaler と行列ならば普段の積.%*%ではエラーになる.
  • %*%:これは数学でも generic よね.
    • xA=行ベクトルAx=列ベクトルと返り値が定まり,xの縦横に依らない.
  • solve(A,B)Bがベクトル \(b\) の時,\(Ax=b\) を解く.行列のときは \(AX=B\) を解く.
    • Bを省略すると対角行列とみなされ,したがって解は \(X=A^{-1}\)

4.3 数学関数はベクトル化されている.

  • sin(), exp(), abs(), sqrt(), log(), log10(), acos(), sinpi()
    • 1:10*2(1:10)*2だが,1:10^21:100
    • log([x=]b,[base=exp(1)])
    • acos:逆三角関数
    • sinpi(3/2)sin(pi*3/2)に同じ
  • round(), floor(), ceiling()
  • factorial()
  • x>3などの評価:条件評価も実際は関数.
    1. 3が recycle されてxと同じ長さになる.
    2. point-wise に評価が行われて,
    3. logical のベクトルを返す.
  • 複数のベクトルを値に取る関数
    • pmax()
    • cumsum(), cumprod()

4.4 関数のベクトル化

  • sapply(x,f)
    • fxの各要素に適用し,返り値を行列とする.
    • fがベクトルの長さを伸ばすような関数だった場合,行列化処理と合成するのが自然で,これを勝手にやってくれる.

5 配列

\(n\) 次元リストで,一番一般的なデータ構造.

  • 構成
    • array(data=NA, dim=length(data), dimnames=NULL)
      • dim=c(3,4,2)などができる.
      • dimnamesはもともと数字で,,,1という調子で切り出して表示される(list みたいな)