-
Notifications
You must be signed in to change notification settings - Fork 0
test modification of intermediate structural representations #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
stephprince
wants to merge
14
commits into
main
Choose a base branch
from
switch-conformers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 6 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
e6c45b6
add conformer pair analysis scripts
stephprince 9f43ab1
add methods to manipulate AF intermediates
stephprince e70fcb0
update .gitignore to exclude data dir
stephprince dd2b282
fix gitignore formatting
stephprince 1febbe8
update requirements.txt
stephprince c3aad2d
update codespell to ignore notebooks
stephprince 305e365
update plots and add pair struct correlations
stephprince 9518e2f
move preprocessing to src and make cli
stephprince dcea10d
remove pandaspdb usage
stephprince 9784a57
update requirements file
stephprince 9f65123
update notebook plotting outputs
stephprince e68e9ee
update analysis to use lddt
stephprince bad501e
Merge branch 'main' into switch-conformers
stephprince 3a64fa6
fix formatting and missing merge
stephprince File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,3 +4,4 @@ __pycache__ | |
| *.egg-info/ | ||
| *.swp | ||
|
|
||
| data/* | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,6 @@ pandas | |
| numpy | ||
| scipy | ||
| periodictable | ||
| biopandas | ||
| seaborn | ||
| alphafold-colabfold | ||
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import pandas as pd | ||
| import pickle | ||
| import glob | ||
| import shutil | ||
|
|
||
| from metfish.utils import get_alphafold_atom_positions, get_rmsd, convert_pdb_to_sequence | ||
| from biopandas.pdb import PandasPdb | ||
|
|
||
| # set up preprocessing parameters and directories | ||
| calculate_less_similar = True # calculates which conformer is less similar to the original AF prediction | ||
| use_subset = True # only selects subset of proteins to run preprocessing | ||
| n_pairs = 6 # if using a subest, the # of pairs to look at, N pairs -> 2N structures predicted | ||
| data_dir = "metfish/data" # directory with pdb files / csv from Saldano et al., 2022 | ||
| output_dir = "metfish/data/output/no_manipulation/240213" # directory with AF output files | ||
|
|
||
| # load apo and holo id names | ||
| pairs_df = pd.read_csv(f"{data_dir}/apo_holo_pairs.csv") | ||
| if use_subset: | ||
| pairs_df = pairs_df.sample(n_pairs, random_state=1234) # selects random rows as subsample | ||
|
|
||
| # convert pdb files into sequences, AF structures to use as AF inputs | ||
| holo_id = pairs_df["holo_id"].to_list() | ||
| apo_id = pairs_df["apo_id"].to_list() | ||
| for name in [*holo_id, *apo_id]: | ||
| # clean pdbs (extract only ATOM coordinates ) | ||
| raw_pdb = f"{data_dir}/pdbs_raw/{name}.pdb" | ||
| clean_pdb = f"{data_dir}/pdbs/{name}_atom_only.pdb" | ||
| PandasPdb().read_pdb(raw_pdb).to_pdb(clean_pdb, records=["ATOM"]) | ||
|
|
||
| # save pairs as fasta sequence files | ||
| seq = convert_pdb_to_sequence(f"{data_dir}/pdbs/{name}_atom_only.pdb") | ||
| seq = "\n".join([f">{name}", seq]) | ||
| with open(f"{data_dir}/sequences/apo_and_holo/{name}.fasta", "w") as f: | ||
| f.write(seq) | ||
|
|
||
| # copy apo ids over to separate folder for AF input (AF only needs to run one of apo/holo pair bc same sequence) | ||
| if name in apo_id: | ||
| shutil.copyfile( | ||
| f"{data_dir}/sequences/apo_and_holo/{name}.fasta", f"{data_dir}/sequences/apo_only/{name}.fasta" | ||
| ) | ||
|
|
||
| # save pairs as alphafold structure representations | ||
| struct = get_alphafold_atom_positions(f"{data_dir}/pdbs/{name}_atom_only.pdb") | ||
| with open(f"{data_dir}/af_structures/{name}.pickle", "wb") as f: | ||
| data = pickle.dump(struct, file=f) | ||
|
|
||
| # calculate RMSD values between alphafold output and apo / holo conformers | ||
| # NOTE - you need to run alphafold in advance to calculate these values | ||
| if calculate_less_similar: | ||
| rmsd_h, rmsd_a = list(), list() | ||
| for h, a in zip(holo_id, apo_id): | ||
| af_output = glob.glob(f"{output_dir}/{a}_unrelaxed_rank_001_*_000.pdb")[0] | ||
| rmsd_h.append(get_rmsd(f"{data_dir}/pdbs/{h}_atom_only.pdb", af_output)) # same af output for same sequence | ||
| rmsd_a.append(get_rmsd(f"{data_dir}/pdbs/{a}_atom_only.pdb", af_output)) # same af output for same sequence | ||
|
|
||
| pairs_df["rmsd_apo_af"] = rmsd_a | ||
| pairs_df["rmsd_holo_af"] = rmsd_h | ||
| pairs_df["less_similar_conformer"] = pairs_df.apply( | ||
| lambda x: x["holo_id"] if x["rmsd_apo_af"] < x["rmsd_holo_af"] else x["apo_id"], axis=1 | ||
| ) | ||
|
|
||
| pairs_df.to_csv(f"{data_dir}/apo_holo_pairs_with_similarity.csv", index=False) | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import pickle | ||
| import numpy as np | ||
| import pandas as pd | ||
| import warnings | ||
|
|
||
| from pathlib import Path | ||
|
|
||
|
|
||
| def modify_representations(prev: dict = None, method: str = "none", **kwargs): | ||
| """Modify the pair, position (structural), or MSA (first row only) representations | ||
| that will be used as inputs for the next recycling iteration of AlphaFold. | ||
|
|
||
| Args: | ||
| prev (dict): Dict of pair, position, and msa representations. | ||
| method (str): Method to use to modify representations. | ||
|
|
||
| Returns: | ||
| dict: modified pair, position and msa representations | ||
| """ | ||
|
|
||
| # apply modification method (test examples, will eventually modify prev outputs based on SAXS data) | ||
| match method: | ||
| case "none": | ||
| repr_modified = prev | ||
| case "reinitialize": | ||
| repr_modified = reinitialize(prev) | ||
| case "add_noise": | ||
| repr_modified = add_noise(prev) | ||
| case "replace_structure": | ||
| repr_modified = replace_structure(prev, **kwargs) | ||
| case _: | ||
| warnings.warn("Representation modification method not supported - defaulting to no modification") | ||
| repr_modified = prev | ||
|
|
||
| return repr_modified | ||
|
|
||
|
|
||
| def add_noise(prev): | ||
| """Adds gaussian noise to the pair, structure, and MSA representations""" | ||
| prev_with_noise = dict() | ||
| rng = np.random.default_rng() | ||
|
|
||
| for key, value in prev.items(): | ||
| noise = rng.standard_normal(value.shape).astype("float16") # gaussian noise with μ = 0, σ = 1 | ||
| prev_with_noise[key] = value + noise | ||
|
|
||
| return prev_with_noise | ||
|
|
||
|
|
||
| def reinitialize(prev): | ||
| """Reinitializes the pair, structure, and MSA, representations to zero arrays""" | ||
|
|
||
| L = np.shape(prev["prev_pair"])[0] | ||
|
|
||
| prev = { | ||
| "prev_msa_first_row": np.zeros([L, 256], dtype=np.float16), | ||
| "prev_pair": np.zeros([L, L, 128], dtype=np.float16), | ||
| "prev_pos": np.zeros([L, 37, 3], dtype=np.float16), | ||
| } | ||
|
|
||
| return prev | ||
|
|
||
|
|
||
| def replace_structure(prev, job_name, input_dir=None, replacement_method="template"): | ||
| """Replace intermediate structure (atom position) representations from alphafold""" | ||
| # load in conformer pair information | ||
| input_dir = input_dir or Path(__file__).resolve().parents[2] / 'data' | ||
| conformer_pairs_fname = f"{input_dir}/apo_holo_pairs_with_similarity.csv" | ||
|
|
||
| pdb_name = job_name.split("_")[0] | ||
| conformer_df = pd.read_csv(conformer_pairs_fname) | ||
| pairs = list(zip(conformer_df["apo_id"], conformer_df["holo_id"])) | ||
|
|
||
| # get relevant pairs | ||
| index = [ind for ind, (a, h) in enumerate(pairs) if pdb_name in a or pdb_name in h] | ||
| pair_info = conformer_df.iloc[index, :] | ||
|
|
||
| # get structure name depending on replacement method | ||
| match replacement_method: | ||
| case "less_similar": | ||
| replacement_pdb_name = pair_info["less_similar_conformer"] | ||
| case "alternate": | ||
| replacement_pdb_name = ( | ||
| pair_info["holo_id"] if pdb_name in pair_info["apo_id"].to_list()[0] else pair_info["apo_id"] | ||
| ) # get opposite conformer | ||
| case "template": | ||
| replacement_pdb_name = ( | ||
| pair_info["apo_id"] if pdb_name in pair_info["apo_id"].to_list()[0] else pair_info["holo_id"] | ||
| ) # provide conformer experimental structure | ||
| case _: | ||
| replacement_pdb_name = [] | ||
|
|
||
| if any(replacement_pdb_name): | ||
| # load replacement structure | ||
| print(f"Replacing {pdb_name} intermediate structure with {replacement_pdb_name.to_list()[0]}.") | ||
| with open(f"{input_dir}/af_structures/{replacement_pdb_name.to_list()[0]}.pickle", "rb") as f: | ||
| replacement_structure = pickle.load(f) | ||
|
|
||
| # NOTE - additional zero values seem to get added to the first dimension of the position array | ||
| # (n_res) when running multiple sequences. AF ignores anything longer than n_res when writing | ||
| # to a pdb file from the protein class, so replacing the first n_res values and adding a warning | ||
| if np.shape(prev["prev_pos"])[0] != np.shape(replacement_structure)[0]: | ||
| warnings.warn( | ||
| f"Alphafold intermediate {np.shape(prev['prev_pos'])} and modified conformer " | ||
| f"{np.shape(replacement_structure)} structures were not the same shape.", | ||
| ) | ||
|
|
||
| # replace conformer with alternative option | ||
| n_res = np.shape(replacement_structure)[0] | ||
| prev["prev_pos"][:n_res, :, :] = replacement_structure.astype("float16") | ||
|
|
||
| else: | ||
| warnings.warn(f'No replacement option found for "{pdb_name}". Continuing without modification') | ||
|
|
||
| return prev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.