Drug/Computer-Aided Drug Discovery

분자 SMARTS 문법 및 rdkit에서 SMARTS 탐색

Novelism 2021. 9. 23. 19:00

SMILES에 대한 설명은 이쪽에서 확인하실 수 있습니다.

https://novelism.tistory.com/106

 

SMARTS의 문법은 복잡해서 하나하나 자세히 설명하긴 어렵고 자주 쓰는 것 몇 개만 이야기하겠습니다., rdkit에서 SMARTS로 분자 탐색을 하는 사용법 위주로 설명하겠습니다.

SMARTS 규칙은 https://www.daylight.com/dayhtml/doc/theory/theory.smarts.html 에서 볼 수 있습니다.

 

Daylight Theory: SMARTS - A Language for Describing Molecular Patterns

4. SMARTS - A Language for Describing Molecular Patterns Substructure searching, the process of finding a particular pattern (subgraph) in a molecule (graph), is one of the most important tasks for computers in chemistry. It is used in virtually every appl

www.daylight.com

기본적인 문법은 SMILES와 유사합니다. 때때로 SMILES를 그대로 입력해도 되는 경우도 있습니다.

하지만 SMARTS를 사용할 때 주의해야 하는 점이 여럿 있습니다.

그중 하나는 특정 원자를 지칭할 때 원소기호를 직접 적으면 안 된다는 것입니다.

 원소기호를 대문자로 적을 경우 aliphatic 이고, 소문자로 적을 경우는 aromatic 이 됩니다.

 그러니까 임의의 탄소 원자를 지칭하고 싶다면, [#6] 이라 적어야 하고 C나 c를 적으면 aliphatic이나 aromatic 만을 검색해줍니다.

또 다른 주의점으로는 수소가 explicit 일 때와 implicit 일 때 검색 결과가 다를 수도 있다는 점이 있습니다.

 

*는 any atom을 지칭하는 wildcard입니다.

a와 A는 각각 임의의 aromatic 원자와 임의의 aliphatic 원자를 지칭합니다.

D<n>는 degree로 <n> 개의 explicit connection을 의미합니다.

H<n>은 <n>개의 total H를 가지는 원자를 의미합니다.

h<n>은 <n>개의 implicit H 를 가지는 원자를 의미합니다. rdkit은 수소를 explicit 으로 처리할 수도 있고, implicit 으로 처리할 수도 있는데, 서로 결과가 다를 수 있어서 주의해야 합니다.

[#n] 원자번호 n인 원자를 의미합니다.

R<n>은 <n>개의 ring에 속한 원자를 의미합니다.

r<n>은 <n> 개의 member를 가지는 ring에 속한 원자를 의미합니다. 예를 들어 벤젠이라면 r6 입니다.

v<n>은 total bond order입니다. bond order는 connection의 수와 다릅니다. 

 

 

bond에 대해선 - (single bond), = (double), # (triple), : (aromatic), ~ (any bond) 등이 있습니다.

논리 연산으로! 은 not을 의미하고, &와 ;는 and를 의미하는데, &는 우선순위가 높고, ;은 우선순위가 낮습니다.

 , 는 or를 의미합니다. [nH1] 처럼 &와 ;는 생략이 가능한 경우도 있습니다. (H가 1개 결합된 aromatic질소)

[c,n&H1] 와 [c,n;H1]는 둘 다 and로 연결되어있지만, 우선순위가 다릅니다.

&를 사용한 경우는 n과 H1에 대해서만 and로 묶입니다. 그러니까 탄소 이거나, 수소가 1개인 aromatic 질소라는 의미입니다.

; 를 사용한 경우는 c와 n이 or로 묶인 후에 H1이 붙습니다. 수소가 1개 붙은 원자인데 aromatic 탄소이거나 aromatic 질소라는 의미입니다.

 별로 안쓸 것 같지만, 원자량을 지정할 수도 있습니다.

 원소 앞에 숫자를 붙일 경우 [35Cl] 같은 경우는 원자량 35인 Cl을 지칭하고, [35*]라면 원자량 35인 임의의 원자를 의미합니다.

 

 

 

rdkit을 jupytor-notebook 에서 사용하면 분자 그림을 바로 볼 수가 있어서 편리합니다.

일단 rdkit에서 필요한 모듈을 import합니다.

from rdkit import Chem
from rdkit.Chem import Draw
%matplotlib inline

분자에서 1개의 ring에만 속하는 원자들을 검색해봅시다.

GetSubstructureMathces 라는 메소드를 사용합니다.

smarts = '[R1]'
smiles = 'CCc1ccc2[nH]ccc2c1'  
size = (200,200)
mol = Chem.MolFromSmiles(smiles)
patt = Chem.MolFromSmarts(smarts)
patoms= list(mol.GetSubstructMatches(patt))
print(patoms)
pp = list()
for ps in patoms:
    for patom in ps:
        pp += [patom]
img = Draw.MolToImage(mol, highlightAtoms=pp, subImgSize=size)
img

결과:

[R0] :  [(0,), (1,)]

[R1] : [(2,), (3,), (4,), (6,), (7,), (8,), (10,)]

[R2] : [(5,), (9,)]

결과는 tuple들의 list로 나옵니다. tuple은 원자들의 index로 구성이 됩니다. 이것이 tuple인 이유는, 원자 여러 개가 한 번에 나올 수도 있기 때문입니다. 지금은 단일 원자에 대한 조건을 걸었기 때문에 tuple의 member가 1개뿐이지만, 다중 원자에 대해 조건을 건다면 여러 원자에 대한 index들이 나올 것입니다. tuple이 list로 쌓여있는 이유는 조건을 만족하는 tuple들이 여러 개이기 때문입니다.

[R0] (좌), [R1] (가운데), [R2] (우)

이번에는 aromatic ring안에 속한, 서로 이웃인 질소와 산소라는 조건으로 검색을 해보겠습니다.

aromatic ring안에 속한 원소는 소문자로 표현됩니다.

이번에는 tuple마다 따로 그리기 위해서 MolsToGridImage 라는 모듈을 사용하겠습니다.

smarts = 'nc'
smiles = 'CCc1ccc2[nH]ccc2c1'  
size = (200,200)
mol_list = list()
patom_list = list()
legend_list = list()
mol = Chem.MolFromSmiles(smiles)
patt = Chem.MolFromSmarts(smarts)
patoms= list(mol.GetSubstructMatches(patt))
print(patoms)
for ps in patoms:
    mol_list += [mol]
    pp = list()
    ll = ''
    for patom in ps:
        pp += [patom]
        ll += '%d,' %patom
    ll=ll.strip(',')
    patom_list += [pp]
    legend_list += [ll]
img = Draw.MolsToGridImage(mol_list, legends=legend_list, highlightAtomLists=patom_list, subImgSize=size, molsPerRow=2)
img

결과:

[(6, 5), (6, 7)]

조건에 맞는 원자 조합은 두 가지가 있습니다. 두가지 조합이 각각 tuple로 묶여서 나옵니다.

위의 경우는 여러 원자가 관련된 조건에서 조건에 연관된 모든 원자의 index를 리턴하는 방식이었습니다.

이웃한 c와 n을 찾아라 라는 검색이었는데,

이웃에 n를 가지는 c을 찾아라 라고 하는 검색도 가능합니다. 여기선 c의 위치만이 리턴됩니다.

smarts만 적고 나머지 줄은 생략하겠습니다. 대괄호 안에 $와 소괄호가 있으면 소괄호의 제일 앞에 있는 원자의 인덱스만이 선택됩니다.

smarts = '[$(cn)]'

결과: [(5,), (7,)]

이런 타입은 hydrogen bonding acceptor 찾을 때 사용하기도 합니다.

예를 들면

'[$([O,S;H1;v2]-[!$(*=[O,N,P,S])])]'

같은 패턴이 있습니다.

total bond order가 2 이면서 수소 1개와 결합되어 있는 aliphatic 산소 또는 황인데, single bond로 연결된 이웃은 O, N, P, S와 이중결합으로 연결되어 있지 않다.라는 참 길고 복잡한 조건입니다.

복잡하게 적었지만, 제외되는 예시는 간단합니다. Carboxyl pH 7.4에선 우측처럼 수소가 떨어져 나가는데, 좌측의 경우 [OH1] 부분은 위 패턴에서 제외됩니다.

우측의 형태일 경우는 또 다른 acceptor pattern인 '[$([O,S;-])]' 로 acceptor로 탐색됩니다.

 

 

 

rdkit으로 SMILES을 입력받고 SMARTS로 출력하고 싶다면,

from rdkit import Chem
smiles = 'OC=O'
mol = Chem.MolFromSmiles(smiles)
smarts = Chem.MolToSmarts(mol)
print(smarts)

결과: [#8]-[#6]=[#8]

 

 

MarvinSketch로 SMARTS를 만들고 싶다면, 분자를 선택하고 우클릭한 후 Copy As에서 SMARTS를 선택할 수 있습니다.

결과: [#8]-[#6]=O

 이 예시에선 단순한 구조를 사용해서 별 상관없을 수도 있지만

  이렇게 툴로 생성한 SMARTS는 탐색에 직접 사용하기에 적합하지 않은 경우가 많습니다.

 좀 더 세부적인 규칙이 들어가야 원치 않는 분자들이 선택되지 않습니다.

 그래서 컴퓨터로 생성한 후에 직접 수정을 해주거나, 처음부터 새로 만들어야 합니다.