統計処理ソフト R 入門 講習会資料

この講習会は Windows 版 R に基づいています。

文書は内容の関連性に従って組織されており,講習会での解説順序のとおりには並んでいません。

この文書には巷のRの入門解説には含まれていないような内容が入っています。その理由は「講習会の目的」の節をご覧ください。

この文書の読み方:

この資料は当初 R version 2.5.0 をもとに作成されました。 その後の R のバージョンアップの都度,動作確認をしていますが,見落としがあるかもしれません。

導入

R についての概略

R とは,主に統計計算とグラフィックスの機能を提供する,アプリケーションシステムの名前です。 また,そこで用いられているスクリプト言語の名前でもあります。

非常に多くのことが R でできます。 確かに,統計計算やグラフィックスに関して R 以外では不可能なことというのは存在しないでしょう。 しかし,あることを行うために既に作られ提供されているものが R しかない場合は多くあります。 また,複数の手段が利用可能であっても,その中で最も簡便なのが R の利用であるという場合も多くあります。

ユーザから見た R の長所

ユーザから見た R の短所

講習会の目的

本講習会は, R についての自習の基盤をつくることを目指します。

たとえ初心者向けの数時間の入門講習でなく1年間の毎週の演習授業であっても,R に関してすべてを説明するのは不可能だと思われます。 R の世界は,縦にはそこそこ深く,横には果てが見えないほど広いです。 CRAN に登録されている R のパッケージは 1000 を超えました。

よって,受講者の幅も広いことですし,受講者各自にとってぴったりな統計解析の実用的な解説をするのはあきらめて, 各自が必要に応じて情報を探し,見つけたものを難なく活用できるようになること,を目標にしました。

ここに自分の求めている分析手法や作図法などの答えがあるとは期待しないで下さい。それは帰ってからのあなたの楽しい仕事です。

R の利用と R 言語

R の根幹は R 言語のインタプリタであり,ユーザはR言語を駆使することでRを操作します。

SPSS や Stata のような GUI (ここではそれを仮に「メニュー+ダイアログ」UIと呼びましょう)に慣れている人は, 同じようなものを R にも求めるかもしれません。 実際にそのような UI は R にもいくつかあります。 例えば有名なのが R Commander です。

一般に「メニュー+ダイアログ」UI が提供するのは, 「知っている統計解析法,作図法の名前」→「統計ソフトでの命令(関数)の名前」のマッピングと, 各命令のオプション(引数)の一覧と,それが取りうる値の一覧です。それ以上の効能はまずないでしょう。 それ以上の効能をもつ UI は開発不可能ではないと思いますが,現在のところ著者はまだ見つけたことがありません。 たとえば,メニューをクリックしたりダイアログで項目を選択するとその統計手法や解析オプションの詳細かつ分かり易い解説をつけてくれて, その後ちょっとしたテスト問題が出て,正解しないと分析を実行しないソフトとか, 何がしたいのかを聞いてくれて日本語音声で答えると適切な分析を提案し説明してくれるソフトとか。 R において関数の名前や可能な引数の一覧を探してくる方法は,他の統計ソフトよりもはるかに簡単で, 少し勉強すれば(この講習会の 1/4 も出席しなくても)すぐに身につきます。

「メニュー+ダイアログ」のほうが打鍵数が少なくて良いという意見があるかもしれません。 それはもっともですが,キーを打ったほうが操作が速くてよいという人も世の中にはかなり居ることを思い出してください (そして残念なことに現状のコンピュータ環境ではそういう人になったほうが一般に仕事の効率が上がります)。 また,R の利用法の真骨頂は,コンソール入力ではなくスクリプトによる再利用です。 ちなみに,R には打鍵数を少なくする工夫があちこちに備わっています。

R 言語を離れて「メニュー+ダイアログ」のみを R の利用手段とすると, R が他より優れている点の半分以上を享受できません。 あらゆる有名な統計ソフトが言語によって操作する機能(それはコマンドと呼ばれたりシンタクスと呼ばれたりします)を残している点で 「メニュー+ダイアログ」UI が不完全なものであることが読み取れますが, 実際,そういうソフトは「メニュー+ダイアログ」でのユーザの選択からバックグラウンドで言語文を生成し,それを実行することで分析を行っています。 R において言語から離れてしまうことによる損失はそれら有名統計ソフトの比ではありません。

このようなことから,本講習会では,R 言語を自分で記述することで R を利用する方法を説明いたします。 これは,学生の「メニュー+ダイアログ」UI の利用を禁止するものではありません。 ただ,そのようなインターフェイスは R を理解してから使ったほうがよいと思います。

早速さわってみよう

なにはともあれ,スタートメニューから R を起動してみましょう。

まずは電卓として,R を使ってみてください(電卓にしてはあまりに高機能ですが)。

2 + 3 - 4
5 * 6 / 7
(8 + 9) / 2 - 10 * 3
11 ^ 4       # 11の4乗
13 %% 6      # 剰余
13 %/% 6     # 整数商
sqrt(16)     # 平方根
abs(-2.5)    # 絶対値
exp(3)
log(2); log(exp(5))     # ; を使うと一行に複数の式を書ける(非推奨)
log2(2); log10(2)
(1+3i) * (2+5i)
sin(1); cos(pi); tan(0)
asin(1); asin(sin(1))   # arcsine
a <- 27 ^ (1/3)         # 電卓のメモリ機能(計算結果を記録)
a * 5                   # メモリ内の数値を計算に使う(メモリ内の数値は変わらない)
a <- sqrt(64) * a       # メモリ内の数値を更新
a <- a %% 7
a                       # メモリ内を表示

次にもうすこし R らしいことをしてみます。

x1 <- c(4.2, 5.0, 3.4, 6.2, 1.8, 2.7)
range(x1)
x2 <- c(4.2, 3.6, 2.8, 4.4, NA, 3.5)
mean(x2)
mean(x2, na.rm=TRUE)   # NAを除く
x1 == x2               # 等しいかどうか

x <- rnorm(2000)       # 正規乱数2000個
y <- rnorm(2000)       # 同じく2000個
plot(x, y)
x > y
sum(x > y)             # x のほうが大きいペアの個数
abline(0, 1)           # y = x の直線を描画

cor(x, y)              # Pearsonの相関係数

R 言語

表現式と評価

統計解析,シミュレーション,作図などを行う一連の手続きの R 言語による記述は,表現式(expression)で構成されます。 表現式とは,R が解釈可能な文です。 表現式はさらにいくつかの表現式に分けられることもあります。

スクリプトファイルを書くときでもコンソールへの対話入力でも, ユーザは基本的に一行に1つの(表現式に解析できる)文を書きます。 ;(セミコロン)で区切ると一行に複数の表現式を書くことができますが, これは使わないようにしましょう(他の人のソースを読むときの参考までに)。 入力された表現式は R によって解析(parse)され,その内部表現が保持されます。 R において,表現式はリスト様式の内部表現を持ちます。複合的な表現式は木構造をしています。

解析された表現式は,前にあるものから順番に評価(evaluate)されます。 評価とは,簡単には,表現式の内容を実際に実行することだと考えてください。 表現式を評価することで,結果として必ず1つの値が得られます。 一つの表現式の中での評価の順序は,基本的には必要なものから順に行われますが, 現実はもう少し複雑です(演算子の優先順位,遅延評価の節を参照)。 公式文書では,曖昧ながら,ステートメント(statement)という用語も登場します。 しかし,R 言語においては,次のような理由によりステートメントと表現式はほぼ同じ意味で,(表現)式ステートメントという用語は役に立ちません。 (1) R 言語では,すべての文が値を返す。制御ステートメントすら値を返す。(2) R 言語には宣言がなく,宣言ステートメントや式ステートメントなどの区別がない。

ブロック

{ } で囲むことで,複数の表現式を1つにまとめることができます。 まとめてできたものも表現式です。よって,それを評価した結果は全体として1つの値が得られます。 ブロックの中に複数の表現式がある場合,最後の表現式の値がブロック全体の値となります。

{
  4 * 5
  (x <- 4 * 5)    # ( ) をつけてもprint()されない
  sqrt(3) + x
}

{ log2(4); 5 ^ 2 } * 3

表現式オブジェクト

直接入力したステートメントを解析してできた表現式だけでなく, R は R 上の文字列データから動的に表現式を作ることができます (R では表現式もオブジェクトとして扱えます)。 次のコードは,文字列から表現式オブジェクトを生成するサンプルです。

str1 <- "1 + 2 ^ 3 / sqrt(4)"  # 文字列を作成
str1
expr1 <- parse(text=str1)      # 文字列を表現式に
expr1
eval(expr1)                    # 表現式を評価

expression() 関数を使うと,表現式を評価しないまま表現式オブジェクトとしてとっておくことができます。 これは後述の関数呼び出しオブジェクトと非常に似ています。

expr2 <- expression( (5:8)[-3] * 2 )  # 表現式オブジェクトを作成
expr2
eval(expr2)                           # 評価する

これは自前の関数を書く際のテクニックとしてだけでなく, 導関数を得るときやグラフに数式を描くときなどにも活用されます。

遅延評価

R では,基本的に,入用にならなければ表現式は評価されません。 例えば,関数内部で使われない引数は,そこへ表現式(e.g., 1+1 ) を渡していても一度も評価されないまま(1+1が計算されないまま),その関数の実行を終えます。 これが R における遅延評価(lazy evaluation)です。

下はこの働きを確かめるためのサンプルです。どうしてこのような出力になるのか考えてみてください。

f <- function(x={print("x!");2}, y={print("y!");0}, z={print("z!");8}) {
  if (y != 0) {
    return(z + 1)
  }
  return(x)
}

f()
f(z=f())
f(y=f())
f(x=f())
f(x=f(), y=1)

この機能は,R の実行速度に一役買っていますが,ユーザは関数を呼び出す際に注意しなければなりません。 引数には,何らかの変数の値を書き換えうるような表現式を書かないようにしましょう。

x <- c("A", "B", NA, "D")
mean(x, na = myNA <- TRUE)  # myNA は作成されない
myNA
x <- c(1, 2, NA, 4)
mean(x, na = myNA <- TRUE)  # myNA が作成される
myNA

オブジェクト

R では,R 言語を通じて操作されるほとんどのものがオブジェクト(object) と呼ばれる共通のデータ構造になっています。

これらは皆,R においてはオブジェクトです。

オブジェクトは一般に,表現式の評価(関数の呼び出し)の結果,作成されます。 R では,作成したオブジェクトを消去する責任はユーザにはありません。 放っておけばそのオブジェクトが要らなくなった後にシステムが自動的に消去します。

代入と削除

作成したオブジェクトをその後においても参照する(得た数値を別の分析に使ったり,結果を図示したりする)ためには, オブジェクトに名前をつけて確保しておく必要があります。
x <- 5 + 6    # 結果を代入
x * 10        # xに入っている値に10をかける

<-代入(assign; 付値とも呼ばれる)を行う演算子です。->= も使えますが,おすすめしません。 演算子 = は,<--> と異なり,使用可能な場所が限定されています。 また,優先順位は3つの中で -> が最も高く,次に = ,一番低いのが <- です。 例では 5 + 6 を計算した結果のオブジェクトを x に代入しています。 以後,x という名前でそのオブジェクトを参照できます。

この x は変数と呼ばれます。中身を入れ替えることができるからです。

x <- 5 + 1  # x は 6
x
x <- 3 - 2  # x の中身を 1 に変更
x

入れ替えたら,もう元のオブジェクトを参照できません。このミスはよく起こります。 大事なデータを無くさないよう気をつけてください。

次のように,変数から変数への代入も可能です。

x <- 4
y <- x  # x の中身を y へ
y

注意しなければならないのは,R では基本的に,代入はオブジェクトのコピーを作成します。 代入で複製された一方の変数の値を変更しても,もう一方の変数の値は変わりません。 環境オブジェクトは特別で,代入してもコピーされません。 実装レベルでもコピーしているかどうかは別問題。さすがに実装は copy-on-write です。

x <- c(3, 1, 2)
y <- x
y[2] <- 5
y  # y の値は変更されている
x  # x はそのまま

名前をつけて確保したオブジェクト(変数の値)は,システムによって「まだ必要」と見なされるので,たくさんのオブジェクトを 名前をつけて置いておくとそのうち利用可能なメモリ容量をオーバーしてしまうかもしれません。 ですので,名前をつけたものについては,もう要らなくなったら削除して(名前を消して)いくように心がけます。 削除には rm() 関数を使います。

rm(x)  # xを削除

現在の環境に存在するオブジェクトの一覧を得るには ls() 関数を使います。 特定の名前のオブジェクトが存在するかどうかは exists() 関数で調べられます。

x <- 3
y <- "Hello"
z <- TRUE
ls()         # 存在するオブジェクトの名前を一覧
exists("y")  # y という名前のついたオブジェクトが存在するかどうか

rm()list という引数に名前のベクトルを渡すことで複数のオブジェクトを一気に削除できるので, これと ls() を組み合わせて全部のオブジェクトを一度に消すことができます。

rm(list=ls())   # ただしこれでは隠しオブジェクトは消えない

R をうまく活用するには,このように複数の関数を組み合わせるテクニックが重要です。

名前に使用可能な文字

オブジェクトの名前には使える文字にほとんど制限はありません。日本語の文字も使えます。 ただし,R 言語で特殊な意味をもつ記号( "+" や "[" や "(" など )を名前に使おうとすると 特別な技巧を駆使しなければならないため,普通はこれらの記号は使いません。 よって,名前には英数字と,"." (ピリオド),"_" (アンダーバー) を使いましょう。

日本語を使うのもよいですが,世界中で開発されているパッケージの中には 日本語などの文字に対応していないものも存在する可能性があるので, 追加パッケージを使うときにはその点を念頭に置いておいてください (R にデフォルトで備わっている関数については問題はないでしょう)。

ただし,オブジェクトの名前の最初の文字に数字やアンダーバーを(通常のやり方で)使うことはできません。 これを試みる場合も特別な技巧が必要です。従って,素直に最初の文字は英字にしましょう。

"." (ピリオド)で始まる名前は特殊な意味を持ちます。 ピリオドから始まる名前がついているオブジェクトは隠しオブジェクトと呼ばれ,ls() 関数などの 一覧に通常は表示されません。

オブジェクトの名前は大文字小文字が区別されます。 (オブジェクトの名前だけに限らず R では基本的に大文字小文字は区別されます。)

内容の表示

オブジェクトの名前だけの表現式を評価すると,オブジェクトの内容が表示されます。

myobj <- c(6, 3, 5, 8, 4)
myobj                    # myobj の内容を表示

これは暗黙にprint()という関数を呼び出しているのと同じことです。

print(myobj)

表現式は1つ以上の複合的な関数呼び出しであり,各関数を呼び出して戻り値を得るということを繰り返していった結果, 最終的に1つの値(オブジェクト)になります。 ということは,どんな表現式も,最後には上のようなオブジェクトだけの式になり, 最後にそれを評価することでコンソールに内容が出力されるはずです。 しかし,評価してもコンソール出力のない表現式もあります。代表的には,

x <- 3 ^ -1

などがそうです。代入の評価は例外的に値を返さないのでしょうか?そんなことはありません。ではどういう事かというと, 関数の戻り値には print() されるかどうかの指定ができ(これは可視性visibilityと呼ばれます), このような関数では自動的に print() しない指定(invisible)がされているのです。

