Chap5 S言語でプログラミング
コンテンツ
5.1 イントロダクション5.2 関数定義5.3 制御構造5.1 イントロダクション
この章では、今までコマンド・ウィンドウだけで実行されていたS言語を、 スクリプトとして実行する方法について学ぶ。 これにより、分析で何度も実行するようなコマンドを、 あらかじめスクリプトとして書いておくことにより、分析を楽に実行することができる。
スクリプトを書くための知識として、if,for,while などの制御構造と、 function() 関数による、新たな関数定義の方法を覚えることで、 S-PLUS 自体には実装されていない新たな分析用の関数を組んだり、 より複雑な分析を行うことが可能になる。
スクリプトを作成するためには、S-PLUS のメニューから、 [ファイル]→[新規ファイル]→[Script File] と選択し、[OK] をクリックすればよい。 すると、スクリプトウィンドウ(実際はただのテキストエディタ) が上下2画面現れる。 そこで、上の画面にスクリプトを書き込み、キーボードの[F10] キーを押すことで、 スクリプトウィンドウに書き込んだ内容をそのまま実行することができる。
5.2 関数定義
関数の作成
新しい関数を作成するためには、以下のように書けばよい。 ここで、引数1, 引数2,…とは、複数の引数を書いてよいということを意味する。 引数は0個でもよい。
関数名 = function(引数1, 引数2,…){ 関数定義本体 }
関数の返り値は、関数定義本体に書いた中で一番最後の行の評価値になる。 また、気をつけなければならないこととして、新しい関数の関数名が、 既存の関数の名前と同じにならないようにしたほうがよいということである。 同じ名前をつけた場合、自分で作成した関数のほうが先に実行されるため、 プログラムが混乱する恐れがある。 対策としては、試しにつけようとしている関数名を、コマンドライン上で実行してみることである。
まず、myfunc という名前の、平均値を求める関数を作るという例を示す。
myfunc = function(x){ sum(x)/length(x) }
スクリプトウィンドウで、ここまで関数を記述した時点で[F10]を押して実行してみる。 何も起こらないように見えるが、書き間違いがない限り、 myfunc(x)という関数が作られているはずである。 ここで、コマンドウィンドウを出して、実際に関数を実行する。 実際にmean関数と値を比較すると、きちんと平均を求める関数が作られていることがわかる。
> x = 1:10 > myfunc(x) [1] 5.5 > mean(x) [1] 5.5
もうひとつの例として、range関数と同じ結果が得られるような関数myrangeを作成する。
myrange = function(x){ mi = min(x) #関数の中で変数や関数を使うことができる ma = max(x) c(mi,ma) }
> x = 1:10 > myrange(x) [1] 1 10 > range(x) [1] 1 10
引数の省略
alpha,beta,gammaを引数とする、次のような関数を考える
exfunc = function(alpha,beta,gamma){ alpha + beta + gamma }
この関数を以下のように実行しようとするとエラーを起こす。
> exfunc = function(alpha, beta, gamma) { alpha + beta + gamma } > exfunc() #引数を指定していない!! Problem in exfunc(): argument "alpha" is missing with no default Use traceback() to see the call stack
次に、先ほどのexfunc()を次のように定義する。
exfunc = function(alpha=1,beta=2,gamma=3){ alpha + beta + gamma }
先ほどエラーが出た方法で実行すると、次のようになる。
> exfunc = function(alpha = 1, beta = 2, gamma = 3){ + alpha + beta + gamma +} > exfunc() [1] 6
このように、関数定義時にあらかじめ引数の値を設定しておくと、 関数の実行時、引数に値が代入されていない場合、 自動的にその値が代入される仕組みになっている。
関数を引数にする
S 言語の引数は、関数名を引数とすることができる。 例えば、funcisarg()を次のように定義する。
funcisarg = function(x,f){ f(x) #引数にfがあるにも関わらず、fという関数を実行している。 }
実際に、この関数を実行してみる。
> x [1] 1 4 9 16 25 36 > sum(x) [1] 91 > funcisarg(x,sum) [1] 91 > sqrt(x) [1] 1 2 3 4 5 6 > funcisarg(x,sqrt) [1] 1 2 3 4 5 6
関数内で、sum,sqrt関数が実行されていることが分かる。
任意個の引数を設定する
...を使うことによって、複数の引数を設定することができる。
somearg = function(...){ tmp = c(...) sum(tmp) }
> x = 1:3 > y = 4:6 > z = 7:9 > sum(1:9) [1] 45 > somearg(x,y,z) [1] 45
5.3 制御構造
条件判断のif
関数を作成しているときに、条件分けをしたいと思うときがある。 たとえば、ある変数の値が正ならばこうしなさい、という命令を書くとする。 このとき、if を使うことによって実現できる。
if(条件式){ 条件式がTrue のときに実行するプログラム }
次のような形もある。
if(条件式){ 条件式がTrue のときに実行するプログラム }else{ 条件式がFalse のときに実行するプログラム }
if(条件式1){ 条件式1 がTrue のときに実行するプログラム } else if(条件式2){ 条件式2 がTrue のときに実行するプログラム } else if(条件式3){ 条件式3 がTrue のときに実行するプログラム } ・・・・ else{ 各条件式がFalse のときに実行するプログラム }
例として、ある数字が与えられたときの順番表記を返す関数を挙げる。
ord = function(n){ if(n == 1){ "1st" } else if(n == 2){ "2nd" } else if(n == 3){ "3rd" }else{ #paste関数は、複数の引数を文字列としてつなぎ合わせる paste(n,"th",sep="") } }
> ord(3) [1] "3rd" > ord(10) [1] "10th"
繰り返しのfor, while
for 文は、繰り返して実行したいプログラムがあるときに使う。for 文は以下のように書く。
for(変数in 変数2){ 繰り返し実行したいプログラム }
for文は、変数2(ベクトル・行列)の要素を順に変数に代入し、 繰り返し実行したいプログラムを実行するのである。 変数2の要素数分だけ実行するので、ループ回数があらかじめ決まっているときにfor 文を使うと良い。 例として、forloopという関数を実行する。
forloop = function(x){ for(i in x){ print(i) #iの要素を表示する関数 } }
x にそれぞれ1:3、c("a","b","c") というベクトルを代入し、実行する。
> forloop(1:3) [1] 1 [1] 2 [1] 3 > forloop(c("a","b","c")) [1] "a" [1] "b" [1] "c"
つまり、for文の変数2には、どの型のベクトル・行列・リストを代入しても良いことが分かる。 for文では、変数2の要素すべてについて実行することは先ほど述べたとおりだが、 要素の途中でfor 文を強制的に終わらせたいときがある。 そのときには、breakを使うと良い。
ベクトルの中にNA があるかどうかを確認するhas.na()関数を例に解説する。
has.na = function(x){ #結果を表すresultにFを代入しておく result = F #xの要素それぞれについて実行 for(i in x){ #iの要素がNAならifの中身を実行 if(is.na(i)){ #結果をTにする result = T #NAを1つ見つければ目的が果たせるので、breakでforをぬける break } } result #結果を返す }
S言語では、for文を使わなくても、暗黙にforを使っている場合が多い。 例えば、ベクトルの要素同士での掛け算がそれにあたる。 通常、forを使うよりベクトル・行列のまま一度に計算が可能ならば、 一度に計算した方が圧倒的に速度が速い。 可能な限りfor文を使わないように心がけるべきである。
繰り返しのwhile
while文は、forと同様、繰り返して実行したいプログラムがあるときに使う。 forと違い、繰り返す回数がよく分からないときに使うと良い。
while(条件式){ 条件式がTrue のとき実行するプログラム }
while文は、条件式がTrueである間、プログラムを繰り返し実行し続ける。 下記のプログラムは、100を超えるまで0に1,2,3,…を加えて行くプログラムである。
> x = 0 > sum = 0 > while(sum < 100){ + x = x + 1 + sum = sum + x + } > c(x,sum) [1] 14 105