Spaces:
Sleeping
Sleeping
""" | |
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 | |