2020年1月28日火曜日

テキストエディタの正規表現だけでうまいこと処理する

学生がデータのファイルを扱う上で、結果の一部を抽出したり、複数のフォルダからファイルを指定したりとか、コピペやGUI操作だと面倒だなと感じる作業がある。スクリプトを書けば一発なのだろうが、あまり詳しくない学生の場合、とてつもなく敷居が高く感じられる。また、教える方もその場でスクリプトを書いて渡すといった時間が取れないことの方が多いし、なんでもやってあげるのはむしろ為にならない気がする。こういった時に、効率は悪いかもしれないが、GUIで使っているテキストエディタの検索・置換機能に正規表現が使える場合、これを使うのが一番手っ取り早い気がしている。

研究室のPCでは、BBEdit (https://www.barebones.com/products/bbedit/) かAtom (https://atom.io/) を主に使っている。これらは両方とも正規表現を使った検索や置換が行える。例えば、Blastのアラインメントの結果ファイルを持っている場合に、そのアラインメントされた配列だけ欲しいといった場合がある。普通にスクリプトを一つ作っておけばいいのだろうが、それができない場合、下手をすると配列をコピー&ペーストして目的のファイルを作ろうとする学生が出てくる。ファイルが小さければまだいいが、たくさんhitがある結果ファイルだったりすると、コピペだけで何日も過ぎてしまうといった悲喜劇になりかねない。こういった時に、あまり詳しくない学生であっても使える手段として、テキストエディタの置換機能があると思う。

実際どうするかは頓知のようなところがあるし、よりうまいやり方を思いつく人もいると思う。例えば下のような結果ファイルについて

Score:80234 bits(88982), Expect:0.0, 
Identities:49320/52479(94%),  Gaps:1419/52479(2%), Strand: Plus/Plus

Query  126153  TTACGACTATGAAAATGAATGAATAAAAAACGTTTATTCGAAATTATAA-----GTATCT  126207
               |||| ||||||||||| |||||| |||||  |||||||| |||||||||     ||||||
Sbjct  50916   TTACAACTATGAAAATTAATGAAGAAAAAG-GTTTATTCTAAATTATAAATTAAGTATCT  50974

Query  126208  ATCTCGATATATCGATAGATAGTGATTGGATCCACTGAAATCAAATGAAATCAAATTTGG  126267
               ||||||||||||||||||||||||||||| |||| |||||| |||||||||         
Sbjct  50975   ATCTCGATATATCGATAGATAGTGATTGGCTCCATTGAAATAAAATGAAAT---------  51025

Query  126268  TTTTCCGTTTTATTCTGAACGACCCCCAGGACTTATGGTTTAGGGTCTGGGAGttttttt  126327
                ||||||||||||||||||||| ||||||||||||||||||| | | ||| | |||||||
Sbjct  51026   -TTTCCGTTTTATTCTGAACGATCCCCAGGACTTATGGTTTATGATATGGAAATTTTTTT  51084

Query  126328  tATGAACCAACAAATTGAAAGTAACCAGTTAGAAATAAAGAATACAATAATAAGTCAAAA  126387
               |||||||| |  |||| ||||||||| |||||||||||||||||||| || |||| ||||
Sbjct  51085   TATGAACCGAGCAATTAAAAGTAACCTGTTAGAAATAAAGAATACAAAAAAAAGTAAAAA  51144


コピペでqueryとsbjctの配列をつなぎ合わせて行っているような場合がある。なんの罰ゲームかという感じだが、まずは下の正規表現で配列だけの状態にする。

検索:
Query[ ]+\d+[ ]+([ATGCNatgcn-]+)[ ]+\d+\n[ ]+[\| ]+\nSbjct[ ]+\d+[ ]+([ATGCNatgcn-]+)[ ]+\d+\n\n

置換:
\1\n\2\n\n

そうした上で、次は、下記の検索・置換を何度か繰り返して、その度に配列をつなげていく。

検索:
([atgcATGCnN-]+)\n([atgcATGCnN-]+)\n\n([atgcATGCnN-]+)\n([atgcATGCnN-]+)\n\n

置換:
\1\3\n\2\4\n\n

見てもらってわかるように、query-sbjctの組み合わせ2つごとに配列をつなげていく処理を繰り返しているだけだ。かなり原始的だが、スクリプトを一つも書かずにとりあえずの目的は達成することができる。

別の例では、複数のフォルダにファイルを4つずつ分けて入れていて、フォルダが合わせて数十あるといった時に、それぞれのファイルをCUIのプログラムで処理したいときに、シェルでの操作とテキストエディタだけで、とりあえずシェルスクリプトを準備してしまう、といったことが考えられる。再帰的にディレクトリの中を表示する為に、まずコマンドラインで、ls の -Rオプションが使える。ファイルに"_R1_"か"_R2_"という文字が入っている場合 (XXXYYZZ_001_R1_0001.fastq.gzなど。で、ファイルのR1とR2が対になっている) は、

ls -1R | grep "_R1_" > list.txt

とすればファイルのリストができる。それをテキストエディタで開いて、例えば

検索:
(.+)_R1_(.+)

置換:
Command -1 \1_R1\2 -2 \1_R2_\2 --output outtext.txt

などとすれば、XXXYYZZ_001_R1_0001.fastq.gzXXXYYZZ_001_R2_0001.fastq.gzの二つのファイルを入力としてoutputをだすCommandのシェルスクリプトができる。

くどいようだが一番いい解決方法ではないかもしれないが、あまり背景知識がない学部生にも簡単に教えられて、処理のかなりのスピードアップが見込めるやり方だと思う。

シェルスクリプトやpythonなどを抵抗感なく使いこなしている学生はほとんどいないと思う。そのベースラインから初めてスクリプトを書くことを覚えていくのは結構厳しい(やったら力つくと思うのだけど)。そういった時に、上のようなテキストエディタ&正規表現での処理が入って行きやすいように思えた。こういうので興味を覚えた人は、実際にスクリプトを書いて正規表現処理することを自分で覚えていくのだろうと思うし、そうして欲しいと思った。