Download model/domain_assignment/util.py from OneScience-Group/Chainsaw: direct link, hf CLI and curl.
- Browser
- Download file 4.71 kB
-
https://huggingface.co/OneScience-Group/Chainsaw/resolve/main/model/domain_assignment/util.py
- Command line
-
hf download hf://OneScience-Group/Chainsaw/model/domain_assignment/util.py
-
curl -L -o util.py https://huggingface.co/OneScience-Group/Chainsaw/resolve/main/model/domain_assignment/util.py
4.71 kB
| """ | |
| Utilties for converting to/from a dictionary representation of domain assignments. | |
| """ | |
| import logging | |
| import os | |
| from itertools import product | |
| import numpy as np | |
| import warnings | |
| from Bio.PDB.PDBExceptions import PDBConstructionWarning | |
| warnings.simplefilter('ignore', PDBConstructionWarning) | |
| LOG = logging.getLogger(__name__) | |
| def make_pair_labels(n_res, domain_dict, id_string=None, save_dir=None, non_aligned_residues=[]): | |
| """n_res: number of residues in the non-trimmed sequence | |
| non_aligned_residues: these will be used to trim down from n_res | |
| domain_dict: eg. {'D1': [0,1,2,3], 'D2': [4,5,6]} | |
| """ | |
| pair_labels = np.zeros([n_res, n_res]) | |
| for domain, res_ix in domain_dict.items(): | |
| if domain == 'linker': | |
| continue | |
| coords_tuples = list(product(res_ix, res_ix)) | |
| x_ix = [i[0] for i in coords_tuples] | |
| y_ix = [i[1] for i in coords_tuples] | |
| pair_labels[x_ix, y_ix] = 1 | |
| if len(non_aligned_residues): | |
| aligned_residues = [i for i in range(n_res) if i not in non_aligned_residues] | |
| pair_labels = pair_labels[aligned_residues,:][:,aligned_residues] | |
| if save_dir is not None: | |
| save_path = os.path.join(save_dir, id_string) | |
| np.savez_compressed(save_path, pair_labels) | |
| return pair_labels | |
| def sort_domain_limits(limits, dom_names): | |
| start_positions = [x[0] for x in limits] | |
| end_positions = [x[1] for x in limits] | |
| sorted_index = np.argsort(start_positions) | |
| assert (sorted_index == np.argsort(end_positions)).all() | |
| return np.array(limits)[sorted_index], list(np.array(dom_names)[sorted_index]) | |
| def resolve_residue_in_multiple_domain(mapping, shared_res): | |
| """ | |
| This is a stupid slow recursive solution: but I think it only applies to one | |
| case so going to leave it for now | |
| """ | |
| for one_shared in shared_res: | |
| for domain, res in mapping.items(): | |
| if one_shared in res: | |
| mapping[domain].remove(one_shared) | |
| return check_no_residue_in_multiple_domains(mapping) | |
| def check_no_residue_in_multiple_domains(mapping, resolve_conflics=True): | |
| # ensures no residue index is associated with more than one domain | |
| for dom, res in mapping.items(): | |
| for dom2, res2 in mapping.items(): | |
| if dom == dom2: | |
| continue | |
| shared_res = set(res).intersection(set(res2)) | |
| if len(shared_res): | |
| print(f'Found {len(shared_res)} shared residues') | |
| if resolve_conflics: | |
| mapping = resolve_residue_in_multiple_domain(mapping, shared_res) | |
| else: | |
| raise ValueError("SAME RESIDUE NUMBER FOUND IN MULTIPLE DOMAINS") | |
| return mapping | |
| def make_domain_mapping_dict(row): | |
| dom_limit_list = row.dom_bounds_pdb_ix.split('|') | |
| dom_names = row.dom_names.split('|') | |
| dom_limit_list = convert_limits_to_numbers(dom_limit_list) | |
| dom_limit_array, dom_names = sort_domain_limits(dom_limit_list, dom_names) | |
| mapping = {} | |
| for i, d_lims in enumerate(dom_limit_array): | |
| dom_name = dom_names[i] | |
| pdb_start, pdb_end = d_lims | |
| if dom_name not in mapping: | |
| mapping[dom_name] = [] | |
| mapping[dom_name] += list(range(pdb_start, pdb_end)) | |
| check_no_residue_in_multiple_domains(mapping) | |
| return mapping | |
| def convert_limits_to_numbers(dom_limit_list): | |
| processed_dom_limit_list = [] | |
| for lim in dom_limit_list: | |
| dash_idx = [i for i, char in enumerate(lim) if char == '-'] | |
| if len(dash_idx) == 1: | |
| start_index = int(lim.split('-')[0]) -1 | |
| end_index = int(lim.split('-')[1]) | |
| else: | |
| raise ValueError('Invalid format for domain limits', str(dom_limit_list)) | |
| processed_dom_limit_list.append((start_index, end_index)) | |
| return processed_dom_limit_list | |
| def convert_domain_dict_strings(domain_dict): | |
| """ | |
| Converts the domain dictionary into domain_name string and domain_bounds string | |
| eg. domain names D1|D2|D1 | |
| eg. domain bounds 0-100|100-200|200-300 | |
| """ | |
| domain_names = [] | |
| domain_bounds = [] | |
| for k,v in domain_dict.items(): | |
| if k=='linker': | |
| continue | |
| residues = sorted(v) | |
| for i, res in enumerate(residues): | |
| if i==0: | |
| start = res | |
| elif residues[i-1] != res - 1: | |
| domain_bounds.append(f'{start}-{residues[i-1]}') | |
| domain_names.append(k) | |
| start = res | |
| if i == len(residues)-1: | |
| domain_bounds.append(f'{start}-{res}') | |
| domain_names.append(k) | |
| return '|'.join(domain_names), '|'.join(domain_bounds) | |