| Top Page | プログラミング | R 自動化 目次 | 索引 | 前へ | 次へ |

R でプログラミング:データの一括処理とグラフ描き

7. 条件分岐と繰り返し:コンピュータらしい仕事

高速かつ柔軟にたくさんの仕事をしてくれるプログラムを書くには, 繰り返しと条件分岐の構文はなくてはならないものです. ほぼすべてのプログラミング言語は,繰り返しと条件分岐の構文を持っています. R も例外ではありません. この章では,条件分岐と繰り返しの使い方を練習します. 次の章では,条件分岐と繰り返しを使って,より柔軟で汎用性の高い 作図プログラムを書いてみます.

条件分岐と繰り返しの構文

条件分岐

まずは条件分岐からはじめます. ある条件が満たされるかどうかによって,その後の処理を変えるのが条件分岐の構文です. R では,以下のような ifをつかった構文が用意されています.

# 擬似コード.このままでは動かない.

if (条件) {
    条件が満たされたときに行う処理
} else {
    条件が満たされないときに行う処理
}

{ } は,行いたい処理が一行で書ける場合には不要です(あってもよい). とはいえ,プログラムの構造が見やすくなるように,かならず {} で囲って, そのなかは一段頭下げ( インデント )をして書くことを習慣にすることをお薦めします。

else 以下は,必要がなければ省略することができます.

if (条件) {
    条件が満たされたときに行いたい処理
}

条件のところには,x > 3 のような論理式を書いてもよいし, TRUE や FALSE といった真偽値そのものでもよいし,それらを論理演算子で 組み合わせたものを書くこともできます. 比較演算子や論理演算子の詳細は, R-Tips の 演算子 のページを参照してください.

なお、if () { } のあとに else が続く場合、else の前の閉じるカッコ "}" と else の 間で改行してしまうとエラーとなりますので注意してください。

以下は、if の閉じ括弧と else を同じ行に書かなくてはいけない理由です。 R は、行の終りまで来たところで、明らかにまだ途中だなと分かる場合のみ、 次の行を続きだと解釈します (1章の説明)。 if ( ... ) { } までで行末となると、 そこで終り(もう else は続かない)だとしても文法上は正しいので、 そのように解釈します。その次の行に else が出てくると、 else は必ず if と組みになって使われるもの、いきなり else が出てくるのは 文法的におかしい、という解釈してエラーと判断されてしまいます。

さっそく実例で試してみましょう.

x <- 5

if (x > 4) {
    cat('x is larger than 4\n')
} else {
    cat('x is less or equal to 4\n')
}

if (3 < x && x < 6 ) {
    cat('x is in the range (3, 6)\n')
}

<練習>

なお,下のようにいくつかの条件を並べて書くこともできます.

if (条件1) {
    条件1が満たされたときの処理
} else if (条件2) {
    条件1は満たされないけど条件2は満たされたときの処理
    …
} else  {
    どの条件も満たされないときの処理
}

繰り返し − for 構文

R には繰り返しのための構文がいくつかありますが, まずは for の構文を紹介します.

for構文は以下のように書きます.

# 擬似コード

for (変数 in データの集合){
    なんらかの処理
}

こう書くと,データの集合(たとえばベクトル)の要素をはじめからひとつづつ変数に代入していき, そのたびに「なんらかの処理」のところを実行します. 繰り返したい内容が一行しかない場合は,{} は省略できるのは if の構文と同じです.

「なんらかの処理」では,データを順次代入した変数を利用してもよいし, しなくてもかまいません.

なお、if 構文の場合と同様、{ と } で囲まれた 「何らかの処理」は必ず インデントして書き、どこが繰り返される部分か、ぱっと見ても分かるようにしましょう。

ところで,ふたつの整数値を : で区切って並べると,値がひとつづつ増えていく (あるいは減っていく)整数のベクトルが得られます.たとえば 1:5 と書くと, c(1, 2, 3, 4, 5) と書いたときと同様の,要素が5つのベクトルが得られますし, 10:7 とかけば c(10, 9, 8, 7) と書いたのと同様のベクトルが得られます. そこで,

