付録 A — Rコードの読み方 ── コードは「指示の文」

この章は、講義で実際に使ったRコードを 「読めて、意味がわかる」 ようになるための副教材です。 プログラミングが目的ではありません。スライドや配布ノートブックに出てくるコードを見たときに、「これは何をしている指示なのか」 を自分で説明できることが目標です。

ヒント読み方のコツ

コードは魔法ではなく、人間の言葉に直せる短い指示文です。各行を「何に・何をして・どんな結果が返るか」の3点で読むクセをつけてください。本章はすべて、講義で実際に使ったコードをもとに説明します。


1. はじめに:コードは怖くない

Rのコードは、つきつめれば次の3種類の文しかありません。

  1. 代入 ── 「この箱に、この値を入れておく」
  2. 関数呼び出し ── 「この道具に材料を渡して、結果を受け取る」
  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 0

seq()数列を作る道具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つ渡せば「だいたいの本数」、列を渡せば「境界そのもの」を指定する。
  • freqTRUE なら個数、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%点(信用区間)

覚え方pq互いに逆向きです。pnorm(値) → 確率qnorm(確率) → 値pnorm(qnorm(0.975)) = 0.975 のように行き来できます。信頼区間で出てくる 1.96qnorm(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 yx で説明する」(単回帰)
y ~ x1 + x2 yx_1x_2 で説明する」(重回帰)
y ~ x1 * x2 x_1x_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" のように使います。

欠損値 NAna.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つを押さえれば、講義のコードはほぼ読めます。