Rのデータフレームは、リストのように異なる型をまとめることができ、行列のように参照や代入ができる、統計解析によく適したデータ構造です。表の型式でデータを保持できます。標準で使えるため、ビルトイン関数などで広く用いられています。
1 リストとの違い
データフレームの便利さを理解するために、リストの不便さを理解しましょう。リストをつくって、i行j列を参照してみるとします。
with(new.env(), {
lst01 <- 100
n <- runif(n)
x <- runif(n)
z <- round(runif(n, min = 0.5, max = 3.5))
kn <- as.factor(letters[kn])
k <- 1 + x - z + (kn==2)*1 + rnorm(n)
y <- list(y, x, z, k)
r <-names(r) <- c("y", "x", "z", "k")
r })
慣れていれば
2; j <- 3
i <- lst01[[j]][i]
[1] 0.3167571
$z[i] lst01
[1] 0.3167571
行列のようにlst01[i, j]
と言う風には参照できません。\(i\)行目のサブセットをつくろうと思うと、lst01[i, ]
と言う風には参照できないので、
sapply(names(lst01), \(cn){
lst01[[cn]][i] })
y x z k
0.2543368 0.2747747 0.3167571 1.0000000
と書くことになります。代入も同様で、気軽にできません。
c(0.78, 0.92, 0.19, "c")
X <-for(j in 1:length(lst01)){
X[j]
lst01[[j]][i] <- }
データフレームは内部的にはリストの拡張となっているので、リストは簡単にデータフレームに変換できます。変換すると
as.data.frame(lst01)
df01 <- df01[i, j]
[1] c
Levels: a b c
df01[i, ]
y x z k
2 0.78 0.92 0.19 c
X df01[i, ] <-
という風に、行列と同様に簡潔に操作ができるようになります。
ウィンドウ・アプリケーション開発などではリスト構造のデータが便利だったりするのですが、そのままでは統計解析では煩雑で、データフレームでは便利です。1990年代ではSとそのクローンであるRぐらいでしか見られないものですが、近年ではR以外のプログラミング言語でも統計解析を行うパッケージなどではデータフレームを実装するようになっており、コンセプトの正しさが示されています。
1.1 データフレームの欠点
リストは要素としてリストやデータフレームを入れられますが、データフレームはデータ構造を入れることができません。
2 行列との違い
行列と同様に操作ができるようになっており、nrow
,ncol
,rbind
,cbind
といった関数もそのまま使えます。一方、行列は一次元ベクトルに属性で次元の情報を加えたものなので、一次元ベクトルとして操作することもできますが、データフレームはリストに属性をつけたものなのでそれは出来ません。
3 デーフレームの作成
上の例ではリストをas.dataframe
でデータフレーム化しましたが、ベクトルをつないで作ることもできます。
data.frame(k = c("a", "b", "c"), v = c(1, 10, 100)) df02 <-
4 ファイルI/O
データフレームは表なので、CSVファイルやタブ区切りのファイルでの保存が可能です1。逆に、CSVファイルやタブ区切りのファイルを読み込むのに過不足ない機能を提供します。リストは複雑な構造にもなりうるためできません。行列もデータ型が一種類になるためできません。統計データの多くは表形式のテキストファイル型式で提供されることもあり、RのファイルI/Oではデータフレームが多く用いられることになります2。
4.1 テキストファイルに保存
テキストファイルとしてデータフレームを保存する場合は、write.table
を使います。
write.table(df01, file = "df01.csv", sep = ",", row.names = FALSE)
タブ区切りの場合はsep = "\t"
となります。標準ではrownames
で参照できる行名を出力しようとするので、row.names = FALSE
で抑制しています。
旧いシステムだと標準ではutf-8での出力にならないことには注意しましょう。
4.2 テキストファイルから読み込む
読み込むときは、read.table
を使います。
read.table("df01.csv", sep = ",", header = TRUE) df01 <-
標準では先頭行が列名として認識しないので、header = TRUE
をつけています。
オプションは多様に取れますが、欠損値はNA
が入ること、#
からはじまる行はコメントとして無視されること、fileEncoding
で文字コード指定ができること3、引数skip
で先頭から何行かを飛ばすことぐらいを知っておくと、?read.table
でヘルプを見なくても済むことが多いと思います。
4.2.1 数値が文字列として認識されたとき
表計算で保存したテキストファイルは、数値データがダブルコーテーションでくくったカンマ付き文字列になっているときがありますが、
$y <- as.numeric(gsub(",", "", df01$y)) df01
と言うようにgsub
などで置換をしてカンマを消して、as.numeric
で型を変換してしまえばよいです。
なお、引数colClasses
で型指定ができますが、as.*
関数群で変換できる程度しか融通が利かないです。
5 サブセットの作成
行列と同様に行番号や列番号を指定すれば、その番号のサブセットができます。
# yがゼロ以下の行を抽出
$y <= 0, ]
df01[df01# yがゼロより大きく1以下の行を抽出
$y > 0 & df01$y <= 1, ]
df01[df01# y列とk列を抽出
c("y", "k")] df01[,
subset
をつかうと、行の抽出がすっきり書けます。
subset(df01, y <= 0)
5.1 頭n行,末尾n行
ベクトルと行列でも使えるのですが、頭n行を抽出するhead
と、末尾n行を抽出するtail
データフレームの状態確認に便利です。
head(df01, 11) # 頭から11行を抽出
tail(df01, 11) # 末尾から11行を抽出
5.2 欠損データの除外
欠損値がない行をTRUE
とするcomplete.cases
を使うと簡単です。
subset(df01, complete.cases(df01))
6 ソート
行列でソートすることは稀だと思いますが、データフレームは行列と同様にソートできます。添字で指定した行番号の順序で並び替えたデータフレームが得られるので、order
で順序をつくって指定します。
# k列で昇順ソート
order(df01$k), ]
df01[# k列で昇順、y列で降順ソート
order(df01$k, df01$y, decreasing = c(FALSE, TRUE), method = "radix"), ] df01[
7 列の追加
データフレームの行数と同じ長さのベクトルを、新たな列名を指定して代入すると列の追加になります。
$n <- 1:nrow(df01) df01
8 データフレームの結合
IDなどのキー列をつきあわせて、二つのデータフレームを結合させることができます。
標準では二つのデータフレームで同じ名前の列がキーとして使われます。
merge(df01, df02)
突き合せにつかうキー列の列名が二つのデータフレームで異なる場合は、それぞれ指定します。
data.frame(m = c("a", "b", "c"), v = c(1, 10, 100))
df03 <-merge(df01, df03, by.x = "k", by.y = "m")
9 データフレームの分割
ベクトルをキーにしたデータフレームの分割はsplit
で簡単にできます。
データフレームの列を使う場合や、多次元に分割する場合はモデル式を使うのが簡単です。
split(df01, ~k) df_split <-
行数を確認すると、合計100行になっています。
for(i in 1:length(df_split)){
print(sprintf("%sは%d行", names(df_split)[i], nrow(df_split[[i]])))
}
[1] "aは30行"
[1] "bは36行"
[1] "cは34行"
モデル式ではなく、ベクトルで指定する事もできます。偶奇でわけてみましょう。
c("even", "odd")[1 + (1:nrow(df01)) %% 2]
oe <- split(df01, oe)) (df_oe <-