データの構造

これまでベクトルやデータフレームを利用してきた。これらは,Rのデータ構造の一つである。Rではほかにもいくつか知っておくと便利なデータ構造が用意されている。

Rのデータ構造には以下のようなものがある。

ここでは,ベクトル,行列,配列,リストについて説明する。データフレームについては,データフレームで説明する。

ベクトル

ベクトルについては,Rの基本でも述べたが,ここではもう少し詳しく説明する。

ベクトルの要素

ベクトルは数値,文字列,論理値 (TRUEFALSEの二値)を要素としてもつことができるが,ベクトルの要素はすべて同じ型でなければならない。たとえば,数値と文字列を混ぜてベクトルを作成すると,すべての要素が文字列に変換される。

vector <- c(1, 2, 3, "4", 5)
vector
[1] "1" "2" "3" "4" "5"
class(vector)
[1] "character"

同様に数値と論理値を混ぜてベクトルを作成すると,論理値は数値 (TRUEは1,FALSEは0)に変換される。

ベクトルの要素の操作

ベクトルの要素を取り出したいときには,ベクトル名の後に[]をつけて,[]内に取り出したい要素の番号 (インデックスという)を指定する。

v <- c(1, 2, 3, 4, 5)

# 3番目の要素を取り出す
v[3]
[1] 3
# 1番目から3番目の要素を取り出す
v[1:3]
[1] 1 2 3
# 1, 3, 5番目の要素を取り出す
v[c(1, 3, 5)]
[1] 1 3 5

[]を用いれば,ベクトルの特定の要素を変更することも可能。

v <- c(1, 2, 3, 4, 5)
v[3] <- 10
v
[1]  1  2 10  4  5

ベクトルの末尾に要素を追加するには,append()関数を用いる。

v <- c(1, 2, 3, 4, 5)
v <- append(v, 10)
v
[1]  1  2  3  4  5 10

または,length()関数を用いて,ベクトルの長さを取得し,その長さに1を足したものをインデックスとして指定して代入することも可能。

v <- c(1, 2, 3, 4, 5)
l <- length(v)
l
[1] 5
v[l + 1] <- 10
v
[1]  1  2  3  4  5 10

ベクトルの要素を削除するには,-を用いる。

v <- c(1, 2, 3, 4, 5)
v <- v[-3]
v
[1] 1 2 4 5

ベクトルを結合するには,c()で要素にベクトルを指定すれば良い。

v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6)
v <- c(v1, v2)
v
[1] 1 2 3 4 5 6

名前つきベクトル

ベクトルの各要素には名前をつけることができる。要素に名前をつけるには,以下のようにする。

# 作成する際に名前をつける
v1 <- c(
  "甲" = 1,
  "乙" = 2,
  "丙" = 3
)
v1
甲 乙 丙 
 1  2  3 
# names()を使う
v2 <- c(1, 2, 3)
names(v2) <- c("a", "b", "c")
v2
a b c 
1 2 3 

名前つきベクトルは,名前を使って要素を取り出すことができる。

v1["甲"]
甲 
 1 
v2["b"]
b 
2 

ベクトルの計算

長さが同じベクトルどうしの計算では,要素どうしの計算が行われる。

v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6)

v1 + v2
[1] 5 7 9
v1 / v2
[1] 0.25 0.40 0.50

長さが異なるベクトルどうしの計算では,短い方のベクトルの要素が繰り返し使われる。

v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6, 7, 8, 9)

# vector1がc(c(1, 2, 3), c(1, 2, 3))として計算される
v1 - v2
[1] -3 -3 -3 -6 -6 -6
v1 * v2
[1]  4 10 18  7 16 27
# vector2の長さがvector1の長さの整数倍でなくても計算はできるが,警告が出る
## この場合は,vector1がc(1, 2, 3, 10, 1, 2)として計算される
v1 <- append(v1, 10)
v1 - v2
Warning in v1 - v2: longer object length is not a multiple of shorter object
length
[1] -3 -3 -3  3 -7 -7
v1 * v2
Warning in v1 * v2: longer object length is not a multiple of shorter object
length
[1]  4 10 18 70  8 18

したがって,ベクトルとスカラー (長さが1のベクトル)の計算では,ベクトルの各要素に対して計算が行われる。