for (i in 1:n) {
    #  なんらかの処理
}

と書けば,{} 内の処理をn回繰り返すことができます.

繰り返し構文のなかで使われるたいせつな命令に, breaknext があります. break は,ある条件が満たされるなら,途中を繰り返しを打ち切りたい,というときに 使います.また,next は,{} 内の処理をしてる途中でも, ある条件が満たされたら あとの処理はやめて,次の繰り返しに行きたい,というときに使います.

以下は,breaknext を使った プログラムの例です(全然実用的ではありません).

for (i in 1:100) {   #  i は,1, 2, 3, .. 100 が順に代入される.
    if (i %% 5 != 0) {  #  %% は剰余(割り算の余り),!= は '等しくないか?'
        next
    } 
    cat (i, '\n')
    if (i > 50) {
        break
    }
}

<練習>

なお,for 文は,何重にでも入れ子 (繰り返しのなかに,また繰り返しをを含む構造)にする ことができます.すぐあとに例が出てきます. 入れ子構造の for 中で break があった場合, その break を包む一番内側の for の 繰り返しを抜けます.


繰り返し − while 構文

forのほかに、 while 構文というものも繰り返し用に用意されています。 こちらは、以下のような形式です。

# 擬似コード

while (条件){
    なんらかの処理
}

条件の部分が真である限り、繰り返しを続けます。 「なんからの処理」を続ける過程で、この条件が成り立たなくなったら、繰り返しを終了する ことになります。

以下は、while を使って x から upto (以下の例では 10)までを合計する例です。

total <- 0  # 積算用の変数
x <- 0      # 初期値
upto  <- 10 # ここまで x を増やす

while (x <= upto) {    # x が upto になるまで以下を繰り返す
    total <- total + x     # total に x を足しこむ
    x <- x + 1             # x を 1 増やす
}

cat("total = ", total, "\n") # 結果を出力

while 構文でも、{ と } で囲まれた繰り返し部分は必ず インデントして書きましょう。

条件のところに TRUE と書いておけば、無限に繰り返すループを 表現できます。(条件) のところでなんらかの判断をするのではなく、 { } で囲われた処理のところで、なんらかの条件が成り立ったら(あるいな成り立たなくなったら) break する、といった使い方をします。

上のプログラム例を、while (TRUE)を使って書き換えると以下のようになります。

total <- 0  # 積算用の変数
x <- 0      # 初期値
upto  <- 10 # ここまで x を増やす

while (TRUE) {      # ひたすら繰り返す
    total <- total + x     # total に x を足しこむ
    x <- x + 1             # x を 1 増やす
    if (x > upto) {        # x が upto を越えたら
        break              #   そこで繰り返しを終了
    }
}

cat("total = ", total, "\n") # 結果を出力

上のふたつの例は、いずれも繰り返し回数が upto の値で決るので、 while ではなく for でも書けます。 while が便利なのは、 繰り返し回数が何回か、あらかじめ決められないような場合です。 たとえば、 ファイルを一行づつ読んで行ってある条件に達したら… とか、乱数を発生していって、ある条件が満たされたら… というよな プログラムでは、while が有効でしょう。


最大値と最小値

繰り返しと条件分岐を使って

ここまでの知識を使って,データの最大値を求めるプログラムを描いてみます. ほんとは,専用の関数を使えば一行で求められるのですが, ここでは練習のために愚直に計算してみます.

使うデータは,前に出てきた温度のデータ, 'temperature.txt' です.このデータファイルから,最高温度を求めます.

以下を読み進める前に, 第2章 で,データフレームの扱いかたを復習しておくとよいかもしれません. 基本的な方針は,

というものです.最後のデータまで調べおわったときに,最大値記録用変数には, すべてのデータのなかの最大値が残っているはずです.

