Oncodrive3D 1.0.5__tar.gz → 1.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/.gitignore +1 -3
  2. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/Dockerfile +2 -2
  3. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/PKG-INFO +31 -4
  4. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/README.md +30 -3
  5. oncodrive3d-1.0.7/docs/build_output.md +19 -0
  6. oncodrive3d-1.0.7/docs/images/graphical_abstract.png +0 -0
  7. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/docs/run_input_output.md +3 -3
  8. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/README.md +1 -1
  9. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/__init__.py +1 -1
  10. oncodrive3d-1.0.7/scripts/datasets/build_datasets.py +173 -0
  11. oncodrive3d-1.0.7/scripts/datasets/custom_pdb.py +144 -0
  12. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/get_pae.py +28 -6
  13. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/get_structures.py +5 -3
  14. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/prob_contact_maps.py +10 -4
  15. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/seq_for_mut_prob.py +236 -111
  16. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/utils.py +2 -1
  17. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/main.py +36 -12
  18. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/chimerax_plot.py +9 -5
  19. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/utils.py +0 -1
  20. oncodrive3d-1.0.7/tools/preprocessing/prepare_samplesheet.py +252 -0
  21. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/uv.lock +888 -887
  22. oncodrive3d-1.0.5/docs/build_output.md +0 -13
  23. oncodrive3d-1.0.5/scripts/datasets/build_datasets.py +0 -126
  24. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/.python-version +0 -0
  25. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/LICENSE +0 -0
  26. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/conf/base.config +0 -0
  27. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/conf/test.config +0 -0
  28. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/main.nf +0 -0
  29. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/modules/o3d_chimerax_plot.nf +0 -0
  30. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/modules/o3d_plot.nf +0 -0
  31. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/modules/o3d_run.nf +0 -0
  32. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/nextflow.config +0 -0
  33. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/validation.nf +0 -0
  34. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/oncodrive3d_pipeline/workflows/oncodrive3d.nf +0 -0
  35. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/pyproject.toml +0 -0
  36. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/clustering_3d.code-workspace +0 -0
  37. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/__init__.py +0 -0
  38. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/af_merge.py +0 -0
  39. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/model_confidence.py +0 -0
  40. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/datasets/parse_pae.py +0 -0
  41. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/globals.py +0 -0
  42. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/__init__.py +0 -0
  43. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/build_annotations.py +0 -0
  44. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/pdb_tool.py +0 -0
  45. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/pfam.py +0 -0
  46. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/plot.py +0 -0
  47. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/stability_change.py +0 -0
  48. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/uniprot_feat.py +0 -0
  49. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/plotting/utils.py +0 -0
  50. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/__init__.py +0 -0
  51. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/clustering.py +0 -0
  52. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/communities.py +0 -0
  53. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/miss_mut_prob.py +0 -0
  54. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/mutability.py +0 -0
  55. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/pvalues.py +0 -0
  56. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/scripts/run/score_and_simulations.py +0 -0
  57. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/maf/TCGA_WXS_ACC.in.maf +0 -0
  58. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/maf/TCGA_WXS_PAAD.in.maf +0 -0
  59. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/mut_profile/TCGA_WXS_ACC.sig.json +0 -0
  60. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/mut_profile/TCGA_WXS_PAAD.sig.json +0 -0
  61. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/vep/TCGA_WXS_ACC.vep.tsv.gz +0 -0
  62. {oncodrive3d-1.0.5 → oncodrive3d-1.0.7}/test/input/vep/TCGA_WXS_PAAD.vep.tsv.gz +0 -0
@@ -192,6 +192,4 @@ run_*
192
192
 
193
193
  # VSC
194
194
  launch.json
195
- .vscode
196
-
197
- test/outputs*
195
+ .vscode
@@ -19,7 +19,7 @@ RUN mkdir -p /root/.cache/uv \
19
19
  && uv build
20
20
 
21
21
  # Second stage: Runtime image
22
- FROM python:3.10-buster AS runtime-stage
22
+ FROM python:3.10.18-bookworm AS runtime-stage
23
23
 
24
24
  # Set environment variables
25
25
  ENV BGDATA_LOCAL="/bgdatacache" \
@@ -30,7 +30,7 @@ COPY --from=build-stage /oncodrive3d/dist /oncodrive3d/dist
30
30
  WORKDIR /oncodrive3d
31
31
 
32
32
  # Install oncodrive3d