こういう不可視(invisible)な戻り値の場合にも,オブジェクトの内容を print() させるテクニックがあります。

(x <- 3 ^ -1)

( ) で囲むことで,戻り値を可視(visible)にできます。"(" も実は一種の関数です。 他にも作図関数など戻り値が print() されないものは多くありますが, 全体を ( ) で囲めば print() させることができます。

オブジェクトには(type)があります。型とは,オブジェクトが持っているデータの種類です。 Rでは,他の言語(例えばC)のような変数の型はありません。R の変数は,いわゆるバリアントです。 typeof() 関数を使って型の情報を得ることができます。

x <- 2
typeof(x)
x <- "abc"
typeof(x)
x <- 1:4
typeof(x)
x <- list(2, "abc")
typeof(x)

同じくデータの種類についての情報ですが,型よりも抽象的なものとして, オブジェクトはモード(mode)も持っています。 型の情報は,R の実装に深く関係しており,R の通常の使用ではモードの情報で十分なので,こちらを利用します。 モードを得るには mode() 関数を使います。

x <- 2
mode(x)
x <- "abc"
mode(x)
x <- 1:4
mode(x)
x <- list(2, "abc")
mode(x)

モードよりももう少し詳細な情報(型との中間のような位置づけ)として,保管モード(storage mode)があります。 モードでは隠蔽されてしまうメモリへの格納方法(例えば integer と double)を区別することができます。 保管モードは storage.mode() 関数で得られます。

モードと保管モードは,実装とは距離を置いた R 言語上での抽象的情報であり,変更するための記法も柔軟になっています。

x <- 2
x
mode(x) <- "character"
x

Rには原子型(atomic types, atomic vector types)と呼ばれる基本的な型がいくつかあります。 下の表はそのうちの主なものです(すべてではない)。

よく使われる原子型: 変換の序列
説明モードリテラルの例
logical論理値logicalTRUE
integer整数numeric18L
double実数numeric1.414e-3
complex複素数complex0.7i
character文字列character"XYZ"

これらの原子型は,表中の上へいくほどその型のオブジェクトが持つ情報が少なくなります。 R では,別々の型のオブジェクトを1つの型にそろえなければならない場合, 情報を保存するために表中の下方にある型へそろえるよう自動的に変換(coerce)します。

(x <- c(TRUE, 3))
typeof(x)
(x <- c(TRUE, 3, "OK"))
typeof(x)

属性

あらゆるオブジェクトには属性(attributes)をつけることができます。 属性とは,オブジェクトのメインの情報(データ)を補助するサブ情報のようなものです。 ある種類のデータ構造を持つオブジェクト(例えば行列など)には必然的に付いている属性もありますし, ユーザが自分の好きな名前の属性を任意のオブジェクトに付加することもできます。

(x <- data.frame(matrix(1:6, nrow=3)))
attributes(x)                                     # このオブジェクトの属性を一覧
attr(x, "names")                                  # "names"属性を取得
attr(x, "my.attribute") <- c("A", "E", "I", "O")  # 新しい属性を付加
attributes(x)
attributes(x) <- NULL                             # 属性をすべて消す

Rのオブジェクトの振舞いの多様性を支える様々なものがこの属性の仕組みに依拠しています。 行列や配列の次元,リストやデータフレームの要素名,ベクトルや行列の行ラベルや列ラベル, ファクターの水準,S3クラス,などなど。

関数

R には特殊なオブジェクトとして関数(function)が存在します。 関数は呼び出す(call)ことができ,このとき関数の内容が評価されます。

R の持つ統計解析,グラフィックスなどの機能はすべて関数として提供されます。基本的な機能を提供する関数は, R のデフォルトパッケージに含まれていて,常に利用できるようになっています。 それ以外の機能がほしい場合はさらなるパッケージをインストールすることで補えますが, このときに行われることも基本的には関数の追加です。

関数名を print() に渡すと,関数の定義内容を表示することができます。

print(cor)
cor   # 上と同じ

戻り値

Rでは,すべての関数は戻り値(return value; 返り値とも呼ばれる)を返します。 返ってくるのは常にオブジェクトです(ただし様々な型があり得ます)。

関数呼び出しとは,表現式の中でその関数が書いてある部分を戻り値で置き換えること, とイメージするとわかりよいかもしれません。

引数

関数は0個以上の引数(arguments)を取ることができます。 引数は関数へのインプットであり,関数を呼び出すときに引数の値を変えることで関数の戻り値や副作用を変化させられます。

ある関数が1つ以上の引数を取るものと定義されている場合,各引数には別々の名前が付いています。 引数の名前や,その引数が何を意味しているか,それぞれの引数が要求する型は何か,などは関数によって異なります。 関数の引数名の一覧は args() 関数で得られますが, それだけの情報では役に立たないので,初心者は help() 関数って引数の意味を調べましょう。

引数に関しては,仮引数(formal arguments)と実引数(actual arguments)を区別する必要があります。 仮引数とは,関数定義において使われているシンボルです。実引数とは,呼び出し時にそのシンボルに当てられた値です。

引数にはデフォルト値(default value)が定義されているものと,そうでないものがあります。 デフォルト値を持つ引数は,呼び出しのときに値の指定を省略することができ,その場合にデフォルト値が用いられます。 デフォルト値を持たない引数は,多くの場合,省略できません(ただし例外はあります)。 デフォルト値の定義されていない引数だからといって,必ず指定しなければならない訳ではありません。 関数の内容を評価する際にその引数が用いられなければ問題はありませんから,実引数を渡さなくても場合によってはエラーが出ないときもあります。

mydata <- c(2, 3, 6, 4, NA, 8)
median(x=mydata)  # na.rm 引数は省略されており,デフォルトの FALSE が用いられる

R言語における実引数の記述方法はとても柔軟です。 どの値がどの引数に割り当てられるかは次の順で決められます。

  1. 名前指定つきで渡されている実引数は,その名前に当てはまる仮引数が探し出され,そこに割り当てられます。
  2. 名前指定されているけれどもそれに完全に一致するものがない場合,部分的に一致(前方一致)する名前があれば,その仮引数に割り当てられます。 名前指定されているのに部分一致すらしないときは,エラーになります。
  3. 名前指定つきを割り当て終わったら,名前の指定されていないものを残っている仮引数の前から順番に割り当てます。
  4. すべての仮引数に割り当て終わってもまだ実引数が残っている場合,もし関数が"..."引数を持っているならすべてそこへ割り当てられます。 もしもっていなければ,エラーになります。
# mean(x, trim = 0, na.rm = FALSE, ...)  # mean()関数の引数
a <- c(4,6,2,7,4,5,NA,2,4,2,6,3,2,2,5,1)
mean(trim = 0.1, na.rm = TRUE, x = a)  # 全部名前を指定しているので並べる順序は関係ない
mean(a, na.rm = TRUE, 0.1)             # a は x に,0.1 は trim に割り当てられる
mean(na = TRUE, a)                     # na は na.rm の省略と見なされる. trim はデフォルト値

"..."(dot-dot-dot)は特別な意味を持つ引数です。簡単に言えば「その他すべて」という意味で, 主に,ある関数内で他の関数を呼び出す際に引数を丸ごと引き渡すのに利用されます。 例えばplot()などの作図関数で活用されています。

関数の定義

function() を使うことで,自分で関数を定義できます。

function(引数リスト) 表現式

表現式の部分はブロックを使うことで複数行にできます(たいていの関数は複数行です)。 しかし,1つの表現式で済む場合は,表現式部分を { } で囲む必要はありません。

plus <- function(x, y = 1) { x + y }  # この場合,{ } は必要ない
plus(10, 20)
plus(10)

myfunc <- function(x = 0, y, z=10) {
  x <- x * 2
  (x + y) ^ z
}
myfunc(10, 20)
myfunc(10)    # エラー
myfunc(y=10)  # これならOK

function()が返すのは関数オブジェクトです。 これを変数に代入すれば関数に名前をつけることになりますが,名前をつけなくても関数を使う(呼び出す)ことはできます。 名前をつけられていない関数は匿名関数(anonymous function)と呼ばれます。

クロージャ

R では関数はおおきく3種類に分けられます: クロージャ (closure),組み込み関数 (builtin functions),特殊関数 (special functions)です。

組み込み関数と特殊関数は print() しようとすると .Primitive("...").Internal(...) という表示がされ,その内容が R 上で見えなくなっています。 組み込み関数と特殊関数の違いは,その引数を評価するかしないか(前者は評価して値を内部関数に渡す,後者は評価しないで渡す)です。 R 上では, typeof() 関数を用いることで組み込み関数と特殊関数を見分けることが可能です。 .Internal.Primitive の違いは,.Primitive のほうが引数渡しの効率がよいことです。 .Internal のほうがより複雑なことをしていますが,そのおかげで名前での引数指定や可変長引数が実現されています。 .Primitive は位置でしか引数を区別できません。よって複数の引数をとる関数の実装に .Primitive を用いるのは非推奨になっています。 基礎的な演算子や制御構造,I/Oなど,R 言語の基盤的機能をもたらす関数が,この組み込み関数や特殊関数として実装されています。

これら以外の一般的な関数はすべて,クロージャです。クロージャとは,関数自体(仮引数 formals と定義内容 body )とそれに結びつけられた環境 environment との組です。 関数を作ったときに,関数定義を行った環境が自動的に結びつけられ,クロージャとなります。 よってクロージャの含む環境は,その後にその関数の呼び出しを行う環境とは異なっているかもしれませんので,注意が必要となる場合があります。 関数定義を行った環境オブジェクトは,クロージャとして結びつけられているために「使用中」となり,その関数(クロージャ)が消されるか, 意図的な操作で関数と切り離されるか,関数と環境があわせて孤立するかしないと,環境は消えません。そうすると,その環境の中のオブジェクトも残り続けます。 R では関数内部で参照される個々の変数ではなく親環境まるごとをクロージャとして結びつけるので,親環境内の使われない変数もガベージコレクションされません。 また,環境オブジェクトはコピーされないという規則に合致して,エンクローズもコピーではなく参照なので,関数定義の後に親環境の変数値を書き換えることで関数の振る舞いが変わります。

関数呼び出し

通常,関数を呼び出すことを表す表現式は,関数名の後に () を付けるだけです。必要ならその()内に引数を書きます。

proc.time()
mean(1:10)

Rでは,関数(function)だけでなく関数呼び出し(call)もオブジェクトです。 関数呼び出しオブジェクトを得る一番簡単な方法は,quote()関数です。

x <- quote(sqrt(2 + 7))  # quote()の引数の表現式は評価されず,callオブジェクトを作る
x
mode(x)
eval(x)  # 呼び出しを実行

関数呼び出しオブジェクトはリスト構造をしています。 リストの最初の要素は関数名のシンボル,2つ目以降の要素は引数を表します。

(x <- quote(sqrt(2 + 7)))
length(x)  # 2 が返る
x[[1]]
x[[2]]
length(x[[2]])  # 3 が返る
x[[2]][[1]]
x[[2]][[2]]
x[[2]][[3]]
x[[2]][[1]] <- as.name("rep")  # 一部を書き換え
x
eval(x)

再帰

関数の中で自分自身を呼び出す再帰関数を作りたい場合,素朴には,

myfunc <- function(x) {
  if (x == 1)
    1
  else
    x * myfunc(x - 1)
}
myfunc(5)

このように作るかもしれませんが,これだとコードが関数につけた名前に依存しているので,関数の名前を変更してしまうと動かなくなります。

yourfunc <- myfunc
rm(myfunc)
yourfunc(5)

Recall() を使うことで,名前に依存せずに再帰関数を作ることができます。

myfunc <- function(x) {
  if (x == 1)
    1
  else
    x * Recall(x - 1)
}
myfunc(6)
yourfunc <- myfunc
rm(myfunc)
yourfunc(6)

# 匿名関数でもOK
(function(x){
  if (x <= 0)
    0
  else
    x + Recall(x - 1)
})(7)

演算子

R における基本的な演算子の一覧はヘルプにありますので参照してください。

基本演算のヘルプ項目
種類ヘルプ項目
論理演算?Logic
比較演算?Comparison
算術演算?Arithmetic

R では,演算子(operators)も実は関数です。 演算子は構文解析において特別視され,トークンの特殊な配置が許されているだけです。

3 + 5      # 通常の表記
"+"(3, 5)  # 関数呼び出しと同じ表記. これでも動作する

典型的な演算子だけでなく, [ ] や ( ) なども同様に関数です。

x <- matrix(1:6, nrow=2)
"["(x, , 2)   # x[,2] と同じ
演算子の定義

演算子を自分で定義することもできます。

5 + 6   # 普通の足し算
"+" <- function(x, y) x * y   # +演算子を上書き
5 + 6   # ???
rm("+")

しかしこのような既存の演算子の上書きは,たとえ許されているとはいえ,しないほうがよいのは明らかです。 R の仕組みを知るためだけに留めておきましょう。

独自の演算子を定義したい場合は %any% が使えます。 any の部分には好きな文字を入れられます。1文字でもよいです。

3 %m% 8   # エラー
"%m%" <- function(x, y) (x + y) / 2
3 %m% 8
演算子の優先順位

一つの表現式の中に複数の演算子が含まれている場合に,どれが先に評価されるかという規則があります。 これを優先順位(precedence)と呼びます。 前から順番に,とは限りません。

ヘルプに優先順位の表がありますので,参照してください。

?Syntax

基本的な関数

基本的で代表的な関数をリストアップしておきます。 もちろんこれがすべてではありません。 とくに,下にリストした関数のヘルプを参照すれば,それぞれに関連した関数などを見つけることができるはずです。

