PythonでNCBIのAPIから文献情報を取得してみた

[前回記事](https://qiita.com/MTNakata/items/fc634d54b3e74b9a5384 "次世代シーケンサー・ナノポアMinIONについての雑感")では、PubMedの検索結果のWebページをBeautifulSoupでパースして解析しました（邪道ですが、、、）。今回は[NCBIのAPI](https://www.ncbi.nlm.nih.gov/home/develop/api/ "APIs-Develop-NCBI")を使って、以下に挑戦してみました。

1. 特定の用語に関して、年ごとの検索ヒット数の変化を調べる
1. ヒットした文献の情報をまとめて取得してリスト化する

PubMed情報を取得できるPythonパッケージとして[Biopython](https://biopython.org/ "Biopython")がありますが、今回は一般的なAPIの取り扱いの練習のために自前で実装してみることにしました。

# 実行環境
- MacOS Sierra
- Xcode 9.2
- Python 3.6.1
- requests 2.14.2
- matplotlib 2.0.2
- pandas 0.20.1
- jupyter 1.0.0

# ソースコード

PubMedクラスの関数として、以下の2つを用意しました。

- クエリから検索ヒット件数とidのリストを返す`search`関数
- idのリストから、論文の詳細情報を取得する`get_article_info`関数

<details><summary>ソースコード（折りたたみ）</summary><div>

```python
%matplotlib inline
import matplotlib.pyplot as plt
import requests
import time
import pandas as pd
from collections import OrderedDict

class PubMed:
    def __init__(self):
        self.baseURL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/"
        
    def search(self,*terms):
        terms = "+AND+".join(list(terms))
        query = f"db=pubmed&term={terms}"
        dataFormat = "json"
        
        search_command = "esearch.fcgi"
        self.searchURI = f"{self.baseURL}{search_command}?{query}&retmode={dataFormat}"
        result = requests.get(self.searchURI)
        print(f"{result}\n")
        
        data = result.json()
        self.count = data["esearchresult"]["count"]
        self.id_list = data["esearchresult"]["idlist"]
        
        time.sleep(2) #アクセスしすぎないように配慮
        
    def get_article_info(self):
        summary_command = "esummary.fcgi"
        pubmedURL = "https://www.ncbi.nlm.nih.gov/pubmed/"
        key_list = ['source','volume','issue','pages']
        new_key_list = ['journal','volume','issue','pages']
        
        self.result_list = []
        
        for n,id in enumerate(self.id_list):
            query = f"db=pubmed&id={id}"
            dataFormat = "json"
            result = requests.get(f"{self.baseURL}{summary_command}?{query}&retmode={dataFormat}")
            print(f"{n}:{id}:{result}\n")
            data = result.json()
            
            result_dic = OrderedDict()
            
            result_dic['title'] = data['result'][id]['title']
            
            author_list = []
            for author in data['result'][id]['authors']:
                author_list.append(author['name'])
            result_dic['all_authors'] = ", ".join(author_list)

            publication_date = data['result'][id]['pubdate']
            result_dic['year'] = publication_date.split(" ")[0]
            
            for key,new_key in zip(key_list,new_key_list):
                result_dic[new_key] = data['result'][id][key]

            result_dic['link'] = f"{pubmedURL}{id}"

            self.result_list.append(result_dic)

            time.sleep(2) #アクセスしすぎないように配慮
```
</div></details>



# 結果

### 1. 特定の用語に関して、年ごとの検索ヒット数の変化を調べる

"rna-seq"という用語に対し、2004年から2018年までの変化を調べてグラフ化してみました。APIで出版年を指定するタグは`[DP]`だそうです。

```python
pubmed = PubMed()
hit_count = {}
for year in range(2004,2019):
    pubmed.search("rna-seq",f"{year}[DP]")
    hit_count[year] = int(pubmed.count)
```

うまくいくと、`<Response [200]>`というメッセージが出るようになっています。HTTPのレスポンス状態コードについては[こちら](https://developer.mozilla.org/ja/docs/Web/HTTP/Status "HTTP レスポンス状態コード")を参照してください。

`hit_count`をPandasのDataFrameに変換します。

```python
df = pd.DataFrame(hit_count,index=["count"]).T
df
```
<details><summary>長いため結果のテーブルは折りたたんでいます。</summary><div>

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>2004</th>
      <td>0</td>
    </tr>
    <tr>
      <th>2005</th>
      <td>0</td>
    </tr>
    <tr>
      <th>2006</th>
      <td>0</td>
    </tr>
    <tr>
      <th>2007</th>
      <td>0</td>
    </tr>
    <tr>
      <th>2008</th>
      <td>6</td>
    </tr>
    <tr>
      <th>2009</th>
      <td>33</td>
    </tr>
    <tr>
      <th>2010</th>
      <td>156</td>
    </tr>
    <tr>
      <th>2011</th>
      <td>359</td>
    </tr>
    <tr>
      <th>2012</th>
      <td>631</td>
    </tr>
    <tr>
      <th>2013</th>
      <td>1127</td>
    </tr>
    <tr>
      <th>2014</th>
      <td>1723</td>
    </tr>
    <tr>
      <th>2015</th>
      <td>2322</td>
    </tr>
    <tr>
      <th>2016</th>
      <td>2809</td>
    </tr>
    <tr>
      <th>2017</th>
      <td>3583</td>
    </tr>
    <tr>
      <th>2018</th>
      <td>3072</td>
    </tr>
  </tbody>
</table>

</div></details>

Matplotlibでグラフ化した結果がこちら。

```python
plt.plot(df)
plt.xlabel("Year")
plt.ylabel("Hit count")
plt.show()
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/204284/008be678-e956-6397-4564-3f2a97382588.png" alt="20180930_rna-seq_hit_count.png" width=70%/>


2008年にはじめの報告があり、その後順調に報告数が増加していることがわかります。

~~近年はやや減少傾向にありますが、理由はよくわかりません。現状、誰でも実施可能なポピュラーな手法になっているため、わざわざ明記しない報告が増えてきた可能性はあります。~~

[9/30 22:37追記] 先程気づいたのですが、2018年のみ9ヶ月分の集計ですので、最終的には2017年を超える可能性が高いですね。お詫びして訂正いたします。

### 2. ヒットした文献の情報をまとめて取得してリスト化する

2008年でヒットした、6件の文献情報を取得してみます。

```python
pubmed = PubMed()
pubmed.search("rna-seq","2008[DP]")

print(pubmed.id_list) #取得したidのリスト
#['19087247', '18599741', '18550803', '18516045', '18488015', '18451266']

pubmed.get_article_info() #全てのidの文献情報取得
```
こちらも実行すると`番号：id：<Response [200]>`といった、HTTPレスポンス状態コードが返ってくるようにしました。

<details><summary>文献情報は`pubmed.result_list`に入っています。（折りたたみ）</summary><div>

```python
import pprint
pprint.pprint(pubmed.result_list[-1])


#OrderedDict([('title',
#              'The transcriptional landscape of the yeast genome defined by '
#              'RNA sequencing.'),
#             ('all_authors',
#              'Nagalakshmi U, Wang Z, Waern K, Shou C, Raha D, Gerstein M, '
#              'Snyder M'),
#             ('year', '2008'),
#             ('journal', 'Science'),
#             ('volume', '320'),
#             ('issue', '5881'),
#             ('pages', '1344-9'),
#             ('link', 'https://www.ncbi.nlm.nih.gov/pubmed/18451266')])
```
</div></details>

Pandasデータフレームで見るとこんな感じ。

```python
df_paper = pd.DataFrame(pubmed.result_list[-1],index=["details"]).T
df_paper
```
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>details</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>title</th>
      <td>The transcriptional landscape of the yeast gen...</td>
    </tr>
    <tr>
      <th>all_authors</th>
      <td>Nagalakshmi U, Wang Z, Waern K, Shou C, Raha D...</td>
    </tr>
    <tr>
      <th>year</th>
      <td>2008</td>
    </tr>
    <tr>
      <th>journal</th>
      <td>Science</td>
    </tr>
    <tr>
      <th>volume</th>
      <td>320</td>
    </tr>
    <tr>
      <th>issue</th>
      <td>5881</td>
    </tr>
    <tr>
      <th>pages</th>
      <td>1344-9</td>
    </tr>
    <tr>
      <th>link</th>
      <td>https://www.ncbi.nlm.nih.gov/pubmed/18451266</td>
    </tr>
  </tbody>
</table>

この論文が"RNA-Seq"という単語を使った、PubMed上で見られる一番最初の報告であると思われます。なんとなく感慨深いですね。