R

トップページ
Google
WWWを検索
サイト内を検索

Rは関数型プログラミング言語なので、コード1とデータの境界は曖昧です。四則演算+,-,*,/も関数で、コードと言っても引数がセットされた関数オブジェクト2のリスト構造のデータの集まりに過ぎません3。Rではコードを格納した変数を、表現式オブジェクトと言って、expressionparseでつくってevalで評価4します・・・と書いても、慣れていないと理解し難いモノなので、ぽちぽちと関係した命令を入力して反応を見て行きましょう。

1 表現式オブジェクトを使ってみる

習うより慣れろと言うことで、試しに動かしてみましょう。

1.1 expressionevalの基本的な挙動

expressionevalの振る舞いを把握するのは難しくなく、以下の4行で済むと思います。

x <- 2
y <- 3
e <- expression(x * y)
sprintf("%sを評価すると%sになります", e, eval(e))
[1] "x * yを評価すると6になります"

すぐに、expressionはRが計算する式を表す式オブジェクトを返し、evalに渡すと実際に評価してくれることが想像つくと思います。

1.2 expressionには関数も行列も入る

追加と言うよりは確認の説明ですが、expressionには関数も行列も入ります。

z <- function(j){ j^2 }
e <- expression(x * y - z(4))
sprintf("%sを評価すると%sになります", e, eval(e))
[1] "x * y - z(4)を評価すると-10になります"
X <- matrix(c(1, 3, 4, 2), 2, 2)
Y <- matrix(c(1, 2, 3, 4), 2, 2)
e <- expression(X %*% Y)
e
expression(X %*% Y)
eval(e)
     [,1] [,2]
[1,]    9   19
[2,]    7   17

1.3 文字列を表現式オブジェクトにする

文字列を表現式オブジェクトにできます。これはsprintf関数と組み合わせると便利なので、よく使います。

e <- parse(text = "x + y")
sprintf("%sを評価すると%sになります", e, eval(e))
[1] "x + yを評価すると5になります"

1.4 表現式オブジェクトを文字列にできる

逆もできますが、今日まで使ったことがありませんでした。

deparse(e)
[1] "structure(expression(x + y), srcfile = <environment>, wholeSrcref = structure(c(1L, "
[2] "0L, 2L, 0L, 0L, 0L, 1L, 2L), srcfile = <environment>, class = \"srcref\"))"          

元に戻すわけではないです。

1.5 表現式オブジェクトをプロットに用いる

Rのplotは、題名や軸名や凡例に表現式オブジェクトをとることで、数式を表示することができます。 ?plotmathでヘルプが出るので描画可能な数式が分かり、demo(plotmath)でプロットの実例が見られますが、試しに使ってみましょう。

x <- seq(-1, 1, length.out=100)
y <- expression(x^2-1)
plot(x, eval(y), type="l", ylab=y)
縦軸のラベルが数式

縦軸のラベルが数式

またcurve関数は表現式をプロットできます。

curve(x^2-1, c(-1, 1), xname = "x", ylab = expression(x^2-1))

あまり綺麗な数式ではなく文法も独特になるので、latex2expパッケージを用いてTeX表記を使う人が多いのですが、ちょっとしたものであれば有用です。

2 表現式以外の言語オブジェクト

表現式オブジェクトの感覚を養ったところで、Rの言語オブジェクトについて説明します。Rにはコール、表現式、ネームの3種類の言語オブジェクトがあります。

callは一般にはクロージャと呼ばれるもので、関数に値をセットしたオブジェクトです。表現式オブジェクトは内部にクロージャを持つオブジェクトになります。クロージャと表現式オブジェクトは似た存在ですが別の扱いなので、表現式オブジェクトを引数にとる関数にクロージャは入れられないですし、逆も同様です。

nameは間接的に参照できるオブジェクトです。eval("x")をしても"x"が文字列として評価されるだけですが、eval(as.name("x"))とすると、変数xとして評価されます。