数学関数
関数名説明
sqrt平方根
abs絶対値
sinサイン
cosコサイン
tanタンジェント
asinアークサイン
acosアークコサイン
atanアークタンジェント
log対数関数
exp指数関数
betaベータ関数
gammaガンマ関数
choose二項係数
factorial階乗
floor切捨て
ceiling切り上げ
round四捨五入
sign符号
文字列操作関数
関数名説明
paste文字列を結合
nchar文字数を取得
substr部分文字列を取得,置換
substring部分文字列を取得,置換
strsplit文字列を区切る
strtrim文字の切捨て
grep文字列内のパターン検索
sub文字列内のパターン置換
pmatch部分文字列のマッチ
charmatch文字の検索
chartr文字の置換
tolower小文字に変換
toupper大文字に変換
行列操作関数
関数名説明
%*%行列積
%o%外積. outer()も同じ
%x%クロネッカー積. kronecker()も同じ
crossprod()クロス積. t(x1) %*% x2 と同じ
t()転置
diag()対角成分を取得,設定
upper.tri()上三角成分を示す論理値ベクトル
lower.tri()下三角成分を示す論理値ベクトル
det()行列式
solve()逆行列
eigen()固有値と固有ベクトル
qr()QR分解
svd()特異値分解
dim()行数,列数
nrow()行数
ncol()列数
rownames()行ラベル
colnames()列ラベル
記述統計関数 ※ の付いた関数が返す値は記述的でありません。
関数名説明
sum()合計値
mean()算術平均値
median()中央値
min()最小値
max()最大値
range()範囲
quantile()分位点
fivenum()Tukeyの五数要約
boxplot.stats()箱ひげ図に用いられる統計量
summary.default()numericの場合,quantile() と mean()
hist(plot=FALSE)ヒストグラムに用いられる値
stem()幹葉表示
ecdf()経験累積分布関数
table()クロス表
mad()中央絶対偏差
var()分散 ※ 正しくは不偏分散
sd()標準偏差 ※ 正しくは不偏分散の平方根
cov()共分散 ※ 正しくは不偏共分散
cor()相関係数
分布関数
d: densityp: probabilityq: quantiler: random
*unifdunifpunifqunifrunif一様分布
*normdnormpnormqnormrnorm正規分布
*lnormdlnormplnormqlnormrlnorm対数正規分布
*betadbetapbetaqbetarbetaベータ分布
*gammadgammapgammaqgammargammaガンマ分布
*chisqdchisqpchisqqchisqrchisq非心カイ二乗分布
*fdfpfqfrfF分布
*tdtptqtrtt分布
*cauchydcauchypcauchyqcauchyrcauchyコーシー分布
*weibulldweibullpweibullqweibullrweibullワイブル分布
*expdexppexpqexprexp指数分布
*logisdlogisplogisqlogisrlogisロジスティック分布
*tukeyptukeyqtukeyステューデント化された範囲の分布
*poisdpoisppoisqpoisrpoisポアソン分布
*binomdbinompbinomqbinomrbinom二項分布
*nbinomdnbinompnbinomqnbinomrnbinom負の二項分布
*multinomdmultinomrmultinom多項分布
*geomdgeompgeomqgeomrgeom幾何分布
*hyperdhyperphyperqhyperrhyper超幾何分布
*wilcoxdwilcoxpwilcoxqwilcoxrwilcoxウィルコクソンの順位和統計量の分布
*signrankdsignrankpsignrankqsignrankrsignrankウィルコクソンの符号付き順位統計量の分布
確率密度(分位点)累積確率(分位点)分位点(累積確率)乱数(個数)

行列に対してふつうの演算子( * など)を使うと,成分ごとの計算をするので注意してください。

(x <- matrix(1:4, nrow=2))
(y <- matrix(5:8, nrow=2))
x %*% y
x * y

ベクトル化

多くの関数(とくに,基本的な関数)はベクトル化(vectorize)されています。 ベクトル化とは,引数に1つの値ではなくベクトルを渡したときに, 要素ごとに処理し,それらの結果をまとめてベクトルで返すようになっている,ということです。

sqrt(1:5)
1:5 * 2
1:5 * 6:10
1:10 > 5

環境

Rには,環境 (environment) の概念があります。 これは,表現式が評価されるときの文脈のようなものです。 特別なことをせず普通に R を使っているだけでも,同時にたくさんの環境が存在しています。 例えば,関数を呼び出すと,そこで新しい環境が作られます。 シンボル(変数名)はどこかの環境に属します。 環境は他の言語でいうところのスコープの概念に似ています。 しかし,厳密にはRでのスコープと環境の概念は異なります。

コンソールへの入力を評価する場合などに用いられる基本となる環境をグローバル環境(global environment)といい, .GlobalEnv という名前で環境オブジェクトを参照できます。 コンソールへの入力で代入を行って変数を作った場合には,その変数はこのグローバル環境に属します。

環境は,フレーム (frame) と エンクロージャ (enclosure) でできています。 フレームとは,シンボルと値のペアのリストのことです。どの名前がどのオブジェクトと結びついているかの情報を保持しています。 これによって,名前でベクトル値や関数などを参照することが可能となります。

環境は,その中に他の環境への参照を持っています。これがエンクロージャです。エンクロージャとはクロージャを作ったもののことです。 シンボルを解決しようとして,フレームにそのシンボルが見つからないときには,エンクローズした環境へ探しに行きます。 探しに行った先の環境もまた,エンクロージャを持っています。シンボルが見つからない場合には,こうやってどんどんエンクロージャを辿っていきます。 環境はただ1つの他の環境を参照しています。また,循環はしません。よって,環境は木構造を形成し,エンクロージャは親環境と捉えることができます。 この木の根にあたる環境(辿っていくと最後に行き着く環境)は,空環境 (empty environment) という特殊な環境です。 そのフレームには何のシンボルも登録されておらず,親も持ちません。通常は,base パッケージの環境の親がこの空環境になります。

サーチパス

サーチパス(search path)とは,特定のシンボル(変数名)が与えられたときに,オブジェクトを探し出す場所の系列です。この「場所」とは,通常は環境です。 R はシンボルを評価するときにサーチパスに登録されている場所を探し,どこにも見つからなければ「オブジェクト "~" は存在しません」 というエラーを出します。 このサーチパスは,R を使用している間に動的に変化します。

search()  # 現在のサーチパスを表示

library() 関数を使ってパッケージをロードした場合,そのパッケージの環境がサーチパスに登録されます。 これによって,パッケージ内の関数や変数が単純な記法で使えるようになります。

library(nlme)
search()

attach() 関数を使うと,リストやデータフレームをサーチパスに登録できます。 登録すると,要素名を変数名のように使うことができます。 サーチパス上の登録される位置はグローバル環境の直後です。

iris$Sepal.Length  # attach()していないとこう書く必要がある
attach(iris)
Sepal.Length    # これでOK

attach()library() を使ったときには,サーチパス上の登録される位置は,デフォルトではグローバル環境の直後になります。 この位置は引数を指定することで変更できますが,通常はデフォルトの位置が使い勝手が良いはずです。

detach() を用いて,attach() されたデータフレームなどをサーチパスから外すことができます。 この関数は library() でロードされたパッケージをサーチパスから外すこともできます。

search()
detach(iris)
search()

レキシカルスコープ

関数を呼び出すときには毎回,関数の内容を評価する場所としての新しい環境が作られます。 関数の中のローカル変数(引数も含みます)は,この新しい評価用環境に追加されていきます。

関数が作成される際( function( args ) body を評価する際),クロージャに関数定義を行った環境が結びつけられますが, 関数呼び出し時に作成される新しい評価用環境の親としてセットされるのが,このクロージャが含む環境です。 これによって,関数を定義しようとした時に「そこにあった」関数の外の変数に,関数内部からいつでも(関数をどこかへ移しても)アクセスすることが可能になります。

一方で,関数評価の際にシンボルを解決しようとしてたどる環境の系列(サーチパスを最後に含む)と,関数呼び出しの連鎖によってできる構造(コールスタック call stack )にもとづく環境の系列は, 異なってくる場合があり,そのせいで自分の作った関数が直観に反する動作をするかもしれません。

print.search <- function(env) {
  print(env)
  cat("\n")
  if (!identical(env, emptyenv()))
    Recall(parent.env(env))
}

print.stack <- function() {
  i = 1
  repeat {
    env <- parent.frame(i)
    print(env)
    cat("\n")
    if (identical(env, globalenv()))
      break
    i <- i + 1
  }
}

f1 <- function() {
  # a <- 2
  function(x) {
    # print.stack()
    # cat("- - - - - - - - - -\n")
    # print.search(environment())
    a * x
  }
}
f2 <- f1()
f3 <- function(x) {
  a <- 3
  f2(x)
}
a <- 4
f3(5)

存在する変数への代入

<<- (逆方向 ->> も可)という演算子は,通常の代入 <- と似ていますが,以下の点で異なる振る舞いをします:

  1. エンクロージャをたどり,その名前ですでに存在する変数を探します。現在の環境は探しません。
  2. 見つかった場合,その変数の値を書き換えます。もしパッケージの環境内で見つかった場合,ロックされているためにエラーが出るかもしれません。
  3. 見つからなかった場合,グローバル環境にその名前の変数を作成し,値を入れます。

このような振る舞いをすることから,<<- を用いる場合は,環境とエンクロージャについて理解していなければ困惑してしまう可能性があります。

demo(scoping)

名前空間

各パッケージはロード時にそのパッケージ用の環境を持ちますが,さらに,パッケージには名前空間 (namespace) を持たせることができます。CRAN で公開されているパッケージのほとんどは名前空間を持っています。 名前空間を持っているパッケージでは,サーチの順序が通常と異なります。 パッケージ内の関数がローカルにない変数を参照しようとしたとき,まずその名前空間内のオブジェクトを探します。 次に,パッケージ同士でインポート関係がある場合,インポートしているパッケージの中を探します。 次に,base パッケージの名前空間の中を探します。 その後,グローバル環境から始まる通常のサーチパスを探します。

package::name という表記を用いることで,特定のパッケージ名を指定してそれがエクスポートしているオブジェクトにアクセスできます。 これを用いれば,例えばグローバル環境に同じ名前のオブジェクトがあったとしても,それを避けて,パッケージ内のオブジェクトを操作することができます。 もしパッケージが名前空間を持っているなら,library() でサーチパスに登録されていなくても,この表記でパッケージ内のオブジェクトにアクセスできます。 また,パッケージが名前空間を持っていない場合でも,ロードされサーチパスに登録されている状態ならば,この表記でパッケージを特定してアクセスできます。

package:::name という表記もあります。こちらは,エクスポートされているオブジェクトだけではなく,パッケージ内の内部オブジェクトにアクセスしようとます。 このようなアクセスはおそらくパッケージの開発者の意図から外れているでしょうから,特別な理由がない限り,使用するのは非推奨とされています。

クラス

R はオブジェクト指向プログラミング(Object-Oriented Programming; OOP)を2種類の方法で取り入れています。 クラス(class)もRのオブジェクトの種類を表す情報の一種なのですが,このOOPと強く結びついた概念です。 現バージョンの R にはS3クラスとS4クラスの2タイプの全く異なったクラス情報が存在します。 R におけるOOPは依然として不完全であり,擬似的なものとして受け取るのがよいでしょう。

S3クラス

S3クラスの実体は,オブジェクトの class 属性です。 class 属性の値はクラスの名前を表す文字列です。 値には文字列ベクトルも指定でき,これによって1つのオブジェクトが複数のクラスに所属していることを表します。 OOPの特徴の1つである「継承」もこれによって実現します。

class 属性もただの属性なので,自分で簡単に変更できます。

(x1 <- gl(3, 3, labels=c("A", "B", "C")))
class(x1)
(x2 <- unclass(x1))  # class属性を消去. class(x1) <- NULL でも消去可.
is.numeric(x2)

class(x2) <- "factor"  # 復活
x2

class 属性がセットされていない場合は,モードが暗黙のクラスとして代用されます。

総称的関数

R の関数のいくつかは総称的関数(generic functions)です。 総称的関数は,引数に渡されたオブジェクトのクラスに応じて, それに適した具体的処理をする関数へ引数を受け渡します。 例えば, print() 関数, summary() 関数, plot() 関数などが総称的関数です。 総称的関数は,関数の中身(ソース)を見たときに UseMethod() 関数を使っています。 これが S3 クラスにおいて「多態性」を実現する手段です。

methods() 関数を使うと,特定の総称的関数が利用可能なメソッド(クラス個別の関数)を一覧できます。

methods(print)

一覧で * の付いていない関数はそのフルネームによって参照できます。

print.anova

一覧で * の付いている関数は名前空間によって隠されているので,それを参照するには getS3method() 関数を使用する必要があります。

getS3method("print", "aov")

S4クラス

S3クラスとは異なり,S4 クラスは専用の関数で定義され,メソッドの定義やプロパティへのアクセスも専用の方法をとります。 ここでの詳細な解説は省きますが,次のドキュメントを参照してください。

R には S4 クラスで実装された stats4 パッケージが標準添付されています。 今後も主要な解析関数が S4 化されていく予定のようです。

help(package=stats4)
require(stats4)
example(mle)

データ構造

R には特徴的なデータ構造がたくさんありますが,よく使われるものを紹介します。

ベクトル

R の最も基本的なデータ構造としてベクトル(vector)があります。 単純な数値や文字列であっても,ベクトルです。 R には純粋なスカラーはなく,長さ1のベクトルとして表現されています。

メモリの許すかぎり任意の長さの,いろいろな型のベクトルを扱えますが,1つのベクトルの要素はすべて同じ型でなければなりません。

ベクトルを作るにはいろいろな仕方があります。簡便でよく使われる方法として c() 関数があります。

x <- c(2, 3, 4, 5)
x

上の例のような整数のシーケンスのベクトルを作る場合は,: (コロン)を使った簡便な記法があります。

x <- 2:5
x

c() 関数は実はベクトルやリストを結合する関数です。 上の例では長さ1のベクトル同士を4つ結合していますが,もっと長いベクトルも結合できます。

x <- 1:40
y <- 100:90
(z <- c(x, y))

ベクトルの長さを得るには length() 関数を使います。

length(z)

行列

統計学で重要な位置を占めるのが行列(matrix)です。 行列を作るには matrix() 関数を使います。 matrix() 関数の第一引数にはベクトルを渡し,第2引数以降で行数や列数などを指定します。

(m1 <- matrix(1:6, nrow=3))
(m2 <- matrix(1:6, nrow=2))
(m3 <- matrix(1:6, nrow=2, byrow=TRUE))

byrow 引数に TRUE を指定することで,行列の値が行方向(横方向)に埋められていきます(デフォルトでは列方向)。

R では,行列も内部ではベクトルで表現されており,次元(dimensions)という属性をもつベクトルと考えられます。 よって,次元属性だけ変更することで,行列の形を変えることができます。

(m1 <- matrix(1:6, nrow=3))
dim(m1)             # 現在の次元情報
dim(m1) <- c(2, 3)  # 次元属性を変更
m1

配列

R における配列(array)とは,二元 two-way である行列を多元 multi-way に拡張したものです。 次元属性を操作することで,任意のn次元配列を作ることができます。 配列を作る array() 関数があります。

(a <- array(1:12, c(3, 2, 2)))
dim(a)

リスト

リスト(list)は,ベクトルと並んで, R において重要なデータ構造です。 統計データを保管する構造としてだけでなく,関数呼び出しなどの R の様々な側面がリストに依拠しています。 R は LISP や Scheme のような関数型言語としても使うことができ,言語がリストを基盤にしている点についてこれらから大きな影響を受けていると思われます。

リストとは,異なる型を要素にできるベクトル,と考えると簡単です。リストを作るには list() 関数です。

(x <- list(3.2, "apple", TRUE))                 # 数値,文字列,論理値を1つのリストに
(y <- list(price=3.2, fruit="apple", red=TRUE)) # 名前つきリスト

リストは,ベクトルと違って,何でも要素にできます。 ベクトルや行列はもちろんのこと,リスト自体もリストの要素にできます。 非常に強力な表現力を持ったデータ構造だということが推し測られます。

データフレーム

データフレーム(data frame) は, R での標準的統計データ構造です。 これは,他の統計ソフト (SAS や SPSS など) の cases × variables データ構造に対応するものです。

そうすると行列でいいように思えるかもしれませんが,データフレームが行列では困ります。 複数の種類の(すなわち複数の型の)変数を格納する必要性にしばしば見舞われるからです。

従って, R のデータフレームは(ベクトルではなく)リストの一種です。 ただし,リストの各要素が同じ長さのベクトルでなければならないという制約が付いています。 これによって,行列のように整って見えます。

データフレームを作るには, data.frame() 関数を使います。

