Source code for PANDORA.PMHC.Anchors

from PANDORA.Contacts import Contacts

[docs]def pMHCI_anchors(pdb): ''' Finds peptide anchor residues of p:MHCI complexes Args: pdb: Bio.PDB object (or path to pdb file (string)) Returns: (list of ints) Peptide anchor residue positions ''' def closest_pept_res(dist_list): '''Find the closest peptide residue based on te minimal mean value between peptide residues and MHC residues''' anch_list = [] for i in set([i[2] for i in dist_list]): l = [x[-1] for x in dist_list if x[2] == i] anch_list.append((i, sum(l) / len(l))) #add mean distance between pept residue and MHC residue return min(((x[1], x[0]) for x in anch_list), default=(0, 0))[1] #get the pept residue with the min mean dist # Rafaella's residues # pocket_M = {'anch1': [24, 25, 35, 36], 'anch2': [81]} # Derek's modified version. Empirically tested to have the highest prediction accuracy. See below. (only best shown) pocket_M = {'anch1': [7, 24, 35], 'anch2': [118, 135]} # Accuracies peptide residue anchor prediction using MHCI residue specified above. # I selected all structures with non-canonically spaced anchors from Rafaella's prediction and 100 random pdbs. # From this dataset I manually determined the peptide anchor residue position. I then looped through all MHCI # residues and selected the ones with the highest accuracy in predicting the correct anchors. # Getting all anchors right --> Accuracy: 100.0 # Anchor 1: 100.0 # Anchor 2: 100.0 # Calculate the contacts with a cutoff of 18. This cutoff because at lower cutoffs, the orientations of side chains # of different structures, result in highly different contacts. cont = Contacts.Contacts(pdb, pept_contacts=True, M_only=sum(pocket_M.values(), []), cutoff=25).chain_contacts # Make sure the first chain is P, second chain is M or N, not the other way around cont = cont + [(i[4], i[5], i[6], i[7], i[0], i[1], i[2], i[3], i[8]) for i in cont if i[1] == 'P' or i[5] == 'P'] cont = list(dict.fromkeys(cont)) # Find the peptide residues that reside in the M and N pockets anch1, anch2 = [], [] for i in cont: if i[1] == 'P' and i[5] == 'M': # Use residues from the M chain to find anchor 1, 3 and 4 # Check the Alpha, Beta and Gamma carbon molecules of the peptide residues. if i[3] == 'CA' or i[3] == 'CB' or i[3] == 'CG' or i[3] == 'C1': if i[6] in pocket_M['anch1']: #If connected (<25 ang) to specified anch 1 MHC residues, add to list anch1.append(i) if i[6] in pocket_M['anch2']: #If connected (<25 ang) to specified anch 2 MHC residues, add to list anch2.append(i) # Now find the peptide residues that are closest to the specified MHCII residues to get the anchors anchor_1 = closest_pept_res(anch1) anchor_2 = closest_pept_res(anch2) # Put the anchors in the right order if they aren't already anchors = sorted([i for i in [anchor_1, anchor_2]]) # Return the ordered set of anchors. Filter out 0's and >25 numbers if they happen to get through. return sorted(set([i for i in anchors if i != 0 or i > 25]))
[docs]def pMHCII_anchors(pdb): ''' Finds peptide anchor residues of p:MHCII complexes Args: pdb: Bio.PDB object (or path to pdb file (string)) Returns: (list of ints) Peptide anchor residue positions ''' def closest_pept_res(dist_list): '''Find the closest peptide residue based on te minimal mean value between peptide residues and MHC residues''' anch_list = [] for i in set([i[2] for i in dist_list]): l = [x[-1] for x in dist_list if x[2] == i] anch_list.append((i, sum(l) / len(l))) #add mean distance between pept residue and MHC residue return min(((x[1], x[0]) for x in anch_list), default=(0, 0))[::-1] #get the pept residue with the min mean dist # Rafaella's residues # pocket_M = {'anch1':[28, 33, 31], 'anch3':[10]} # pocket_N = {'anch2':[10, 11, 21, 22, 23], 'anch3':[6,7], 'anch4':[ 28, 27, 31, 33]} # Derek's modified version. Empirically tested to have the highest prediction accuracy. # Has a 100% accuracy on all IMGT templates pocket_M = {'anch1': [28, 29], 'anch2': [82, 81, 80], 'anch4': [82, 81, 80], 'anch3': [113, 114]} pocket_N = {'anch2': [24, 25], 'anch3': []} cont = Contacts.Contacts(pdb, pept_contacts=True, M_only=sum(pocket_M.values(), []), N_only=sum(pocket_N.values(), []), cutoff=25).chain_contacts # Make sure the first chain is P, second chain is M or N, not the other way around cont = cont + [(i[4], i[5], i[6], i[7], i[0], i[1], i[2], i[3], i[8]) for i in cont if i[1] == 'P' or i[5] == 'P'] cont = list(dict.fromkeys(cont)) pept_len = max([i[2] for i in cont if i[1] == 'P']) # Find the peptide residues that reside in the M and N pockets anch1, anch2, anch3, anch4 = [], [], [], [] for i in cont: if i[1] == 'P' and i[5] == 'M': # Use residues from the M chain to find anchor 1, 3 and 4 # Check the Alpha, Beta and Gamma carbon molecules of the peptide residues. if i[3] == 'CA' or i[3] == 'CB' or i[3] == 'CG': if i[6] in pocket_M['anch1']: #If connected (<25 ang) to specified anch 1 MHC residues, add to list anch1.append(i) if i[6] in pocket_M['anch2']: #If connected (<25 ang) to specified anch 2 MHC residues, add to list anch2.append(i) if i[6] in pocket_M['anch3']: #If connected (<25 ang) to specified anch 3 MHC residues, add to list anch3.append(i) if i[6] in pocket_M['anch4']: #If connected (<25 ang) to specified anch 4 MHC residues, add to list anch4.append(i) if i[1] == 'P' and i[5] == 'N': # Use chain N for the second anchor # Check the Alpha, Beta and Gamma carbon molecules of the peptide residues if i[3] == 'CA' or i[3] == 'CB' or i[3] == 'CG': if i[6] in pocket_N['anch2']: #If connected (<25 ang) to specified anch 2 MHC residues, add to list anch2.append(i) # First check if the peptide is inverted: C to N term instead of N to C term (this is very rare) anchor_1 = closest_pept_res(anch1) extra_rules = True # If the first anchor is very deep inside the 1st pocket, we can be pretty confident in assigning the other anchors if anchor_1[1] < 18: # find the distance between res M51 and the predicted first anchor. Make sure its less than 15 angstrom. cont_p1 = Contacts.Contacts(pdb, pept_contacts=True, M_only=[51], N_only=[], cutoff=12).chain_contacts # Make sure the first chain is P, second chain is M or N, not the other way around cont_p1 = cont_p1 + [(i[4], i[5], i[6], i[7], i[0], i[1], i[2], i[3], i[8]) for i in cont_p1 if i[1] == 'P' or i[5] == 'P'] cont_p1 = list(dict.fromkeys(cont_p1)) # Check the Alpha, Beta and Gamma carbon molecules of the peptide residues. cont_p1 = [i for i in cont_p1 if i[1] == 'P' and i[5] == 'M' and (i[3] == 'CA' or i[3] == 'CB' or i[3] == 'CG')] # Find the distance between residue M51 and the predicted 1st anchor dist_between_51_anch1 = closest_pept_res([i for i in cont_p1 if i[2] == anchor_1[0]])[1] # If predicted anchor 1 is very close to res M51, then we can be very sure to have found pept res anchor 1 # deep and firmly inside in the pocket --> assume canonical spacing then. if dist_between_51_anch1 != 0 and dist_between_51_anch1 < 12: anchor_1 = anchor_1[0] anchor_2 = anchor_1 + 3 anchor_3 = anchor_1 + 5 if pept_len - anchor_1 >= 8: anchor_4 = anchor_1 + 8 else: anchor_4 = 0 extra_rules = False # If its not, use some extra rules to increase prediction accuracy. if extra_rules: # First find anchor 2, this anchor is found with the highest accuracy if pept_len >= 9: anch2 = [i for i in anch2 if i[2] < pept_len - 4] anchor_2 = closest_pept_res(anch2)[0] # Make sure anchor 1 if smaller than anchor 2, but bigger than anchor 2 - 5 # This makes sure residues from long flanking regions that are flipped over the MHC structure aren't selected anch1_mod = [i for i in anch1 if i[2] < anchor_2 - 1 and i[2] > anchor_2 - 5] anchor_1 = closest_pept_res(anch1_mod)[0] # Make sure anchor 3 is close to anchor 2 as well (but not too far, to prevent selecting anchor 4) anch3_mod = [i for i in anch3 if i[2] > anchor_2 + 1 and i[2] < anchor_2 + 4] anchor_3 = closest_pept_res(anch3_mod)[0] # Same for anchor 4. Make sure its not too far away. Otherwise distant flanking residues might get selected. anch4_mod = [i for i in anch4 if i[2] > anchor_2 + 2 and i[2] < anchor_2 + 6] anchor_4 = closest_pept_res(anch4_mod)[0] # Put the anchors in the right order if they aren't already anchors = sorted([i for i in [anchor_1, anchor_2, anchor_3, anchor_4]]) # If the spacing between 1-4 and 1-2 is both wrong, infer anchors positions based on anchor 1 if anchors[-1] - anchors[0] != 8 and anchors[1] - anchors[0] != 3: anchor_1 = closest_pept_res(anch1)[0] if pept_len - anchor_1 >= 8: anchors = [anchor_1, anchor_1 + 3, anchor_1 + 5, anchor_1 + 8] else: anchors = [anchor_1, anchor_1 + 3, anchor_1 + 5, 0] # If anchor 1 and 4 are spaced in the canonical way, and anchor 2 and 3 are not, then just jump the gun and # use the canonical spacing for anchor 2 and 3. if anchors[-1] - anchors[0] == 8: if anchors[1] - anchors[2] != -2 or anchors[2] - anchors[1] != 2: anchors[1] = anchors[0] + 3 anchors[2] = anchors[0] + 5 # If there is the 3rd and 4th anchor are adjacent and the 4th anchor is the last peptide, remove 4th anchor if anchors[3] - anchors[2] == 1 and pept_len == anchors[3]: anchors[3] = 0 # Return the ordered set of anchors. Filter out 0's and >25 numbers if they happen to get through. return sorted(set([i for i in anchors if i != 0 or i > 25]))