まずは,temperature.txt を読み込んだデータフレームの2列めに入っている Site01 のデータから最大値を探してみます.

t <- read.table ('temperature.txt', header = TRUE)  # データの読み込み

max.t <- t[1, 2]             # まず,2列めの最初の行のデータが最高だとする.
for (j in 1:length(t[[2]])) {    #  j で,列内のデータを順に数える.
                                 #  1行めから最後の行まで順に…
    if (max.t < t[j, 2]) {       #  もし,そのデータが max.t より高ければ,
        max.t <- t[j, 2]         #  そのデータをmax.t に代入.
    }
}
cat(max.t)       #  max.t を表示

上がプログラム例です.max.t が最大値をしまっておく変数です. for 文で,2列目のデータを上から順に調べています. すべてのデータを調べおわったところで,max.t に最大値が入っています. このプログラムを実行すると,23.6 という数値が表示されるはずです.

ここでまちがいやすいのは,

    for (j in 1:length(t[[2]])) {

のところです.t[[2]] の要素数を length で調べて,2番めの列の 最初から最後のデータまでを順次調べています.t[[2]] は,データフレーム t の 2番めの列の要素がならんだベクトルを意味します.

うっかり t[[2]] のかわりに t[2] と書いてしまうと, データフレーム t の,2番めの列のみからなるデータフレームという意味になります (不確かだったら第2章を復習). データフレームを length 関数に渡すと,列の数が返ってきます. これでは期待通りに動いてくれません.

なお,t[[2]] のかわりに t[,2] と書いても 2列めの全要素からなるベクトルを 意味しますので正しく動作します.


次に,3地点(Site01,Site02,Site03)すべてを通じての最高温度を求めまてみます. 上のプログラムの,各行のデータを調べる for 文を,各列を順次調べるための for 文で囲んで 入れ子にします.プログラムは以下のようになります.

t <- read.table ('temperature.txt', header = TRUE)  # データの読み込み

max.t <- t[1, 2]             # まず,2列めの最初の行のデータが最高だとする.
for (i in 2:4) {             #  i で列の番号を数える.2列めから4列めまで順に…

    for (j in 1:length(t[[i]])) {    #  j で,列内のデータを順に数える.
                                     #  1行めから最後の行まで順に…
        if (max.t < t[j, i]) {       #  もし,そのデータが max.t より高ければ,
            max.t <- t[j, i]         #  そのデータをmax.t に代入.
        }
    }
}
cat(max.t)       #  max.t を表示

前のプログラムとよく見比べてください. 各列を調べるため, さらに for の繰り返しで囲ったこと, 前のプログラムで2列めを指定するために書かれていた 2 という数字の部分が, 何番めの列を見ているかが記録されている変数 i に変わっていることに注意しましょう.

このプログラムを実行すると,26.6 という数値が表示されるはずです.

ところで,このプログラムでは,温度データが3地点分あるものと決めて しまっていますが,何地点並んでいても対処できるようにしたほうが汎用性が 高いプログラムになります.

for (i in 2:4) {  #  i で列の番号を数える.2列めから4列めまで順に…

のところ,4列めまであるものと決めてしまっているところを,最後の列まで, というふうに変えれば,地点数にかかわりなく動作します. 最後の列は何番めの列かといえば,データフレーム中の列の総数と同じです. 列の数は ncolという関数で求めるのでした.したがって, 4 のかわりに ncol(t) を書いて,

for (i in 2:ncol(t)) {    #  i で列の番号を数える.2列めから最後の列まで順に…

とすれば,何地点分のデータが並んでいるファイルでも対応できます. これを使って書きかえると,プログラムはこんなふうになります.

t <- read.table ('temperature.txt', header = TRUE)  # データの読み込み

max.t <- t[1, 2]             # まず,2列めの最初の行のデータが最高だとする.
for (i in 2:ncol(t)) {       #  i で列の番号を数える.2列めから最後の列まで順に…

    for (j in 1:length(t[[i]])) {    #  j で,列内のデータを順に数える.
                                     #  1行めから最後の行まで順に…
        if (max.t < t[j, i]) {       #  もし,そのデータが max.t より高ければ,
            max.t <- t[j, i]         #  そのデータをmax.t に代入.
        }
    }
}
cat(max.t)       #  max.t を表示

<練習>


最大値と最小値:実はこんなに簡単に

せっかく最大値,最小値を求めるプログラムを書いたのですが,これは練習のため. 最初にも書いたように,こういうことは関数ひとつで簡単にすんでしまいます. 繰り返しと条件分岐の話からは離れますが,ここで紹介しておきます.

最大値を求める関数は maxです. maxに,数値データからなるベクトルや, そういうベクトルを含んだデータフレームをわたすと, ベクトルやデータフレームに含まれるすべての数値データの最大値を求めてくれます. ベクトルだけでなく,データフレームまるごとでもよいのです.

では,t をそっくり渡して max(t) でいいのかというと,それではだめ. 1列めの日付データまで含めて最大値を求めてしまうので,きっと 365 が返ってくるでしょう.温度データが入ったところだけを渡す必要があります.

データフレームにしまったデータのうち,ある列をとりだすには,[] で囲って 何列目かを指定する数値(あるいはその数値がしまわれた変数の名前)を書けばよいのでした. たとえば今の場合,t[2] と書けば,tの2列めを取り出せます.

[] のなかに,ひとつの数値ではなく,数値が並んだベクトルを書けば, ベクトルの要素で指定した列すべてを含むデータフレームが得られます. t[c(2,3)] とすれば,もとのデータフレーム t の2列めと3列めからなる データフレームが 得られます(c はたった一文字ですが,ベクトルを作る関数でした).

すでに見たように,1:6 のようにふたつの整数値を : で区切って並べると, 値がひとつづつ増えていく(あるいは減っていく)整数のベクトルが得られます. そこで,t[2:4] とすれば,t の2列めから4列めまでの3列からなるデータフレームが 得られます.

ここで,全部で4列あると決めてしまわず,

max(t[2:ncol(t)]) 

と書いておけば,何地点分のデータが並んだファイルであっても,2列めから最後の列までの すべてのデータの最大値が求まることになります. なお,最大値を求める max に対応して, 最小値を求める min という関数もちゃんと用意されています.

さらにありがたいことに,最小値と最大値をいちどにもとめて,ふたつの数値が並んだ ベクトルを返してくれる range という関数があります. 入力画面で

range(t[2:ncol(t)]) 

と入力すれば,最小値,最大値が並んだベクトルが表示されるはずです. 高水準作図関数 plot の xlim や ylim 引数にわたすのは, 軸の下限,上限がならんだベクトルですから,range が返すベクトルをそのまま これらの引数に設定できるわけですね.次のページでは,この手を使うことにします.

<練習>

発展:apply系の関数

統計計算を一番の得意技にしている R では,多数のデータのかたまりを扱うことが多くなります. そういうプログラムを書きやすくしてくれる工夫が,たとえば x + 1 と書いたとき, x がひとつの数値ならそれに1を足すだけだけれど,x がベクトルならばその成分すべて に1を加えてくれるというようなところに見られます. (じつは,x がひとつの数値データのときも,要素がひとつのベクトルとみなすのが R の流儀です).

ほかにも,やたらと for などの繰り返し構文を書かないですます道具があります. apply 系の関数です. データのかたまりと,データの処理に使いたい関数とを apply に渡すと, この関数に,かたまりを構成する個々のデータを渡して処理をしてくれる,という ものです. これを使いこなすと,より Rっぽいプログラムになります。 簡単な apply 解説ページを作りました。 ぜひ読んで、試してみてください。


| Top Page | プログラミング | R 自動化 目次 | 索引 | 前へ | 次へ |