mydata <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6), Tire=c(rep("Michelin", 4), "BS"))
mydata

データフレームは名前つきリストで,リストの各要素の名前をいわゆる統計学的な「変数」の名前として扱います。

ファクター

少々特殊ですが,覚えおくべきデータの種類として,ファクター(factor)があります。 これはいわゆる分散分析で言うところ「要因」,すなわち名義変数(カテゴリカルな変数)を表すためのものです。 順序変数を表す順序つきファクター(ordered factor)もあります。

ファクターも内部ではベクトルで表現されていますが,水準(levels)という属性が設定されており, この水準名が値と見なされます。水準名は内部表現の数値と一対一で対応しています。

ファクターを作るにはfactor()関数を使います。

x <- factor(c("apple", "banana", "banana", "orange", "apple", "orange"))
x
y <- factor(c(1, 2, 1, 1, 2, 1, 2))
y      # 1, 2 は数値ではなく水準名
y * 2  # 計算不可能なので NA が返る

levels() 関数で,水準名の一覧を得ることができます。 また, as.numeric() 関数などで数値に変換すると内部で使われている整数値が得られます。 これは作図の場面などで有益ですので覚えておきましょう。

x <- factor(c("apple", "banana", "banana", "orange", "apple", "orange"))
x
levels(x)
as.numeric(x)

gl() 関数で簡便にファクターを作成することもできます。

gl(2, 4, label = c("Control", "Treatment"))  # 水準数 2,1つの水準の反復数 4

時系列

時系列データを保持するために専用の時系列オブジェクトがあります。

ts(1:21, frequency=12, start=c(2007, 5))

モデル式

モデル式(formula)は R (そして S) の大きな特徴です。 R ではモデル式で統計モデルを表現します。 モデル式もオブジェクトであり,通常は ~ 演算子を使って生成します。

y ~ x1 + x2   # モデル式オブジェクトを返す

モデル式は,線型モデルなどのフィッティング関数やグラフィックスで主に利用されます。 モデル式の中では様々な演算子は通常と異なる意味を持ちます。 詳しくは統計モデルの節を参照して下さい。

種類のチェックと変換

原子型のベクトルからより複雑なデータ構造まで,あるオブジェクトが特定の種類のオブジェクトかどうかの確認や, 特定の種類のオブジェクトへ強制変換をするための関数があります。

多くの種類(行列とか論理値とか)には is.~() 関数(~は種類名)が用意されていて, この関数にオブジェクトを渡すことでその種類であるかどうかを確かめられます。

また, as.~() 関数(~は種類名)も用意されおり, この関数にオブジェクトを渡すことでその特定の種類に変換できます(もちろん渡すオブジェクトの種類によっては変換不可能な場合もあります)。

mydat <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6))
is.data.frame(mydat)
is.list(mydat)        # これも TRUE
is.matrix(mydat)      # これは FALSE
(mymat <- as.matrix(mydat))
is.matrix(mymat)      # TRUE に
is.numeric(mymat)
mymat2 <- as.character(mymat) # 属性は変換によって消える
dim(mymat2) <- c(5, 2)        # 次元属性を復活
mymat2
is.character(mymat2)

また,str()関数がオブジェクトを短い文字列で要約したものを返してくれるので, これを種類の識別に利用することもできます。

mydat <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6))
str(mydat)
str(as.matrix(mydat))

S4オブジェクト

S4クラスに属するオブジェクトは,他の一般的なオブジェクトとは異なるデータ構造を有しています。 オブジェクトが持つ情報を得たり変更したりするには, S4オブジェクトにアクセスする専用の関数や専用のインデクス記号を使わなければなりません。

データハンドリング

添え字

ベクトル,行列,リストなどは,添え字指定(indexing)によってその要素を非常に柔軟に操作できます。 R では添え字は1から始まります。

まず基本として,添え字には数値ベクトルを渡すことができます。

(x <- 4:10)
x[3]           # 3番目だけ
x[1:4]         # 1~4番目
x[c(2, 3, 5)]  # 2, 3, 5番目
x[-4]          # 4番目以外
x[-4:-2]       # 2~4番目以外
x[2] <- 0      # 2番目を 0 に変更
x
x[5:7] <- NA   # 5~7番目を NA に
x
x[]            # xと同じ. ただし重要でない属性が消されたものが返る

添え字には論理値ベクトルも渡すことができます。その場合, TRUE が指定された要素だけが取りだされます。

(x <- 4:10)
x[c(T, T, F, F, T, F, T)]   # 1, 2, 5, 7番目だけ
x[x >= 8]                   # 8以上の値をもつものだけ

要素に名前が付いている場合,名前の文字列でも要素を取り出せます。

x <- 4:7
names(x) <- c("赤", "緑", "青", "黄")
x
x["緑"]

行列やデータフレームは二次元,配列には多次元の添え字が使えます。

(x <- matrix(1:12, nrow=3))
x[2, 3]       # 2行3列目の要素
x[2,]         # 2行目だけ
x[,3:4]       # 3,4列目だけ
x[-2,-1]      # 2行目と1列目以外
x[c(3,1,2),]  # ならべかえ
x[1,] <- 0    # 1行目をすべて 0 に
x
x[2, , drop=F]  # ベクトルにせず行列のままで取り出す

order()関数で,ベクトルの要素の順位を得ることができます。 これを利用してソート(昇順,降順並べ替え)をします。

(x <- matrix(c(5,3,2,7,2,5,4,3,1,6,3,6), nrow=4))
x[order(x[,1]),]                   # 1列目の順序でソート
x[order(x[,1], decreasing=TRUE),]  # 降順

リスト(データフレームも含む)には [ ] による添え字操作だけでなく,[[ ]] による添え字操作もよく用いられます。 [ ] と [[ ]] では意味が異なります。

[ ] による添え字操作では,取り出されるのは(部分的になった)リストです。 [[ ]] による操作では,リストの要素の中身(いろいろな種類がありえる)が取り出されます。

mydata <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6), Tire=c(rep("Michelin", 4), "BS"))
(x <- mydata[2])    # 2列目(リストの2番目)だけのデータフレームを取得
is.data.frame(x); is.vector(x)
(x <- mydata[[2]])  # 2列目の数値ベクトルを取得
is.data.frame(x); is.vector(x)

さらに,リストにおいては,名前が付いている要素には [[ ]] だけでなく $ を使った簡単な記述でもアクセスできます。

mydata <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6), Tire=c(rep("Michelin", 4), "BS"))
mydata$Year  # mydata[["Year"]] と同じ

サブセット

データセットを表すデータフレームからサブセットを取り出すには,上記の添え字を使うのもひとつの手ですが, サブセットを取り出すための専用の関数もあります。

mydata <- data.frame(Year=2002:2006, CPR=c(10, 8, 8, 4, 6), Tire=c(rep("Michelin", 4), "BS"))
subset(mydata, Tire == "Michelin" & CPR < 9) # mydata[mydata$Tire == "Michelin" & mydata$CPR < 9,] と同じ
subset(mydata, Year %in% 2004:2006, select=-2)  # 2列目以外

結合

複数のオブジェクトを結合して新しいオブジェクトを作ることもできます。 とくに,行列やデータフレームを延長する仕方で結合する方法が重要です。

c(1:3, 5:7)  # ベクトルの結合

m <- matrix(1:6, nrow=2)
cbind(m, c(10, 11))                  # 列を追加
cbind(m, c(10, 11), c(20, 21))       # 複数列
cbind(m, matrix(10, nrow=2, ncol=2)) # 行列でもよい
rbind(m, 10:12)                      # 行を追加
rbind(m, matrix(10, nrow=2, ncol=3))
rbind(m, m, m)

d <- data.frame(X=seq(10,25,len=4), Y=I(c("red","blue","green","yellow")))
d1 <- cbind( d, Z=gl(2,2,lab=c("A","B")) )  # 変数を追加
rbind(d1, list(100, "brown", "A"))   # ケースを追加
rbind(d1, list(100, "brown", "C"))   # これは警告
cbind(d, d)

d2 <- data.frame(AA=seq(0,by=0.1,len=5), Y=I(c("green","red","yellow","blue","black")))
merge(d1, d2)   # 共通する変数名でマージ
d3 <- data.frame(AA=seq(0,by=0.1,len=3), BB=I(c("green","blue","white")))
merge(d1, d3, by.x="Y", by.y="BB")  # 変数名を指定 
merge(d1, d3)   # 共通する変数がないと...

apply系関数

行列やリストに対して,すべての要素に同じ関数を繰り返し適用したい場合(そのようなことはよく起こるのですが), ループで処理していたのでは実行速度が低速になるし,記述も煩雑になります。 そういう場合にapply系関数が役に立ちます。

apply()関数は,配列(行列を含む)の任意のマージン(行ごとや列ごと)に,指定の関数を適用し, その結果をまとめて返します。

(x <- matrix(1:12, nrow=3))
apply(x, 1, mean)  # 1 は行ごと
apply(x, 2, mean)  # 2 は列ごと

lapply()関数は,apply()関数のリスト版です。リストに対して,要素ごとに関数を適用します。

(x <- list(a=1:5, b=10, c=rep(4, 6)))
lapply(x, sqrt)

sapply()関数は,lapply()関数と同じくリストを対象にしますが, lapply()関数がリストを返すのに対し, sapply()関数は返すオブジェクトを可能なかぎり単純に(ベクトルや行列に)します。

( x <- list( a=1:6, b=rep(10,6), c=c(rep(4,3),rep(3,3)) ) )
sapply(x, mean)
sapply(x, function(x) x ^ 2)

mapply()関数は,sapply()関数の多変量版で,引数のリストやベクトルを複数とれます。 例えば,長さ4のベクトルを2つ渡したら,それぞれの要素を1つずつ引数として関数が適用され, 全部で関数は4回適用されることになります。

mapply(rep, 1:4, 6:3)

tapply()関数は,ファクターの水準ごとに関数を適用します。 水準別の基本統計量などを求めるのに便利です。

tapply(iris$Sepal.Length, iris$Species, mean)

replicate()関数は,sapply()関数に似ていますが, リストやベクトルを渡すのではなく,1つの数(回数)を指定します。 その回数だけ,第2引数に渡した表現式を評価します。これはシミュレーションなどに便利です。

replicate(100, mean(rnorm(20, m=50, s=10)))

apply()関数は汎用的ですが,行列の合計や平均を求めることはよくあるので, これらについてはさらに高速な専用の関数が用意されています。

(x <- matrix(1:12, nrow=3))
rowSums(x)
colSums(x)
rowMeans(x)
colMeans(x)

制御フロー

Rにも制御の流れを操る構文が用意されています。

条件分岐

条件分岐に使われるのは,他の言語と同様, if 文です。

if (表現式1) 表現式2 else 表現式3

表現式1の評価結果がTRUEの場合,続いて表現式2が評価されます。FALSEの場合は,表現式3が評価されます。 表現式1の評価結果が数値の場合は,論理値に変換して判断されます。 0 は FALSE,その他は TRUE です(ただし NA や NaN は許されません)。

"else 表現式3" は省くことができます。その場合,この構文が意味するのは,表現式2が評価されるかされないか,です。

x <- 4
if (x > 0) { print("Hello") } else { print("Bye") }

if文であっても,全体で表現式であるので,値を返します。 if文全体の値は,表現式2または表現式3のどちらか評価されたほうの表現式の値です。 もし else が省略され,かつ,表現式1がFALSEであった場合,if文の値はNULLです。

x <- 4
y <- if (x > 0) { x ^ 2 } else { 0 }
y

Rには,if文とは別に,ifelse() という関数も用意されています。

ifelse(表現式1, 表現式2, 表現式3)

これも同じく,表現式1のTRUE/FALSEに応じて,表現式2または表現式3が評価され, その値がifelse()関数の戻り値になります。

if文も値を返すにもかかわらず,どうしてこれが用意されているのかというと, ifelse()関数はベクトル化されているのです。 if文はベクトル化されておらず,表現式1にベクトルを渡してもその最初の要素だけが使われます。

x <- -4:4
y <- ifelse(x >= 0, x ^ 2, 0)
y
z <- if (x >= 0) x ^ 2 else 0
z

反復

反復,すなわち,表現式の繰り返し評価(ループ)によく使用されるのは,for文とwhile文です。 とくに for は,Rに特徴的なベクトルやリストをうまく扱えるようデザインされています。

for (変数名 in 表現式1) 表現式2

表現式1は,評価の結果,ベクトルまたはリストを返すものでなければなりません。 そのベクトルまたはリストの各要素が1つ目から順番に「変数名」に与えた名前の変数に代入され, その後,毎回,表現式2が評価されます。 よって,例えば,表現式1が長さ4のベクトルであれば,for文は4回ループし,表現式2は4回評価されることになります。

for (x in 1:10) {
  y <- sqrt(x)
  cat(x, "の正の平方根は", y, "\n")
}

while文では,for文のように反復回数が与えられるのではなく,条件が満たされるまで延々とループします。

while (表現式1) 表現式2

表現式1は条件判断であり,if文と同様,評価結果を論理値として解釈できるものを指定します。 TRUEであれば,ループが継続し,表現式2が評価され,その後また条件判断に戻ってきます。 FALSEであれば,その時点でループは終了し,whileの次の文へ移行します。

x <- 5
while (x > 0) {
  x <- x - 1
  print(x)
}

forwhileの表現式2の中では,breaknextという2つの特殊な文が使えます。 breakが評価されると,ループを中断して(forwhileの)次の文へ移ります。 nextが評価されると,現在の回の評価を中断して,ループの次の回に強制的に移ります。

x <- 5
while (x > 0) {
  x <- x - 1
  if (x == 2) break
  print(x)
}
x <- 5
while (x > 0) {
  x <- x - 1
  if (x == 2) next
  print(x)
}

ループさせる方法を紹介しましたが,基本的にRのプログラミングではこのようなループ構文の使用はできるかぎり避け,同じことをする高速な関数を利用します。 ループは,どうしてもそれを使わなければ目的の処理が記述できない場合にのみ使いましょう。

この他にもrepeatswitchといった構文があります。

定数

下は R の定数と擬似定数をまとめたものです。

定数と擬似定数
シンボル説明
NULLNULL空を意味する
NA各原子型のNAがある欠損(Not Available)を意味する.
NaNdouble非数(Not a Number)を意味する
Infdouble正の無限
12 などdoubledoubleである点に注意
1.2 などdouble
1.2e-3 などdouble指数表現
12L などinteger接尾辞Lはintegerを意味する
12e3L などintegerLは指数表現にも使える
0x12 などdouble16進表記. doubleである点に注意
0x12L などinteger※仕様には入っているが現バージョンではうまく動かない動作するようになった(v2.5.1)
TRUE, FALSElogical
T, Flogicalこれらの省略形は擬似定数なので注意
pidouble円周率. これは擬似定数
LETTERScharacterアルファベット大文字のベクトル. これは擬似定数
letterscharacterアルファベット小文字のベクトル. これは擬似定数
month.namecharacter月名. これは擬似定数
month.abbcharacter月名の3文字略記. これは擬似定数

Tpi などは文法上の定数ではありません。 よって,次のようなことが可能です。

T <- FALSE
pi <- 1

