第20章 Rプログラミング応用

Rによる、より高度な作業のために

などを学びます。

20.1 オブジェクトのクラス

オブジェクトの種類をクラスと呼びます。 class()にオブジェクトを入力するとクラスが分かります。

最も使われるのは数値 ()numeric, real) です。

class(1)
## [1] "numeric"

厳密には数値と整数 (integer) は異なりますが、気にしないといけない局面は少ないと思います。

class(2L)
## [1] "integer"

他には、文字列 (character) や

class("Hello, World.")
## [1] "character"

論理値 (logical) などもあります。

class(TRUE)
## [1] "logical"

論理値は主に条件式が満たされるかどうかを示します。

1 == 1
## [1] TRUE
0 > 2
## [1] FALSE

ちなみに、TRUEは数値としての1FALSE0にもなります。

TRUE + 1
## [1] 2
FALSE * 2
## [1] 0

また、因子型 (factor) と呼ばれるクラスもあります。 カテゴリカル変数と言ったほうが分かりやすいかもしれません。

x <- factor(1)
x
## [1] 1
## Levels: 1
class(x)
## [1] "factor"

Xの中身は1ですが、数値ではなくカテゴリーになっているので、数値として操作することはできません。

x + 1
## Warning in Ops.factor(x, 1): '+' not meaningful for factors
## [1] NA

Rではベクトルには特別なクラスは付与されていません。 ベクトルはc()に中身を入力して作成します。

x <- c(1,3,5)
x
## [1] 1 3 5

行列 (matrix) はクラスとして存在します。

x <- matrix(c(1,3,5,7), 2, 2)
x
##      [,1] [,2]
## [1,]    1    5
## [2,]    3    7
class(x)
## [1] "matrix" "array"

他に、データフレーム (data.frame) やリスト (list) などもあります。

クラスを確認するときは、is.*()の形をとる関数を使います。

is.numeric(1)
## [1] TRUE
is.character(1)
## [1] FALSE

クラスを変更するときは、as.*()のような関数を使います。

as.factor(1)
## [1] 1
## Levels: 1
as.character(1)
## [1] "1"
  • 必ずしも全てのクラスが任意のクラスに変換できるわけではありません(例えば、文字列から数値など)。

20.2 関数の作成

Rで関数を自作する際はfunction(){}という関数を使います。

  • ()の中に入力引数を記述します。
  • {}の中に処理内容を記述し、最後にreturn()で出力引数を指定します。

例えば、数値ベクトルを入力引数として、平均と標準偏差を出力引数とする関数を作成します。

mean_sd <- function(x) { # 入力引数の名前をxとしておきます。
  mean.x <- mean(x) # 平均を計算します。
  sd.x <- sd(x) # 標準偏差を計算します。
  return(c(mean.x, sd.x)) # 出力引数を指定します。
}

実際に実行してみます。

x <- rnorm(100)
mean_sd(x)
## [1] -0.01472691  0.96133642

20.3 ループ

ループとは同一の処理を複数回実行することを指します。 例えば、100個の標準正規分布に従う乱数の平均を5回求める処理は次のようになります。

for (i in 1:5) {
  print(mean(rnorm(100)))
}
## [1] 0.09851629
## [1] 0.0636278
## [1] -0.1019185
## [1] -0.01609289
## [1] -0.04542928

forループとは()の中のinのあとのベクトルの第1要素から順番にiに代入して繰り返しています。 そのことは、次の例から解ると思います。

head(letters)
## [1] "a" "b" "c" "d" "e" "f"
  • lettersとはアルファベットのベクトルです。
