LoginSignup
4
6

More than 5 years have passed since last update.

pythonの文字列処理 mapとlambda

Last updated at Posted at 2017-03-15

pythonのmapとlambdaの使い方について、fastqファイルを使って説明します。

以下のテストファイルは、bioinformaticsではおなじみのfastqファイルとよばれるDNAシーケンサーの解析結果のファイルで、@行はheader、次の行がDNAの塩基配列、3行目に+を挟んで、4行目は2行目のDNA塩基配列の一文字一文字に対する品質評価値になっており、品質評価値に33を足した数値がASCII文字で変換された数になっています。

test.fastq
@test1
GAGCACACGTCTNNANNCNAGTCANNANNNANNNNNNNNNNANNCNNNNNNTNNNNNNNNANNNNTGTCCATTGCNNNCACATCATTGTTTACTTGCGCNT
+
;<<:?@9<?############################################################################################

品質評価値をもとの数値に直したい。そこでpythonでごちゃごちゃ書こうとしたのだけれど、とびきり便利なmapとlambdaの組み合わせに出会ったのでメモします。 ちなみに、環境はpython2です。

メモ: pythonのversion間の高階関数の使い方の違いについて。

たとえば、Aという品質評価値を数値に変換するには、pythonではアスキーコードを変換するord(反対はchr)という組み込み関数をつかって、数値に変換し、それから33を引けば元の値になります。

>  python -c 'print ord("A")-33'
32

これをtestファイルの品質値の行にある101文字全て変換するには、for文を使うと

convert_asci.py
asci_string = ";<<:?@9<?############################################################################################"
for baseq in asci_string:
    score = ord(baseq) - 33
    print score,

と書くことができます。

convert_asci.py実行結果
26 27 27 25 30 31 24 27 30 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

この値は大きいほど品質が良いことを示しますので、"#"の品質値の塩基はとても品質が悪いことがわかります。さて、このASCIコードの変換プログラムの書き方はfor文を使っていて、プログラムが長くなるにつれて縦に長いコードになるのは辛いですし、実行速度も遅そうです。そこで、mapを使って表現してみます。

convert_asci.2.py
asci_string = ";<<:?@9<?############################################################################################"

def convert_func(x):
    score = ord(x) - 33
    return score

res_score = map(convert_func, asci_string)
print res_score
convert_asci.2.py実行結果
[26, 27, 27, 25, 30, 31, 24, 27, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

このようにfor文を回避しても関数を定義する行の文長くなってしまいます。
そこで、lambdaという無名関数を使って、mapの中に"convert_func"関数に相当する処理を書いてしまえるということを知りました(いままで知らなかったんかい!)。次のようになります。

convert_asci.3.py修正後のスクリプト
asci_string = ";<<:?@9<?############################################################################################"
res_score = map(lambda x:ord(x) - 33, asci_string)
print res_score  

文字列は、for文やmapでは内部で1文字に分割されてイテレートされる。という情報をいただきました。ありがとうございます、修正させていただきました。

convert_asci.3.py修正前のスクリプト
asci_string = ";<<:?@9<?############################################################################################"
asci_list = list(asci_string) #この処理(リスト化)を行う必要がありませんでした。

res_score = map(lambda x:ord(x) - 33, asci_list)
print res_score

どうでしょう。一行でかけてしまいました。mapはリストを返してくれるので結果は同じです。無名関数は、一度だけしか使われない使い捨ての関数のことです。一度きり使うだけなので、名前をつける必要がないため無名関数と呼ばれています。lambda式を使って無名関数を作る書式は次のようになります。

lambda 引数(例ではx):返り値 (例では、ord(x) - 33)

例では、xが引数でmapからasci_listの値を一つ一つ受け取り、指定した処理を実行してから返り値を返してくれます。これはとても便利ですね!

4
6
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6