v <- c(1, 2, 3, 4, 5)
v * 2
[1]  2  4  6  8 10
v * 2
[1]  2  4  6  8 10

行列

行列の作成

行列もすべての要素が同じ型でなければならない。

行列を作成するにはmatrix()関数を用いる。matrix()関数は第1引数にベクトル,第2引数にnrowで行数,またはncolで列数を指定する。

m1 <-matrix(1:12, nrow = 3)
m1
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
m2 <-matrix(1:12, ncol = 4)
m2
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

引数で与えたベクトルの要素が順番に行数の分ずつ取り出され,行列の列になる。これは,nrowを指定してもncolを指定しても同じである。ベクトルの要素を順番に列数の分ずつ取り出して,行列の行にしたい場合には,byrow = TRUEを指定する。

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

行列の要素を取り出したいときには,[行番号, 列番号]を用いる。

m <-matrix(1:12, nrow = 3)
m[1, 1]
[1] 1
m[3, 4]
[1] 12

行番号もしくは列番号のいずれか一方だけを指定すれば,行ベクトルもしくは列ベクトルを取り出すことができる。

m <-matrix(1:12, nrow = 3)

# 2行目の行ベクトルを取り出す
## 行を指定したことがわかるように,[2, ]のように
## 行番号のあとにカンマをつける必要があることに注意
m[2, ]
[1]  2  5  8 11
# 3列目の列ベクトルを取り出す
m[, 3]
[1] 7 8 9

ベクトルと同様に[]を用いれば,行列の特定の要素を変更することもできる。

行名,列名の設定

行列は,行と列に名前をつけることができる。行の名前はrownames(),列の名前はcolnamesで設定できる。行列の要素は名前を使って取り出すことができる。

m <-matrix(1:12, nrow = 3)

rownames(m) <- c("甲", "乙", "丙")
colnames(m) <- c("A", "B", "C", "D")
m
   A B C  D
甲 1 4 7 10
乙 2 5 8 11
丙 3 6 9 12
# 「甲」行を取り出す
m["甲", ]
 A  B  C  D 
 1  4  7 10 
# 「C」列を取り出す
m[, "C"]
甲 乙 丙 
 7  8  9 
# 「乙」行の「B」列の要素を取り出す
m["乙", "B"]
[1] 5

ベクトル・行列の結合

ベクトルや行列を結合するには,cbind()関数やrbind()関数を用いる。cbind()は列方向の結合,rbind()は行方向の結合を行う。たとえば,各ベクトルを行列の列として結合するには,cbind(),各ベクトルを行列の行として結合するには,rbind()を用いる。

v1 <- c(1, 2, 3)
v2 <- c(4, 5, 6)

# ベクトルの結合
v3 <- cbind(v1, v2)
v3
     v1 v2
[1,]  1  4
[2,]  2  5
[3,]  3  6
v4 <-rbind(v1, v2)
v4
   [,1] [,2] [,3]
v1    1    2    3
v2    4    5    6
# 行列の結合
rbind(v3, v3)
     v1 v2
[1,]  1  4
[2,]  2  5
[3,]  3  6
[4,]  1  4
[5,]  2  5
[6,]  3  6
cbind(v4, v4)
   [,1] [,2] [,3] [,4] [,5] [,6]
v1    1    2    3    1    2    3
v2    4    5    6    4    5    6
# rbind()では列数が違うと,cbind()は行数が違うとエラーになる
cbind(v3, v4)
Error in cbind(v3, v4): number of rows of matrices must match (see arg 2)
rbind(v3, v4)
Error in rbind(v3, v4): number of columns of matrices must match (see arg 2)

行列の計算

行列どうしで+, -, *, /といった演算を行うと,ベクトルの場合と同様に行列の要素どうしの計算となる。次元が異なる行列どうしの計算はできない (エラーになる)。

m1 <-matrix(1:12, nrow = 3)
m2 <-matrix(13:24, nrow = 3)

m1 + m2
     [,1] [,2] [,3] [,4]
[1,]   14   20   26   32
[2,]   16   22   28   34
[3,]   18   24   30   36
m1 * m2
     [,1] [,2] [,3] [,4]
