# リスト・データフレーム・行列
```{r, setup, include=FALSE, echo=FALSE}
knitr::opts_chunk$set(
collapse = TRUE
)
```
## リスト(list)
**リスト(list)**は様々なデータ型・クラスを一つにまとめたものです。リストの要素となるのは、ベクター、リスト、データフレーム、行列などで、どのようなデータ型・クラスであってもリストの要素にすることができます。
リストの作成には、`list` 関数を用います。`list` 関数の引数がリストの要素となります。
ベクターと同様に、リストの要素には**名前(names)**をつけることができ、要素を名前から呼び出すことができます。名前からの呼び出しでは、ベクターと同じように四角カッコ(`[ ]` )内に文字列を記入して呼び出してもよいですし、`$` (ドルマーク)を用いて呼び出すこともできます。ただし、`[ ]` と`$` では、返り値の型が異なります(`[ ]` ではリスト、`$` では要素が返ってくる)。
名前の確認・設定はベクターと同じ方法で行います。リストの名前の確認・変更には、`names` 関数を用います。`names` 関数の引数にリストを取ると、そのリストの名前が返ってきます。`names` 関数の引数にリストを取り、文字列ベクターを代入するとリストの名前を設定できます。
```{r, filename="リストの作成と名前"}
# リストの作成(=の左がnames、右が要素)
lst <- list(x = c(1, 2, 3, 4), y = "dog", z = c(T, F, T, T, F))
lst # 名前付きリストを表示する
names(lst) # リストの名前を表示する
names(lst) <- c("a", "b", "c") # リストの名前を変更する
names(lst) # 変更後の名前
lst["a"] # 名前での要素の呼び出し(リストが返ってくる)
lst$a # ドルマーク($)を用いた呼び出し(要素が返ってくる)
```
### リストのインデックス
リストのインデックスは、基本的には**二重角カッコ(`[[ ]]`)**で指定します。通常の角カッコ(`[ ]`)で指定すると、**リストの要素がリストのまま**返ってきます。角カッコ(`[ ]` )で返ってくるのはリストですので、コロンを用いて複数の要素をリストとして取り出すことができます。
リストの要素(ベクターや行列)のさらに要素を取り出すには、二重角カッコの後に、ベクターや行列に対応したインデックス指定(`[1]` や`[1, 2]` など)をつけることになります。
```{r, filename="リストのインデックス"}
lst[1] # リストの1番目の要素(リストが返ってくる)
lst[[1]] # リストの1番目の要素(要素が返ってくる)
lst[1:2] # リストの1~2番目の要素(リストが返ってくる)
lst[[1]][2] # リストの1番目の要素(ベクター)の2つ目の要素
```
### リストの長さ(length)
リストは、名前(names)の他に、**長さ(length)**の特性を持ちます。リストのlengthはリストの要素の数です。リストのlengthもベクターと同じく、`length` 関数で確認することができます。
リストのアトリビュートは`attributes` 関数で確認することができます。アトリビュートを`attr` 関数で別途追加しない場合には、アトリビュートとしては`names` だけが表示されます。各要素のデータ型は`str` 関数や`summary` 関数を用いて確認することができます。
```{r, filename="リストの長さとデータ型"}
length(lst)
attributes(lst)
str(lst)
summary(lst)
```
### リストへの要素の追加
`c` 関数を用いることで、リストに要素を追加することができます。また、リストに要素を追加する場合には、ドルマークを用いて設定されていない名前を指定し、代入を行うこともできます。設定されている名前を用いて代入した場合には、その要素が書き換えられます。
```{r, filename="リストへの要素の追加"}
lst <- c(lst, d = "added list") # リストに名前dの要素を追加
lst
# リストに名前eの要素を追加(データフレームにも使える)
lst$e <- "object can be added with named index" # 名前eの要素を追加
lst$d <- "revised list" # dの要素を変更
lst # 追加・変更後のリスト
```
### リストのベクター化
リストを全部まとめて1つのベクターにする場合には、`unlist` 関数を用います。`unlist` 関数はリストの要素をすべて1次元のベクターに変更します。ベクターに変更すると、ベクターの型が要素の型によって変更されるので、注意が必要です。`unlist` 関数はリストだけでなく、データフレームの行のデータをベクターにするのにも用いられます。
```{r, filename="リストのベクター化"}
unlist(lst[1:3]) # 文字列のベクターに変換される
```
## データフレーム(data.frame)
Rで最も使用頻度が高いクラスの一つが**データフレーム(data.frame)**です。データフレームはExcelの表のような構造を持つクラスで、`data.frame` 関数を用いて作成することができます。`data.frame` 関数の引数は各列のベクターで、各列の名前を引数で設定することができます(リストと同じく、「列名=ベクター」という形で設定)。
データフレームは、**同じ長さのベクターを列に取ったリスト**です。ですので、`class` 関数ではデータフレーム、`mode` 関数ではリストが返ってきます。
データフレームの作成時に、長さの異なるベクターを用いた場合には、**反復(recycling)**のルールに従い、短いベクターが長いベクターの長さに合わせて反復されます。
```{r, error=TRUE, filename="データフレームを作成する"}
# データフレームをdata.frame関数で作成する
d <- data.frame(x = c("dog", "cat", "pig", "horse"), y = c(1, 2, 3, 4), z = c(T, T, F, T))
d
class(d) # classはdata.frame
mode(d) # modeはlist
# 長さが異なるベクターを用いると、反復が適用される
d2 <- data.frame(x = c("dog", "cat"), y = c(1, 2, 3, 4), z = T)
d2
```
### リストからデータフレームを作成する
データフレームはリストと同じですので、長さが同じベクターからなるリストはそのままデータフレームに変換できます。データフレームへの変換には`as.data.frame` 関数を用います。この変換は、`data.frame` 関数を用いても行うことができます。
長さが異なるベクターや、ベクター以外の要素を含むリストをデータフレームに変換しようとすると、エラーが出ます。反復は行われません。
```{r, error=TRUE, filename="リストをデータフレームに変換"}
# 長さが同じベクターのリストは、そのままデータフレームに変換できる
lst <- list(x = c("dog", "cat", "pig", "horse"), y = c(1, 2, 3, 4), z = c(T, T, F, T))
as.data.frame(lst)
data.frame(lst)
# 長さが異なるベクターのリストは、データフレームに変換できない
lst2 <- list(x = c(1, 2, 3, 4), y = "dog", z = c(T, F, T, T, F))
as.data.frame(lst2) # エラー
```
### データフレームのインデックス
データフレームはリストではありますが、行(横方向)と列(縦方向)を持つ、表の形で表されます。データフレームは表型のデータですので、行・列の2つの**次元(dimension)**を持ちます。
データフレームの要素をインデックスで指定する場合には、**`[行, 列]`**という形を用います。このとき、インデックスの数値は行列共に1行目が1、1列目が1となります。インデックスは数値のベクターで指定することもできます。数値のベクターで指定した場合には、ベクターに記載した順番に行・列を取り出すことになります。
ベクターと同様に、データフレームのインデックスは**コロン(`:`)を用いた連続整数**の形でも指定できますし、**論理型(`TRUE`と`FALSE`)のベクター**を用いても指定できます。論理型のベクターの長さが行数・列数に足りない場合には、ベクターが反復されます。**マイナスのインデックスを用いた場合には、指定した行・列が削除**されます。
インデックスでデータフレームの列のみを指定した場合には、返り値としてその列のベクターが返ってきます。一方、**行のみを指定した場合には、ベクターではなく、その行がデータフレーム**として返ってきます。行をベクターに変換するには、`unlist` 関数を用います。
データフレームのインデックスの指定時には、コンマで行列を区切らず、1つのインデックスだけで指定することもできます。インデックスを1つだけ指定した場合には、ベクターではなく、データフレームが返ってきます。
```{r, filename="データフレームのインデックス"}
d1 <- data.frame(a = 1:6, b = seq(4, 6.5, by = 0.5), c = rep(1, 6))
d1
d1[1, 1] # 1行1列目の要素を取り出す
d1[1:3, 1] # 1~3行目、1列目の要素を取り出す(ベクター)
d1[c(5, 2, 1), ] # 5行目、2行目、1行目を取り出す
d1[1, ] # 1行目の要素を取り出す(データフレーム)
unlist(d1[1, ]) # 1行目の要素を取り出して、ベクターに変換する
d1[2:3, ] # 2~3行目の要素を取り出す(データフレーム)
d1[, 2:3] # 2~3列目の要素を取り出す(データフレーム)
d1[c(T, T, F, T, F, F), ] # 論理型もインデックスに使用できる
d1[c(T, T, F), ] # 反復
d1[, c(T, T, F)] # 列のインデックスにも論理型を使用できる
d1[-1:-2, ] # 1~2行目を削除
d1[, -1:-2] # 1~2列目を削除
d1[1] # 1列目を取り出す(データフレーム)
class(d1[1]) # 行列で指定([,1])しないと、データフレームが返ってくる
```
### データフレームの行数・列数
データフレームには、行方向と列方向の長さがあります。この行方向と列方向の長さを返すのが、`dim` 関数、`nrow` 関数、`ncol` 関数です。`dim` 関数は、データフレームを引数に取り、行数,列数の2つの値のベクターを返します。`nrow` 関数はデータフレームの行数を、`ncol` 関数はデータフレームの列数を返す関数です。
```{r, filename="データフレームの行・列数"}
d # dは4行3列のデータフレーム
dim(d) # 4行3列なので、4と3が返ってくる
nrow(d) # 行数が返ってくる
ncol(d) # 列数が返ってくる
```
### データフレームの名前
データフレームの各行・各列には、それぞれ名前をつけることができます。データフレームの**行の名前は`rownames`関数、列の名前は`colnames`関数**で求めることができます。
列名は`data.frame` 関数でデータフレームを作成するときにつけることができます。また、ベクターと同様に、**`colnames`関数に列名を代入する**形でも列名をつけることができます。
行名をつける場合には、**`rownames`関数に行名を代入**します。特に行名を指定していない場合には、行の番号が1, 2, 3,...といった形で行名として設定されます。
行名・列名のいずれもインデックスとして利用することができます。特に**列名は、`$`(ドルマーク)を用いた形**で列の取り出しに用いることができます。
```{r, filename="データフレームの行名・列名"}
d
colnames(d) # 列名
colnames(d) <- c("a", "b", "c") # 列名を変更する
rownames(d) # 行名
rownames(d) <- c("x", "y", "z", "aa") # 行名を変更する
d["x", ] # 行名をインデックスに用いることもできる
d[, "a"] # 列名もインデックス指定に使うことができる
d$a # ドルマークを使って列を指定することもできる
```
データフレームにおいても、インデックスに論理型のベクターを使用することができるため、**列名と比較演算子を利用して、行を選択する**ことができます。
インデックスでの比較演算子の利用と同様に条件によって行を選択する際に用いる関数が`subset` 関数です。`subset` 関数は第一引数にデータフレーム、第二引数に比較演算子を用いた条件式を記載することで、条件に合った行のみを取り出すことができます。
行のデータを利用して論理型ベクターを作成し、列を選択する事もできます。
```{r, 比較演算子を用いた行・列の選択}
d1
d1$b > 5 # b列が5以上ならTRUE
d1[d1$b > 5, ] # b列が5以上の行を選択
subset(d1, d1$b > 5) # 上と同じ行の選択をsubset関数で行う
d1[1, ] > 3 # 1行目の値が3以上ならTRUE
d1[, d1[1, ] > 3] # 1行目が3以上になる列(2列目)を選択
```
### データフレームの並べ替え
データフレームの並べ替えには、`order` 関数を用います。`order` 関数はベクターの要素の順番に数値を付け、返すだけの関数です。データフレームのインデックスはベクターを取ることができ、ベクターに記載した数値の順番に行を取得します。ですので、`order` 関数の返り値を行のインデックスとして用いると、`order` 関数の返り値の通りにデータフレームが並べ替えられます。
`order` 関数には`decreasing` (降順)という引数を設定できます。`decreasing` のデフォルト値は`FALSE` で、通常は`order` 関数を用いた並べかえは昇順(一番小さいものが一番上)になります。`decreasing` を`TRUE` に設定すると、`order` 関数の結果が逆順になります。したがって、`decreasing=TRUE` とした場合には、データフレームを降順(一番大きなものが一番上)に並べ替えることができます。
`rev` 関数は引数にベクターを取り、ベクターを逆順に変換する関数です。この`rev` 関数を用いても、データフレームを降順に並べ替えることができます。
```{r, filename="データフレームの並べ替え"}
d <- data.frame(x = c("dog", "cat", "pig", "horse"), y = c(1, 2, 3, 4), z = c(T, T, F, T))
order(d$x) # x列の順序を返す(文字列はアルファベット順)
d[order(d$x),] # x列を昇順に並べ替え
order(d$x, decreasing=TRUE)
d[order(d$x, decreasing=TRUE), ] # x列を降順に並べ替え
d[rev(order(d$x)), ] # rev関数でもベクターを逆順にできる
```
:::{.callout-tip collapse="true"}
## rank関数で並べ替え
`order` 関数の代わりに`rank` 関数を用いることもできます。ただし、`rank` 関数はデフォルトではタイデータ(同じ値のデータ)に整数で順位を付けないので(2.5位が2つなど)、`order` 関数の方が使い勝手はよいでしょう。
:::
### データフレームに行・列を追加する
データフレームに列を追加する方法はいくつかあります。
- `$` (ドルマーク)に列名に使われていない名前を指定し、ベクターを代入する
- 現在の列数+1のインデックスを指定し、ベクターを代入する
- `cbind` 関数を用いる
`$` と新しい列名を用いて、ベクターを代入した場合には、最も右側にその列名を持つ列が追加されます。数値のインデックスで指定した場合には、Vにインデックスが付いた列名(V1、V2など)が自動的に設定され、列が追加されます。`cbind` 関数を用いた場合には、1つ目の引数(データフレーム)に2つ目の引数(ベクターもしくはデータフレーム)が右側から追加されます。
データフレームに行を追加する方法もほぼ同じです。行を追加する場合には、`cbind` 関数ではなく、`rbind` 関数を用います。
ただし、データフレームに行をベクターで追加する場合には、**追加した行に従い各列のデータ型が変化**します。データ型の変換を起こさないように行を追加するには、追加する行がデータフレームである必要があります。さらに、追加するデータフレームの列名が元のデータフレームの列名と一致する必要があります。
データフレームに行を追加すると予期せぬ型変換が起こり、間違いの原因となりますので、行の追加を行うときには型を逐次確認することをおすすめします。
```{r, filename="列・行の追加"}
d3 <- d
d3
d3$newCol <- 5:8
d3[, 5] <- 10:13
d3[5, ] <- c("rat", 6, T, 9, 14) # 各列の型に沿ったベクターを追加
summary(d3) # すべての列が文字列になる
d
cbind(d, 1:4) # 列を追加
rbind(d, 1:3) # 行を追加する(列のデータ型が変化する)
# データフレームを追加する場合は、列名が同じである必要がある
rbind(d, data.frame(x = "pig", y = 1, z = TRUE))
```
### データフレームの要約
データフレームの各列の要約を調べる場合には、ベクターなどと同様に、`summary` 関数を用います。`summary` 関数はデータフレームを引数に取ると、列が文字列の場合にはデータ型と要素の数、論理型や因子の場合はそのレベルごとの要素の数、数値型であれば平均値や中央値、最大値・最小値などを示してくれます。
データフレームの列方向の合計値を求める場合には`colSums` 関数を、行方向の合計値を求める場合には`rowSums` 関数を用います。データフレームに文字列の列がある場合には、`colSums` 関数・`rowSums` 関数はエラーを返します。
同様の関数に、`colMeans` 関数、`rowMeans` 関数もあります。これらはそれぞれ列方向・行方向の平均値を計算する関数です。
データフレームの一部を確認したいときには、ベクターと同様に`head` 関数と`tail` 関数を用いることができます。`head` 関数はデータフレームの上から6行を、`tail` 関数はデータフレームの下から6行をそれぞれ表示します。
```{r, error=TRUE}
summary(d) # データフレームの要約
d1
colSums(d1) # 列方向に合計した値
colSums(d) # 数値以外が含まれているとエラーになる
rowSums(d1) # 行方向に合計した値
colMeans(d1) # 列方向の平均値
rowMeans(d1) # 行方向の平均値
head(d1) # データフレームの上から6行を表示
tail(d1) # データフレームの下から6行を表示
```
```{r, echo=FALSE}
d <- data.frame(
func = c("data.frame(x, y)", "dim(x)", "ncol(x)", "nrow(x)", "colnames(x)", "rownames(x)", "colnames(x) <- y", "rownames(x) <- y", "subset(x, 条件)", "x[order(x$name), ]", "x[rev(order(x$name)), ]", "cbind(x, y)", "rbind(x, y)", "summary(x)", "colSums(x)", "rowSums(x)", "colMeans(x)", "rowMeans(x)", "head(x)", "tail(x)"),
meaning = c("データフレームを作成する", "次元(dimension)を返す","列数を返す", "行数を返す", "列の名前を返す", "行の名前を返す", "yを列の名前に設定する", "yを行の名前に設定する", "条件に適合した行を抽出する", "name列で昇順に並べ替え", "name列で降順に並べ替え", "列を追加する", "行を追加する", "データフレームの要約を表示", "列(縦)方向の和を返す", "行(横)方向の和を返す", "列(縦)方向の平均値を返す", "行(横)方向の平均値を返す", "データフレームの上から6行を表示", "データフレームの下から6行を表示")
)
colnames(d) <- c("関数名", "データフレームに適用する演算")
knitr::kable(d, caption="表1:データフレームに関する関数")
```
:::{.callout-tip collapse="true"}
## データフレームの要約
もっと複雑なデータフレームの要約を行う場合には、`apply` 関数群や`dplyr` パッケージ [ @dplyr_bib ] 、`tidyr` パッケージ [ @tidyr_bib ] などを用います。`apply` 関数群、`dplyr` 、`tidyr` に関しては[ 15章 ](./chapter15.html) 、[ 16章 ](./chapter16.html) でそれぞれ紹介します。
:::
### 組み合わせのデータフレームを作成する
様々な要素について、すべての組み合わせを含むデータを作りたい、という場合が時にあります。このような場合には、`expand.grid` 関数を用いると簡単に組み合わせのデータフレームを作成することができます。`expand.grid` 関数は`data.frame` 関数と同じように引数を設定し、引数に設定したベクターのすべての組み合わせを含むデータフレームを作成してくれます。
```{r, filename="expand.grid関数"}
expand.grid(sex=c("M", "F"), age=c(20,25), location=c("Osaka", "Kobe"))
```
## 行列(matrix)
Rは高校数学で学んだ行列、線形代数の行列計算を行うためのクラスである、**行列(matrix)**を持ちます。行列はベクターと同様に同じ型を持つ要素の集合で、行と列の2つの**次元(dimension)**を持ちます。
データフレームも行列と同様に次元を持ちますが、データフレームが行方向には異なるデータ型を取ることができるのに対して、行列はすべての要素のデータ型が同じです。また、データフレームがリストであるのに対し、行列は縦・横の2つの次元を持つベクターです。
行列の基本的な取り扱い(インデックス、行・列の追加、行名・列名)はデータフレームとほぼ同じです。上に述べたデータフレームの取り扱いに関する方法は、概ね行列にも適用することができます。
行列の作成には、`matrix` 関数を用います。`matrix` 関数は第一引数にベクター、`nrow` 引数に行の数、`ncol` 引数に列の数を指定します。`matrix` 関数はベクターの数値を縦方向(列方向)に並べて配置します。数値を横(行方向)に並べて配置する場合には、`byrow` 引数を`TRUE` に指定します。
```{r, error=TRUE, filename="行列の作成とインデックス"}
mat <- matrix(1:12, nrow = 3, ncol = 4) # 列方向にベクターが配置される
mat
matrix(1:12, nrow = 3, ncol = 4, byrow = TRUE) # 行方向に配置するとき
dim(mat) # 行数・列数を返す
nrow(mat) # 行数
ncol(mat) # 列数
colnames(mat) <- c("dog", "cat", "pig", "horse") # 列名の設定
rownames(mat) <- c("first", "second", "third") # 行名の設定
mat
colnames(mat) # 列名の取得
rownames(mat) # 行名の取得
mat[1, 1] # 1行1列目の要素
mat[1, ] # 1行目の要素
mat[, 1] # 1列目の要素
mat[-1, ] # 1行目を削除
mat[c(T, T, F), ] # 論理型でも指定できる
mat[1:2, ] # 1~2行目の要素
mat[, "dog"] # インデックスは列名でも指定できる
mat$dog # ドルマークを用いることはできない
mat["first", ] # 行名での指定
cbind(mat, rat = c(4, 6, 8)) # 列の追加
rbind(mat, fourth = c(1, 1, 1, 1)) # 行の追加
```
:::{.callout-tip collapse="true"}
## 3次元以上のクラス:array
Rには、次元を3つ以上持つクラスである、arrayも存在しますが、matrixほどには使用頻度は高くありません。
:::
### 行列の演算
統計の計算には、線形代数(行列)が用いられます。Rには行列の演算に関する関数が一通り揃っています。
```{r, echo=FALSE}
d <- data.frame(
func = c("*", "%*%", "%o%", "%x%", "diag", "det", "t", "upper.tri", "lower.tri", "solve", "qr"),
meaning = c("スカラー積", "行列の積", "外積", "クロネッカー積", "単位行列を作成する", "行列式を返す", "転置を返す", "上三角行列にTRUEを返す", "下三角行列にTRUEを返す", "逆行列を返す", "QR分解")
)
colnames(d) <- c("関数名", "行列に適用する演算")
knitr::kable(d, caption="表2:行列に関する関数")
```
### 行列の積
Rでは、`%*%` を演算子として行列の積の計算を行います。単に「`*` 」で計算すると、行列の積ではなく、スカラー積が返ってきます。行列同士を「`*` 」で掛け算した場合には、行列の各要素がそれぞれ掛け算されたもの(アダマール積)が返ってきます。行列の演算子として、他に外積(`%o%` )やクロネッカー積(`%x%` )の演算子も設定されています。
```{r, echo=FALSE}
set.seed(1)
```
```{r, filename="行列の演算子"}
# 要素が1~16のランダムな行列を作成
mat <- matrix(sample(1:16, 16), nrow=4)
mat
mat * 3 # スカラー積
mat * mat # アダマール積
mat %*% mat # 行列の積
mat[, 1] %o% mat[, 2] # 外積
mat[1:2, 1:2] %x% mat[3:4, 3:4] # クロネッカー積
```
### 行列式・転置・逆行列の演算
行列式は`det` 関数、行列の転置は`t` 関数、逆行列は`solve` 関数で求めることができます。`t` 関数は行列だけでなく、データフレームを引数に取ることができますが、**`t`関数の返り値は必ず行列**になります。データフレームを`t` 関数の引数にして転置する場合にはデータ型が変換されることが多いため、注意が必要です。
```{r, filename="行列式・転置・逆行列"}
det(mat) # matの行列式
t(mat) # matを転置
solve(mat) # matの逆行列
mat %*% solve(mat) # 丸め誤差はあるが、単位行列になる
```
### 単位行列・三角行列の作成
`diag` 関数は単位行列を作成するための関数です。`diag` 関数は数値を引数に取り、その数値の行数・列数の単位行列を返します。
三角行列の作成には、`upper.tri` 関数と`lower.tri` 関数を用います。この2つの関数は、行列を引数に取り、行列と同じ行数・列数の論理型の上三角行列(上が`TRUE` 、下三角が`FALSE` の行列)、下三角行列(下が`TRUE` 、上三角が`FALSE` の行列)をそれぞれ返します。いずれも対角成分は`FALSE` となります。
ただし、`upper.tri` 関数、`lower.tri` 関数の返り値をそのまま行列のインデックスに用いても上三角行列・下三角行列を作ることはできません。上三角行列を作成する場合には、`lower.tri` 関数の返り値をインデックスに取り、このインデックスに0を代入します。
逆に、下三角行列を作成する場合には、`upper.tri` 関数の返り値をインデックスに取り、このインデックスに0を代入します。このように0を下三角・上三角に代入することで、上三角行列・下三角行列を得ることができます。
```{r}
diag (3 ) # 3行3列の単位行列
upper.tri (mat) # 上三角行列にTRUEを返す
lower.tri (mat) # 下三角行列にTRUEを返す
mat.u <- mat
mat.l <- mat
mat.u[lower.tri (mat)] <- 0 # 下三角行列のみに0を代入
mat.l[upper.tri (mat)] <- 0 # 上三角行列のみに0を代入
mat.u # 下三角に0を代入すると上三角行列が得られる
mat.l # 上三角に0を代入すると下三角行列が得られる
```