これらは名前を隠しているだけで, Tpi の本体に代入しているわけではないので, rm() を使えば元に戻すことはできます。 ただ,このような操作は混乱を招き,バグの温床になるだけなので, T などを変数の名前に使わないようにしましょう。

NULLNANaN は,比較演算すらふつうにできません。 何をしようとしても,NULL は長さ0のベクトルを返しますし, NANaNNA または NaN を返します。

NA == NA    # TRUE ではない
NULL != NA
0 * NaN   # NaN
0 * NA    # NA

比較演算が使えないので,NULLかどうか,NAかどうかなどを調べるために, 専用の関数が用意されています。

is.null(NULL)  # TRUE
is.na(NA)      # TRUE
is.nan(NaN)    # TRUE

コメント

R言語では,一行の中で # 以降はコメントと見なされ,解析する際に無視されます。 何が書いてあってもコードに影響を与えないので,そのコードが何をしているかの説明や注釈をつけるために使います。

また,スクリプトなどにおいて,Rの表現式を一時的に無効にする(解釈されないようにする)ためにも使います。

x <- 1:6
#x <- mean(x)  # 実行されない
print(x)

R言語では複数行コメントはサポートされていません。 if 文を使って擬似的な無効ブロックを作ることはできますが,これは本来の複数行コメントではありません。 例えば,if の { } の中が妥当なRの構文でなければ,シンタクスエラーが生じます。

x <- 1:6
if (FALSE) {  # 複数の行をまとめて実行されないようにできる
  x <- mean(x)
  print(x)
}

ファイル入出力

統計データの入出力

様々な形式の統計データファイルに対して入出力を行うための関数が用意されています。 それらの関数は基本的にデータフレームを作成します。

read.table()関数は,テキストファイルを読み込みます。 デフォルト引数は,タブ区切りデータを読み取るのに適した値になっています。

(mydata <- read.table("sample1.txt"))   # ヘッダのないデータ
(mydata <- read.table("sample2.txt", header=TRUE))  # ヘッダつきデータ

# 下記にサンプルのデータファイルを置いています
# http://668628.oan9aupt.asia/doc/r-intro-lecture/sample1.txt
# http://668628.oan9aupt.asia/doc/r-intro-lecture/sample2.txt

また,read.table()関数のラッパーとして read.csv()関数が用意されており, CSVファイルを読み取るのに適したデフォルト引数が設定されています。

データフレームをテキストファイルに書き出すには, write.table()関数やwrite.csv()関数を使います。

write.csv(mydata, "sampledata.csv", row.names=FALSE)

Excelからデータを読み込むには,一旦上記の形式のファイルに書き出してから読み込んでもよいですが, それほど大きくないデータならクリップボード経由で受け渡すと簡単です。

mydata <- read.table("clipboard")  # fileに"clipboard"を指定できる

write.table(mydata, file="clipboard", sep="\t", qmethod="double") # sepやqmethodに注意

foreignパッケージにはいくつかの統計ソフト(SASやSPSSなど) の独自形式のデータファイルを読み込む関数が含まれています。 Excelブックをそのまま読み込みたければ,RODBCパッケージやgdataパッケージを使うこともできます。

スクリプトの入出力

ある程度まとまった処理をするようになったら(例えば,データを読み込んで,プロットして,回帰分析して・・・など), コンソールに入力するよりもスクリプトファイルを作ったほうが効率的です。 R の動作を試したりしている段階でなければ,むしろ,最初から全部スクリプトに書いてもよいくらいです。

内蔵のスクリプトエディタの用法についてはRguiの節を参照してください。

スクリプトファイルを読み込んで実行するのは,source()関数です。

source("sample3.R")

# 下記にサンプルのスクリプトファイルを置いています
# http://668628.oan9aupt.asia/doc/r-intro-lecture/sample3.R

オブジェクトをR言語の表現式の形式で出力したり,それを読み込んだりする関数があります。 これを利用すると,現存するオブジェクトのデータをエディタで一部編集したり, R の異なったバージョン間でオブジェクトを読み書きしたりできます。 出力されたファイルは人が読めるものですから, どのように記述されているかを見てみることも理解を深める助けになるでしょう。

aq10 <- airquality[1:10,]
dput(aq10, file="airqual1.R")    # こちらでは出力に変数名がつかない
dump("aq10", file="airqual2.R")  # こちらでは変数名も保存される

x <- dget("airqual1.R") # 評価された結果が返ってくるので代入
source("airqual2.R")    # こちらはそのまま評価するだけでオブジェクトが作成される

オブジェクトの入出力

オブジェクトはバイナリ形式で入出力できます。 この形式はふつうに人が内容を読めるものではありませんが,入出力は高速です。 下に紹介している関数はオプションでASCII形式にもできますが, それでもふつうは内容を読めません。

いくつかのオブジェクトをファイルに保存する関数,ワークスペース全体を保存する関数, それらを読み込む関数があります。

swomen <- women[women$height <= 60,]
save(swomen, file="women.Rdata")  # 1つまたは複数のオブジェクトを保存
save.image(file="workspace.Rdata")   # ワークスペースのオブジェクト全部を保存

load(file="women.Rdata")  # 読み込み

アウトプット

R における評価結果の出力は,通常コンソールウィンドウに表示されます。 コンソールウィンドウからは,現在バッファに残っている内容をコピー&ペーストで他のアプリケーションやファイルに移すことができますが, アウトプットを他へもっていくためのもっと直接的な方法があります。

sink("test-output.txt")
summary(iris)
sink()  # 出力の迂回をやめる

sink() 関数の第一引数にファイル名を与えて呼び出すと,それ以降にコンソールウィンドウで出力されるはずの内容を ファイルへ書き出すことができます。引数を指定することで,メッセージや警告なども sink できます。

このとき,コンソールウィンドウには何も表示されなくなるので注意。 また,sink() によるファイル出力において,折り返しの桁はコンソールウィンドウのそれと同じになります。

第一引数は,他のストリーム処理系の関数と同様の扱いなので,工夫すれば標準出力(stdout)やクリップボードへの sink もできます。

コネクション

R では上の例のように,通常のファイルだけでなく,zipやgzipファイル,HTTPレスポンスやFTPデータ,標準入出力やパイプ,ソケット,クリップボード,文字列型のオブジェクトなど,様々なものを入出力対象として統一的に扱うことができます。 これらを統一するインターフェイスをコネクション (connection) と呼び,"connection" クラスを持つ専用のオブジェクトとして R 上で操作できます。

my.conn <- url("http://www.iimc.kyoto-u.ac.jp/", encoding="EUC-JP")
attributes(my.conn)
print(my.conn)
readLines(my.conn, n=10)

開いたコネクションは,自分で明示的に閉じることもできますが,コネクションオブジェクトが使われなくなった時にオブジェクトの破棄と共に自動的に閉じられます。 ただしその際は警告が出ます。

グラフィックス

R のグラフィックスの様子を見るために,まずはデモを実行してみましょう。

demo(graphics)

作図命令

統計グラフなどの図を描くための R への命令を作図命令(plotting commands), あるいはそれが関数であることから作図関数(plotting functions),と呼びます。 作図命令は,大きく3つに分類できます。

高水準グラフィックス関数(high-level graphics functions)あるいは 高水準作図関数(high-level plotting functions)は, それ1つ呼び出すだけでとりあえず体裁のとれた図を描くことができる,便利な関数です。 代表的な高水準グラフィックス関数を下の表にまとめました。

代表的な高水準グラフィックス関数
関数名説明
plot()総称的関数で,渡すオブジェクトの種類によって様々な図を描く
boxplot()箱ひげ図
hist()ヒストグラム
stripchart()ストリップチャート
barplot()棒グラフ
dotchart()ドットチャート
pairs()散布図行列
curve()関数グラフ
qqplot()Q-Qプロット
coplot()条件つき散布図
matplot()多変量散布図
sunflowerplot()ひまわり散布図
stars()星形図
symbols()シンボル散布図
image()カラー分布図
contour()等高線図
persp()俯瞰図
scatterplot3d()三次元散布図
pie()円グラフ
mosaicplot()モザイクプロット
assocplot()連関プロット
fourfoldplot()四重プロット

高水準グラフィックス関数は,引数を指定することで,図の様々な要素をカスタマイズすることができます。 (場合によっては図を描画しないことすらできます!) しかし,引数によって変更できる範囲にはたいてい限界があって, 引数ですべての要素を自由自在にカスタマイズできるわけではありません。

低水準グラフィックス関数(low-level graphics functions)あるいは 低水準作図関数(low-level plotting functions)とは, 図の個別の要素(プロットの点や目盛りや凡例などなど)を描画するものです。 高水準グラフィックス関数の引数ではどうにもならない部分も, 低水準グラフィックス関数を適用すれば任意の図を描くことができます。

しかし,低水準グラフィックス関数だけを使って図を描こうとすると, 図のすべての要素の描画を自分で指定せねばならず(たくさんの関数呼び出しを行わねばならず), とても簡便とはいえません。 そこで,通常は高水準グラフィックス関数に大部分描画をまかせて, カスタマイズしたい部分だけ低水準グラフィックス関数を用いて高水準関数が描いた図の上に重ね描きする という手段がとられることが多いです。

代表的な低水準グラフィックス関数を下の表にまとめました。 高水準グラフィックス関数は低水準グラフィックス関数の組み合わせて作成されています。

代表的な低水準グラフィックス関数
関数名説明
points()点を描画
segments()1つずつ離れた線分を描画
lines()連なった線分を描画
arrows()矢印を描画
abline()直線を描画
rect()矩形を描画
polygon()多角形を描画
text()文字を描画
mtext()マージン上の文字を描画
title()タイトルを描画
legend()凡例を描画
axis()軸を描画
grid()格子線を描画
box()枠を描画
plot.window()座標系設定
plot.new()新しいプロットフレームを作る
rug()ラグプロット

text()legend()axis() など, 文字列を描画する関数には,表現式オブジェクトまたは関数呼び出しオブジェクトを渡すことで, 数式を描画することができます。詳しくは数式の節を参照してください。

curve(sin, main=expression(y == sin(x)), xlim=c(0, pi * 2))

対話的グラフィックス関数(interactive graphics functions)とは, グラフィックウィンドウへのユーザの入力(マウス入力)に応じて, グラフィックスの情報を取得したり,描画したりする関数です。 代表的な対話的グラフィックス関数を下にまとめました。

代表的な対話的グラフィックス関数
関数名説明
locator()座標の取得
identify()プロットポイントの情報を取得,追加描画. 総称的関数

グラフィックス デバイス

グラフィックス デバイス(graphics devices; graphical devices)とは, グラフィックスの出力先です。 出力先がなければ図は描画できませんので,グラフィックスを利用する場合は最初に何らかのグラフィックスデバイスを開始させておく必要があります。 しかしながら,高水準グラフィックス関数(実際には plot.new() 関数)は, 何もグラフィックスデバイスが開始されていない場合には,デフォルトのデバイスを自動的に開始しますので, いきなり plot() などしても大丈夫なようになっています。

利用可能なグラフィックスデバイスは1種類ではありません。 Windows版 R においては,下の表にあるデバイスが利用可能です。

Windows版で利用可能なグラフィックスデバイス
関数名説明
windows()グラフィックスウィンドウ
postscript()PostScriptファイル
pdf()PDFファイル
png()PNG画像
jpeg()JPEG画像
bmp()ビットマップ画像
tiff()TIFF画像
pictex()PicTeX画像(LaTeX用)
xfig()XFig画像
win.metafile()Windowsメタファイル画像
win.print()プリンタへ出力
bitmap()GhostScriptがインストールされている場合に,GhostScriptを通して
サポートされる任意のフォーマットの画像へ変換する

これらの関数を明示的に呼び出すことで,そのグラフィックスデバイスを開始させることができます。 同時に複数のデバイスを開始させることもできますが,描画対象になるのはどれか1つです。 この場合,どれかが「現在のグラフィックスデバイス」になり,関数で現在のデバイスを切り替えることができます。 デバイスからデバイスへのコピーも可能です(グラフィックスウィンドウの持つ「ファイルに保存する」機能はこれを利用しています)。

png("sample.png")
hist(rnorm(1000))
dev.off()  # デバイスを閉じる

一部のデバイス( postscript()pdf() )は複数のページを作る機能を持っているので, 次々と複数の図を描画した場合にそれらを1つのファイルにまとめて出力することができます。

グラフィックス パラメータ

グラフィックス パラメータ(graphics parameters; graphical parameters)は, R のグラフィックス一般に適用されるオプションです。 パラメータは作図関数の引数で指定できるものもありますが,指定できないものもあります。 指定できない分は,そのときに設定されているグラフィックス パラメータの値が用いられます。 逆に,作図関数の呼び出しで指定したパラメータは,グラフィックス パラメータの値よりも優先されます。

グラフィックスパラメータの一覧はヘルプに載っています。 日本語の情報が必要であれば,Rのグラフィックスパラメータ を参考にしてください。

?par

グラフィックスパラメータは大域的です。つまり,作図関数の引数とは違って,一度設定するとずっとその設定が残ります。 よって,「この図を描くときだけ一時的にパラメータを変更したい」というような使い方をするなら(そして多くのRユーザはこの使い方ですが), 作図が終わった後に元のパラメータ値に戻す必要があります。

par()関数でグラフィックスパラメータを設定したとき,戻り値は設定変更前のパラメータリストなので, これをとっておいて,後でその値に設定しなおします。

oldpar <- par(bg="lightblue1")
plot(airquality[3:4]) # 何か作図
par(oldpar)

点と線の種類

グラフィックスパラメータのうち pch はプロットする際の点(マーク)の種類, lty は折れ線グラフなどで線を描く際の線の種類を指定するものです。

プロットの点の種類の指定や線の種類の指定はよく使うと思われるので,種類を一覧できるスクリプトを下に用意しました。

local({
	pch_mat <- matrix(0:127, ncol=16, byrow=TRUE)
	plot.new()
	plot.window(xlim=c(0.5,ncol(pch_mat)), ylim=c(nrow(pch_mat), 0.5))
	for (y in 1:nrow(pch_mat)) {
		for (x in 1:ncol(pch_mat)) {
			points(x, y, pch=pch_mat[y,x], cex=2.5)
			text(x-0.35, y-0.35, pch_mat[y,x], cex=0.5)
		}
	}
	title("pch")
})

pch には文字か数値を指定できます。文字を指定した場合はその文字が座標上の点を表すマークとして使われます。 2文字以上の文字列を指定したなら,使われるのは最初の1文字だけです。

pch に数値を指定した場合は,0 ~ 25 の数値はプロット用のマーク(○とか△とか)を指定する番号として扱われます。 32 以上の数値は文字コードとして扱われ,対応する文字がマークに使われます。 26 ~ 31 の数値は現在のバージョンでは使用できないようです。

local({
	lty_list <- c(list("blank", "solid", "dashed", "dotted", "dotdash", "longdash", "twodash"),
		0:15, c("11", "12", "18", "1F", "FF", "1814", "2224282C"))
	plot.new()
	plot.window(xlim=c(0,1), ylim=c(1, length(lty_list)))
	for (y in 1:length(lty_list)) {
		segments(0, y, 1, y, lty=lty_list[[y]])
		mtext(ifelse(is.character(lty_list[[y]]), paste('"', lty_list[[y]], '"', sep=""),
			lty_list[[y]]), 2, at=y, las=1, cex=0.7)
	}
	title("lty")
})