33
- RUN pip install --no-cache-dir dist/*.tar.gz
33
+ RUN pip install dist/*.tar.gz
34
34
 
35
35
  # Create required directories
36
36
  RUN install -d -m 0755 "$BGDATA_LOCAL" "$BBGLAB_HOME"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Oncodrive3D
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Oncodrive3D is a method designed to analyse patterns of somatic mutations across tumors to identify three-dimensional (3D) clusters of missense mutations and detect genes that are under positive selection.
5
5
  Project-URL: Homepage, https://github.com/bbglab/clustering_3d
6
6
  Project-URL: Repository, https://github.com/bbglab/clustering_3d
@@ -37,14 +37,15 @@ Description-Content-Type: text/markdown
37
37
 
38
38
  **Oncodrive3D** is a fast and accurate computational method designed to analyze patterns of somatic mutation across tumors, with the goal of identifying **three-dimensional (3D) clusters** of missense mutations and detecting genes under **positive selection**.
39
39
 
40
- The method leverages **AlphaFold 2-predicted protein structures** and Predicted Aligned Error (PAE) to define residue contacts within the protein's 3D space. When available, it integrates **mutational profiles** to build an accurate background model of neutral mutagenesis. By applying a novel **rank-based statistical approach**, Oncodrive3D scores potential 3D clusters and computes empirical p-values."
40
+ The method leverages **AlphaFold 2-predicted protein structures** and Predicted Aligned Error (PAE) to define residue contacts within the protein's 3D space. When available, it integrates **mutational profiles** to build an accurate background model of neutral mutagenesis. By applying a novel **rank-based statistical approach**, Oncodrive3D scores potential 3D clusters and computes empirical p-values.
41
41
 
42
42
  [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
43
43
  [![docker](https://img.shields.io/docker/v/bbglab/oncodrive3d?logo=docker)](https://hub.docker.com/r/bbglab/oncodrive3d)
44
44
  [![PyPI - Version](https://img.shields.io/pypi/v/oncodrive3d?logo=pypi)](https://pypi.org/project/Oncodrive3D/)
45
45
 
46
- ---
46
+ ![Graphical abstract of Oncodrive3D](docs/images/graphical_abstract.png "Oncodrive3D")
47
47
 
48
+ ---
48
49
 
49
50
  ## Requirements
50
51
 
@@ -118,6 +119,13 @@ Options:
118
119
  Default: human
119
120
  -m, --mane Use structures predicted from MANE Select transcripts
120
121
  (applicable to Homo sapiens only).
122
+ -M, --mane_only Use only structures predicted from MANE Select transcripts
123
+ (applicable to Homo sapiens only).
124
+ -C, --custom_mane_pdb_dir Path to directory containing custom MANE PDB structures.
125
+ Default: None
126
+ -f, --custom_mane_metadata_path Path to a dataframe (typically a samplesheet.csv) including
127
+ Ensembl IDs and sequences of the custom pdbs.
128
+ Default: None
121
129
  -d, --distance_threshold INT Distance threshold (Å) for defining residues contacts.
122
130
  Default: 10
123
131
  -c, --cores INT Number of CPU cores for computation.
@@ -128,6 +136,25 @@ Options:
128
136
 
129
137
  For more information on the output of this step, please refer to the [Building Datasets Output Documentation](https://github.com/bbglab/oncodrive3d/tree/master/docs/build_output.md).
130
138
 
139
+ > [!TIP]
140
+ > ### Increasing MANE Structural Coverage
141
+ > To maximize structural coverage of **MANE Select transcripts**, you can predict missing structures locally and integrate them into Oncodrive3D using:
142
+ >
143
+ > - `tools/preprocessing/prepare_samplesheet.py`: a standalone utility that:
144
+ > - Retrieve the full MANE entries from NCBI.
145
+ > - Identifies proteins missing from the AlphaFold MANE dataset.
146
+ > - Generates:
147
+ > - A `samplesheet.csv` with Ensembl protein IDs, FASTA paths, and optional sequences.
148
+ > - Individual FASTA files for each missing protein.
149
+ >
150
+ > - `--custom_mane_pdb_dir`: use this to provide your own predicted PDB structures (e.g., from [nf-core/proteinfold](https://nf-co.re/proteinfold/1.1.1/)).
151
+ >
152
+ > - `--custom_mane_metadata_path`: path to the corresponding `samplesheet.csv`, which must include:
153
+ > - `sequence`: Ensembl protein ID (required)
154
+ > - `refseq`: amino acid sequence (used to inject sequence into PDB if missing)
155
+ >
156
+
157
+
131
158
 
132
159
  ## Running 3D clustering Analysis
133
160
 
@@ -305,4 +332,4 @@ Oncodrive3D is available to the general public subject to certain conditions des
305
332
 
306
333
 
307
334
  ## Citation
308
- If you use Oncodrive3D in your research, please cite the original paper: [Oncodrive3D: Fast and accurate detection of structural clusters of somatic mutations under positive selection](https://www.biorxiv.org/content/10.1101/2025.01.11.632354v2).
335
+ If you use Oncodrive3D in your research, please cite the original paper: [Oncodrive3D: fast and accurate detection of structural clusters of somatic mutations under positive selection](https://academic.oup.com/nar/article/53/15/gkaf776/8234003).
@@ -2,14 +2,15 @@
2
2
 
3
3
  **Oncodrive3D** is a fast and accurate computational method designed to analyze patterns of somatic mutation across tumors, with the goal of identifying **three-dimensional (3D) clusters** of missense mutations and detecting genes under **positive selection**.
4
4
 
5
- The method leverages **AlphaFold 2-predicted protein structures** and Predicted Aligned Error (PAE) to define residue contacts within the protein's 3D space. When available, it integrates **mutational profiles** to build an accurate background model of neutral mutagenesis. By applying a novel **rank-based statistical approach**, Oncodrive3D scores potential 3D clusters and computes empirical p-values."
5
+ The method leverages **AlphaFold 2-predicted protein structures** and Predicted Aligned Error (PAE) to define residue contacts within the protein's 3D space. When available, it integrates **mutational profiles** to build an accurate background model of neutral mutagenesis. By applying a novel **rank-based statistical approach**, Oncodrive3D scores potential 3D clusters and computes empirical p-values.
6
6
 
7
7
  [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
8
8
  [![docker](https://img.shields.io/docker/v/bbglab/oncodrive3d?logo=docker)](https://hub.docker.com/r/bbglab/oncodrive3d)
9
9
  [![PyPI - Version](https://img.shields.io/pypi/v/oncodrive3d?logo=pypi)](https://pypi.org/project/Oncodrive3D/)
10
10
 
11
- ---
11
+ ![Graphical abstract of Oncodrive3D](docs/images/graphical_abstract.png "Oncodrive3D")
12
12
 
13
+ ---
13
14
 
14
15
  ## Requirements
15
16
 
@@ -83,6 +84,13 @@ Options:
83
84
  Default: human
84
85
  -m, --mane Use structures predicted from MANE Select transcripts
85
86
  (applicable to Homo sapiens only).
87
+ -M, --mane_only Use only structures predicted from MANE Select transcripts
88
+ (applicable to Homo sapiens only).
89
+ -C, --custom_mane_pdb_dir Path to directory containing custom MANE PDB structures.
90
+ Default: None
91
+ -f, --custom_mane_metadata_path Path to a dataframe (typically a samplesheet.csv) including
92
+ Ensembl IDs and sequences of the custom pdbs.
93
+ Default: None
86
94
  -d, --distance_threshold INT Distance threshold (Å) for defining residues contacts.
87
95
  Default: 10
88
96
  -c, --cores INT Number of CPU cores for computation.
@@ -93,6 +101,25 @@ Options:
93
101
 
94
102
  For more information on the output of this step, please refer to the [Building Datasets Output Documentation](https://github.com/bbglab/oncodrive3d/tree/master/docs/build_output.md).
95
103
 
104
+ > [!TIP]
105
+ > ### Increasing MANE Structural Coverage
106
+ > To maximize structural coverage of **MANE Select transcripts**, you can predict missing structures locally and integrate them into Oncodrive3D using:
107
+ >
108
+ > - `tools/preprocessing/prepare_samplesheet.py`: a standalone utility that:
109
+ > - Retrieve the full MANE entries from NCBI.
110
+ > - Identifies proteins missing from the AlphaFold MANE dataset.
111
+ > - Generates:
112
+ > - A `samplesheet.csv` with Ensembl protein IDs, FASTA paths, and optional sequences.
113
+ > - Individual FASTA files for each missing protein.
114
+ >
115
+ > - `--custom_mane_pdb_dir`: use this to provide your own predicted PDB structures (e.g., from [nf-core/proteinfold](https://nf-co.re/proteinfold/1.1.1/)).
116
+ >
117
+ > - `--custom_mane_metadata_path`: path to the corresponding `samplesheet.csv`, which must include:
118
+ > - `sequence`: Ensembl protein ID (required)
119
+ > - `refseq`: amino acid sequence (used to inject sequence into PDB if missing)
120
+ >
121
+
122
+
96
123
 
97
124
  ## Running 3D clustering Analysis
98
125
 
@@ -270,4 +297,4 @@ Oncodrive3D is available to the general public subject to certain conditions des
270
297
 
271
298
 
272
299
  ## Citation
273
- If you use Oncodrive3D in your research, please cite the original paper: [Oncodrive3D: Fast and accurate detection of structural clusters of somatic mutations under positive selection](https://www.biorxiv.org/content/10.1101/2025.01.11.632354v2).
300
+ If you use Oncodrive3D in your research, please cite the original paper: [Oncodrive3D: fast and accurate detection of structural clusters of somatic mutations under positive selection](https://academic.oup.com/nar/article/53/15/gkaf776/8234003).
@@ -0,0 +1,19 @@
1
+ # Output
2
+
3
+ After successfully completing this step, the build folder must include the following files and directories:
4
+
5
+ - **pae/**: Directory including the AlphaFold predicted aligned error (PAE) for any protein of the proteome with a length lower than 2700 amino acids
6
+
7
+ - **prob_cmaps/**: Directory including the contact probability map (pCMAPs) for any protein of the proteome
8
+
9
+ - **confidence.csv**: CSV file including per-residue predicted local distance difference test (pLDDT) score for any protein of the proteome
10
+
11
+ - **seq_for_mut_prob.csv**: CSV file including HUGO symbol, Uniprot ID, DNA and protein sequences for any proteine of the proteome
12
+
13
+ - **pdb_structures/**: Directory containing all PDB structure files—both those downloaded from the AlphaFold database and any custom in-house predicted structures (if provided).
14
+
15
+ - **log/**: Directory containing log files produced during the build-datasets execution.
16
+
17
+ - **biomart_metadata.csv**: Metadata file from Ensembl BioMart used to prioritize canonical transcripts when multiple transcripts map to the same protein structure.
18
+
19
+ ...
@@ -176,7 +176,7 @@ It includes the following fields:
176
176
  - **C_pos**: List of protein positions of the gene clusters.
177
177
  - **C_label**: List of labels indicating the clump to which each cluster is grouped.
178
178
  - **Pos_top_vol**: Position of the most significant cluster.
179
- - **Score_obs_sim_top_vol**: 3D clustering score of the gene (normalized 3D clustering score of the residue with the lowest p-value; if multiple residues have the same lowest p-value, the maximum normalized 3D clustering score among those residues is used).
179
+ - **Score_obs_sim_top_vol**: 3D clustering score of the gene (rescaled 3D clustering score of the residue with the lowest p-value; if multiple residues have the same lowest p-value, the maximum rescaled 3D clustering score among those residues is used).
180
180
  - **Mut_in_gene**: Number of missense mutations in the gene.
181
181
  - **Clust_mut**: Number of missense mutations in significant clusters.
182
182
  - **Clust_res**: Number of residues detected as significant clusters.
@@ -227,12 +227,12 @@ It includes the following fields:
227
227
  - **Mut_in_res**: Number of missense mutations in the residue.
228
228
  - **Mut_in_vol**: Number of missense mutations in the volume of the residue.
229
229
  - **Score**: 3D clustering score for the residue.
230
- - **Score_obs_sim**: Normalized 3D clustering score for the residue.
230
+ - **Score_obs_sim**: Rescaled 3D clustering score for the residue.
231
231
  - **pval**: The p-value of the residue in the 3D clustering analysis.
232
232
  - **C**: Binary label indicating whether the cluster at that residue is significant (1) or not (0). A cluster is marked as significant either because it meets the significance criteria directly or because it has been rescued by contributing mutations to another significant cluster.
233
233
  - **C_ext**: Binary label indicating whether the cluster has been rescued by contributing mutations to another significant cluster (1) or if it was significant on its own (0).
234
234
  - **Clump**: Identifier for the clump to which the cluster at that residue has been assigned.
235
- - **Rank**: Rank used to perform the calculation of the normalized 3D clustering score and p-values.
235
+ - **Rank**: Rank used to perform the calculation of the 3D clustering score and p-values.
236
236
  - **Mut_in_cl_vol**: Number of missense mutations in the clusters of the clump to which the cluster has been assigned.
237
237
  - **Res_in_cl**: Positions of the clusters of the clump to which the cluster has been assigned.
238
238
  - **PAE_vol**: Weighted average predicted aligned error (PAE) of the residues in the volume of the cluster.
@@ -29,7 +29,7 @@ Run a test to ensure that everything is set up correctly and functioning as expe
29
29
  nextflow run main.nf -profile test,container --data_dir <build_folder>
30
30
  ```
31
31
 
32
- Replace `<build_folder>` with the path to the Oncodrive3D datasets built in the [building datasets](#building-datasets) step.
32
+ Replace `<build_folder>` with the path to the Oncodrive3D datasets built in the [building datasets](../README.md#building-datasets) step.
33
33
  If you prefer to use Conda, replace `container` in the `-profile` argument with `conda`.
34
34
 
35
35
  ## Usage
@@ -1,2 +1,2 @@
1
1
  __logger_name__ = 'oncodrive3d'
2
- __version__ = "1.0.5"
2
+ __version__ = "1.0.7"
@@ -0,0 +1,173 @@
1
+ """
2
+ Module to generate datasets necessary to run Oncodrive3D.
3
+
4
+ The build is a pipeline that perform the following tasks:
5
+ - Download the PDB structures of the selected proteome
6
+ predicted by AlphaFold 2 from AlphaFold DB.
7
+ - Merge the overlapping structures processed as fragments.
8
+ - Optionally download the PDB structures corresponding to
9
+ the MANE Select transcripts.
10
+ - Optionally copy and parse custom PDB structures.
11
+ - Generate a dataframe including Uniprot_ID, HUGO Symbol,
12
+ protein, DNA sequence, and other gene's information.
13
+ - Extract AlphaFold model confidence (pLDDT).
14
+ - Download AlphaFold predicted aligned error (PAE) from
15
+ AlphaFold DB and convert the files into npy format.
16
+ - Use the PDB structure and PAE to create maps of
17
+ probability of contacts (pCMAPs) for any protein of the
18
+ downloaded proteome with available PAE.
19
+ - Remove unnecessary temp files.
20
+ """
21
+
22
+
23
+ import os
24
+ import daiquiri
25
+
26
+ from scripts import __logger_name__
27
+ from scripts.datasets.af_merge import merge_af_fragments
28
+ from scripts.datasets.get_pae import get_pae
29
+ from scripts.datasets.get_structures import get_structures, mv_mane_pdb
30
+ from scripts.datasets.model_confidence import get_confidence
31
+ from scripts.datasets.parse_pae import parse_pae
32
+ from scripts.datasets.prob_contact_maps import get_prob_cmaps_mp
33
+ from scripts.datasets.seq_for_mut_prob import get_seq_df
34
+ from scripts.datasets.custom_pdb import copy_and_parse_custom_pdbs
35
+ from scripts.datasets.utils import get_species
36
+ from scripts.globals import clean_dir, clean_temp_files
37
+
38
+ logger = daiquiri.getLogger(__logger_name__ + ".build")
39
+
40
+
41
+ def build(output_datasets,
42
+ organism,
43
+ mane,
44
+ mane_only,
45
+ custom_pdb_dir,
46
+ custom_mane_metadata_path,
47
+ distance_threshold,
48
+ num_cores,
49
+ af_version,
50
+ mane_version):
51
+ """
52
+ Build datasets necessary to run Oncodrive3D.
53
+ """
54
+
55
+ # Empty directory
56
+ clean_dir(output_datasets, 'd', txt_file=True)
57
+
58
+ # Download PDB structures
59
+ species = get_species(organism)
60
+ if not mane_only:
61
+ logger.info("Downloading AlphaFold (AF) predicted structures...")
62
+ get_structures(
63
+ path=os.path.join(output_datasets,"pdb_structures"),
64
+ species=species,
65
+ af_version=str(af_version),
66
+ threads=num_cores
67
+ )
68
+ logger.info("Download of structures completed!")
69
+
70
+ # Merge fragmented structures
71
+ logger.info("Merging fragmented structures...")
72
+ merge_af_fragments(input_dir=os.path.join(output_datasets,"pdb_structures"), gzip=True)
73
+
74
+ # Download PDB MANE structures
75
+ if species == "Homo sapiens" and mane:
76
+ logger.info("Downloading AlphaFold (AF) predicted structures overlap with MANE...")
77
+ get_structures(
78
+ path=os.path.join(output_datasets,"pdb_structures_mane"),
79
+ species=species,
80
+ mane=True,
81
+ threads=num_cores
82
+ )
83
+ mv_mane_pdb(output_datasets, "pdb_structures", "pdb_structures_mane")
84
+ logger.info("Download of MANE structures completed!")
85
+
86
+ # Copy custom PDB structures and optinally add SEQRES
87
+ if custom_pdb_dir is not None:
88
+ if custom_mane_metadata_path is None:
89
+ logger.error(
90
+ "custom_mane_metadata_path must be provided when custom_pdb_dir is specified"
91
+ )
92
+ raise ValueError(
93
+ "Both custom_pdb_dir and custom_mane_metadata_path must be provided together"
94
+ )
95
+
96
+ logger.info("Copying custom PDB structures...")
97
+ if os.path.exists(custom_pdb_dir):
98
+ copy_and_parse_custom_pdbs(
99
+ src_dir=custom_pdb_dir,
100
+ dst_dir=os.path.join(output_datasets,"pdb_structures"),
101
+ af_version=int(af_version),
102
+ custom_mane_metadata_path=custom_mane_metadata_path
103
+ )
104
+ else:
105
+ logger.error(f"Custom PDB directory does not exist: {custom_pdb_dir}")
106
+ raise FileNotFoundError(f"Custom PDB directory not found: {custom_pdb_dir}")
107
+
108
+ # Create df including genes and proteins sequences & Hugo to Uniprot_ID mapping
109
+ logger.info("Generating dataframe for genes and proteins sequences...")
110
+ seq_df = get_seq_df(
111
+ datasets_dir=output_datasets,
112
+ output_seq_df=os.path.join(output_datasets, "seq_for_mut_prob.tsv"),
113
+ organism=species,
114
+ mane=mane,
115
+ num_cores=num_cores,
116
+ mane_version=mane_version,
117
+ custom_mane_metadata_path=custom_mane_metadata_path
118
+ )
119
+ logger.info("Generation of sequences dataframe completed!")
120
+
121
+ # Get model confidence
122
+ logger.info("Extracting AF model confidence...")
123
+ get_confidence(
124
+ input=os.path.join(output_datasets, "pdb_structures"),
125
+ output_dir=os.path.join(output_datasets),
126
+ seq_df=seq_df
127
+ )
128
+
129
+ # Get PAE
130
+ logger.info("Downloading AF predicted aligned error (PAE)...")
131
+ get_pae(
132
+ input_dir=os.path.join(output_datasets,"pdb_structures"),
133
+ output_dir=os.path.join(output_datasets,"pae"),
134
+ num_cores=num_cores,
135
+ af_version=str(af_version),
136
+ custom_pdb_dir=custom_pdb_dir
137
+ )
138
+
139
+ # Parse PAE
140
+ logger.info("Parsing PAE...")
141
+ parse_pae(input=os.path.join(output_datasets, 'pae'))
142
+ logger.info("Parsing PAE completed!")
143
+
144
+ # Get pCAMPs
145
+ logger.info("Generating contact probability maps (pCMAPs)..")
146
+ get_prob_cmaps_mp(
147
+ input_pdb=os.path.join(output_datasets, "pdb_structures"),
148
+ input_pae=os.path.join(output_datasets, "pae"),
149
+ output=os.path.join(output_datasets,"prob_cmaps"),
150
+ distance=distance_threshold,
151
+ num_cores=num_cores
152
+ )
153
+ logger.info("Generation pCMAPs completed!")
154
+
155
+ # Clean datasets
156
+ logger.info("Cleaning datasets...")
157
+ clean_temp_files(path=output_datasets)
158
+ logger.info("Datasets cleaning completed!")
159
+ logger.info("Datasets have been successfully built and are ready for analysis!")
160
+
161
+ if __name__ == "__main__":
162
+ build(
163
+ output_datasets="/data/bbg/nobackup/scratch/oncodrive3d/mane_missing/oncodrive3d/datasets/datasets-mane_only-mane_custom-250729",
164
+ organism="Homo sapiens",
165
+ mane=False,
166
+ mane_only=True,
167
+ custom_pdb_dir="/data/bbg/nobackup/scratch/oncodrive3d/mane_missing/data/250724-no_fragments/all_pdbs-pred_and_retrieved/pdbs",
168
+ custom_mane_metadata_path="/data/bbg/nobackup/scratch/oncodrive3d/mane_missing/data/250724-no_fragments/all_pdbs-pred_and_retrieved/samplesheet.csv",
169
+ distance_threshold=10,
170
+ num_cores=8,
171
+ af_version=4,
172
+ mane_version=1.4
173
+ )
@@ -0,0 +1,144 @@
1
+ import os
2
+ import gzip
3
+ import shutil
4
+ import daiquiri
5
+ import pandas as pd
6
+ from scripts.datasets.utils import get_seq_from_pdb
7
+
8
+ from scripts import __logger_name__
9
+
10
+ logger = daiquiri.getLogger(__logger_name__ + ".build.custom_PDB")
11
+
12
+
13
+ # Mapping single-letter to three-letter codes
14
+ one_to_three_res_map = {
15
+ 'A':'ALA','C':'CYS','D':'ASP','E':'GLU','F':'PHE','G':'GLY',
16
+ 'H':'HIS','I':'ILE','K':'LYS','L':'LEU','M':'MET','N':'ASN',
17
+ 'P':'PRO','Q':'GLN','R':'ARG','S':'SER','T':'THR','V':'VAL',
18
+ 'W':'TRP','Y':'TYR'
19
+ }
20
+
21
+
22
+ def get_pdb_seqres_records(lst_res):
23
+ """
24
+ Given a list of three-letter residue names, construct SEQRES records.
25
+
26
+ :param lst_res: List of residue names (e.g. ['MET','ALA',...])
27
+ :return: List of formatted SEQRES lines ending with '\n'
28
+ """
29
+ records = []
30
+ num_res = len(lst_res)
31
+ for i in range(0, num_res, 13):
32
+ block = lst_res[i:i+13]
33
+ rec_num = len(records) + 1
34
+ # Atom name block: up to 13 residues separated by spaces, padded to 51 chars
35
+ residues_str = ' '.join(block)
36
+ records.append(
37
+ f"SEQRES {rec_num:>3} A {num_res:>4} {residues_str:<51}\n"
38
+ )
39
+ return records
40
+
41
+
42
+ def add_seqres_to_pdb(path_pdb: str, residues: list) -> None:
43
+ """
44
+ Insert SEQRES records at the very top of a PDB file (supports gzipped and plain).
45
+
46
+ :param path_pdb: Path to the PDB file to modify in-place (can be .pdb or .pdb.gz)
47
+ :param residues: List of three-letter residue codes for the sequence
48
+ """
49
+ # Choose appropriate open functions
50
+ open_in = gzip.open if path_pdb.endswith('.gz') else open
51
+ mode_in = 'rt' if path_pdb.endswith('.gz') else 'r'
52
+ open_out = gzip.open if path_pdb.endswith('.gz') else open
53
+ mode_out = 'wt' if path_pdb.endswith('.gz') else 'w'
54
+
55
+ # Read original lines
56
+ with open_in(path_pdb, mode_in) as fh:
57
+ lines = fh.readlines()
58
+
59
+ # Generate SEQRES lines
60
+ seqres = get_pdb_seqres_records(residues)
61
+
62
+ # Insert at top
63
+ new_lines = seqres + lines
64
+
65
+ # Write back
66
+ with open_out(path_pdb, mode_out) as fh:
67
+ fh.writelines(new_lines)
68
+
69
+
70
+ def copy_and_parse_custom_pdbs(
71
+ src_dir: str,
72
+ dst_dir: str,
73
+ af_version: int = 4,
74
+ custom_mane_metadata_path: str = None
75
+ ) -> None:
76
+ """
77
+ Copy all .pdb files from src_dir to dst_dir, renaming and gzipping them.
78
+ Optionally add residues information (SEQRES) from samplesheet.
79
+ """
80
+
81
+ # Ensure destination directory exists
82
+ os.makedirs(dst_dir, exist_ok=True)
83
+
84
+ # Handle metadata
85
+ if custom_mane_metadata_path and os.path.isfile(custom_mane_metadata_path):
86
+ samplesheet_df = pd.read_csv(custom_mane_metadata_path)
87
+ else:
88
+ if not custom_mane_metadata_path:
89
+ logger.warning(
90
+ "No samplesheet path provided; skipping SEQRES insertion for accession %s",
91
+ accession
92
+ )
93
+ else:
94
+ logger.warning(
95
+ "samplesheet not found at '%s'; skipping SEQRES insertion for accession %s",
96
+ custom_mane_metadata_path,
97
+ accession
98
+ )
99
+ samplesheet_df = None
100
+
101
+ # Copy and gzip pdb and optionally add REFSEQ
102
+ for fname in os.listdir(src_dir):
103
+ if not fname.endswith('.pdb'):
104
+ continue
105
+
106
+ parts = fname.split('.') # e.g. [ACCESSION, fragment_code, 'alphafold', 'pdb']
107
+ if len(parts) < 4:
108
+ logger.warning(f"Skipping unexpected filename format: {fname}")
109
+ continue
110
+
111
+ accession = parts[0]
112
+ fragment = parts[1]
113
+
114
+ new_name = f'AF-{accession}-F{fragment}-model_v{af_version}.pdb.gz'
115
+
116
+ src_path = os.path.join(src_dir, fname)
117
+ dst_path = os.path.join(dst_dir, new_name)
118
+
119
+ with open(src_path, 'rb') as fin, gzip.open(dst_path, 'wb') as fout:
120
+ shutil.copyfileobj(fin, fout)
121
+
122
+ logger.debug(f'Copied and gzipped: {fname} -> {new_name}')
123
+
124
+ # Optionally add SEQRES records
125
+ if samplesheet_df is not None:
126
+ if accession not in samplesheet_df["sequence"].values:
127
+ logger.warning("Accession %s not in samplesheet %s", accession, custom_mane_metadata_path)
128
+ continue
129
+ seq = samplesheet_df[samplesheet_df["sequence"] == accession].refseq.values[0]
130
+
131
+ if not pd.isna(seq):
132
+ seq = [one_to_three_res_map[aa] for aa in seq]
133
+ add_seqres_to_pdb(path_pdb=dst_path, residues=seq)
134
+ logger.debug(f"Inserted SEQRES records into: {new_name}")
135
+ else:
136
+ try:
137
+ seq = "".join(list(get_seq_from_pdb(dst_path)))
138
+ if not pd.isna(seq):
139
+ logger.debug(f"SEQRES record already present in the structure: {new_name}")
140
+ else:
141
+ logger.warning(f"SEQRES not found in samplesheet and its extraction from structure failed: {new_name}")
142
+ except Exception as e:
143
+ logger.warning(f"SEQRES not found in samplesheet and its extraction from structure failed: {new_name}")
144
+ logger.warning(f"Exception captured: {e}")
@@ -3,6 +3,7 @@ import os
3
3
  import daiquiri
4
4
  import requests
5
5
  import time
6
+ from typing import Optional
6
7
  import concurrent.futures
7
8
  from tqdm import tqdm
8
9
 
@@ -11,14 +12,20 @@ from scripts import __logger_name__
11
12
  logger = daiquiri.getLogger(__logger_name__ + ".build.PAE")
12
13
 
13
14
 
14
- def download_pae(uniprot_id: str, af_version: int, output_dir: str) -> None:
15
+ def download_pae(
16
+ uniprot_id: str,
17
+ af_version: int,
18
+ output_dir: str,
19
+ max_retries: int = 100
20
+ ) -> None:
15
21
  """
16
22
  Download Predicted Aligned Error (PAE) file from AlphaFold DB.
17
23
 
18
24
  Args:
19
- uniprot_id (str): Uniprot ID of the structure.
20
- af_version (int): AlphaFold 2 version.
21
- output_dir (str): Output directory where to download the PAE files.
25
+ uniprot_id: Uniprot ID of the structure.
26
+ af_version: AlphaFold 2 version.
27
+ output_dir: Output directory where to download the PAE files.
28
+ max_retries: Break the loop if the download fails too many times.
22
29
  """
23
30
 
24
31
  file_path = os.path.join(output_dir, f"{uniprot_id}-F1-predicted_aligned_error.json")
@@ -28,6 +35,9 @@ def download_pae(uniprot_id: str, af_version: int, output_dir: str) -> None:
28
35
  status = "INIT"
29
36
  while status != "FINISHED":
30
37
  i += 1
38
+ if i > max_retries:
39
+ logger.warning(f"Reached {max_retries} attempts; proceeding without download.")
40
+ break
31
41
  if status != "INIT":
32
42
  time.sleep(30)
33
43
  try:
@@ -43,7 +53,13 @@ def download_pae(uniprot_id: str, af_version: int, output_dir: str) -> None:
43
53
  logger.debug(f"Request failed {e}: Retrying")
44
54
 
45
55
 
46
- def get_pae(input_dir: str, output_dir: str, num_cores: int, af_version: int = 4) -> None:
56
+ def get_pae(
57
+ input_dir: str,
58
+ output_dir: str,
59
+ num_cores: int,
60
+ af_version: int = 4,
61
+ custom_pdb_dir: Optional[str] = None
62
+ ) -> None:
47
63
  """
48
64
  Download Predicted Aligned Error (PAE) files for all non-fragmented PDB
49
65
  structures in the input directory.
@@ -53,6 +69,7 @@ def get_pae(input_dir: str, output_dir: str, num_cores: int, af_version: int = 4
53
69
  output_dir (str): Output directory where to download the PAE files.
54
70
  num_cores (int): Number of cores for multithreading download.
55
71
  af_version (int): AlphaFold 2 version (default is 4).
72
+ custom_pdb_dir (str | None): Directory including provided custom PDB structures.
56
73
  """
57
74
 
58
75
  if not os.path.exists(output_dir):
@@ -62,9 +79,14 @@ def get_pae(input_dir: str, output_dir: str, num_cores: int, af_version: int = 4
62
79
  if os.path.exists(checkpoint):
63
80
  logger.debug("PAE already downloaded: Skipping...")
64
81
  return
65
-
82
+
66
83
  pdb_files = [file for file in os.listdir(input_dir) if file.startswith("AF-") and file.endswith(f"-model_v{af_version}.pdb.gz")]
67
84
  uniprot_ids = [pdb_file.split("-")[1] for pdb_file in pdb_files]
85
+
86
+ # Do not download PAE for custom provided structures
87
+ if custom_pdb_dir is not None:
88
+ custom_uniprot_ids = [fname.split('.')[0] for fname in os.listdir(custom_pdb_dir) if fname.endswith('.pdb')]
89
+ uniprot_ids = [uni_id for uni_id in uniprot_ids if uni_id not in custom_uniprot_ids]
68
90
 
69
91
  with concurrent.futures.ThreadPoolExecutor(max_workers=num_cores) as executor:
70
92
  tasks = [executor.submit(download_pae, uniprot_id, af_version, output_dir) for uniprot_id in uniprot_ids]
@@ -22,8 +22,8 @@ def mv_mane_pdb(path_datasets, pdb_dir, mane_pdb_dir) -> None:
22
22
 
23
23
  path_pdb = os.path.join(path_datasets, pdb_dir)
24
24
  path_mane_pdb = os.path.join(path_datasets, mane_pdb_dir)
25
- if not os.path.exists(path_mane_pdb):
26
- os.makedirs(path_mane_pdb)
25
+ for path in (path_pdb, path_mane_pdb):
26
+ os.makedirs(path, exist_ok=True)
27
27
 
28
28
  # Move MANE structures
29
29
  for filename in [file for file in os.listdir(path_mane_pdb) if file.endswith(".pdb.gz") or file.endswith(".pdb")]:
@@ -66,6 +66,8 @@ def get_structures(path: str,
66
66
  # Select proteome
67
67
  if mane:
68
68
  if species == "Homo sapiens":
69
+ if str(af_version) not in {"3", "4"}:
70
+ raise RuntimeError("AlphaFold MANE overlaps are available only for versions 3 or 4. When using --mane or --mane_only, please set --af_version to 3 or 4.")
69
71
  proteome = f"mane_overlap_v{af_version}"
70
72
  else:
71
73
  raise RuntimeError("Structures with MANE transcripts overlap are available only for 'Homo sapiens'. Exiting...")
@@ -78,7 +80,7 @@ def get_structures(path: str,
78
80
  raise RuntimeError(f"Failed to recognize '{species}' as organism. Currently accepted ones are 'Homo sapiens' and 'Mus musculus'. Exiting...")
79
81
 
80
82
  logger.debug(f"Proteome to download: {proteome}")
81
- af_url = f"https://ftp.ebi.ac.uk/pub/databases/alphafold/latest/{proteome}.tar"
83
+ af_url = f"https://ftp.ebi.ac.uk/pub/databases/alphafold/v{af_version}/{proteome}.tar"
82
84
  file_path = os.path.join(path, f"{proteome}.tar")
83
85
 
84
86
  try: