pandasでタンパク質配列の変異ライブラリを作成する

## はじめに
#### 目的
タンパク質の配列-機能相関を調べる上で、
ある一つのタンパク質配列に対して任意のサイトに任意のアミノ酸変異を施した変異配列を
簡便に生成させたかったので作成しました。
pythonの練習も兼ねているので、他の方法やより良い方法など教えて頂けるとありがたいです。

#### GitHub
https://github.com/kyo46n/Mutant-Library-Generator

#### 実行環境
jupyter notebook
python 3.7.4 (Anaconda)
pandas 0.25.3
biopython 1.74

## 内容
#### インポート
親となるタンパク質配列をfastaファイルで持っていると想定し、
ファイルの読み書きにbiopythonを使用。

```python

from Bio import SeqIO
from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord
import pandas as pd
```
#### 関数定義
fastaファイルの配列名(id)と配列(seq)を辞書型で読み込み、
seq部分を1文字1列のDataFrameに変換した後、配列名の列をnameとして左端に挿入。
そのままではアミノ酸配列が0始まりとなってしまいサイト位置が直感とズレるので
1始まりに列indexを修正。

```python
#fasta to dataframe
def fd(p):
    d = {rec.id : list(str(rec.seq)) for rec in SeqIO.parse(p, "fasta")}
    df = pd.DataFrame(d.values())
    for i in d.keys():
        name = i
    df.insert(0, 'name', name)
    df.iat[0, 0] = name
    plus_one = {}
    j = 1
    for i in range(len(df.columns)+1):
        plus_one[i] = j
        j = j+1
    df = df.rename(columns=plus_one)
    return df
```

基本的な計算用の関数として、
特定の1サイトを特定のアミノ酸に変換するsdm()と
特定の1サイトを全てのアミノ酸に変換するssm()を作成。

```python
#for calculation of site directed mutagenesis
def sdm(df, site, mut):
    df_mut = df.copy()
    df_mut.iat[0, 0] = df_mut.iat[0,0] + "_" + df_mut.iat[0,site] + str(site) + mut
    df_mut.iat[0,site] = mut
    return df_mut

#for calculation of site saturation mutagenesis
def ssm(df, site):
    aa_list = ['R', 'H', 'K', 'D', 'E', 'S', 'T', 'N', 'Q', 'C', 'G', 'P', 'A', 'V', 'I', 'L', 'M', 'F', 'Y', 'W']
    df_mut = df.copy()
    for i in range(20):
        df_mut = df_mut.append(df.iloc[0])
    j = 1
    for i in aa_list:
        df_mut.iat[j, 0] = df_mut.iat[0,0] + "_" + df_mut.iat[0,site] + str(site) + i
        df_mut.iat[j,site] = i
        j = j + 1
    df_mut.reset_index(drop=True,inplace=True)
    return df_mut
```

複数の変異体をまとめて生成できるようにisdm()とissm()を作成。
これら2つの関数は任意の1箇所に変異が入った配列を個別の行に生成する。

```python
#individual site directed mutagenesis
def isdm(df, site_list, mut_list):
    mylist = []
    j = 0
    for i in site_list:
        mylist.insert(j, sdm(df, i, mut_list[j]))
        j = j+1
    df = pd.concat(mylist)
    df = df.drop_duplicates(subset='name')
    df.reset_index(drop=True,inplace=True)
    return df

#individual site site saturation mutagenesis
def issm(df, site_list):
    mylist = []
    j = 0
    for i in site_list:
        mylist.insert(j, ssm(df,i))
        j = j+1
    df = pd.concat(mylist)
    df = df.drop_duplicates(subset='name')
    df.reset_index(drop=True,inplace=True)
    return df
```

複数箇所に同時に変異を入れた配列を生成するため、ssdm()を作成。

```python
#simultaneous site directed mutagenesis
def ssdm(df, site_list, mut_list):
    j = 0
    for i in site_list:
        df = sdm(df, i, mut_list[j])
        j = j+1
    return df
```

複数箇所の飽和変異の全組み合わせを生成するため、sssm()を作成。

```python
#simultaneous site saturation mutagenesis
def sssm(df, site_list):
    for x in range(len(site_list)):
        df_mut = df.copy()
        templist = []
        j = 0
        for i in range(len(df_mut)):
            dftemp = ssm(df_mut[i:i+1], site_list[x])
            templist.insert(j, dftemp)
            j = j + 1
        df = pd.concat(templist)
        df = df.drop_duplicates(subset='name')
        df.reset_index(drop=True,inplace=True)
    return df
```
#### 実行例
親配列サンプルとしてinput.txt(fasta形式、id:seq1 seq:MTIKE)を用意。読み込み。

```python
#read fasta
file = "input.txt"
df_wt = fd(file)
df_wt
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/56a4eea7-a2ca-cdae-b4e4-a24034baa9b0.png)

複数サイトの1変異体を生成したい場合↓

```python
site_list = [2,4,5]
mut_list = ["L","A","R"]
df_isdm = isdm(df_wt, site_list, mut_list)
df_isdm
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/d8fe46e8-5b05-9e48-b965-6a52f7a5af7c.png)

複数サイトの飽和変異体を生成したい場合↓
(例えば2番目から5番目までの飽和変異体)

```python
site_list = range(2,6)
df_issm = issm(df_wt, site_list)
df_issm
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/65d01d13-ff4a-640c-f44a-a051b02dd3ce.png)

複数サイトに変異を入れた一つの配列を生成したい場合↓

```python
site_list = [2,4,5]
mut_list = ["L","A","R"]
df_ssdm = ssdm(df_wt, site_list, mut_list)
df_ssdm
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/b424d17c-f9ef-00ab-2c74-eb722bf737e1.png)

複数サイトの飽和変異体の組み合わせを生成したい場合↓

```python
site_list = [2,3]
df_sssm = sssm(df_wt, site_list)
df_sssm
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/37d84d0d-ccea-7587-99cd-1303c0dacb46.png)

#### 配列の結合と重複削除
1文字1列のままだと研究上使いづらいので配列形式に結合し、同一配列を削除。

```python
df = df_sssm.copy()
df.insert(1, 'sequence', "-")
df.loc[:,'sequence'] = df[1].str.cat(df[range(2,len(df.columns)-1)])
df_seq = df.iloc[:,0:2]
df_seq = df_seq.drop_duplicates(subset='sequence')
df_seq.reset_index(drop=True,inplace=True)
df_seq
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157746/038d2cf8-f233-ad70-c878-56ec78059662.png)

#### エクスポート
csvとfastaにそれぞれ書き出し。

```python
df_seq.to_csv("output.csv")

with open("output.txt", "w") as handle: 
    for i in range(len(df_seq.index)):
        seq = Seq(df_seq.iloc[i,1])
        rec = SeqRecord(seq, description="")
        rec.id = df_seq.iloc[i,0]
        SeqIO.write(rec, handle, "fasta")
```

## おわりに
関数の作り方やfor文まわり等にとても無駄が多い気がするが、とりあえず得たい出力は得られるようになった。

#### 今後の課題
- 単変異と飽和変異の混在("X"としたら飽和変異を実行)
- 複数の親配列の処理
- 巨大な配列の処理(実行時間短縮)
- コードの洗練