オブジェクト is.call is.expression is.name is.language
call
表現式
name

2.1 クロージャをつくる

exprssion関数で表現式オブジェクトをつくれるように、quote関数でクロージャ(callオブジェクト)をつくれます。

cl <- quote(x - y)
cl
x - y
is.call(cl)
[1] TRUE
is.expression(cl)
[1] FALSE
is.name(cl)
[1] FALSE
is.language(cl)
[1] TRUE

2.2 表現式の変数に値を代入してクロージャをつくる

後述する理由で使い勝手が悪いのですが、substitute関数を使うと代入できます。

e1 <- expression(x^y)
e2 <- substitute(x^y, list(x=2))
e2
2^y

bquote関数を使うと、リストを作らなくても代入できます。

e1 <- expression(x^y)
x <- 3
e2 <- bquote(.(x)^y)
e2
3^y

環境にある変数の値を代入する変数名を.()で囲います。

substitutebquoteも表現式オブジェクトを引数にとり、クロージャ(callオブジェクト)を戻します。

2.2.1 表現式オブジェクトの中には代入ができない

さてsubstituteは第1引数にRの式をとるわけですが、表現式オブジェクトを代入しようとすると、表現式オブジェクトの式として処理します。e1を引数にとるとe1と言う式だと考えて、x^yだとは考えないので、xyに何かを代入することはできません。

substitute(e1, list(x=123))
e1

eval時に参照する環境を代えれば、評価時に使う変数の値を代えられるので、明示的に代入する必要は無いのですが、substituteを繰り返してプロットに表示する式を作るようなことはできないので注意してください。

2.3 クロージャをリストにする

例えば以下のようにすると、-も関数であることが分かります。

as.list(quote(x - y))
[[1]]
`-`

[[2]]
x

[[3]]
y

2.4 クロージャを書き換える

クロージャはlistではないのですが、リスト構造を持つオブジェクトで書き換えることもできます。

x <- 3
y <- 2
e <- quote(x + y)

eval(e)とすれば5になるわけですが、

e[[1]] <- as.name("-")
eval(e)
[1] 1

関数+を呼ぶところを、関数-に差し替えることで1の計算結果を得ています。

以下のようにすると、同様の結果が得られますが、関数-への参照ではなく、関数-の中身が入るので注意してください。

e[[1]] <- `-` # notice: backquote
eval(e)
[1] 1

3 evalが使う環境

eval関数が使う環境は、デフォルトではeval関数を呼び出す親の環境になり、引数envirを指定することで変えられます。

e1 <- expression(x^y)
x <- 2; y <- 3
myenv <- new.env()
myenv$x <- 3
myenv$y <- 4
eval(e1) # x=2, y=3で計算
[1] 8
eval(e1, envir=myenv) # x=3, y=4で計算
[1] 81

関数内では別環境になるので、以上のようにわざわざ指定することは少ないと思います。

e1 <- expression(x^y)
fn <- function(e){
    x <- 3
    y <- 4
    eval(e) # 関数のenvironmentを参照して評価する
}
x <- 2; y <- 3
eval(e1) # x=2, y=3で計算
[1] 8
fn(e1) # x=3, y=4で計算
[1] 81

4 まとめ

表現式オブジェクトなどの言語オブジェクトは、多くのユーザーがプロットに数式を入れるとき以外は意識していない存在で、使うときもおまじないぐらいに捉えられていると思いますが、汎用的な関数を作るときには役立つときもあります。知っていれば使いたくなるものなので、大雑把に目を通されると、後々、ためになるかも知れません。


  1. 厳密なR用語では文(statement)になります。↩︎

  2. 一般にはクロージャと呼びます。↩︎

  3. 取扱説明書では非推奨ですが、as.listas.functionとリストと相互変換ができます。↩︎

  4. 評価と言っても、プロットなどの「副作用」がある式だと、実行と言う感じです。↩︎