lty には数値か文字列を指定します。 線の種類を表す文字列が定められており,上のスクリプトによる一覧にある下方の 7 種類です。

lty に数値を指定する場合,通常,使用するのは 0 ~ 6 です。 上のスクリプトによる一覧でわかるように,0 は線を描かないという指定になり,1 ~ 6 は線の各種類を表す番号です。 それぞれ上の文字列値と対応します。 7 以上の数値 n を指定した場合,n %% 6 と同じ種類を指定したことになるようです。

また,lty は上述の 6 種類だけでなく,いろいろな種類の破線を指定できるようにもなっています。 これを用いる場合,1 ~ 9 と A, B, C, D, E, F (16進数を表す) の文字を,偶数個(ただし 2~8 文字)並べた文字列を指定します。 最初の文字から順番に,線の長さと空白の長さを表すことになります。

色の指定は,色名,RGB指定,HSV指定,HCL指定などが使えます。 色名を使うのがわかりやすいですが,"#RRGGBB" という文字列でのRGB指定もできます。 あるいは,下の表にある色指定の関数を利用することもできます (これらの関数は引数の値に対応した"#RRGGBB"を返します)。

色指定に関する関数
関数名説明
colors()利用可能な色名の一覧
rgb()RGBの値で色を指定
hsv()HSVの値で色を指定
hcl()HCLの値で色を指定
gray()グレースケールのレベルで指定
col2rgb()Rでの様々な色指定からRGB値を得る

R のグラフィクスでは色分けやグラデーション描画などを行うためにパレット(palette)を使うことが多いです。 パレットとは,単純に,色指定文字列のベクトルであり,番号を指定するとそれに対応する色が得られます。 R にはあらかじめパレット作成関数がいくつか備わっています。

パレットに関する関数
関数名説明
palette()現在のパレット(番号指定した場合の色)の値を取得,設定
rainbow()指定の長さで虹色のパレットを作成
heat.colors()指定の長さで熱色のパレットを作成
terrain.colors()指定の長さで地形用のパレットを作成
topo.colors()指定の長さで地理用のパレットを作成
cm.colors()指定の長さでCM(Cyan-Magenta?)のパレットを作成
gray.colors()指定の長さで灰色のバリエーションのパレットを作成
colorRampPalette()指定の色の間を補間した(グラデーションの)パレットを作成

パレットの色の具合を確認するには,円グラフの関数を利用すると手軽です。

pie(rep(1,16), col=topo.colors(16))  # 16段階のtopo.color

フォント

グラフィックスにおいて Rdevga にて設定された既定のフォント以外を使用したい場合, windowsFonts() 関数を使って新たなフォントファミリーを定義します。

定義されたフォントファミリーは作図関数やグラフィックスパラメータの family 引数で使用できます。

windowsFonts()  # 現在定義されているファミリー
windowsFonts(MyFont1 = "HGS教科書体")  # 新しくMyFont1を定義する. 名前は任意
plot(1:5)
text(3, 4.5, "新しく定義したフォントファミリー", family="MyFont1", cex=2)

領域とマージン

グラフィックスデバイスの中の領域は3つに分類されます。

プロット領域(plot region)
真ん中のグラフを描く部分です。この領域の外周が軸になります。
作図領域(figure region)
プロット領域を含む,1つの図全体を表す領域です。軸ラベルやタイトルなどもこの領域に入ります。 この領域の中でプロット領域以外の部分は内部マージン(inner margin)あるいは単にマージンと呼ばれます。
デバイス領域(device region)
作図領域を含む,グラフィックスデバイスの領域全体です。 この領域の中で作図領域以外の部分は外部マージン(outer margin)と呼ばれます。 デフォルトの設定では外部マージンのサイズは 0 なので,作図領域とデバイス領域はぴったり重なっています。

次のスクリプトを実行すれば,これらの関係が一目でわかります。

op <- par(oma=rep(3,4), mar=rep(3,4))
plot.new()
plot.window(xlim=c(-1,1), ylim=c(-1,1))
box("plot", lty="dotted")
box("figure", lty="dotted")
text(0, 0, "プロット領域")
mtext(paste("内部マージン",1:4), 1:4, line=1)
mtext("作図領域", 3, line=2, at=-1.1)
mtext(paste("外部マージン",1:4), 1:4, line=1, outer=TRUE)
mtext("デバイス領域", 3, line=2, outer=TRUE, at=0)
par(op)

領域分割

複数のグラフをまとめて一つの図にすることもできます。 R標準のグラフィックスでは,これを行うための手段が3種類あります。

mfrow, mfcol

グラフィックスパラメータの mfrow または mfcol を設定することで, 作図領域を m×n の格子状に分割できます。 分割の設定がされた状態で作図関数を繰り返し実行すると,セルが順番に埋まっていきます。

oldpar <- par(mfrow=c(2, 2))  # 2 x 2 に分割する
example(plot)  # 4枚のグラフを描画
par(oldpar)

mfrowmfcol の違いは,埋まっていく方向が行方向か列方向かの違いです。

次のスクリプトを実行すると,分割された状態で各領域の占める範囲が理解できます。

op <- par(mfrow=c(2,2), oma=rep(3,4), mar=rep(3,4))
for (i in 1:4) {
  plot.new()
  plot.window(xlim=c(-1,1), ylim=c(-1,1))
  box("plot", lty="dotted")
  box("figure", lty="dotted")
  text(0, 0, "プロット領域")
  mtext(paste("内部マージン",1:4), 1:4, line=1)
  mtext(paste("作図領域", LETTERS[i]), 3, line=2, at=-1.1)
}
mtext(paste("外部マージン",1:4), 1:4, line=1, outer=TRUE)
mtext("デバイス領域", 3, line=2, outer=TRUE, at=0)
par(op)

layout

layout()関数を使うことでも,作図領域を分割できます。 違いは,layout()関数ではより柔軟な割り当てができるという点です。

layout()関数には,領域の割り当て方を示す引数として行列を渡します。 この行列の値は,個々の領域の範囲を指定するのに加えて,描画の順番にもかかわります。

(m <- matrix(c(1,2,3,2), nrow=2))  # 領域を3つに分割. 下段は2列で1つ
layout(m)
example(barplot)   # この例は9つの図を描くので,3ページ

split.screen

split.screen()関数は,layout()関数と同様,領域を分割するのですが, mfrowmfcollayout() のように, 作図関数を繰り返し呼び出せば分割された領域が埋まっていくというタイプではありません。 split.screen()関数によって分割された領域(これを便宜的にスクリーンと呼びます)には番号が振られます。 また,「現在のスクリーン」という情報が記録されており,番号指定して現在のスクリーンを切り替えます。 作図関数による描画は現在のスクリーンに対してしか行えません。

split.screen()関数による分割は,mfrowmfcolと同様に,格子状です。 しかし,split.screen()関数は,分割してできたスクリーンをさらに分割するということができ, これによってlayout()関数のような柔軟な分割も可能になります。

split.screen(c(1,2)) # 1行2列に分割
split.screen(c(2,1), screen=1) # さらに左側を2行1列に分割
screen(3)
curve(x^3-3*x, -2, 2)
screen(4)
qqnorm(precip)
screen(2)
dotchart(VADeaths)

数式

テキストを描画する低水準作図関数や,高水準作図関数のテキスト関連の引数において, 表現式オブジェクトまたは関数呼び出しオブジェクトを渡すことで,数式を描画することができます。

この目的で表現式を用いる場合,描画する数式を表現するために,本来の意味ではない特別な表現式の組み方をする必要があります。 その書式は ?plotmathdemo(plotmath) で参照してください。

pdf("plotmath.pdf")
plot(1:5, type="n")
text(3, 5,             "hat(beta) == (X^t * X)^{-1} * X^t * y", cex=0.7)
text(3, 4.5, expression(hat(beta) == (X^t * X)^{-1} * X^t * y) )
text(3, 3.5,         "bar(x) == sum(frac(x[i], n), i==1, n)", cex=0.7)
text(3, 3, expression(bar(x) == sum(frac(x[i], n), i==1, n)) )
text(3, 2,             'paste(frac(1, sigma*sqrt(2*pi)), " ", plain(e)^{frac(-(x-mu)^2, 2*sigma^2)})', cex=0.7)
text(3, 1.5, expression(paste(frac(1, sigma*sqrt(2*pi)), " ", plain(e)^{frac(-(x-mu)^2, 2*sigma^2)})) )
dev.off()
demo(plotmath)

拡張的なグラフィックス・システム

上述の標準的なグラフィックスに加えて, 追加パッケージによって,様々な特徴を持つ拡張的なグラフィックス・システムが提供されています。 その中でも有名なものをいくつか紹介します。

グリッドグラフィックス

グリッドグラフィックス(grid graphics)は トレリスグラフィックス(trellis graphics)とかラティスグラフィックス(lattice graphics)とも呼ばれます。 どれも「格子」という意味です。 グリッドグラフィックスは,上述の領域分割して複数の図をまとめる場合のように, 格子を描き,その中に個々のグラフを描くものです。 主に,1つのデータセットを条件ごとに層分けして,それぞれについて同じ種類のグラフを描くという場合に用いられます。

グリッドグラフィックスの利用はR標準のグラフィックスと手順が少々異なるため,注意が必要です。 詳しくは他のドキュメントやメーリングリストのアーカイブを参照してください。

require(lattice)
old.prompt <- grid::grid.prompt(TRUE)
example(xyplot)
grid::grid.prompt(old.prompt)

3Dグラフィックス

rglパッケージを入れることで,OpenGLによる3Dグラフィックスが利用可能です。 この3Dグラフィックスのデバイスは,対話的な操作を可能にします。 出版用にはあまり利点がないかもしれませんが, インタラクティブであることが高次元データの把握を非常に促進すると思われるので, 集めたデータの特徴を探索的に調べる場面や口頭でのプレゼンテーションにおいて ぜひ活用することをお勧めします。

require(rgl)
plot3d(airquality[1:3], size=5)
example(surface3d)

統計モデル

この節では,モデル式を使った統計モデルのフィッティングや作図に関する手法を紹介します。 モデル式は,R の統計ソフトとしての主たる特徴の1つであり, また,活用するには少々追加的な知識が必要ですので,これに焦点を当てます。

まず,単純な回帰分析の例を挙げます。

aq.lm <- lm(Temp ~ Ozone, data = airquality)
summary(aq.lm)

Temp ~ Ozone がモデル式で,~ がモデル式を作る特殊な演算子です。 ~ の左辺が被説明変数,右辺が説明変数にあたります。

次に,説明変数を1つ増やした重回帰分析の例です。

aq.lm2 <- lm(Temp ~ Ozone + Wind, data = airquality)
summary(aq.lm2)

モデル式の文法

モデル式の中では,そこで用いられている記号は,通常のR言語の演算子とは異なる意味をもちます。 下に,一覧を挙げます。

モデル式での記号の意味
記号説明
+ AA を加える
- AA を減らす
A:BAB の交互作用項
A * BA + B + A:B と同じ
A * A などとした場合, 交互作用項は含まれるが自乗の項は含まれない
例えば (a + b) * (a + b) なら a + b + a:b と同じ
A^2A * A と同じ
A^3 , A^4, 以降も同様
A %in% BB にネストされている A
A / BA + (B %in% A) と同じ
A | BB で条件付けした場合の A
I(A)I( ) の中では記号は通常のR言語の演算子と同じ意味で使える
-1定数項を除く
0定数項を持たない
.データセットに含まれるすべての変数. データセットの指定が必要

モデル式中のトークンをどのように解釈するかは,モデル式を渡す関数に依存しています。 つまり,ある関数では有効であるけど別の関数では無効な記法なども存在し,共通性は保証されていません。 例えば, aov() 関数はモデル式の中で誤差項を指定するための Error() という記法が使えますが, 他の関数ではこれはダメです。条件付きなどもこれに当たります。 上の表に挙げたのは,多くの関数で「だいたい」共通している記号用法だと捉えてください。

他にも,モデル式の中で特定の関数が使える場合もあります。 log() などがよくある例です。

モデル式には,場合によっては ~ の左辺は必要ありません(右辺は必要)。

線型モデルでの例

いくつか統計モデルを指定する例を挙げておきます。 出力に,実際に計算に使われている項が表示されますので,確認してみてください。 これらはモデル指定の参考のために用意した例であり, ここで指定されている統計モデルによる airquality データ等の分析が実学的に有効であるとは意味していません。

lm(Temp ~ Ozone + Wind - 1, data = airquality)  # 定数項なし
lm(Temp ~ (Ozone + Wind + Solar.R)^2, data = airquality)
lm(Temp ~ (Ozone + Wind + Solar.R)^3, data = airquality)
lm(Temp ~ Ozone * Wind * Solar.R - Wind, data = airquality)  # Windの主効果を除く
lm(Temp ~ Ozone + Month / Day, data = airquality)
lm(Temp ~ I(Ozone^2) + Wind, data = airquality)  # 二乗の項をつくる
lm(log(Temp) ~ log(Solar.R), data = airquality)  # 両対数をとる
lm(Temp ~ ., data = airquality)    # すべての変数. 交互作用は入らない
lm(Temp ~ .^2, data = airquality)  # 一次交互作用まで

作図関数での例

作図関数での例を挙げます。数学の関数グラフに似て,~ の左辺が縦軸,右辺が横軸等の次元に用いられることが多いです。

plot(Sepal.Width ~ Sepal.Length, data = iris)

boxplot(Sepal.Width ~ Species, data = iris)

coplot(lat ~ long | depth, data = quakes)

様々な解析での例

各種の分析関数にて用いられる,より複雑なモデル式の例を挙げます。

# from R help files

library(MASS)
summary(aov(yield ~ N * P * K + Error(block), data = npk))

library(nlme)
summary(lme(distance ~ age + Sex, random = ~ age | Subject, data = Orthodont))

library(MASS)
summary(glmmPQL(y ~ trt + I(week > 2), random = ~ 1 | ID, family = binomial, data = bacteria))

library(lme4)
lmer(Reaction ~ Days + (1 | Subject) + (0 + Days | Subject), sleepstudy)

glmer(cbind(incidence, size - incidence) ~ period + (1 | herd), family = binomial, data = cbpp)

nlmer(circumference ~ SSlogis(age, Asym, xmid, scal) ~ Asym | Tree, data = Orange, start = c(Asym = 200, xmid = 725, scal = 350))

拡張と高度化

追加パッケージ

R はパッケージを追加することでその機能を拡張できることが大きな特徴です。 追加のパッケージをインストールするには Rgui の「パッケージ」メニューを使用するのが便利です。

自動化したい場合などでは,関数を呼び出すことでインストールを行うこともできます。

CRAN では,CRAN Task Viewsという,特定の分野でよく使われるパッケージの詰め合わせセットのようなものが作られています。 R では,基本的に,パッケージは必要になってからインストールすればよいのですが, 他者にguest権限で使用させるPCに予め R をインストールしておくなど, とりあえず頻繁に使われるパッケージを入れておく必要がある場合は,この CRAN Task Views を利用すると便利かと思います。

