PythonでGGGenome検索

GGGenomeといえば配列を高速で検索してくれるサービスですが、そのトップページに記載の通り、REST APIの形でサービスを提供しているため、Pythonからも結構簡単に結果を取得できます。そこで、今回はpythonからGGGenome検索を行うためのスクリプトを描いてみました。

#!/usr/bin/env python3
import time
import requests

class GGGenome:
    # 前回アクセスした時間
    time_of_last_run = time.time()
    # アクセス間隔(秒間)
    dulation = 10

    # GGGenomeにクエリを投げ、結果をdictで返す
    def __call__(self, sequence, db="hg19", k=0, strand="both", nogap=False):

        if len(sequence) < 6:
            raise ValueError("query sequence should be 6 nt or more")
        self.sequence = sequence

        self.db = db
        
        if k * 4 > len(sequence):
            raise ValueError("number of mismatches/gaps should be 25% or less")
        self.k = k

        if strand == "both":
            self.strand = ""
        elif strand == "forward":
            self.strand = "+"
        elif strand == "reverse":
            self.strand = "-"
        else:
            ValueError("strand should be one of 'both', 'forward' or 'reverse'")

        if nogap:
            self.nogap = "nogap"
        else:
            self.nogap = ""

        # 前回のアクセスから最低{dulation}秒間待つようにする
        seconds_to_wait = GGGenome.dulation - (time.time() - GGGenome.time_of_last_run)
        time.sleep(max(0, seconds_to_wait))

        url = f"http://GGGenome.dbcls.jp/{self.db}/{self.k}/{self.strand}/{self.nogap}/{self.sequence}.json"

        result = requests.get(url)
        
        # アクセスした時間を更新する
        GGGenome.time_of_last_run = time.time()

        if result.status_code != 200:
            raise ValueError(f"HTTP Error. [status code: {result.status_code}]")

        if result.json()["error"] != "none":
            raise ValueError(f"searcher error. [{result.json()['error']}]")

        return result.json()

if __name__ == "__main__":

    import argparse
    parser = argparse.ArgumentParser(description="GGGenome command line")
    parser.add_argument('--db', type=str, default="hg19",
                        help="database name. default: hg19")
    parser.add_argument('-k', type=int, default=0,
                        help="number of mismachs allowed. default: 0")
    parser.add_argument('--strand', metavar='strand', type=str, default="both",
                        choices=["both", "forward", "reverse"],
                        help="which strand to query: default: both")
    parser.add_argument('--nogap', default=False, action="store_true",
                        help="whether or not to allow gap")
    parser.add_argument('sequence', metavar='sequence', type=str,
                        help="query sequence")
    args = parser.parse_args()

    gggenome = GGGenome()
    result_dict = gggenome(sequence=args.sequence, db=args.db, k=args.k, strand=args.strand, nogap=args.nogap)

    print(f"database: {result_dict['database']}")
    print(f"time: {result_dict['time']}")
    if args.strand == "both":
        print(f"    query(forward): {result_dict['summary'][0]['query']}")
        print(f"        number of hits: {result_dict['summary'][0]['count']}")
        print(f"    query(reverse): {result_dict['summary'][1]['query']}")
        print(f"        number of hits: {result_dict['summary'][1]['count']}")
    elif args.strand == "forward":
        print(f"    query(forward): {result_dict['summary'][0]['query']}")
        print(f"        number of hits: {result_dict['summary'][0]['count']}")
    elif args.strand == "reverse":
        print(f"    query(reverse): {result_dict['summary'][1]['query']}")
        print(f"        number of hits: {result_dict['summary'][1]['count']}")

スクリプトではGGGenomeクラスを定義しています。関数として実装しても良かったのですが、サーバに負荷をかけないように前回の検索から一定時間待機後次の検索をかける仕組みにしておきたかったのでクラス変数に前回のアクセス時刻とそこからの待機時間を記録できるようにしてあります。スクリプトを起動してから最初の検索までにも一定時間待機するため、bash等でスクリプトをループで回してもちゃんと一定時間待機すると思います。

スクリプトのメインは以下です。

url = f"http://GGGenome.dbcls.jp/{self.db}/{self.k}/{self.strand}/{self.nogap}/{self.sequence}.json"

result = requests.get(url)

pythonのf-stringを用いてユーザーが指定したデータベース、ミスマッチ数、配列の向き、ギャップの許容、配列をもとにURLを構築し、requestsパッケージのget関数でHTTPリクエストをおこなっています。requestsのいいところの1つは結果をjson形式で受け取るとjsonメソッドで結果をPythonの辞書形式に直してくれるところだと思います。本スクリプトもこの機能を利用して結果を返しています。

参考