SMILES에서 분자 기술자와 지문을 계산하여 데이터 프레임에 저장하는 [Python, RDKit]

2020/1/8

RDKit에서 화합물 데이터 세트의 SMILES에서 분자 설명자(descriptor)와 지문(fingerprint)을 포함하는 데이터 프레임을 만드는 방법입니다. QSAR/기계학습모델을 자작하려고 하더라도, 분자 기술자나 핑거프린트의 작성으로 상당히 집계했으므로 이하에 정리합니다.

데이터 프레임에 저장하는 의미

기계 학습에 넣기 위해서만이라면 리스트형으로 작성해도 됩니다만, 데이터 프레임으로 하는 것으로 이하의 것이 쉬워집니다.

  1. 작성한 디스크립터 / 지문을 기반으로 한 화합물 데이터 세트의 조감
  2. 누락 값 대응 및 차원 감소 등의 데이터 전처리

또, RDKit에서는 기술자를 산출하기 위해서 SMILES를 일단 mol 오브젝트에 변환합니다만, 그 때 잘 변환할 수 없었던 것이 있어도 데이터 프레임 쪽이 대응하기 쉽습니다.

실천해 보자

아래 준비

샘플 데이터에는 MoleculeNet의 BBBP(blood-brain barrier penetration: 화합물의 혈액뇌 장벽 투과성 데이터 세트)의 SMELS를 사용해 보겠습니다.

RDKit mol 객체는 ROMol이라는 열에 저장되므로 이를 기반으로 설명자를 만듭니다.

참고: 화합물 데이터 세트 목록

import numpy as np
import pandas as pd
 
from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, PandasTools, Descriptors
from rdkit.Chem.Draw import IPythonConsole
 
print('rdkit version: ',rdBase.rdkitVersion)  # rdkit version:  2019.03.4
 
# 下準備
# データセットの読み込み
df = pd.read_csv("BBBP.csv")
 
# dfのSMILES列を参照してMolオブジェクト列をデータフレームに加える
PandasTools.AddMoleculeColumnToFrame(df,'smiles')
 
# Molオブジェクトが作成できたか確認
print(df.shape)
print(df.isnull().sum())  
(2050, 4) num 0 name 0 p_np 0 smiles 0 ROMol 11 dtype: int64

에러로서 「Explicit valence for atom # 1 N, 4, is greater than permitted」가 나옵니다만, 이것은 이상한 원자가를 가지는 분자(이온 등)가 있었기 때문입니다 가는 허용치를 넘는다고 합니다.)이러한 분자의 ROMol에는 None이 반환되어 버렸고, 여기에서는 그러한 SMILES가 4개 있었습니다.

하나하나 대응해도 좋습니다만, 수가 적으면 우선 제외해 버리는 것이 손쉽게 빠릅니다.그래서 isnull().sum() 로 ROMol 열에 있어서의 결측값의 유무를 조사해, 있으면 그 행을 제거해 둡니다.

참고: 화합물 데이터 로딩 문제 해결

# ROMolが作成できなかったものを確認
print(df[df.ROMol.isnull()])

# 欠損行の除去
df = df.dropna() 
SMILES에서 분자 설명자와 지문을 계산하여 데이터 프레임에 저장

「WARNING: not removing hydrogen atom without neighbors」라고 나오는 경우는, 소금 등이 데이터에 포함되어 있기 때문이라고 생각됩니다. RDKit에서는 디폴트로 H가 제외된 상태로 보존되기 때문에, 옆과 결합하지 않은 H(소금 등)가 있으면 그러한 H는 제거할 수 없고, 경고해 줍니다.

 

분자 기술자 작성

데이터 프레임의 각 행에 있는 대상에 있는 함수를 적용하려면 Map 함수가 유용합니다.

RDKit의 「Descriptors.descList」에는 기술자의 명칭과 함수(function)가 리스트 되고 있기 때문에, 조금 시간이 걸립니다만 for 함수와 map 함수로 일괄 계산해, 데이터 프레임에 돌려줄 수 있었습니다.

for i,j in Descriptors.descList:
    df[i] = df.ROMol.map(j)
 
df.shape
# (2039, 205)

df.head()
분자 기술자의 dataframe 생성

201 열의 설명자가 추가되었습니다.

결과 변수를 scikit-learn 또는 딥 러닝 프레임워크에 적용하는 경우 "ValueError: Input contains NaN, infinity or a value too large for dtype('float64')" 오류가 발생할 수 있습니다. 는 다음과 같이 하면 괜찮았습니다.

for i,j in Descriptors.descList:
    df[i] = df['ROMol'].map(j)

df['Ipc'] = [Descriptors.Ipc(mol, avg=True) for mol in df['ROMol']]  

기술자의 일부 「IPC」의 값에 무한과 같이 큰 것이 작성되어 버리는 것이 원인인 것 같습니다.

참고 : # 12 RDKit 2D descriptor 인 IPC 값이 매우 큰 경우의 해결책
참고: 분자 기술자 목록

지문 만들기

apply 함수를 사용하면 빠르게 계산할 수 있지만 지문 목록이 한 열에 저장되는 것 같습니다.

지문은 ExplicitBitVect object라는 형식으로 저장되어 있기 때문에 각 값을 한 열씩 저장하는 경우는 조금 번거로웠습니다.

# 下準備
df = pd.read_csv("BBBP.csv")
PandasTools.AddMoleculeColumnToFrame(df,'smiles') 
df = df.dropna()
 
# 1列にfingerprintのリストを追加する場合
df['FP'] = df.apply(lambda x: AllChem.GetMorganFingerprintAsBitVect(x.ROMol, 2, 1024), axis=1)

# fingerprintの各値を各列に格納する場合
# 個別に01をデータフレームに格納する
FP = [AllChem.GetMorganFingerprintAsBitVect(mol, 2, 1024) for mol in df.ROMol]
df_FP = pd.DataFrame(np.array(FP)) 

# フィンガープリントをもとのデータフレームに結合
df_FP.index = df.index
df = pd.concat([df, df_FP], axis=1)
한 열에 fingerprint 목록을 추가하면
fingerprint의 각 값을 각 열에 저장하는 경우