install.packages("ctv")  # まずパッケージ ctv をインストール
available.views()  # 利用可能なビューの一覧
install.views("Multivariate")

あるいは,どんな有名パッケージがあるのか勉強するために CRAN Task Views をのぞいてみるのもよいでしょう。

CRAN Task Views は,パッケージをまとめてインストールするためのものであって,結果的には含まれるパッケージを1つずつインストールしているのと同じです。 ビューでインストールしたからといって特別何かが違うわけではありませんし,通常のパッケージインストールと同様,重複などを気にする必要はありません。

外部ルーチン

R には,R上の関数だけでなく,R 外部の関数を呼び出す方法がいくつか用意されています。

.C() および .Fortran() という関数があり,それぞれ,C 言語の関数,FORTRAN 77 のサブルーチン,のコンパイル済みコードを呼び出すための R 関数です。 しかし,呼び出し規約さえ守っていれば,例えば .C() を使って C 言語形式のインタフェースを使用できる他の言語のルーチンも呼び出せるはずです( C++ 等)。 呼び出しにあたっては,次の順で処理が行われます:

  1. 下の表のように引数の型が翻訳されます。デフォルトではコピーが作られます。
  2. C または Fortran のルーチンを実行します。その中で引数の指す値を書き換えることができます。
  3. 書き換え後の引数が再び R の型に戻されます
  4. 戻された引数は R でのリストにまとめられ .C() または .Fortran() の戻り値となります。 .C() または .Fortran() の呼び出し時に名前付きで引数を指定している場合は,それらの名前が戻り値のリストにて使われます。
R での 保管モードC での型FORTRAN での型
logicalint*INTEGER
integerint*INTEGER
doubledouble*DOUBLE PRECISION
doublefloat*REAL
complexRcomplex*DOUBLE COMPLEX
characterchar**CHARACTER*255
rawunsigned char*なし
listSEXP*なし
その他SEXPなし

以下のサンプルは入力チェックが甘いので注意してください。

/* C 関数 */
void my_inner_product(double *a, double *b, int *n, double *result)
{
  int i;

  *result = 0.0;
  for (i = 0; i < *n; i++)
    *result += a[i] * b[i];
}
# R から呼び出し
inner_product <- function(a, b) {
  stopifnot(length(a) == length(b))
  .C("my_inner_product",
    as.double(a),
    as.double(b),
    as.integer(length(a)),
    result = double(1)
    )$result
}

inner_product(2:4, 8:6)

.C() および .Fortran() で外部の関数を呼び出した場合,高速に数値計算を行うなどには向いていますが,R のオブジェクトを直接に扱うことができません。 これはとくに C の中で R の関数を呼び出したい場合に問題になってきます。

.External() および .Call() という関数を用いて C 言語の関数を呼び出せば,これが可能になります。 この2関数の違いはインターフェイスの定義の違いであり,.External() のほうがより一般的で可変長の引数も扱えますが,S4 との互換性のために .Call() も使えるようになっています。 後者のほうが幾分シンプルです。 .External().Call() も引数をコピーしません。よって,C 言語等で関数を書こうとする場合は,このインターフェイスは注意深く用いる必要があります。 必要な計算が R 言語でのルーチンで実現でき速度的にも問題ないなら R だけで行うように,そうでない場合でも .C() で可能なことならこちらを使うように,推奨されています。

R のオブジェクトを操作するには R の内部構造を熟知しているほうがよいですが,簡単にオブジェクトを操作するためのマクロも用意されています。

/* C 関数 */
#include <R.h>
#include <Rdefines.h>

SEXP my_inner_product2(SEXP a, SEXP b)
{
  int i, n;
  double *pa, *pb, *pr;
  SEXP result;

  PROTECT(a = AS_NUMERIC(a));
  PROTECT(b = AS_NUMERIC(b));
  n = LENGTH(a);
  PROTECT(result = NEW_NUMERIC(1));
  pa = NUMERIC_POINTER(a);
  pb = NUMERIC_POINTER(b);
  pr = NUMERIC_POINTER(result);
  *pr = 0.0;
  for(i = 0; i < n; i++)
    *pr += pa[i] * pb[i];
  UNPROTECT(3);
  return result;
}
# R から呼び出し
.Call("my_inner_product2", -1:1, c(3, 5, 7))

.External.graphics().Call.graphics() という関数もあり,これらは統計計算ではなく低レベルのグラフィックス操作を行いたい場合に用いられます。

動的ロード

dyn.load()dyn.unload() を用いて,DLLのロード/アンロードを行うことができます。 これによって,自分の必要とする外部ルーチンの入った任意の DLL を R のプロセスに動的にロードし,使用することができます。 ロード済みの DLL の一覧は getLoadedDLLs() で得ることができます。

dyn.load("sample.dll")
getLoadedDLLs()
dyn.unload("sample.dll")
getLoadedDLLs()

外部ルーチンを使うには,R がそのエントリーポイントを知らなければなりません。DLLがエントリーポイントを R に登録する方法は2種類あります。 1つはOSの標準的DLLローディングを用いる方法,もう1つは R が備えるプラットフォーム独立な登録手続きの使用です。 前者は,関数をエクスポートするだけで利用できます。後者では,用意されている登録用の関数をDLL内で呼ぶよう実装が必要です。 後者の方法を用いるなら,関数をエクスポートする必要はありません。

後者を用いて明示的に登録されたルーチンのみ getDLLRegisteredRoutines() で一覧することができます。 また,is.loaded() で,個別のルーチン名を指定して,登録済みかどうかをチェックできます。こちらは前者のルーチンでも使用可能です。

getNativeSymbolInfo() を用いると,登録された各外部ルーチンの情報を得ることができます。これも,登録方法に関係なく利用できます。

is.loaded("my_inner_product")

getNativeSymbolInfo("my_inner_product")
getNativeSymbolInfo("dqrls")
サンプル

下記の場所に,上で用いているサンプルを置いています。

# C ソース
# http://668628.oan9aupt.asia/doc/r-intro-lecture/sample.c
# これを R CMD SHLIB でビルドしたもの
# http://668628.oan9aupt.asia/doc/r-intro-lecture/sample.dll

デバッグ

R には,他の言語の開発環境と比べるとそれほど強力ではありませんが, デバッグのための機能が備わっています。

browser()

my.func1 <- function(x, y) {
    for (i in 1:5) {
        x <- x + i
        browser()
        y <- y / i
    }
    print(x)
    print(y)
}
my.func1(2, 3)

コードの実行途中で browser() 関数を呼び出すと,その箇所で実行を一時停止させることができます。 停止している状態でコンソール入力が可能ですので,変数の内容を調べたり書き換えたり,何か関数を呼び出したりすることもできます。

browser() 関数で一時停止している間は,いくつか特別な命令が使用できます。

命令意味
c一時停止状態を解除して実行を継続する
nステップ実行(次の1ステートメントだけ実行)を行う. これを行うと c 命令の意味が変わります
whereスタックトレースを出力する
Q実行を中止してトップレベル(プロンプト)に戻る

browser()中にリターンキーだけ入力すると c 命令と同じ意味になります。

debug()

debug() 関数は,browser() 関数と似ています。debug() 関数の引数として他の関数を渡すと, その関数に「デバッグ」マークが付き,以後,その関数が呼び出されるたびに,関数内容を実行する寸前の段階でbrowser()のように一時停止します。 一時停止中に行える操作はbrowser()関数の場合と同様ですが,リターンキーの動作が n 命令と同じになっています。

undebug() 関数で,「デバッグ」マークを取り除き,もとの状態(関数を呼び出しても一時停止しない)に戻せます。

my.func1 <- function(x, y) {
    for (i in 1:5) {
        x <- x + i
        y <- y / i
    }
    print(x)
    print(y)
}
debug(my.func1)
my.func1(3, 4)

undebug(my.func1)
my.func1(3, 4)

trace()

trace() 関数の引数としてある関数を渡すと,その関数の呼び出しが監視されます。デフォルトでは,関数呼び出しのたびに,その実引数の表現式とともに,呼び出された旨がプリントされます。

監視対象の関数以外に,関数や表現式を trace() 関数の引数として渡すことで,監視対象の関数の呼び出し直前,呼び出し直後などにその関数/表現式を評価するようセットすることもできます。

監視中の関数に対して untrace() 関数を用いれば,監視状態を解除できます。また,tracingState() 関数で監視機能を全体的にON/OFFできます。

trace(sum)
boxplot(rnorm(100))

untrace(sum)
boxplot(rnorm(100))

traceback()

実行中にエラーが起こった場合に,traceback() 関数を呼び出すと,最後にエラーが起こったときのコールスタックのようなもの(エラーに至るまでの関数呼び出しの系列)を表示してくれます。

my.func1 <- function(x) {
  cat("my.func1 ", x, "\n")
  my.func2(x)
}
my.func2 <- function(x) {
  cat("my.func1 ", x ^ 2, "\n")
  x ^ 2 + no.such.a.variable
}
my.func1(5)
traceback()

recover()

recover() 関数を呼び出すと,そこに至るまでの関数呼び出しのリストを表示し,選択待ちの状態になります。 ユーザは,リストのうちの特定のものを選んでその環境で browser() を実行するか,または browser() を実行せずに先に進むかを選択します。

下のような使い方ができます:

options(error = recover)

この設定によって,エラー発生時に自動的にその場で recover() が実行され,エラーの状況を細かく調べることができます。

もちろん自分で自作関数に recover() の呼び出しを入れておくこともできます。

例外処理

処理中に R から標準エラーへ出される情報は,大きく分けてメッセージ,警告,エラーの3種類があります。 メッセージや警告は,suppressMessages()suppressWarnings() を用いることで出力を抑制できます。 options("warn") などをセットすることで,警告へのデフォルトの対処法を変更できます。

エラーが発生した場合は,デフォルトの対処法として,R はその場で処理を中止します。 しかし,エラーが発生しても処理を完全に中止せず,ある地点から継続してほしい場合があります。 そのような場合,try() を用います。

f1 <- function(a, b, c, d) {
  print(log2(a))
  print(log2(b))
  print(log2(c))
  print(log2(d))
}
f2 <- function(a, b, c, d) {
  try(print(log2(a)))
  try(print(log2(b)))
  try(print(log2(c)))
  try(print(log2(d)))
}
f1(2, "a", 8, 16) # 2つ目で停止
f2(2, "a", 8, 16) # 継続して3つ目以降も評価

f3 <- function(a, b, c, d) {
  try({
    print(log2(a))
    print(log2(b))
    print(log2(c))
  })
  print(log2(d))
}
f3(2, "a", 8, 16)

try() はその引数の表現式を評価した際に発生したエラーシグナルを受け止め,それ以上戻らないようにして,try() の次の表現式から評価を継続します。 引数を指定することで,エラーメッセージを消すこともできます。

try() は,エラーが生じなかった場合,引数の表現式の評価結果をそのまま戻り値として返します。エラーが生じた場合は,try-error というクラスのオブジェクトを戻り値とします。

f4 <- function(a, b, c, d) {
  print(try(log2(a)))
  print(try(log2(b)))
  print(try(log2(c)))
  print(try(log2(d)))
}
f4(2, "a", 8, 16)

try() でエラーシグナルが捕まえられた場合は,設定されているデフォルトエラーハンドラは働きません。従って,options(error = recover) していても recover() は呼ばれません。

システムコマンド

system() 関数を用いることで,OS のシェルのコマンドや任意の実行ファイルを実行することができ,その結果(標準出力など)を R 上で取得できます。

system("cmd /c dir")

デフォルトでは system() の戻り値はコマンドのリターンコードですが,引数の指定によって,結果を R のオブジェクトとして取得することもできます。

(x <- system("cmd /c attrib", intern=TRUE))

R はセキュリティ関係の事をほとんど考慮せず実装されています。 セキュリティは R の外側に任されており,それが R そのものを非常に強力で高速にさせています。 例えばこの system() 関数によって,R でほとんど何でもできてしまうことになるので,使い方には注意してください。

実行環境情報

複数の実行環境で使用できる汎用的なスクリプトを書こうとしている場合は,実行環境の情報の取得とそれを用いたコードの切替が必要となるかもしれません。

paste(Sys.getenv("HOME"), R.home(), sep=.Platform$path.sep)

R に関する情報源

ヘルプ

R はそれ自体にバンドルされているヘルプが充実しています。

help() 関数で,すべての関数やパッケージやデータセットのヘルプを見ることができます。

help(matrix)
help(package=MASS)  # パッケージのヘルプ
?matrix  # 関数やデータセットの場合は ? でも代用可

目的の作業をするための関数名がわからないときは, help.search() 関数で,ヘルプを検索できます。 検索対象になるのは,ヘルプのタイトル,キーワードなどです(全文検索ではありません)。

help.search("test")

目的に適いそうな関数を見つけたら,ヘルプの説明文を読むよりも,まずは例を見てみるのがわかりやすいです。 example() 関数で,関数やパッケージの使用例を実行することができます。

example(plot)

R のいくつかの特徴的な側面については,サンプル事例を集めたデモンストレーションが用意されています。 デモの項目名を demo() 関数の引数にして呼び出せばそのデモが開始されます。

demo()   # デモが用意されている項目の一覧

R には HTML 形式のヘルプとマニュアルが付属しています。ここに R の基礎についてのほぼすべての情報が詰まっています。 ブラウザを立ち上げてこのマニュアルを読むには, help.start() 関数が使えます。 (もちろん普通に HTML ファイルを開いて読んでもよいです。)

help.start()

ローカルのヘルプファイルだけでなくオンラインの情報 (たとえばメーリングリスト R-help の内容)を検索するには, RSiteSearch() 関数が使えます。 R-help には多くの質問が寄せられ, FAQ もたくさんあるので, わからないことがあったらここで同じ質問がないか調べてみることをお勧めします。

RSiteSearch("factor")

ウェブサイト

ウェブ上には山のような情報があります。 特に,共同開発のフリーソフトウェアについては,詳細な実装方法から応用のTipsまで,そのソフトに関するすべての情報がウェブ上にあるのがほぼ常です。 R もこのようなソフトの一つです。ですから,はっきり言えば,ウェブを調べるだけですべてが事足ります。

基本は,R Rroject のサイトと CRAN を調べることです。 CRAN のミラーサイトは世界中にあるので自分が高速に通信できるところに接続するのがよいです。

ここには,現在開発中の最新の R の情報もありますし,関連プロジェクトの情報もありますし,何よりソースファイルがあります。

上で紹介した RSiteSearch() で利用している Jonathan Baron のサーチエンジンも r-project.org ドメインで利用できます。

最近は Wiki サイトも用意されました。ここには tips もたくさん載っています。

日本語の情報は,やはり RjpWiki を参照するのがよいでしょう。 ウェブに散在する情報も自然とここに集まってきますので,ここのリンクをたどるだけでウェブ上に現存するほとんどの日本語 R 情報を網羅できます。

事例集

R の使い方,とくにグラフィックスについては,事例を見るのがスキルアップに有益です。 図のバリエーションというのは,まさにクリエイティヴな世界ですから膨大な可能性があります。 多くの表現方法を目にしておかないと,いつまでたっても自分が知っているものだけ (例えばシンプルな棒グラフと折れ線グラフだけ)の偏狭な世界に留まってしまいかねません。 プレゼンテーション方法の重要性はいまさら受講者の皆さんに説くまでもないでしょう。 わかりやすい(そしてもれのない)説明のためには,事例から学ぶことが短期間で前進する良い方法です。

