付録 A — Rコードの読み方 ── コードは「指示の文」
この章は、講義で実際に使ったRコードを 「読めて、意味がわかる」 ようになるための副教材です。 プログラミングが目的ではありません。スライドや配布ノートブックに出てくるコードを見たときに、「これは何をしている指示なのか」 を自分で説明できることが目標です。
コードは魔法ではなく、人間の言葉に直せる短い指示文です。各行を「何に・何をして・どんな結果が返るか」の3点で読むクセをつけてください。本章はすべて、講義で実際に使ったコードをもとに説明します。
1. はじめに:コードは怖くない
Rのコードは、つきつめれば次の3種類の文しかありません。
- 代入 ── 「この箱に、この値を入れておく」
- 関数呼び出し ── 「この道具に材料を渡して、結果を受け取る」
- その繰り返し・組み合わせ
たとえば講義に出てきたこの2行は、
x <- c(1, 2, 3, 4, 5) # 1〜5 の数を並べた「ベクトル」を x に入れる
mean(x) # x の平均を計算する(結果は 3)日本語に直すと「1から5までの数をまとめて x と名づけ、その平均を求めなさい」というだけの指示です。 コードを読むときは、英単語に身構えず、まず 「代入か、関数呼び出しか」 を見分けてください。
2. Rの基本文法を読む
代入 <-(左向きの矢印)
height <- 170 # 「170 を height という名前に入れる」<- は 「右の値を左の名前に入れる」 という矢印です。height = 170 と書いても動きますが、Rでは慣習として <- を使います(理由は第9節)。一度入れた名前は、あとから何度でも呼び出せます。
ベクトル c() ── 値を「ひとまとめ」にする
c() は combine(つなげる) の頭文字。複数の値を1本の列にまとめます。
scores <- c(60, 75, 80, 95) # 4人のテスト点をひとまとめにするRでは「1個の数」も「たくさんの数の列」も同じように扱えるのが強みです。mean(scores) のように、列まるごとを関数に渡せます。
規則的な列を作る seq() と rep()
seq(1.5, 12.5, by = 1) # 1.5 から 12.5 まで「1きざみ」で並べる(ヒストグラムの境界に使った)
seq(0, 1, length = 100) # 0 から 1 を「100点」に等分する(曲線を描くときに使う)
rep(0, 5) # 0 を 5回くりかえす → 0 0 0 0 0seq() は 数列を作る道具、rep() は 同じ値をくり返す道具です。
添字 [ ] ── 列の中から取り出す
角かっこ [ ] は 「何番目を取り出すか」 の指定です。Rの番号は1から始まります(第9節で再注意)。
scores[1] # 1番目の値(= 60)
scores[c(1, 3)] # 1番目と3番目(= 60, 80)論理添字 ── 「条件に合うものだけ」取り出す
[ ] の中に TRUE/FALSE の列 を入れると、TRUE の位置だけが残ります。これが統計で多用される「条件で絞り込む」操作です。
scores[scores >= 80] # 80点以上だけを取り出す(= 80, 95)scores >= 80 はまず FALSE FALSE TRUE TRUE という列を作り、[ ] がその TRUE の場所だけを選びます。
関数呼び出しの読み方 ── 位置引数と名前付き引数
関数は 関数名(材料1, 材料2, ...) の形です。材料(引数といいます)の渡し方には2通りあります。
hist(NHANES$Age, breaks = seq(-0.5, 80.5, by = 1), col = "lightblue")NHANES$Age… 位置引数。順番で「最初の材料」と決まる(ここでは「描くデータ」)。breaks = ...,col = ...… 名前付き引数。名前 = 値の形で「どのオプションか」を明示する。
読むときのコツ:位置引数は「主役の材料」、名前付き引数は「細かい注文(オプション)」 と思えばよい。名前付き引数は順番を入れ替えても、省略しても動きます(省略時は既定値が使われる)。
ヘルプの引き方 ?
意味がわからない関数は、コードの中で次のように調べられます。
?hist # hist 関数の説明(引数の一覧と既定値)を表示する3. データを読む ── データフレームと NHANES
データフレーム ── 「表」のデータ
この授業で繰り返し使う NHANES(米国の健康・栄養調査、約1万人分)は データフレーム という形式です。Excelの表と同じで、行が人、列が項目(年齢・身長・性別…) になっています。
$ ── 列を1本取り出す
NHANES$Age # NHANES という表から「Age(年齢)」の列だけを取り出す$ は 「表のうち、この名前の列」 という指定です。表名$列名 と読みます。
行を絞り込む ── 論理フィルタと subset()
データフレームの [ 行の条件 , 列の条件 ] で、必要な行・列だけを取り出せます。コンマの左が行、右が列です。
adults <- NHANES[NHANES$Age >= 18, ] # 「年齢18以上」の行だけ。コンマ右が空 = 全列を残す同じことを読みやすく書く subset() もあります。
adults <- subset(NHANES, Age >= 18) # 年齢18以上の行を取り出す(意味は上と同じ)NHANESの前処理 ── !duplicated() で重複を消す
NHANESには 同じ人が複数回登場する重複 があります。講義では必ず次の1行で重複を消してから使いました。
# duplicated() は「すでに出てきた行」を TRUE にする。! で否定して「重複でない行」だけ残す
NHANES <- NHANES[!duplicated(NHANES$ID), ]duplicated(NHANES$ID)… ID列を上から見て、2回目以降に現れたIDをTRUEにする。!… 否定(NOT)。TRUE↔︎FALSEを反転させる。だから「重複 でない」行がTRUEになる。- これを行の論理添字に使い、各人を1回だけ残す。
条件を &(かつ)でつなげて、重複除去と年齢フィルタを同時にかけることもあります。
nhanes_adult <- NHANES[!duplicated(NHANES$ID) & NHANES$Age >= 18, ] # 重複除去 かつ 成人4. 記述統計の関数
データを「要約する数」を返す関数です。引数にはふつう データの列 を渡します。
| 関数 | 何を返すか | 例 |
|---|---|---|
mean(x) |
平均 | mean(scores) |
median(x) |
中央値(まんなかの値) | median(scores) |
var(x) |
不偏分散(n−1で割る) | var(scores) |
sd(x) |
標準偏差(不偏分散の平方根、n−1版) | sd(scores) |
quantile(x, p) |
分位点(下から割合 p の位置) | quantile(scores, 0.25) |
range(x) |
最小値と最大値 | range(scores) |
summary(x) |
最小・四分位・平均・最大の一覧 | summary(scores) |
table(x) |
各値が何回出たかの度数 | table(NHANES$PhysActive) |
重要:var() と sd() は n−1 で割る
ここは試験でも講義でも繰り返し強調した点です。Rの var() と sd() は、データの個数 n ではなく n-1 で割った値を返します。
\texttt{var(x)} = \frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2,\qquad \texttt{sd(x)} = \sqrt{\texttt{var(x)}}
var(x) は標本から母分散を推定するときの 不偏分散 s^2 にあたります(n で割る値ではない)。なお sd(x) はその平方根で、ばらつきの目安として使いますが、厳密には母標準偏差 \sigma そのものの不偏推定ではありません(この点は本講義の範囲を超えます)。 たとえば NHANES の成人身長の標準偏差を計算したコードはこうでした。
adults <- NHANES[NHANES$Age >= 18, ] # 成人だけ
sd(adults$Height, na.rm = TRUE) # 身長の標準偏差(n-1 版)。na.rm で欠損を除外na.rm = TRUE の意味は第9節で説明します(欠損値の扱い)。
table() と割合
table(NHANES$PhysActive, useNA = "no") # 各カテゴリの人数を数える(NAは数えない)
prop.table(table(NHANES$PhysActive, useNA = "no")) # それを割合(合計1)に直すtable()… カテゴリごとの 個数(度数) を数える。useNA = "no"… 欠損(NA)を集計に含めない。prop.table()… 度数を 割合 に変換する。
5. 図を描く関数とオプションの読み方
base R の作図関数は 「まず1枚描く関数」+「上に描き足す関数」 という二段構えです。
hist() ── ヒストグラム
hist(NHANES$SleepHrsNight,
breaks = seq(1.5, 12.5, by = 1), # ビン(棒)の境界を指定する
freq = TRUE, # TRUE: 縦軸=度数(人数) / FALSE: 縦軸=密度(面積=1)
col = "lightblue", # 棒の色
main = "睡眠時間の分布", # グラフのタイトル
xlab = "睡眠時間", ylab = "人数") # x軸・y軸のラベルbreaks… 棒の刻みかた。数値を1つ渡せば「だいたいの本数」、列を渡せば「境界そのもの」を指定する。freq… TRUE なら個数、FALSE なら密度。密度ヒストグラムに理論曲線を重ねるときはFALSEにする。
plot() と描き足し lines() / abline()
plot() が土台の1枚を描き、lines() や abline() が その上に線を足します。
x <- seq(140, 200, length = 100) # 横軸の点を100個用意
plot(x, dnorm(x, 170, 5), type = "l", # type="l": 折れ線で描く
col = "blue", lwd = 2, ylab = "f(x)") # col: 色、lwd: 線の太さ(line width)
lines(x, dnorm(x, 170, 10), col = "red", lwd = 2) # 既存の図に2本目の曲線を描き足す
abline(h = 0.04, col = "red", lwd = 2) # 水平線 y=0.04 を引く(h=横線, v=縦線)
legend("topright", legend = c("σ=5", "σ=10"), # 凡例(どの線が何か)
col = c("blue", "red"), lwd = 2)type = "l"(line)は折れ線、type = "h"は垂直線(棒状)、既定のtype = "p"は点。abline(h = ...)は水平線、abline(v = ...)は垂直線、abline(a, b)は切片 a・傾き b の直線。lwdは線の太さ、colは色、ltyは線の種類(2は破線)。
boxplot() ── 群ごとの箱ひげ図と ~ 記法
boxplot(Height ~ Gender, data = adults) # 「Height(数値) を Gender(群) ごとに」箱ひげ図にするここで初めて チルダ ~ が出てきます。Height ~ Gender は 「左の数値を、右の変数で分けて見る」 という意味の式(formula)で、~ は「〜を〜で説明する/分ける」と読みます(第8節で再登場)。
barplot() ── 棒グラフと描き足し add
barplot(rbind(obs, theory), beside = TRUE, # beside=TRUE: 2系列を並べて表示
col = c("gray", "skyblue")) # 系列ごとの色beside = TRUE… 複数系列を 横に並べる(FALSE なら積み上げ)。add = TRUE… (一部の作図関数で)既存の図に重ねて描く。新しい図にせず上書きする指定。
6. 確率分布の d / p / q / r 体系
ここが base R の中で最も体系的で、覚えると一気にコードが読めるようになる部分です。 Rでは各分布に対して 接頭辞(先頭の1文字)+分布名 という命名規則があります。
| 接頭辞 | 意味 | 何を返すか | 言いかえ |
|---|---|---|---|
d |
density / mass | 密度 or 確率質量 f(x) | 「その値の高さ・確率」 |
p |
probability | 累積確率 P(X \le x) | 「その値以下になる確率」 |
q |
quantile | 分位点(p の逆) | 「下からこの割合に来る値」 |
r |
random | 乱数 | 「その分布からサンプルを生成」 |
分布名は binom(二項), norm(正規), unif(一様), beta(ベータ)などです。組み合わせると次のようになります。
| 二項分布 | 正規分布 | |
|---|---|---|
密度・確率 d |
dbinom(k, n, p) |
dnorm(x, μ, σ) |
累積確率 p |
pbinom(k, n, p) |
pnorm(x, μ, σ) |
分位点 q |
qbinom(prob, n, p) |
qnorm(prob, μ, σ) |
乱数 r |
rbinom(回数, n, p) |
rnorm(個数, μ, σ) |
講義で出てきた実例:
dbinom(3, 10, 0.5) # コインを10回投げて表が「ちょうど3回」出る確率
pnorm(1.96) # 標準正規分布で 1.96 以下になる確率(≒ 0.975)
qnorm(0.975) # 下から 97.5% に来る値(≒ 1.96)← pnorm の逆向き
rnorm(100, 170, 5) # 平均170・標準偏差5 の正規分布から 100個の乱数を生成
qbeta(c(0.025, 0.975), alpha_post, beta_post) # ベータ分布の2.5%点と97.5%点(信用区間)覚え方:
pとqは 互いに逆向きです。pnorm(値) → 確率、qnorm(確率) → 値。pnorm(qnorm(0.975)) = 0.975のように行き来できます。信頼区間で出てくる 1.96 はqnorm(0.975)の値です。
7. ランダムネスとシミュレーション
統計の多くの考え方(標本分布・ブートストラップ・並べ替え検定)は、コンピュータで何度もくじを引いて確かめる ことで実感できます。そのための道具です。
set.seed() ── 乱数を「再現可能」にする
set.seed(12345) # 乱数の出発点を固定する
rnorm(3) # → 何度実行しても、同じ3つの値が出る乱数は本来「毎回ちがう」ものですが、set.seed(数字) を先に置くと 同じ数字なら毎回まったく同じ乱数列 が出ます。これは結果を 再現できる(他人が確かめられる) ようにするためです。数字そのものに意味はなく、「どのくじ箱を使うか」の合言葉だと思ってください。
sample() ── 標本を抜き出す(復元・非復元)
sample(nrow(nhanes_adult), 250) # 行番号から 250個を「重複なし」で抜く
sample(data, size = length(data), replace = TRUE) # 同じ個数を「復元抽出」(ブートストラップ)replace = FALSE(既定)… 非復元抽出。一度選んだものは戻さない(ふつうの標本抽出)。replace = TRUE… 復元抽出。選んだものを戻すので同じ人が何度も出る(ブートストラップで使う)。
replicate() と for ループ ── 「同じことを何回もやる」
同じ実験をくり返して結果をためるのが、シミュレーションの中心です。書き方は2通り。
# 方法A:replicate(回数, { やること }) ── 結果が自動で集まる
boot_means <- replicate(10000, {
bs <- sample(samp, length(samp), replace = TRUE) # 復元抽出で疑似標本を作り
mean(bs) # その平均を返す
})
# → boot_means には 10000個の平均が入る(これが標本分布の近似)# 方法B:for ループ ── 1回ずつ番号 i を動かしながら回す
results <- numeric(1000) # 結果を入れる長さ1000の空き箱を用意
for (i in 1:1000) { # i を 1 から 1000 まで動かす
results[i] <- mean(sample(data, 30)) # 30個を抜き出した標本の平均(1個)を i番目の箱に入れる
}replicate() は「回数 → 中身 → 結果が並んで返る」、for は「番号を動かしながら箱に詰める」と読みます。中身は同じことができます。
8. 検定とモデル
ここまでの道具を使って、いよいよ 検定 や モデルあてはめ を行う関数です。出力(結果)の読み方も大事です。
formula 記法 ~ ── 「結果 ~ 説明」
~(チルダ)は 「左を、右で説明する/分ける」 という関係を表します。検定・回帰で共通の書き方です。
| 書き方 | 読み方 |
|---|---|
y ~ x |
「y を x で説明する」(単回帰) |
y ~ x1 + x2 |
「y を x_1 と x_2 で説明する」(重回帰) |
y ~ x1 * x2 |
「x_1・x_2 に加えて、その 交互作用 も入れる」 |
数値 ~ 群 |
「数値を群ごとに比べる」(t検定・箱ひげ図) |
t.test() ── 2群の平均の差を検定する
result <- t.test(BPSysAve ~ PhysActive, data = nhanes_sample) # 血圧を「運動するか」で2群比較血圧 ~ 群 という formula で「群ごとに平均を比べる」指示になります。var.equal = TRUE を足すと、2群の分散が等しいと仮定する版(プールしたt検定)になります。
cor() と cor.test() ── 相関
cor(x, y) # x と y の相関係数だけを返す(-1〜1 の数)
cor.test(x, y) # 相関係数に加えて、t統計量・p値・信頼区間も返す(検定つき)lm() と summary() ── 線形モデルの出力を読む
lm() は Linear Model(線形モデル)= 直線あてはめ・回帰 の関数です。
model <- lm(grade ~ studyTime, data = df) # 成績を勉強時間で説明する直線をあてはめる
summary(model) # 結果(係数・標準誤差・p値・R^2 など)を一覧表示summary(model) の出力は、次のように読みます(数値は例)。
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.000 8.000 3.125 0.0123 *
studyTime 4.500 1.200 3.750 0.0048 **
...
Multiple R-squared: 0.612
Estimate… 係数の推定値。(Intercept)は切片、studyTimeは 「勉強時間が1増えると成績が平均4.5増える」 という傾き。Std. Error… その係数のばらつき(標準誤差)。t value… 推定値 ÷ 標準誤差。0からどれだけ離れているか。Pr(>|t|)… その係数が0だとしたら、これくらい以上に極端な値が出る確率(p値)。*が多いほど小さい。R-squared… モデルがデータのばらつきを説明できた割合(0〜1。例では約61%)。
$ を使えば結果の一部だけ取り出せます。
t.test(bp ~ act, var.equal = TRUE)$p.value # 検定結果から p値だけを取り出す9. よくあるつまずき
最後に、初学者が必ず一度はひっかかる点をまとめます。
= と <-
代入はどちらでも動きますが、Rでは 代入は <-、関数の引数指定は = と使い分けるのが慣習です。
x <- 10 # 代入は <-
hist(x, breaks = 5) # 引数指定は =(breaks に 5 を渡す)<-(代入)と ==(等しいか判定)
これは別物です。= 1個は代入、== 2個は「等しいか?」の比較で、TRUE/FALSE を返します。
x <- 5 # x に 5 を入れる(代入)
x == 5 # x は 5 と等しいか? → TRUE(判定)条件で絞り込むときは必ず ==(2個)です。NHANES$Gender == "female" のように使います。
欠損値 NA と na.rm
データに 欠損(NA) が1つでもあると、mean() や sd() は NA を返してしまいます。
mean(c(1, 2, NA)) # → NA(計算できない)
mean(c(1, 2, NA), na.rm = TRUE) # → 1.5(na.rm=TRUE で欠損を除いて計算)na.rm は 「NA を remove(除く)するか」 の指定。NHANESのような実データではほぼ必須です。 欠損かどうかの判定は is.na(x) を使います(x == NA は 使えないので注意)。
添字は1から始まる
多くの言語と違い、Rの番号は0ではなく1から始まります。scores[1] が最初の要素です。
c() の付け忘れ
複数の値を渡すときは必ず c() でまとめます。col = "blue", "red" ではなく col = c("blue", "red") です。
大文字・小文字、全角・半角
Rは 大文字と小文字を区別 します(Mean() は存在せず mean())。また、かっこ () やコンマ , は 必ず半角で。全角スペースや全角かっこはエラーの原因の定番です。
まとめ:コードを読むときは、(1) 代入か関数呼び出しか、(2) 主役の材料(位置引数)とオプション(名前付き引数)、(3) 返ってくる結果は何か、の3点を口に出して説明してみてください。
d/p/q/rの体系と~の意味、var/sdが n−1 であることの3つを押さえれば、講義のコードはほぼ読めます。