| Top Page | プログラミング | R 自動化 目次 | 索引 |
プログラミングを習得するには、とにかく自分で試してみることが大切です。 R での作図だの統計解析だのを試すとなると、まずはデータが必要です。 手元に適当なデータがない場合は、自分で架空データを作成することになります。 for 構文を使えばいろいろなデータを作成できますが、 既存の関数ひとつで書けるならそのほうが楽です。 このページではそうした便利な関数の使い方を簡単に紹介します。
プログラムで作ったデータは、数値シミュレーションにも活用できます。 特に乱数はシミュレーションに不可欠です。 R では、さまざまな確率分布にしたがう乱数の発生関数が提供されています。 こちらは for 構文を使えば済むというわけにはいきません。 用意されている関数をありがたく利用します。
便利な関数を紹介と書いたそばから、いきなり手作業的なやりかたの紹介ですが、 すべての基本は c という関数です。 このあとで紹介する関数を使う際にも、 c と組み合わせることで、できることの幅が広がります。
c(1, 2, 3, 4, 5)
と書けば、1から5までの整数が並んだベクトルができます。 c に渡す引数はベクトルでもよいので、
c(1, 2, 3, 4, 5) -> x c(x, 6, 7, 8, 9) -> y
と書けば、y は 1 から 9 までの整数が並んだベクトルとなります。
rep は、複製する(replicate する)関数です。 最初の引数で複製して繰り返したいもの、次の引数で複製回数を与えます。
rep(1, 100)
を実行すると、x には、1 が 100個並んだベクトルが表示されます。 2番めの引数は times という名前なので、より明示的に
rep(1, times = 100)
と書いても同じ意味です。
最初の引数として与える複製したいものは、ベクトルであってもかまいません。
rep(c(1, 2, 4), 10)
を、実行すると、1, 2, 4, 1, 2, 4, ... と、1, 2, 4 が10回繰り返された 要素数30のベクトルが表示されます。 times ではなく each という引数で要素の数を指定することもできます。
rep(c(1, 2, 4), each = 10)
この場合、まず 1 が 10個、次に 2 が 10個、最後に 4 が10個並んだベクトルができます。
要素の数の指定には、times, each のほか、length.out という引数も使えます。 トータルの要素数が length.out に達するまで繰り返せ、という指定です。
rep(c(1, 2, 4), length.out = 10)
とすると、1, 2, 4, 1, 2, 4, 1, 2, 4, 1 までの10個の要素からなるベクトルができます。
なお、R の関数の引数名は、他の引数名と一意的に区別できる範囲なら短く省略することができます。 rep の引数名は、x, times, each, length.out なので、1文字だけでも区別できますから、以下の4行はどれでも正しく動作します。
rep(c(1, 2, 4), length.out = 10) rep(c(1, 2, 4), length = 10) rep(c(1, 2, 4), len = 10) rep(c(1, 2, 4), l = 10)
ひとつの数値ではなく、ベクトルを複製して繰り返せryことを説明しましたが、 要素の数もベクトルで指定できます。 times に、複製されるものの個数と同じだけの要素を並べたベクトルを渡して、 各要素の複製回数をそれぞれ指定できます。 以下のコードでは、 1 が 5個、2 が 10個、4 が 5個ならんだベクトルが得られます。
rep(c(1, 2, 4), times = c(5, 10, 5)) rep(c(1, 2, 4), c(5, 10, 5)) # 上と同じ。
複製して繰り返すものは、数値(や、数値が並んだベクトル)に限りません。 文字列を繰り返すこともできます。
rep('Apple', 10)
と書けば 'Apple' という文字列 10個が得られますし、
rep(c('Dog', 'Cat', 'Mouse'), times = c(3, 5, 10))
なら、'Dog' が3個、'Cat' が 5個、 'Mouse' が 10個並んだ文字列のベクトルが得られます。
もとになる文字列の生成に paste や sprintf を活用することもできます。 pasteは、引数としてあたえた任意の数の文字列を連結してひとつの文字列 にしてくれます。paste という名前の引数で、連結するときに間に挟む文字列を指定することも できます。何も指定しないと空白がひとつ挿入されます。なので、空白なしで繋げたいなら、 sep = "" と指定することが必要です。下の例では、下線 "_" を挿入しています。
rep(paste("Species", c('A', 'B', 'C'), sep = "_"), each = 10)
これで、'Species_A', 'Species_B', 'Species_C' それぞれが 10個並んだベクトルが作れます。
rep(sprintf("Species_%s", c('A', 'B', 'C')), each = 10)
順に1ずつ増える、ないしは減っていく数列は、最初と最後の数値をコロン ':' で 挟んで並べるだけで作れます。
1:10 # 1からスタートして10までの10個の要素が並んだベクトル 100:0 # 100からスタートして0まで減っていく101個の要素が並んだベクトル
スタートする値は、整数である必要はありません。
4.5:0 # 4.5, 3.5, 2.5, 1.5, 0.5。最後の数を行き過ぎることはない。
より柔軟に、増分・減分を指定するには、 seqという関数を使います。 最初の引数にスタートする数、次の引数に最後の数、そして3番めに引数に変化の大きさを 与えます。3番めの引数の名前は by です。 by = 0.1 などと明示的に書くとあとから 読むときに分かりやすいでしょう。
seq(0, 100, by = 5) # 0 から 100 まで 5 ずつ増えるベクトル seq(10, 20, by = 0.1) # 10 から 20 まで、0.1ずつ増えるベクトル
length.out で、生成する要素の数を指定することもできます。 間隔は適切に決めてくれます。
seq(0, 100, length.out = 11) # 0 から 100 までの11個の要素からなる等差数列のベクトル seq(0, 100, length = 11) # 上と同様 seq(0, 100, len = 11) # 上と同様
等差数列をべき乗の指数に使えば、等比数列を作ることもできます。
2^(1:10) # 2 の 1乗から 10乗までが並んだ数列(初項が 2、公比が 2) 10 * 1.2^(0:10) # 初項が 10, 公比が 1.2 の数列。べきの指数が 0 から始まることに注意
最初にも書いたように、R ではいろいろな乱数の発生関数があります。 均一分布だけでなく、正規分布、ポアソン分布、二項分布、ガンマ分布、指数分布など、さまざまな分布 にしたがう乱数を作ってくれます (参考:R-Tips 「60. 確率分布と乱数」)。
乱数発生関数の名前は、分布の名前(pois, norm など)の前に random の r がついたもの (rpois, rnorm など)です。関数に渡すひとつめの引数は発生する乱数の数で、 そのあとに分布の形を決めるパラメータを並べます。どのようなパラメータが必要かは 分布によります。たとえば正規分布にしたがう乱数を発生する rnormでは、平均(mean)と標準偏差(sd)を指定します。
rnorm(100, mean = 10, sd = 4) # 平均 10, 標準偏差 4 の分布に従う乱数を100個
生成した乱数をそのままヒストグラムを描く histに渡せば、 たしかにそれらしい分布に従っていることが確かめられます。
hist( rnorm(10000, mean = 10, sd = 4)) # 1万個の要素
その他の乱数発生関数の例をいくつか載せておきます。
runif(100, min = 0, max = 5) # 0 から 5の一様分布。runif は、r + unif (uniform)。
一様分布する整数の乱数が必要な場合は、 runif で生成したベクトルを as.integer に渡せば少数点以下が切り落とされます。 なお、負の数の場合は 0 に近い側へと整数化されますので注意が必要です (as.integer(-1.5) は、-2 ではなく -1 を返す)。
as.integer( runif(100, min = 1, max = 7) )# 1 から 6 の整数がランダムに並んだベクトル
以下は、ポアソン分布と二項分布の例です。
rpois(100, lambda = 2) # 期待値が 2 のポアソン分布に従う乱数 rbinom(100, size = 10, prob = 0.5) # 「成功確率 0.5 の試行を 10回試みたときの成功回数の分布」に従う乱数
上で紹介した関数はごく一部の例です。全貌は R-Tips のページ などで調べてください。 多次元の正規乱数を生成する方法も紹介されています。
乱数発生関数に渡す分布パラメータに、ベクトルを使うこともできます。
rnorm(100, seq(0.1, 10, by = 0.1), 1) # 正規分布の平均を、0.1 から 10まで 0.1 ずつ増やす
上記のコードだと、平均 0.1、標準偏差 1 の正規乱数をひとつ、次に平均が 0.2 の正規乱数をひとつ、 と平均を10 まで順次増やしながら、全部で 100個の乱数を生成します。それぞれの平均値ごとに100個の 乱数を作るわけではありません。 各平均値ごとの乱数をたくさん欲しいなら、欲しい数だけ平均値を繰り返して渡す必要があります。 たとえば平均が 1, 2, 3, 4, 5 の乱数をそれぞれ 10個ずつ欲しいなら、以下のように 平均を指定するためのベクトルを repを使って作ればよいでしょう。
rnorm(50, rep(1:5, each = 10))
なお、ここで
rep( rnorm(5, 1:5), 10)
と書くと、rnorm が 10回繰り返し実行されるのではなく、 1度だけ実行したrnorm の結果を rep でそのまま10回反復して返してきます。 rep は式を受けとるのではなく、 計算結果を受けとるのだと考えれば当然ですね。 ※意味がよく分からなかったら、ぜひ実行して試してみてください。
登場する要素は決っているが、その順番はランダムにしたいという場合は、 sampleという関数が便利です。 この関数は、与えられたベクトルの中から、指定した数の要素をランダムな順番で 選び取ります。選んだ要素をもとのベクトルに戻して次の選択をするか、一度選んだものは もう選べないとするかをパラメータで指定することが出来ます(replace = TRUE か FALSEか)。 とくに指定しなければ replace = FALSEとなります。 選ぶサンプルの数を与えるベクトルの要素数と同じにして、一度選んだものは二回選ばない ようにすれば、ベクトルのすべての要素がランダムな順番で一度だけ 登場する、すなわちランダム・シャッフルしたことになります。
x <- rep(c('Dog', 'Cat', 'Mouse'), each = 10) # Dog, Cat, Moust が10個ずつ並んだベクトル y <- sample(x, length(x)) # 順番をシャッフルする
シャッフルとは逆に、大きさ順に整列させたい場合は、sort を使います。 数値だけでなく、文字列も整列してくれます。
x <- as.integer(runif(20, 0, 101)) # 0 から 100 の整数乱数20個 y1 <- sort(x) # 昇順に整列 y2 <- sort(x, decreasing = TRUE) # 降順に整列
あとひとつだけ。生成したデータ列のなかの重複を排除した場合には、unique という関数を使います。
d <- as.integer(runif(30, 0, 101)) # 0 から100の整数を 30個 dd <- unique(d)
これで、d の要素の重複を除いたベクトルが得られます。上のコードを実行し、 d と dd を見比べてみてください。たまたま最初から d に重複がないと効果が分かりませんので、 何度か試してみてください。
作ったベクトルはいろいろな形で加工したり繋げたりできます。 ベクトルを伸ばすには最初に書いたように c を使うだけです。 いくつかのベクトルをそれぞれ列として束ねてデータフレームをつくるには、 data.frameを使います。 この関数は、渡されたベクトルや行列、データフレームなどを束ねて 新たなデータフレームを作ります。
d <- data.frame(1:10, runif(20, min = 100, max = 200))
これで、一列めには1から10の整数、二列めには100から200の範囲の一様乱数が入った データフレームができます。
下はもう少し凝った例です。一列めは文字列の A と B が10個ずつ並びます。二列めには、 最初の10個は平均3.7, 次の10個は平均 6.5 の正規分布に従う乱数が並びます。
d <- data.frame(rep(c("A", "B"), each = 10), rnorm(20, rep(c(3.7, 6.5), each = 10)) )
こうして作ったデータフレームの列名をcolnames(d) などとして調べてみると、 なんだかとても使いにくい名前になっていることが分かります。 d[1] のように番号で列を指定して使うならそのままでもよいですが、人間が見て分かるような 意味のある名前を付けたいなら、以下のような colnames の使い方をします。
d <- data.frame(rep(c("A", "B"), each = 10), rnorm(20, rep(c(3.7, 6.5), each = 10)) ) colnames(d) <- c("type", "weight")
これで、データフレーム d の一列めは type、二列めは weight という名前になります。
ところで、ベクトルを束ねるには cbind という関数もあります。 8章では、データフレームにベクトルを足して、あらたなデータフレームを作るのに使いました。 cbindに渡された引数のなかにデータフレームが含まれていれば、 作ってくれるものもデータフレームなので、8章ではcbind を使ったのですが、 ベクトルだけを渡した場合にはデータフレームは作ってくれません。返されるのは行列です。 それをさらに data.frame(行列) のように書いてデータフレームにすることもできますが、 最初から data.frame(ベクトル、ベクトル…) と書いたほうが簡潔でしょう。
データフレームのある列の値の大小によって、その列内だけでなく、行まるごとの整列 をしたい場合(たとえば体重 w、身長 h、年齢 age の列があり、1行が一人のデータという データフレームで、年齢 順に行を並べ替えたいという場合)には、 ちょっと前に紹介した sort ではできません。 かわりにorder という関数を利用します。 与えられたベクトルを大きさ順に並び替えるとしたら、最初に来るのは何番めの要素、 2番めに来るのは何番めの要素… という、いわば並べ替え方の指示表を返す関数です。 たとえば以下のように使います。
# まずテストデータ作り。10人のデータで、一列めが体重、二列めが身長、三列めが年齢を想定。 d <- data.frame(runif(10, 50, 90), runif(10, 150, 180), as.integer(runif(10, 10, 60))) colnames(d) <- c('w', 'h', 'age') # 分かりやすいように列の名前を付ける d.sorted <- d[order(d$age), ] # d$age の昇順に行を並べ替え
なぜ上のコードで狙い通りに行の並べ替えができるのか、 じっくり考えてみてください。 d[c(5,10,1), ] と書くと、d の5行め、10行め、1行めが順にならんだデータフレームに なることを頭におきつつ、 d$age と、order(d$age) を見比べて考えるとよいでしょう。