for (i in head(letters)) {
  print(i)
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"
## [1] "e"
## [1] "f"
  • forループとは別に、特定の条件が満たされるまで繰り返されるwhileループもあります。

ループ処理の結果を格納するには少しテクニックが必要です。 100個の乱数の平均を5回取ったものをxとして保存したいとします。

まず、xNULLオブジェクトとして作成します。

x <- NULL
x
## NULL
  • NULLとは空っぽのオブジェクト(0という数値や空白という文字ではない)です。

先程のループ処理の中で、計算した平均をc()xにくっつけていきます。

for (i in 1:5) {
  x <- c(x, mean(rnorm(100)))
}
x
## [1]  0.01242996  0.00349691 -0.07151655 -0.01892009 -0.17742199

無事、5個の平均値がxに保存されていることがわかります。

実際にforループの中で何が起こっているかは、次のコードで解ると思います。

x <- NULL
for (i in 1:5) {
  x <- c(x, mean(rnorm(100)))
  print(x)
}
## [1] 0.07132597
## [1] 0.07132597 0.01892245
## [1]  0.07132597  0.01892245 -0.16014605
## [1]  0.07132597  0.01892245 -0.16014605 -0.04272138
## [1]  0.071325969  0.018922447 -0.160146050 -0.042721381 -0.003891877
  • ループが一周するたびに、前回のxに新しい要素が付け加わり、新しいxとして保存されています。

NULLオブジェクトを使ったループ結果の保存でよくあるミスは、やり直す際にNULLでリセットするのを忘れることです。 例えば、同じコードをもう一度実行しましょう。

for (i in 1:5) {
  x <- c(x, mean(rnorm(100)))
}
x
##  [1]  0.071325969  0.018922447 -0.160146050 -0.042721381 -0.003891877
##  [6]  0.181980210  0.017098258  0.234961971  0.007104972 -0.021349265
  • xに10個の平均値が入っています。

このようなミスを避ける方法の一つは、全体を関数として作成することです。

multi_mean <- function() {
  x <- NULL
  for (i in 1:5) {
    x <- c(x, mean(rnorm(100)))
  }
  return(x)
}
x <- multi_mean()
x
## [1]  0.02361923  0.10858991  0.11928147 -0.05369383  0.19632307

20.4 条件分岐

条件分岐とは、特定の条件の場合に特定の動作を行うようにすることです。 例えば、正の場合positive、負の場合negativeと出力するコマンドは次のようになります。

x <- rnorm(1)
if (x > 0) {
  print("positive")
} else {
  print("negative")
}
## [1] "positive"
print(x)
## [1] 0.1087327
  • if(){}()の中に条件式を書き、{}の中に処理内容を書きます。
  • それ以外の条件はelseで示します。

条件式は3つ以上でも構いません。

x <- rnorm(1)
if (x > -0.5) {
  print("x is less than -0.5.")
} else if (x >= -0.5 & x <= 0.5) {
  print("x is between -0.5 and 0.5.")
} else {
  print("x is more than 0.5.ー")
}
## [1] "x is less than -0.5."
print(x)
## [1] 1.066247
  • &は「かつ」を意味します。
  • 「または」は|を使います。
  • >=\(\geq\) を意味します。
  • 「同じ値である」は==を使います(=ではない点に注意)。

20.5 練習問題

20.5.1 フィボナッチ数列

フィボナッチ数列とは以下の条件を満たす数列です。

\[ \begin{aligned} F_0 &= 0 \\ F_1 &= 1 \\ F_{n} &= F_{n-1} + F_{n-2} \quad n \geq 2 \end{aligned} \]

例えば、

\[ \begin{aligned} F_2 = 1, F_3 = 2, F_4 = 3, F_5 = 5, F_6 = 8,\ldots \end{aligned} \]

となります。

フィボナッチ数列の第\(n\)項を(解析解を使わずに)求める関数を作成してみて下さい。

また、\(F_n \geq m\)となるような\(n\)を求める関数を作成してみて下さい。

20.5.2 モンテカルロ・シミュレーション

モンテカルロ・シミュレーション(モンテカルロ法)とは乱数を用いて近似解を求める手法です。

例えば、円周率\(\pi\)の近似解は以下のように求めることができます。

  1. 0以上1未満の一様分布から\(n\)個の乱数\(x_i\)\(n\)個の乱数\(y_i\)を発生させます (\(i = 1,2,\ldots,n\)) 。
  2. 原点と\((x_i,y_i)\)の距離が1以下である回数を計算し\(n_1\)とします。
  3. 円周率の近似解として\(\hat{\pi} = 4 \times n_1/n\)を得ます。

モンテカルロ・シミュレーションによる円周率の近似解を求める関数を作成してみて下さい。

また、モンテカルロ・シミュレーションによる円周率の近似解を\(m\)回求めて、その平均値や標準偏差が\(n\)によってどのように変化するか検討してみて下さい。