[1,]   13   64  133  220
[2,]   28   85  160  253
[3,]   45  108  189  288

ここからは,いわゆる線形代数のオペレーション。

行列の転置は,t()関数で行うことができる。

m <-matrix(1:12, nrow = 3)
m
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
t(m)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12

行列の積を求めるには,%*%を用いる。

m1 <-matrix(1:12, nrow = 3)
m2 <-matrix(13:24, nrow = 4)

m1 %*% m2
     [,1] [,2] [,3]
[1,]  334  422  510
[2,]  392  496  600
[3,]  450  570  690

逆行列を求めるには,solve()関数を用いる。

m <-matrix(
    c(1, 4, 2, 3),
    nrow = 2
)
solve(m)
     [,1] [,2]
[1,] -0.6  0.4
[2,]  0.8 -0.2

例として,最小二乗法をやってみよう。

y <- c(2, 6, 4, 3, 8)
x1 <- c(1, 2, 2, 1, 2)
x2 <- c(2, 1, 2, 4, 5)
c <- c(1, 1, 1, 1, 1)

# X行列の作成
X <- cbind(c, x1, x2)
X
     c x1 x2
[1,] 1  1  2
[2,] 1  2  1
[3,] 1  2  2
[4,] 1  1  4
[5,] 1  2  5
# 最小二乗法の係数を求める
beta <- solve(t(X) %*% X) %*% t(X) %*% y
beta
       [,1]
c  -3.18750
x1  3.71875
x2  0.65625
# 予測値を求める
y_hat <- X %*% beta
y_hat
        [,1]
[1,] 1.84375
[2,] 4.90625
[3,] 5.56250
[4,] 3.15625
[5,] 7.53125

配列

配列は行列を多次元に拡張したもの,というより,正しくは2次元の配列が行列。

a <- array(1:24, dim = c(2, 3, 4))
a
, , 1

     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

, , 2

     [,1] [,2] [,3]
[1,]    7    9   11
[2,]    8   10   12

, , 3

     [,1] [,2] [,3]
[1,]   13   15   17
[2,]   14   16   18

, , 4

     [,1] [,2] [,3]
[1,]   19   21   23
[2,]   20   22   24

リスト

リストは,異なる構造のデータを束ねることができるオブジェクトである。リストは,ベクトル,行列はもちろん,リストも要素とすることができる。

l <- list(
  4:1,
  c("a", "b", "c"),
  matrix(1:6, nrow = 2)
)

l
[[1]]
[1] 4 3 2 1

[[2]]
[1] "a" "b" "c"

[[3]]
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

この例では,2つのベクトルと1つの行列を要素にもつlというリストを作成している。長さが異なるベクトルも,次元が異なる行列もすべて1つのオブジェクトにまとめることができる。

リストから要素を取り出すには,[[ ]]でインデックスを指定する。

l[[1]]
[1] 4 3 2 1
l[[2]]
[1] "a" "b" "c"
l[[3]]
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

[ ]でインデックスを指定した場合は,リストが返されることに注意しよう。たとえば,l[2]lの2番目の要素をもつ長さが1のリストを表す。

l[2]
[[1]]
[1] "a" "b" "c"
l[[c(1, 3)]]
[1] 2

リストの中にリストを入れることもできる。

l2 <- list(
  l,
  c("A", "B", "C")
)

l2
[[1]]
[[1]][[1]]
[1] 4 3 2 1

[[1]][[2]]
[1] "a" "b" "c"

[[1]][[3]]
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6


[[2]]
[1] "A" "B" "C"
l2[[1]]
[[1]]
[1] 4 3 2 1

[[2]]
[1] "a" "b" "c"

[[3]]
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
l2[[2]]
[1] "A" "B" "C"

リストの要素には名前をつけることができる。

l3 <- list(
  x = 1:5,
  y = c("a", "b", "c"),
  z = matrix(1:6, nrow = 2)
)

l3
$x
[1] 1 2 3 4 5

$y
[1] "a" "b" "c"

$z
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

名前つきのリストの要素は,$で取り出すことができる。

l3$x
[1] 1 2 3 4 5
l3$y
[1] "a" "b" "c"
l3$z
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

また,リストの名前はnames()関数で取得できる。

names(l3)
[1] "x" "y" "z"