グラフィックスの事例集をつくっているページのうち有名ないくつかを挙げます。他にもあるかもしれません。

片や,統計解析の事例は,それこそ山ほどあります。 書籍にも R で分析する際のコードが載っています。 特定の分析手法や tips を集めたウェブページもたくさんありますし, ウェブ検索エンジンで検索してみるとよいでしょう。 検索にあたっては, R だけではヒットしにくいので,手法の名前を合わせて入れるのがコツです。

書籍

上述したように,R の情報はウェブにすべてあります。 よって,書籍にしか載っていない情報などというのは,まずありません。 そうすると書籍を買うことに意味がないような気がするかもしれません。 書籍を買う意味は,わかりやすく「まとめている」という点に見出せます。 よって,この講習会で得た情報, R に付属しているドキュメント,ウェブ上の情報だけで理解できる人は, 書籍を買う必要はありません。 ドキュメントに書いてあることの意味が分らないという人は,書籍を買ってみるのもいいでしょう。

最近は R に関する書籍も増えてきました。 滋賀大学の熊澤吉起さんが R に関する書籍のリストを作っておられます。 洋書,和書ともに含まれています。

その中で,いくつかお勧めする書籍をあげておきます。

Rの基礎とプログラミング技法 Uwe Ligges 石田基広 シュプリンガー・ジャパン
特定の統計解析を R で実行する方法ではなく, R 自体についてのわかりやすい解説を求めているならば, この本を試してみてください。この本は,つまり,この講習会と同じ路線です。
Statistics: An Introduction Using R Michael J. Crawley John Wiley & Sons, Ltd.
(専門家でなく)統計学のユーザが,推測統計学の基礎を R と絡めて学ぶための本です。 とくに GLM 関連を中心にしています。 R の関数やオプションと統計学的知識との対応をつけるのによいでしょう。 「共分散分析(ANCOVA)という名前の関数が見つからないんだけど?」といった質問をしてしまう人は, これを読んでください。 同著者の本に The R Book があります。こちらのほうが扱っている範囲が広いですが,まだハードカバーしか出ていないので高いです。
The R Book ー データ解析環境Rの活用事例集 ー 岡田昌史 九天社
この本は,主に,具体的な統計手法の実行方法を紹介するものです。 上で書いたように,そういう情報はウェブで手に入ります。 ただ,具体的な実行方法を解説するものの中でも,おそらくこの本が最も内容の範囲に幅があり(言い換えれば内容がバラバラ), この本が扱っているような内容は他の日本語のR関連書籍で扱われていないようです(英語では存在しますが)。 そういう意味で,日本語のまとまった文章がほしい人のために,挙げておきました。

予備

R のインストールと設定

インストール

講習会ではインストール済み PC の利用が前提なので省略します。情報が必要な場合は CRAN"R Installation and Administration", RjpWikiの「Rのインストール」の項を参照してください。

大学の研究室などで使用する場合は,CRAN など外部への接続のためにhttpプロキシの設定が必要である点に注意してください。 プロキシサーバを Sys.setenv() で指定するよりも,インストーラ実行時にカスタマイズを選んで --internet2 オプションを付加するのが楽です。

日本語設定

講習会では日本語使用設定の済んでいるPCの利用が前提なので省略します。 情報が必要な場合は RjpWiki の「Rのインストール」の項を参照してください。

ユーザ個人用設定

R_USER という名前の(Windowsの)環境変数をセットすることで, ユーザのホームディレクトリを明示的に設定できます。 (これがセットされていない場合, R は他の方法でユーザのホームディレクトリを決めます。)

この機能は,主に以下の3つの設定ファイルを使うときに利用します。 R_USER にパスをセットしたディレクトリが,以下の設定ファイルを置く場所になります。

Rconsole

Rconsole という名前のファイルを作ることで, その中にコンソールに関する自分用の設定(MDI/SDI の切り替え,コンソールのフォント,バッファサイズなど) を記述できます。 このファイルの記法は,そのインストレーションのコンソール設定ファイル R_HOME/etc/Rconsole と同じです。 また,コンソールのメニューの「GUI プリファレンス」で「保存」を行うと, これと同じ Rconsole ファイルの形表現式で,現在のコンソール設定を書き出すことができます。

Rの実行中に動的にコンソール設定ファイルを読み込むには, loadRconsole() 関数を使用します。

Rdevga

Rdevga という名前のファイルを作ることで, その中にグラフィックデバイスに関する自分用の設定(グラフィックスに使うフォント)を記述できます。 ファイルの記法は,そのインストレーションのグラフィックデバイス設定ファイル R_HOME/etc/Rdevga と同じです。

このファイルで設定できるのは一部のグラフィックデバイス(windows, win.metafile など) に対してであり, postscript などに関しては別途設定する必要があります。

.Rprofile

.Rprofile という名前のファイル(起動プロファイル)を作ると, この中に書いたR言語の表現式が R の起動時に評価されます。 これを利用することで,環境設定,追加パッケージのロード,自作関数の定義,データ読み込みなどを, 起動時に自動的に行うことができます。

ただし, R 起動時のカレントディレクトリ (Windowsではショートカットのプロパティなどで指定可能)に .Rprofile というファイルが存在する場合はそちらが評価され, R_USER に置いた .Rprofile は評価されません。

サイトワイド(全ユーザに対して適用される)の起動プロファイルは R_HOME/etc/Rprofile.site です。(メディアセンターの端末ではこのファイルをユーザが書き換えることはできません。) 起動時の順番としては,各ユーザの起動プロファイルよりもこちらが先に評価され, その後ユーザの起動プロファイルが評価されます。 環境変数 R_PROFILE をセットすることで,サイトワイド起動プロファイルの位置を変更することができます。 しかし,サイトワイド起動プロファイルはユーザ用起動プロファイルとは勝手が違うので,サイトワイド起動プロファイルを作成する際には注意してください。 サイトワイド起動プロファイルでの評価結果は base パッケージに影響します。

R にアクセスする方法

R の本体プログラムは UI を切り離したコンポーネント(Windows で言うと DLL)になっていて, 複数のインターフェイスから R を利用することができます。 厳密に言うと R.dll に完全にUI関係のコードが無いわけではなく,UIの実装に用いられるサブルーチンがエクスポートされています。

ウィンドウ(Rgui)

Rgui.exe はRのGUIフロントエンドで,皆がよく使う方法です。 スタートメニューにはこれへのショートカットが置かれています。

Rgui のメイン機能はコンソールウィンドウです。 ユーザはここに文字を打ち,同じくここに結果が表示されます。 他にも,グラフィックスウィンドウやスクリプトエディタ,データ編集ウィンドウなどを持ちます。

Rguiは,インターフェイスの種類として MDISDI を選択できます。 現バージョンのデフォルトは MDI ですが, 著者としては SDI がお勧めです。 MDI/SDI の切り替えは,Rconsole で設定できますし,起動オプションでも変更できます。 Rgui の起動後に変更することはできません。

スクリプトエディタ

それほど高機能ではないですが, R にはスクリプトを編集,保存するエディタ機能が内蔵されています。

このエディタは,その上で書いているスクリプトの全部または選択された一部を R コンソールに送り, 実行させる機能がついています(「編集」メニューを見てください)。 この機能は,試行錯誤しながらスクリプトを書いている段階では非常に便利です。

この内蔵エディタがデフォルトのエディタになっていますが, options("editor") を設定することで, 他のエディタをデフォルトにできます。

スクリプトエディタは,コンソールウィンドウの「ファイル」メニューや file.edit() 関数から起動できます。

グラフィックスウィンドウ

グラフィックスウィンドウは,Windows におけるデフォルトのグラフィックデバイスです。 このウィンドウは対話的グラフィックスを利用するときに大活躍します。

グラフィックスウィンドウには,描画した図を自動的に履歴として記録しておく機能があり(デフォルトではオフになっています), これを利用すると作図履歴を前に戻ったり先に進んだりできます。 グラフィックスウィンドウを閉じると履歴は消えるので注意してください。 履歴を変数に保存しておくこともできます。

また,「ファイル」メニューまたはコンテキストメニューから,図を様々な形式でファイルに保存したり, クリップボードへコピーしたりできます。

データ編集ウィンドウ

R にはデータフレームや行列の簡易な編集ウィンドウが内蔵されています。 コンソールウィンドウの「編集」メニューの「データエディタ」や, edit() 関数で開くことができます。

しかし,少なくとも現バージョンでは,この編集ウィンドウは使い勝手が悪いので,利用をお勧めしません。 使うとしても閲覧用にしたほうがよいでしょう。 データの入力や編集には表計算ソフトなどを使うことをお勧めします。

追加パッケージのインストール

パッケージのインストールやアップデートは自分で関数を入力しても可能ですが, コンソールウィンドウの「パッケージ」メニューから簡便に行うことができます。

もしパッケージをダウンロードする CRAN ミラーサイトの設定がされていなければ, 最初にミラーサイトの選択ウィンドウが表示され, その後,新規インストールの場合は対象パッケージの選択ウィンドウが現れます。

作業ディレクトリ

R には作業ディレクトリの概念があります。 作業ディレクトリとは, R の様々な機能にてファイルの相対パスが指定された場合に,基準となるディレクトリです。

現在の作業ディレクトリは getwd() 関数と setwd() 関数で取得,設定できます。 「ファイル」メニューの「ディレクトリの変更」から,ディレクトリツリーを参照して設定することもできます。

R 起動時の作業ディレクトリは,Windows のショートカットの「作業フォルダ」プロパティを反映します。 R のインストーラによってスタートメニューの項目を作った場合,作業ディレクトリは R のインストールディレクトリになっているので, 適宜変更したほうがよいでしょう。

ワークスペース

現在のワークスペース(グローバル環境に保持されているオブジェクト群)を丸々ファイルに保存したり, ファイルから読み出したりできます。この保存はバイナリ形式です。 q() 関数は,デフォルトでは,このワークスペースの保存を行うかどうかを確認します。

この機能を実行するのは save.image() 関数と load() 関数です。 「ファイル」メニューの「作業スペースのxxx」から行うこともできます。

ワークスペースを .Rdata という名前でホームディレクトリに保存しておくと, 起動時に自動的に読み込まれます。

コンソール履歴

コンソールに入力した内容は履歴が取られています。 コンソールでは,上下矢印キーで履歴の項目を読み出せます。

この履歴をファイルに保存したり,ファイルから読み込んだりできます。 「ファイル」メニューの「履歴のxxx」から行うのが簡単です。 履歴を .Rhistory という名前でホームディレクトリに保存しておくと,起動時に自動的に読み込まれます。

入力内容の履歴ではなく,入力も出力も含んだ現在のコンソールバッファの内容全体(スクロールで見れる範囲のすべて) をテキストファイルに保存することもできます。 「ファイル」メニューの「ファイルを保存」から行えます。

自動補完

コンソールウィンドウに関数名を先頭の何文字か入力した状態でTabキーを押すと,関数名の自動補完が行われます。 該当する候補がない場合,あるいは複数の候補が該当する場合は,補完は行われません。

複数の候補が該当する場合,1回目のTab押しでは何も表示されませんが, もう一度Tabキーを押すことで,候補のリストを表示できます。

ドラッグ&ドロップ

コンソールウィンドウに .R ファイルをドロップすると, source() によってスクリプト内容が実行されます。 .Rdata ファイルをドロップすると, load() によってワークスペースが復元されます。

起動オプション

まとまったドキュメントは見かけませんが, Rgui にはたくさんの起動オプションがあります。 ユーザにとって有益なものでどのようなオプションがあるかは, R User Configulation を参考にしてください。

コマンドシェル(Rterm)

Rterm.exe は Windows のコマンドシェル(コマンドプロンプト)上で動くフロントエンド・プログラムです。 これは Linux のシェルでの R の利用方法に似ています。

Rterm の利用においては,コマンドプロンプトのウィンドウが Rgui でのコンソールウィンドウの代わりをするわけですが, その他のウィンドウが利用不可能なのではなく, Rgui と同様に,スクリプトエディタやグラフィックウィンドウなど, 上で紹介したすべてのウィンドウが利用可能です。 また,履歴などの機能も Rgui と同等です。 GUI のメニューがない点くらいしか大きな違いはないかもしれません。 もちろん, Rconsole のフォント指定などは反映されず,コマンドプロンプトの設定のままです。

コマンドプロンプト上で標準入出力を利用して動作するので, コマンドプロンプトの持つリダイレクト機能が Rterm に対しても利用できます。 リダイレクトで入力を与えた場合は,指定の入力に対する処理を終え次第 Rterm が終了します。 その場合のデフォルトのグラフィックデバイスは postscript になります。

バッチ実行(Rcmd BATCH)

Rterm にリダイレクトしてもバッチ処理ができますが,バッチ処理用のコマンドも用意されています。 (内部では Rterm が使われています。)

Rcmd BATCH [--options] inputfile [outputfile]

outputfile を省略した場合,出力は,(入力ファイル名).Rout というファイルに書き出されます。 もし入力ファイルが .R という拡張子だったら,その場合に限り拡張子が取り除かれます。

Rcmd には他にも,コンパイルやアドオンの更新など,いろいろなことをするコマンドが用意されています。 詳細は次のコマンドで参照してください。

Rcmd --help

バッチ実行(Rscript)

バッチ実行には,Rscript.exe を使うこともできます。

Rscript [--options] [-e expr] inputfile [args]

Rscript には出力ファイルを指定するオプションがありませんが,出力のリダイレクトで代用できます。

Windows 版の Rcmd BATCH ではスクリプトへ引数を渡す機能がうまく使えませんが, Rscript ではちゃんと引数を渡すことができます。

Rscript は直接に表現式を書ける -e オプションが利用でき(このオプションは Rterm の機能),かなり便利です。

Rscript -e "commandArgs(); proc.time()" arg1 arg2 arg3

COM

R-(D)COM を合わせてインストールすることで, COM インターフェイスを介してRを利用できます。 R-(D)COM には COM インターフェイスの利用例として RExcel (Microsoft Excel のアドイン) が付属しています。 WSHHTA などから R をインタラクティブに利用することもできるでしょう。

R 本体には DDE サーバは実装されていません。 どうしても DDE を使いたければ, SciViewssvIDE::guiDDEInstall() が利用できるかもしれませんが, お勧めはしません。

その他

他にもいくつかのメジャープログラミング言語や既存ソフトウェアに向けたバインディングが開発されています。CRAN をご覧ください。


後記

このページは京都大学にて 2007-05-24 から 2007-05-25 にかけて全学向けに開催した講習会の資料を部分的に変更したものです。

変更点は以下の通りです。

同じく京都大学にて 2007-08-29 から 2007-08-30 にかけて全学向けに開催した講習会では,資料に以下の改訂を加えました。

京都大学にて 2008-06-24, 2008-06-25, 2008-06-27 にかけて全学向けに開催した講習会では,資料に以下の改訂を加えました。

京都大学にて 2009-08-05 に全学向けに開催した講習会では,資料に以下の改訂を加えました。

  1. 講習会用に作成 on 2007-05-24
  2. 講習会用に修正 on 2009-07-31