« 読了:島田(2018) 最近の「なんなら」 | メイン | 読了:荒川・菅原(2019) 弁護士にとってフォーカス・グループ・インタビューは有用か »
2019年8月14日 (水)
昔取ったメモのなかからR言語の自作チートシートが出てきた。2011年4月、転職をきっかけにしてRを使わなければらない羽目になり、何冊かの参考書をめくりながらとったノートである。当時はRについて全くの初心者で、右も左もわからない状態であった。
その後数か月間は折に触れて眺めては、新たに気が付いたことを追記・更新したと思うけれど、いずれ参照しなくなり、すっかり忘れ去っていた。チートシートというのはそういうものであろう。
それから8年の月日が流れ (自分で書いててショック)、いま見返してみると、いまではごく当然に思えることに新鮮な驚きを感じている形跡があったり、いまでは全く使わなくなった関数について真剣に調べていたり、それまで日々使っていたSAS言語といちいち比べていたり... なんというか、感慨深い。ああ、あのころ私は若かった... (嘘です。当時からおっさんでした)
せっかくなので、ちょっと整形してブログに載せ、供養としたい。
式
- 数値演算子は ^(ないし**), *, /, +, -, %/%(整数除算), %%(剰余)
- 演算子の優先順位がわからなくなったら
?Syntax
をみる - 代入記号として
<-
が推奨されている - 代入式を評価すると、左辺の代入後の値が戻る
- オブジェクト名はcase sensitive
- ピリオドから始まるオブジェクトはhiddenとなる
データ型
- atomicなデータ型は5つ: {NULL, logical, numeric, complex, character}
- numericはメモリ上ではintegerだったりdoubleだったりする
is.character()
etc. で型を確認as.character()
etc. で型キャスト
データ構造
- 主なデータ構造は{vector, matrix, array, list, data frame}
- scalarという構造はない。要素1のvectorとして表現する
str()
で構造とか中身の要約とかを表示- vector: ベクトル
- 要素は任意のデータ型
- ただし、あるベクトルの中の要素はすべて同じデータ型でなければならない。無理に突っ込むと一番上位の型に強制変換される
- 生成方法:
-
c()
で要素を結合して生成 numeric(length=3)
etc. でいきなり生成1:3
ないしseq(3)
でベクトル(1,2,3)を生成rep(2:4, 1:3)
でベクトル((2),(3,3),(4,4,4))を生成
-
- 生成時の注意点
c()
やnumeric()
でいきなり数値をいれた場合、メモリ上では double になる (Matroff本p.54)。ゆえにc(1,2,3) != 1:3
- コロン演算子 「:」 の挙動に注意 (Matroff本p.33)。
x==0
のとき、1:x
はc(1,0)
になってしまう。正方向にのみ動かしたかったらseq()
を使うべし。ただし、is.null(x)
のとき1:x
は NULL になるので都合がよいという面もある
length()
が長さを返す- ベクトルの加減乗除が可能。要素ごとに計算される。長さが違うときは短いほうが繰り返される。例:
t(2:4) %*% 1:3
ないしcrossprod(2:4,1:3)
が内積20を返す x[3]
がベクトルxの3番目の要素(からなるベクトル)を表す。添え字はベクトルでもよいx[-3]
はベクトルxの「3番目の要素を除く全要素」のベクトルを表すx == c(1,3,5)
のとき、x>4
はベクトル(FALSE, FALSE, TRUE)を返すので、x[x>4]
はベクトル(5)を表す- このとき、
which(x>4)
はベクトル(3)を返す。3番目の要素がヒットしたという意味 x[] <- 2
は xの全要素を2に置き換える (x <- 2
との違いに注意)- 要素に名前をつけることができる。例)
x <- c(itemA = 1, itemB=2)
。x["itemA"]
が1を返す (perlのハッシュのような感じで使えるようだ)
- 次元つきのvector (←ベクトルの特殊な場合と考える。面白いなあ)
matrix(1:6, ncol=3)
というようにして生成。→ 1列目が(1,2)になるX <- c(1:6); dim(X) <- c(2,3)
でも同じall(X == Y)
という風に比較dim(), ncol(), nrow()
が次元数、列数、行数を返すrow(), col()
は同じサイズの行列を返す。その要素は行番号ないし列番号で埋まっているX[i,j]
が要素を表すX[i,]
が行ベクトル、X[,j]
が列ベクトルを表す (気持ち悪い...)- 上の記法でアクセスすると、返ってくるのはmatrixかもしれないしvectorかもしれない。
X[1,1,drop=FALSE]
とするとXと同じ次元数のmatrixを返す - 添え字は行列でもよい。これを利用して、(行番号、列番号、値) を行としてもつ行列から表をつくることができる(cf. スペクター本のp.97)
diag(X)
は対角要素のベクトルを表す
- 任意の数の次元を持つ
array(1:12, dim=c(2,3,2))
というようにして生成
- 要素として異なる型のデータ構造を含みうる
list()
で要素を結合して生成L[3]
はリストLの3番目の要素からなるリストを返す。添え字はベクトルでもよいL[[3]]
がリストLの3番目の要素そのものを表す。それがどんな型のデータ構造かはわからない- 二重括弧添字をベクトルにすると再帰的な指定になる。例)
L <- list(1:3, c("Hello", "World"))
のとき、L[[c(2,1)]]
とL[[2]][1]
は等しい ("Hello") - 要素の名前を付けることができる。例)
L <- list(Start = 1, End = 2)
。L[["Start"]]
が 1 を返す。これをL$Start
と略記できる
- データセットを表す特殊なリスト
- 要素は同じ長さのベクトル。つまり、Rではデータセットを行構造体のベクトルとしてではなく、列ベクトルの集合として扱うわけだ
data.frame()
で要素(データセットの列ベクトル)を結合して生成- 文字は勝手にfactorにされてしまう。嫌なら
stringsAsFactors=TRUE
をつける - 要素の名前をつけることができる。例)
purchase <- data.frame( product = c("a", "b"))
$
略記も使える。例)purchase$product[2] == "b"
- [行番号、列番号] と添え字をつけてアクセスできる。
purchase[2,1] == "b"
attach()
すると変数名を直接呼べるようになる。detach()
で解放される。attach()
はデータフレームの中身を新しい環境にコピーしている。元のオブジェクトを書き換える際には、attach()
していようがいいがデータフレーム名から呼ぶこと。attach()
はまちがいのもと、つかわないほうがよい- カテゴリカル変数はfactor型として表現される。factorはatomicな型ではなく、
factor()
で生成されるオブジェクト
リストの操作
stack()
: リスト要素がすべてベクトルであるとき、それをすべて縦に結合した列と、タグ名を縦に結合した列の2列からなるデータフレームをつくる。例)stack(list(x=1:3, y=4:6, z=7:9))
データフレームの操作
- データフレームの例:
purchase <- data.frame(
product = c("apple", "cheese", "yogurt", "ham", "water"),
shop = c("drink", "daily", "daily", "meat", "drink"),
amount = c(2,3,1,5,6)
) - ソート:
order()
は順位ベクトルを返すので、purchase[order(purchase$amount) , ]
でソートしたことになる (なるほどー) - 行抽出(SASでいうサブセットif)
purchase[["shop"]] == "daily"
はベクトル(FALSE, TRUE, TRUE, FALSE, FALSE)を返すから、purchase[purchase[["shop"]] == "daily" , ]
とすればよいsubset(purchase, shop == "daily")
でもよい。subset()
は条件がNAになったときにfalse扱いしてくれる%in%
演算子をつかってベクトルと比較してもよい。subset(purchase, shop %in% c("daily","meat"))
- 分割 (SASでいう when ... output ...):
LD <- split(purchase, purchase$shop)
。LDは3つのデータフレームからなるリストになる。要素はLD$drink, LD$daily, LD$meat
となる - 縦に結合 (SASでいうset A B):
rbind(LD$drink, LD$daily)
- 横に結合 (SASでいう byなしのmerge):
cbind(1:3,11:13)
。ただし長さが同じでないといけない - マッチマージ (SASでいうby文つきのmerge):
merge(X, Y [, オプション])
- マッチキーの指定
- 指定しないと名前が共通する列すべてがマッチキーになる (SQLでいう natural join)
by=(ベクトル)
... マッチキーを指定するby.X=(ベクトル), by.Y=(ベクトル)
... データフレーム別にマッチキーを指定する- 長さ0のベクトルを指定するとデカルト積になる(SQLの cross join)
- マッチキーによる事前のソートは不要 (そりゃ助かるね)
- マージの種類の指定
all = FALSE
... マッチした行だけ (SQLの inner join)all.X = TRUE
... Xは全行、Yはマッチした行だけ (SQLの left join)all.Y = TRUE
... Xはマッチした行だけ、Yは全行 (SQLの right join)all = TRUE
... マッチしようがしまいが全行 (SQLの full join)
データの集約(スペクター本の8章)
table(X)
- Xはベクトル、リスト、データフレーム
X
にはいっているユニークな行ごとに、その行数を数えるX
に入っているベクトルの数の次元数の配列を返す。例, 3本なら3次元配列- 各次元には名前がつく。それはデータベクトルに出現する水準
exclude=NULL
引数で欠損値を含むことができるas.data.frame()
に渡すと便利addmargins()
に渡すと、各次元につきSum行を追加してくれるprop.table()
に渡すと、割合の表に変換してくれる
xtabs(formula, data=X)
: formulaの左辺はなし、ないし頻度ウェイトftable(X)
※本項は間瀬本p.181X
はふつうtable()
なりxtabs()
なりの出力X
を二次元の表にして返すrow.vars = 1:3
で、1,2,3次元が表側、残りが表頭になるwrite.ftable()
に渡すときれいなascii形式にしてくれる。いいんだかわるいんだか
- 集約のためのヒント
- グループがすでにリストの要素に分けられている → sapply, lapply
- グループが行列の行or列である → apply
- グループが一つ以上のグループ化変数で示されている
- 操作対象が単一のベクトル
- 結果はグループごとのスカラー → aggregate
- 結果はベクトル → tapply
- 操作対象が複数のベクトル → tapply, by
- 操作対象が単一のベクトル
lapply(X, FUN)
ないしsapply(X, FUN)
- ふつうXはリスト。各要素に
FUN
を適用する。例)x <- strsplit(c("Hello world", "How do you do")," ")
。length(x)
は 2 を返す。sapply(x, length)
は c(2,4)を返す - lapplyは常にリストを、sapplyは(もし可能なら)ベクトルないし行列を返す
- Xがデータフレームの場合、各列にFUNを適用する
- ふつうXはリスト。各要素に
apply(X, MARGIN, FUN)
- 配列
X
の次元MARGIN
にFUN
を適用する - 結果はベクトルないし行列
- よく使う
FUN
についてはすでに関数が用意してあるので探すこと。rowSums(), colSums(), rowMeans(), colMeans()
, etc. - sweep(X, MARGIN, STAT, FUN)のSTATに渡す。例) 列ごとに最大値を出し, 値をその列の最大値で割る:
Y <- sweep(X, 2, apply(X, 2, max) , "/")
- 配列
aggregate(X, BY, FUN)
- ふつう
X
はデータフレーム。X
の全列 についてBY
ごとにFUN
を適用 BY
はグループ分け変数のリストでなければならない。X$byvar1
なんていうのはだめ。X[c("byvar1","byvar2")]
ないしlist(v1 = X$byvar1, v2=X$byvar2)
とする。BY
の値の組み合わせ数ぶんの行を持つデータフレームを返す。BY
にNAを持っている行は無視される!
- ふつう
tapply(X, INDEX, FUN)
- ベクトル
X
について、INDEX
の値ごとにFUN
を適用 FUN
がスカラーを返したら、名前付きベクトルないし行列を返す。as.data.frame(as.table(), responseName="xxx")
に渡すとよい。responseName
を指定しないと、結果がFreqという列になってしまうFUN
がベクトルを返したら、名前付きリストを返す- 例1) INDEXがひとつ
ranges <- tapply(PlantGrowth$weight, PlantGrowth$group, range)
# 下の行でcbindとするとresultは行列になり全要素が文字列に強制変換される
result <- data.frame(
group = dimnames(ranges)[[1]],
matrix (unlist(ranges), ncol=2, byrow=TRUE) #ベクトルの長さが2だとわかってる
) - 例2) INDEXが複数
ranges = tapply(CO2$uptake, CO2[c("Type","Treatment")], range)
# rangesは2x2のmatrixで、その要素が名前なしのリストで、
# リストの要素が長さ2のベクトル。悪夢だ
result <- data.frame(
expand.grid(dimnames(ranges)),
matrix(unlist(ranges),byrow=TRUE,ncol=2)
) - FUNを指定しないと、指定したときに生成されるであろうベクトル(行列, リスト)の添え字を返す
# 中央値を格納する2x2行列。
meds <- tapply(CO2$uptake, CO2[c("Type","Treatment")], median)
# 行数ぶんのベクトル。tapplyが生成する行列の添え字を格納している。
# ここでは(1,1,1,...,3,3,3,....,2,2,2,...,1,1,1...) となる。
inds <- tapply(CO2$uptake, CO2[c("Type","Treatment")])
# それぞれの値から、そのセルの中央値を引くのは
adj.uptake <- CO2$uptake - meds[inds]
# この例は、ave()にFUNを指定しても同じことができる
adj.uptake <- CO2$uptake - ave(CO2$uptake, CO2$Type, CO2$Treatment, FUN=median
- 例1) INDEXがひとつ
- ベクトル
split(X, F)
- データフレーム
X
を、因子(のリスト)F
で分割し リストにして返す - 例) iris データセットの"Species" ごとに、残りの変数の相関行列の第一固有値を求める
# もし全体についてだったら
# eigen(cor(iris[setdiff(colnames(iris),"Species")]))$values[1]
frames <- split(iris[setdiff(colnames(iris),"Species")], iris$Species)
maxeig <- function(df) eigen(cor(df))$value1[1]
sapply(frames, maxeig) split()
のかわりに行番号をtapply()
に与える手もある。 例) iris データセットの"Species" ごとに、残りの変数の相関行列の第一固有値を求める# 話を簡単にするために、"Species"は5列目だとわかっているとしよう
# もし全体についてだったら eigen(cor(iris[-5]))$values[1]
# いま仮に行番号のベクトルがindであるとすると eigen(cor(iris[ind,-5]))$values[1]
maxeig <- function(ind, data) eigen(cor(data[ind,-5]))$values[1]
tapply(1:nrow(iris), iris[5], maxeig, data=iris)
- データフレーム
by(X, INDEX, FUN)
- データフレーム
X
について、INDEX
の値ごとにFUN
を適用 - 名前付きリストを返す
FUN
がデータフレームを返すようにすると便利- 例)
CO2$uptake
の観測数、平均値、標準偏差からなるデータフレームをつくるsumfun <- function(x) {
data.frame(n=length(x$uptake), mean=mean(x$uptake), sd=sd(x$uptake))
}
bb <- by(CO2, CO2[c("Type", "Treatment")], sumfun)
cbind(
expand.grid(dimnames(bb)), # リストbbのグループ因子をデータフレームに展開
do.call (rbind, bb) # rbindにリストbbを与え、要素ごとに処理させている。つまり縦結合
)
- データフレーム
雑記:データ解析 - 覚え書き: 私のRチートシート in 2011年4月