| Top Page | プログラミング | R 自動化 目次 | 索引 | 前へ | 次へ |
ひとつのデータファイルを読んで処理することができるようになったら、 今度はいくつものデータファイルの処理も自動的にやってもらいましょう. 人間が一回一回ファイル名を指定して、手動で10回も20回も繰り返す などということをしてはいけません.そんな単純作業はコンピュータにだって できるのですから、コンピュータにやらせます.
ほとんど新しい知識は必要ありません. ファイルからのデータの読み込みはすでに実習済みです. あとは,読み込むファイル名を変えながら,同じ作業を繰り返すだけです. もちろん,同じ作業内容をプログラムのなかで何度も書くなどという 無駄なことはしません.繰り返し構文という便利なものを活用します.
擬似コードで書くとこんな感じです.
for (...){ # ファイルごとの繰り返し ファイル名を設定する. ファイルの内容を読み込む. 読み込んだデータを使ってなんらかの処理をする. }
ファイルの読み込みも,読み込んだデータを使っての処理もすでにいろいろ勉強しました. あとは,繰り返しごとに別のファイル名を与えるようにするだけ. では,どうやって名前を設定するか.いろいろなやりかたがあるでしょう. たとえば…
などなど.組み合わせワザもあるでしょう. この章では,まずもっとも素朴な最初の方法でいろいろ練習してみます. ファイル名をプログラム中に書き並べるやりかたです. ファイルの数があまり多くない場合に向いています. ほかのファイル名生成方法は次の章で紹介します.
練習用に,4つのデータファイルを用意しました.全部を保存しておいてください.
これらのひとつを読み込んで,グラフを描くプログラムも書いてみて, うまく動くかためしてみましょう.これらのデータファイルはいずれも height と diameter の2列のデータからなっています. 描画プログラムは,たとえばこんな感じになるでしょう.
pdf('figures.pdf') # デバイスの用意 data.file <- 'site_a_01.txt' d <- read.table (data.file, header = TRUE) # データフレームにファイルの内容を読み込む plot(main = data.file, d$height, d$diameter, type = 'p') # 散布図を描く dev.off() # デバイスを閉じる
たくさんグラフを描くとき,どれがどのデータのグラフか分からなくなると困るので, plot() 関数のパラメータ main にデータファイルの名前を設定しています.
繰り返しの for 構文では,for (変数 in ベクトル) {...} という書き方をすると, 一回の繰り返しごとにベクトルの要素が順番に変数に代入されていきます. ベクトルのなかにファイルの名前そのものを並べておけば, 繰り返しの一回ごとに,それぞれのファイル名を使うことができます.
簡単な例を書いてみましょう. 作図デバイスとしてひとつの PDF ファイルを開けておいて, グラフをどんどん描き足しています. PDFなら,描けば描くだけ新しいページが作られていきます. 前のグラフが消えたりはしません.
# データファイル名を並べたベクトルを用意する files <- c('site_a_01.txt', 'site_a_02.txt', 'site_b_01.txt', 'site_b_02.txt') pdf('figures.pdf') # デバイスの用意 for (file.name in files) { d <- read.table (file.name, header = TRUE) # ファイルをデータファイルに読み込む plot(main = file.name, d$height, d$diameter, type = 'p') # 散布図を描く } dev.off() # デバイスを閉じる
ベクトル files には4つの要素がありますから、for 構文の {} の中の処理は 4回繰り返されます. file.name の値は、繰り返しごとに、files の最初の要素、2番めの要素、 というように替っていきます.
PDF ファイルに全部のグラフを書き込むかわりに, データファイルひとつひとつに対応する画像ファイルを作ることもできます. その場合,それぞれの画像ファイルに名前をつける必要があります.
そのためには,拡張子以外の部分(data01.txt なら data01)を書き並べておき, データファイル名はこれに .txt を付ける, 画像ファイル名は,たとえば png なら .png を付ける,というように書けばよいでしょう. 文字列を連結するには,paste() という関数がありました.
さっそくプログラムを書きかえてみます.
data.names <- c('site_a_01', 'site_a_02', 'site_b_01', 'site_b_02') for (name in data.names) { # name に拡張子 .txt を付けてデータファイル名を作る. # sep = '' を指定しないと余計な空白が入るので注意. data.file <- paste(name, '.txt', sep = '') d <- read.table(data.file, header = TRUE) # ファイルの内容を読み込む. # name に拡張子 .jpg を付けて画像ファイル名を作る. image.file <- paste(name, '.jpg', sep = '') jpeg(image.file) # デバイスの用意 plot(main = name, d$height, d$diameter, type = 'p') # 散布図を描く dev.off() # デバイスを閉じる }
ひとつの画像ファイルに全部を書き込む上の例では,for の繰り返しが始まる前に デバイスを用意し,すべての繰り返しを終了してから dev.off() しました. これに対し,ひとつのデータファイルごとに画像ファイルを作るのであれば, for のくりかえしの内側でデバイスを開けたり閉めたりする 必要があります. このことに注意しながら,プログラムを読んでみてください.
最初のデータファイル名リストに拡張子まで書かず,あとからくっつけるという手は、 画像ファイルをひとつしか作らない場合でもそのまま使えますね. ひとつひとつファイル名を書いていくのは面倒です. 手抜きできるところはなるべく手抜きしましょう.
上の例では,4つのグラフを一気に描いてみました. どれも似たようなグラフですが,height と diameter の値の範囲が データファイルごとにちがっているので,軸の最大値,最小値もグラフごとに 違います.これを共通にしてグラフ間の比較をしやすくしたいこともあるでしょう.
そのためには,軸の範囲を plot 関数おまかせにするのではなく, すべてのグラフでxlim, ylim パラメータに同じ値を指定すればいいはずです. では,どんな値を設定しましょうか.
xlim = c(0, 100), ylim = c(0, 50) のように数値を書き込んでしまうのも ひとつの手です.あらかじめどの範囲にしたいか決っているならこれでもよいでしょう.
もうひとつの手はデータに合せて決めることです.すべてのデータファイルを通じての 最大値,最小値を探し、これを使ってすべてのグラフの xlim, ylim を指定すれば、 全部のデータがプロットされ,かつ軸の範囲が共通なので見比べやすくなります.
たくさんのデータファイルのうち、ひとつを読み込んだだけでは、 全部のデータファイルを通じての最大,最小は分かりません. すべてのデータを読み込む必要があります. 以下のプログラムでは、すべてのファイルを2回読み込んでいます. 最初は最大・最小探しのため,もう一度はグラフを描くためです.
data.names <- c('site_a_01', 'site_a_02', 'site_b_01', 'site_b_02') x.data = c() # 空のベクトルを用意する. y.data = c() # 全ファイルの1列め,2列めのデータをx.data, y.data にベクトルとしてため込む. for (name in data.names) { data.file <- paste(name, '.txt', sep = '') # データファイル名 d <- read.table(data.file, header = TRUE) # ファイルの内容を読み込む. x.data <- c(x.data, d[[1]]) # ベクトルをつなげていく. y.data <- c(y.data, d[[2]]) } pdf('figures.pdf') # デバイスの用意 for (name in data.names) { # グラフを描くための繰り返し data.file <- paste(name, '.txt', sep = '') # データファイル名 d <- read.table(data.file, header = TRUE) # ファイルの内容を読み込む. plot(xlim = range(x.data), ylim = range(y.data), # xlim, ylim を指定して main = name, d$height, d$diameter, type = 'p') # 散布図を描く } dev.off() # デバイスを閉じる
x.data <- c(x.data, d[[1]]) では、ベクトル x.data のその時点で要素全部と、 データフレーム d の一列めのデータからなるベクトルの要素全部を並べたベクトルを 作り、x.data に上書きしています.結果として、 x.data の全要素のうしろに d の 一列めのデータが追加されたことになります.これを全ファイルについて実行すれば、 すべてのデータファイルの1列めのデータが並んだベクトルができあがります.
range という関数はすでにでてきました.引数にベクトルを 与えると、その最小値と最大値が並んだ要素2つのベクトルを返してくれます. range(x.data) を plot 関数の xlim パラメータに設定すれば、全データが 入りきるように軸の範囲を決めてくれるはずです.
すべてのデータファイルを2回読むのはムダな感じがしますが、その無駄が 実用上問題になるような処理速度の低下を招くことは,まずめったにありません. こうした無駄を避けて、データを読みながらファイルごとのデータを まとめて記憶していくというやりかたもあります. それについてはまたあとで.
ここまでの方法で、read.table で読み込もうとしたファイルが実在しないと, エラーが出て,プログラムの実行が止ってしまいます. 読み込み前にファイルがあるかどうかをチェックして,存在しなければ スキップするように改造してみましょう.
ファイルの有無のチェックには,file.access という関数が使えます. 引数としてファイル名を与えると,そのファイルが存在するなら 0を, 存在しないなら -1 を返す関数です.
最初のプログラムにこれを組み込んでみましょう.
# データファイル site_x_01.txt は存在しない. files <- c('site_a_01.txt', 'site_a_02.txt', 'site_x_01.txt', 'site_b_01.txt', 'site_b_02.txt') pdf('figures.pdf') # デバイスの用意 for (file.name in files) { if (file.access(file.name) != 0) { # ファイルが存在しない. print (paste(file.name, ' does not exist.')) # エラーメッセージ next # 次の繰り返しにうつる. } d <- read.table (file.name, header = TRUE) # ファイルの内容を読み込む plot(main = data.file, d$height, d$diameter, type = 'p') # 散布図を描く } dev.off() # デバイスを閉じる
上のプログラムでは、変数 file.name の値が site_x_01.txt となったとき、 file.access(file.name) が -1 (すなわち 0 でない)となり、if 構文の {} 内が実行されます.
繰り返し構文のなかでの next の使い方を思い出してください.その回の繰り返しはそこで 終了し、ブロック中のあとの部分は実行せず、次の回の繰り返しにうつる、という命令でした. 上の例では、ファイルが存在しない場合に、 print 関数でエラーメッセージを表示したあと next で次のファイル名を設定しての繰り返しへと進んでいます.