生物生産科学科 担当 國分 尚(5月28日、6月4日、6月11日)
コンピュータが電子計算機と呼ばれた頃は数値計算をさせるのが最も一般的な使い方であった。しかし、今日では文章を扱ったり、画像の編集をしたり、音楽を聴いたりすることの方が圧倒的に多く、「情報処理機」と呼ぶほうが的確であろう。
ここでは文字情報のファイルを扱う実用例としてGenBank (http://www.ncbi.nlm.nih.gov/)という遺伝子情報データベースの出力を使いやすい形に書き換えるプログラムを作ってみる。
GenBankからの出力は次のような形をしている。
さて、この形式のファイルが与えられたが、あなたが必要なのはそのうちのわずかな情報で、しかも塩基配列はすべて1行につながっていて数字や空白がない方が都合がいいとしたらどうするだろう。
エディタやワープロで編集することもできるが、せいぜい数十レコードまでであろう。それ以上は人間がすべき仕事ではない。このような単純作業こそコンピュータが最も得意とする分野なのだから。
そこで、このようなプログラムを作ってみる。必要な情報は塩基配列の定義[DEFINITION]、レコード番号[ACCESSION]、核酸が取られた生物の名前[SOURCE]、配列そのもの[ORIGIN]の数字と改行、空白を除いたもの、とする。
プログラム9.genbank.rb
# GenBank output formatting program
# H. Kokubun
# ver 0.1 30 Apr 2004 The first rendation
# ver 0.2 1 May 2004 Selected some fields to display.
while line = ARGF.gets #コマンドラインで指定したファイルから1行読み込み、
#まだファイルの終わりでない間繰り返す
if /LOCUS/ =~ line
#読んだ行の中にLOCUSという文字列があれば、レコードの始まりと見なす
print "\n----------\n",line #レコードの始まりなので区切り線を書く
elsif /DEFINITION|ACCESSION|SOURCE/ =~ line
#これらの文字列を含む行が必要なので、出力する
print line
elsif /ORIGIN/ =~ line
#塩基配列の場合の処理
print line
line = ARGF.gets
seq = ""
begin
line.scan /[A-Za-z]+/ do | aChunk |
seq << aChunk
end
line = ARGF.gets
end until /\/\// =~ line
print seq, "\n"
end
end
while line = ARGF.gets あれこれ end
この形がRubyでファイルからデータを読み込む基本となる。ARGFとは特殊なファイルで、
keyaki% ruby test.rb a.txt b.txt c.txt
という形でコマンドが与えられたとき、a.txt、b.txt、c.txtの3つのファイルから1行づつデータを読み出す。最後のファイルの最後に達するとnilという特別な値が返り、whileの条件を満たさなくなるのでループから脱出する。
例えば、UNIXのcatコマンドをRubyで書くと
プログラム10.cat.rb
while line = ARGF.gets
print line
end
と、たった3行で済んでしまう。(1行で書くやり方もある。)
Rubyでファイルにデータを書き込む方法も当然あるが(IOクラスのputsメソッドを使う)、今回はOSのリダイレクションを使うことにする。コマンドラインで
keyaki% ruby fact.rb 100 > output.txt
とすればprint文の出力は画面ではなくoutput.txtというファイルに保存される。リダイレクションについては「キャンパス情報リテラシー」p. 90を参照のこと。今回の例題ではこれで十分なのでRubyのファイルの扱いに関しては参考書で勉強してほしい。
genbank.rbのプログラム中には /あれこれ/ =~ 変数 という部分が多くあるが、/あれこれ/が正規表現と呼ばれるもので、文字列中にあるパターンが含まれる(マッチする)かどうかを見つける手段である。文字列の検索や加工には欠かすことができない。正規表現はそれ自体がミニプログラミング言語であると言われるほど複雑なことができ、その説明だけで1冊の本が書けるほどである。ここではプログラム9.の例について最低限のものにしぼって説明する。
/ORIGIN/ =~ line
この表現はlineの中に“ORIGIN"という文字列があればマッチし、式の値が真になる。マッチした部分は$~という変数に格納されている。
/REFERENCE|FEATURES|COMMENT/ =~ line
この表現はlineの中に“REFERENCE"、“FEATURES"、“COMMENT"という文字列のいずれかがあればマッチする。複数ある時は最も左側のものにマッチする。
/[A-Za-z]/ =~ line
この表現はlineの中の最初の英文字にマッチし、その文字を返す。A-ZはABCDEF...XYZを省略した表記法である。
/^REFERENCE\s+1\s/ =~ line
この表現はlineの先頭に“REFERENCE"があり、最低1文字のスペースのあとに“1"があり、その直後にスペースがあるような文字列にマッチする。
REFERENCE 1 abcde マッチする a REFERENCE 1 12345 マッチしない(REFERENCEが先頭にない) REFERENCE 12 マッチしない(1の直後がスペースでない)
/\/\// =~ line
この表現は//にマッチする。
/abc+/ はabc, abcc, abcccなどにマッチするのに対し /(abc)+/ はabc, abcabc, abcabcabcなどにマッチする。
課題3.genbank.rbを書き換えて塩基配列の中に特定な配列、例えばtcga(制限酵素TaqIの認識配列)があったらその配列の前後20塩基までを出力するようにせよ。次の正規表現を使うと簡単にできる。
if /.{0,20}tcga.{0,20}/ =~ seq
print "Taq I recognition site: ",$~,"\n"
end
.はどんな1文字にもマッチする。{0,20}は直前の表現を0から20回繰り返したものにマッチする(この書き方はgrepやawkでは使えない)。Rubyの正規表現は最長一致を原則としているのでtcgaの前に20文字以上あれば20文字がマッチする。後ろも同様である。$~に直前にマッチした文字列が格納されている。
課題4.genbankの出力はこのままではExcelなどの表計算ソフトに取り込むことはできない。そのためには一般に1レコードを1行とし、各データをタブという特殊文字( \t で表す)で区切って書き出すのが便利である。これをタブ区切りテキストと言う。
そこで、上のgenbank.txtを読んで各レコードを
LOCUS \t ACCESSION \t 長さ \t SOURCE \n
のような形式で出力するプログラムを作成する。
例えば
LOCUS PHMYBPH22 1057 bp mRNA linear PLN
12-SEP-1993
DEFINITION P.Hybrida myb.Ph2 gene encoding protein 2.
ACCESSION Z13997
SOURCE Petunia x hybrida
というレコードなら
PHMYBPH22\tZ13997\t1057\tPetunia x hybrida\n
のようにする。
LOCUS名と長さはLOCUSの行の2番目と3番目の単語を取り出すようにすればよいだろう。1行のデータを単語に分けるには文字列のsplitメソッドを使う。
words = line.split()
とするとwordsという配列にlineの内容を空白で分割した文字列が入る。上のLOCUSの行であればwords[0]はLOCUS, words[2]は1057となる。
前章の課題2、上の課題3、4のどれか1つ作成したプログラムとその説明をeメールで國分(hkokubun@faculty.chiba-u.jp)に提出すること。どうしてもプログラムができない場合はアルゴリズムを考えて提出すること。期日は7月30日(金)とする。
2004年5月14日作成、2004年6月10日更新
國分 尚