ASCARIS / code /add_3Dalignment.py
fatmacankara's picture
Update code/add_3Dalignment.py
2d335db
raw
history blame
12.6 kB
"""
This code file produces alignments between the structure and the sequence for a given protein.
"""
import math
import glob
import numpy as np
from Bio import Align
import gzip
from pathlib import Path
from Bio.Align import substitution_matrices
from Bio.PDB.Polypeptide import *
aligner = Align.PairwiseAligner()
import requests
from Bio.PDB import PDBParser, PPBuilder
from io import StringIO
def convert_non_standard_amino_acids(sequence):
"""
Convert non-standard or ambiguous amino acid codes to their closest relatives.
"""
# Define a dictionary to map non-standard codes to standard amino acids
conversion_dict = {
'B': 'D', # Aspartic Acid (D) is often used for B (Asx)
'Z': 'E', # Glutamic Acid (E) is often used for Z (Glx)
'X': 'A', # Alanine (A) is a common placeholder for unknown/ambiguous
'U': 'C', # Cysteine (C) is often used for Selenocysteine (U)
'J': 'L', # Leucine (L) is often used for J (Leu/Ile)
'O': 'K', # Lysine (K) is often used for O (Pyrrolysine)
# '*' or 'Stop' represents a stop codon; you may replace with '' to remove
'*': '',
}
# Replace non-standard codes with their closest relatives
converted_sequence = ''.join([conversion_dict.get(aa, aa) for aa in sequence])
return converted_sequence
def distance(x1, y1, z1, x2, y2, z2):
d = math.sqrt(math.pow(x2 - x1, 2) +
math.pow(y2 - y1, 2) +
math.pow(z2 - z1, 2) * 1.0)
return d
def find_distance(coordMut, coordAnnot):
if coordMut != np.NaN:
try:
dist = distance(float(coordMut[0]), float(coordMut[1]), float(coordMut[2]), float(coordAnnot[0]),
float(coordAnnot[1]), float(coordAnnot[2]))
return "%.2f" % dist
except:
ValueError
dist = 'nan'
return dist
else:
return np.NaN
def threeToOne(variant):
if variant == "ALA":
variant = "A"
elif variant == "ARG":
variant = "R"
elif variant == "VAL":
variant = "V"
elif variant == "GLU":
variant = "E"
elif variant == "PRO":
variant = "P"
elif variant == "LEU":
variant = "L"
elif variant == "GLY":
variant = "G"
elif variant == "ASN":
variant = "N"
elif variant == "SER":
variant = "S"
elif variant == "GLN":
variant = "Q"
elif variant == "THR":
variant = "T"
elif variant == "MET":
variant = "M"
elif variant == "LYS":
variant = "K"
elif variant == "ASP":
variant = "D"
elif variant == "ILE":
variant = "I"
elif variant == "PHE":
variant = "F"
elif variant == "TRP":
variant = "W"
elif variant == "TYR":
variant = "Y"
elif variant == "HIS":
variant = "H"
elif variant == "CYS":
variant = "C"
elif variant == 'UNK':
variant = 'X'
elif variant == 'ASX':
variant = 'O'
return (variant)
def get_coords(annot, alignments, coords, resnums_for_sasa, mode):
if mode == 1:
for alignment in alignments[0]:
alignment = (str(alignment).strip().split('\n'))
startGap = 0
if alignment[0].startswith('.'):
for k in alignment[0]:
if k == '.' or k == '-':
startGap += 1
else:
break
countGap = startGap
countResidue = 0
for j in alignment[0][startGap:]:
if j == '.' or j == '-':
countGap += 1
else:
countResidue += 1
if countResidue == float(annot):
break
countGap_pdb = 0
countResidue_pdb = 0
for m in alignment[2][0:countResidue + countGap - 1]:
if m == '.' or m == '-':
countGap_pdb += 1
posAtom = countResidue + countGap - countGap_pdb
realpdbStart = 0
for j in alignment[2]:
if j == '.' or j == '-':
realpdbStart += 1
else:
break
if (alignment[2][countResidue + countGap - 1] != '-') and (float(annot) >= float(realpdbStart) + 1):
try:
coordinates = alignments[1]
residue_numbers = alignments[2]
coordWeWant = coordinates[posAtom - 1]
residue_number_we_want = residue_numbers[posAtom - 1]
except:
IndexError
coordWeWant = 'nan'
else:
coordWeWant = 'nan'
return coordWeWant, posAtom, residue_number_we_want
if mode == 2:
if annot != 'nan':
if int(annot) <= 1400:
alignment = (str(alignments).strip().split('\n'))
startGap = 0
if alignment[0].startswith('.'):
for k in alignment[0]:
if k == '.' or k == '-':
startGap += 1
else:
break
countGap = startGap
countResidue = 0
for j in alignment[0][startGap:]:
if j == '.' or j == '-':
countGap += 1
else:
countResidue += 1
if countResidue == float(annot):
break
countGap_pdb = 0
countResidue_pdb = 0
for m in alignment[2][0:countResidue + countGap - 1]:
if m == '.' or m == '-':
countGap_pdb += 1
posAtom = countResidue + countGap - countGap_pdb
realpdbStart = 0
for j in alignment[2]:
if j == '.' or j == '-':
realpdbStart += 1
else:
break
if len(alignment[2]) > (countResidue + countGap - 1):
if (alignment[2][countResidue + countGap - 1] != '-') and (float(annot) >= float(realpdbStart) + 1):
try:
coordinates = coords
residue_numbers = resnums_for_sasa
coordWeWant = coordinates[posAtom - 1]
residue_number_we_want = residue_numbers[posAtom - 1]
except:
IndexError
coordWeWant = 'nan'
residue_number_we_want = 'nan'
else:
coordWeWant = 'nan'
residue_number_we_want = 'nan'
return coordWeWant, posAtom, residue_number_we_want
else:
coordWeWant = 'nan'
residue_number_we_want = 'nan'
return coordWeWant, posAtom, residue_number_we_want
else:
return np.NaN, np.NaN, np.NaN
else:
return np.NaN, np.NaN, np.NaN
def get_alignments_3D(identifier, model_num, pdb_path, pdbSequence, source, chain, pdbID, mode, path_3D_alignment,file_format = 'gzip'):
pdbSequence = convert_non_standard_amino_acids(pdbSequence)
if mode == 1:
if source == 'PDB':
# Step 1: Fetch the PDB file
pdb_url = f"https://files.rcsb.org/download/{pdbID}.pdb"
response = requests.get(pdb_url)
response.raise_for_status() # Check for a successful response
# Step 2: Parse the PDB file from memory
atoms = [i for i in response.text.split('\n') if i.startswith('ATOM')]
atoms = [i.split() for i in atoms]
atoms = [i for i in atoms if (i[2] == 'CA' and i[4] == chain)]
atoms = [[x[i][-3:] if i == 3 else x[i] for i in range(len(x))] for x in atoms]
atomSequence = ''.join([threeToOne(i[3]) for i in atoms])
coords = [[i[6] ,i[7] ,i[8]] for i in atoms]
resnums_for_sasa = [i[5] for i in atoms]
elif source == 'SWISSMODEL':
atomSequence = ''
coords = []
resnums_for_sasa = []
with open(pdb_path, encoding="utf8") as f:
for line in f.readlines():
if line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA' and line[21].upper() == chain.upper():
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
elif line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA' and line[21] == ' ':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
elif source == 'MODBASE':
atomSequence = ''
coords = []
resnums_for_sasa = []
with open(pdb_path, encoding="utf8") as f:
for line in f.readlines():
if line[0:7].strip() == 'ATOM' and line[13:15].strip() == 'CA':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
aligner.mode = 'local'
aligner.substitution_matrix = substitution_matrices.load("BLOSUM62")
aligner.open_gap_score = -11
aligner.extend_gap_score = -1
atomSequence = convert_non_standard_amino_acids(atomSequence)
alignments = aligner.align(pdbSequence, atomSequence)
alignments = (list(alignments))
return alignments, coords, resnums_for_sasa
elif mode==2:
atomSequence = ''
coords = []
resnums_for_sasa = []
if file_format == 'txt':
with open(name, encoding="utf8") as f:
for line in f.readlines():
if line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
elif line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA' and line[21] == ' ':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
elif file_format == 'gzip':
with gzip.open(pdb_path, mode='rb') as f:
for line in f:
line = line.decode()
if line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
elif line[0:4].strip() == 'ATOM' and line[13:15].strip() == 'CA' and line[21] == ' ':
atomSequence += threeToOne(line[17:20].strip())
coords.append([line[31:38].strip(), line[39:46].strip(), line[47:54].strip()])
resnums_for_sasa.append(line[22:26].strip())
#f = open(Path(path_3D_alignment / f'{identifier}_{str(model_num)}_3Dalignment.txt'),"w")
aligner.mode = 'local'
aligner.substitution_matrix = substitution_matrices.load("BLOSUM62")
aligner.open_gap_score = -11
aligner.extend_gap_score = -1
atomSequence = convert_non_standard_amino_acids(atomSequence)
alignments = aligner.align(pdbSequence, atomSequence)
alignments = (list(alignments))
return alignments, coords, resnums_for_sasa