papyrus-scripts 2.1.0__tar.gz → 2.1.2__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 (30) hide show
  1. {papyrus_scripts-2.1.0/src/papyrus_scripts.egg-info → papyrus_scripts-2.1.2}/PKG-INFO +8 -7
  2. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/README.md +4 -4
  3. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/setup.cfg +1 -1
  4. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/__init__.py +1 -1
  5. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/oop.py +278 -27
  6. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/preprocess.py +7 -7
  7. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2/src/papyrus_scripts.egg-info}/PKG-INFO +8 -7
  8. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts.egg-info/requires.txt +1 -1
  9. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/tests/test_oop.py +2 -2
  10. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/LICENSE +0 -0
  11. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/setup.py +0 -0
  12. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/__main__.py +0 -0
  13. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/cli.py +0 -0
  14. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/download.py +0 -0
  15. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/fingerprint.py +0 -0
  16. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/matchRCSB.py +0 -0
  17. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/modelling.py +0 -0
  18. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/neuralnet.py +0 -0
  19. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/reader.py +0 -0
  20. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/subsim_search.py +0 -0
  21. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/IO.py +0 -0
  22. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/UniprotMatch.py +0 -0
  23. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/__init__.py +0 -0
  24. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/aliases.json +0 -0
  25. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/links.json +0 -0
  26. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts/utils/mol_reader.py +0 -0
  27. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts.egg-info/SOURCES.txt +0 -0
  28. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts.egg-info/dependency_links.txt +0 -0
  29. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts.egg-info/entry_points.txt +0 -0
  30. {papyrus_scripts-2.1.0 → papyrus_scripts-2.1.2}/src/papyrus_scripts.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: papyrus_scripts
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: A collection of scripts to handle the Papyrus bioactivity dataset
5
5
  Home-page: https://github.com/OlivierBeq/Papyrus-scripts
6
6
  Author: Olivier J. M. Béquignon - Brandon J. Bongers - Willem Jespers
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.7
17
17
  Classifier: Programming Language :: Python :: 3.6
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: numpy
20
+ Requires-Dist: numpy>=2.0.0
21
21
  Requires-Dist: pandas
22
22
  Requires-Dist: rdkit
23
23
  Requires-Dist: requests
@@ -36,6 +36,7 @@ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
36
36
  Provides-Extra: testing
37
37
  Requires-Dist: pytest; extra == "testing"
38
38
  Requires-Dist: parameterized; extra == "testing"
39
+ Dynamic: license-file
39
40
 
40
41
  # Papyrus-scripts
41
42
 
@@ -168,10 +169,10 @@ Different online servers host the Papyrus data based on release and ChEMBL versi
168
169
 
169
170
  | Papyrus version | ChEMBL version | Zenodo | 4TU |
170
171
  |:---------------:|:--------------:|:---------------------------------------------------------:|:---------------------------------------------------------:|
171
- | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10944245) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
172
- | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/10943207) | :x: |
173
- | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7821775) | :x: |
174
- | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787634) | :x: |
172
+ | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10943992) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
173
+ | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/7019873) | :x: |
174
+ | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7373213) | :x: |
175
+ | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787633) | :x: |
175
176
 
176
177
  Precomputed molecular and protein descriptors along with molecular structures (2D for default set and 3D for low quality set with stereochemistry) are not available for version 05.4 from 4TU but are from Google Drive.
177
178
 
@@ -129,10 +129,10 @@ Different online servers host the Papyrus data based on release and ChEMBL versi
129
129
 
130
130
  | Papyrus version | ChEMBL version | Zenodo | 4TU |
131
131
  |:---------------:|:--------------:|:---------------------------------------------------------:|:---------------------------------------------------------:|
132
- | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10944245) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
133
- | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/10943207) | :x: |
134
- | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7821775) | :x: |
135
- | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787634) | :x: |
132
+ | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10943992) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
133
+ | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/7019873) | :x: |
134
+ | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7373213) | :x: |
135
+ | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787633) | :x: |
136
136
 
137
137
  Precomputed molecular and protein descriptors along with molecular structures (2D for default set and 3D for low quality set with stereochemistry) are not available for version 05.4 from 4TU but are from Google Drive.
138
138
 
@@ -32,7 +32,7 @@ packages = find:
32
32
  package_dir =
33
33
  = src
34
34
  install_requires =
35
- numpy
35
+ numpy>=2.0.0
36
36
  pandas
37
37
  rdkit
38
38
  requests
@@ -20,4 +20,4 @@ from .utils.IO import PapyrusVersion
20
20
 
21
21
  from .oop import PapyrusDataset
22
22
 
23
- __version__ = '2.1.0'
23
+ __version__ = '2.1.2'
@@ -5,6 +5,7 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  import os
8
+ from abc import ABC
8
9
  from typing import Any, Dict, Iterator, List, Union, Optional
9
10
 
10
11
  import pystow
@@ -26,6 +27,15 @@ class PapyrusDataset:
26
27
  def __init__(self, version: str | IO.PapyrusVersion = 'latest', is3d: bool = False, plusplus: bool = True,
27
28
  chunksize: Optional[int] = 1_000_000, source_path: Optional[str] = None,
28
29
  download_progress: bool = False):
30
+ """Read, filter and aggregate data from a release of the Papyrus dataset.
31
+
32
+ :param version: version to be used. Either a `PapyrusVersion` or a `str` object to be passed to `PapyrusVersion` (default : 'latest')
33
+ :param is3d: should the lower-quality data with stereochemistry be read (default: False)
34
+ :param plusplus: read the Papyrus++ curated subset of even higher quality (defulat: True)
35
+ :param chunksize: number of lines per chunk. To read without chunks (not recommended) set to None (default: 1_000_000)
36
+ :param source_path: folder containing the bioactivity dataset (default: pystow's home folder)
37
+ :param download_progress: if the data not be on disk, should progress of the download be shown
38
+ """
29
39
  version = IO.PapyrusVersion(version=version)
30
40
  if not IO.is_local_version_available(version=version.version_old_fmt, root_folder=source_path):
31
41
  download.download_papyrus(outdir=source_path, version=version.version_old_fmt, nostereo=True, stereo=True,
@@ -48,13 +58,24 @@ class PapyrusDataset:
48
58
  is3d: bool, version: str,
49
59
  plusplus: bool = True,
50
60
  source_path: Optional[str] = None,
51
- download_progress: bool = False
61
+ download_progress: bool = False,
62
+ chunksize: int = None
52
63
  ) -> PapyrusDataset:
64
+ """Create a PapyrusDataset from a pandas DataFrame.
65
+
66
+ :param df: pandas DataFrame containing filtered Papyrus data samples (must preserve all default columns)
67
+ :param is3d: are the samples derived from the lower-quality data with stereochemistry
68
+ :param version: version of the dataset the samples were obtained from
69
+ :param plusplus: whether the samples were derived from Papyrus++
70
+ :param source_path: folder containing the original bioactivity dataset (default: pystow's home folder)
71
+ :param download_progress: if the data was not on disk, was progress of the download shown
72
+ :return: the `PapyrusDataset` object corresponding to the given samples
73
+ """
53
74
  dataset = PapyrusDataset.__new__(PapyrusDataset)
54
75
  dataset.papyrus_bioactivity_data = df
55
76
  dataset.papyrus_protein_data = reader.read_protein_set(source_path=source_path, version=version)
56
77
  dataset.papyrus_params = dict(is3d=is3d, version=version, plusplus=plusplus,
57
- chunksize=len(df), source_path=source_path, num_rows=len(df),
78
+ chunksize=chunksize, source_path=source_path, num_rows=len(df),
58
79
  download_progress=download_progress)
59
80
  dataset._can_reset = False
60
81
  return dataset
@@ -64,6 +85,14 @@ class PapyrusDataset:
64
85
  papyrus_protein_data: pd.DataFrame,
65
86
  papyrus_params: Dict
66
87
  ) -> PapyrusDataset:
88
+ """Create a PapyrusDataset from a bioactivities, proteins and initial parameters.
89
+
90
+ :param papyrus_bioactivity_data: bioactivity information obtained from a PapyrusDataset object
91
+ :param papyrus_protein_data: protein information obtained from a PapyrusDataset object
92
+ :param papyrus_params: parameters of the PapyrusDataset object `papyrus_bioactivity_data` and
93
+ `papyrus_protein_data` were obtained from
94
+ :return: the `PapyrusDataset` object corresponding to the given data
95
+ """
67
96
  dataset = PapyrusDataset.__new__(PapyrusDataset)
68
97
  dataset.papyrus_bioactivity_data = papyrus_bioactivity_data
69
98
  dataset.papyrus_protein_data = papyrus_protein_data
@@ -73,12 +102,14 @@ class PapyrusDataset:
73
102
 
74
103
  @property
75
104
  def _filter(self) -> PapyrusDataFilter:
105
+ """Create a PapyrusDataFilter object around the current dataset."""
76
106
  return PapyrusDataFilter(papyrus_bioactivity_data=self.papyrus_bioactivity_data,
77
107
  papyrus_protein_data=self.papyrus_protein_data,
78
108
  papyrus_params=self.papyrus_params)
79
109
 
80
110
  @property
81
111
  def _fpsubsim2(self) -> FPSubSim2Engine:
112
+ """Obtain the FPSubSim2Engine for substructure and similarity searches."""
82
113
  if self._fpsubsim2_ is None:
83
114
  self._fpsubsim2_ = FPSubSim2Engine(self.papyrus_params)
84
115
  self._fpsubsim2_._set_data(papyrus_bioactivity_data=self.papyrus_bioactivity_data,
@@ -86,56 +117,121 @@ class PapyrusDataset:
86
117
  return self._fpsubsim2_
87
118
 
88
119
  def keep_quality(self, min_quality: str) -> PapyrusDataset:
120
+ """Keep samples whose quality is at least the one supplied (e.g. 'medium' for both medium and high-quality)."""
89
121
  return self._filter.keep_quality(min_quality=min_quality)
90
122
 
91
123
  def keep_source(self, source: Union[List[str], str]) -> PapyrusDataset:
124
+ """Keep samples of specific data source(s) (e.g. 'chembl' or ['chembl', 'klaeger'])."""
92
125
  return self._filter.keep_source(source=source)
93
126
 
94
127
  def keep_activity_type(self, activity_types: Union[List[str], str]) -> PapyrusDataset:
128
+ """Keep samples of specific activity type(s) (e.g. 'ic50' or ['ki', 'ec50'])."""
95
129
  return self._filter.keep_activity_type(activity_types=activity_types)
96
130
 
97
131
  def keep_accession(self, accession: Union[List[str], str] = 'all') -> PapyrusDataset:
132
+ """Keep samples of specific accession(s) (e.g. 'P00533' or ['P11362', 'P35968'])."""
98
133
  return self._filter.keep_accession(accession=accession)
99
134
 
100
135
  def keep_protein_class(self, classes: Optional[Union[dict, List[dict]]],
101
136
  generic_regex: bool = False) -> PapyrusDataset:
137
+ """Keep samples whose protein targets belong to a specific protein class(es).
138
+
139
+ :param classes: protein class(es) (e.g. {'l2': 'Kinase'} or [{'l2': 'Kinase'}, {'l1': 'Membrane receptor'}]).
140
+ :param generic_regex: should the generic pattern 'l?' be considered as a regex, allowing for partial matching.
141
+ """
102
142
  return self._filter.keep_protein_class(classes=classes, generic_regex=generic_regex)
103
143
 
104
144
  def keep_organism(self, organism: Optional[Union[str, List[str]]],
105
145
  generic_regex: bool = False) -> PapyrusDataset:
146
+ """Keep samples whose protein targets belong to specific organisms.
147
+
148
+ :param organism: organism (e.g. 'Homo sapiens (Human)' or ['Bos taurus (Bovine)', 'Rattus norvegicus (Rat)'])
149
+ :param generic_regex: should partial matching be considered (e.g. 'human' or 'Pig')
150
+ """
106
151
  return self._filter.keep_organism(organism=organism, generic_regex=generic_regex)
107
152
 
108
153
  def contains(self, column: str, value: str, case: bool = True, regex: bool = False) -> PapyrusDataset:
154
+ """Keep samples with the specified field corresponding to the given value.
155
+
156
+ :param column: column to be checked for `value` in order for samples to be included
157
+ :param value: value the column must match
158
+ :param case: should the value matching be case-sensitive (default: True)
159
+ :param regex: should the given `value` be interpreted as a regular expression
160
+ """
109
161
  return self._filter.contains(column=column, value=value, case=case, regex=regex)
110
162
 
111
163
  def not_contains(self, column: str, value: str, case: bool = True, regex: bool = False) -> PapyrusDataset:
164
+ """Keep samples whose specified field not corresponding to the given value (opposite of the `contains` method).
165
+
166
+ :param column: column to be checked for `value` in order for samples to be included
167
+ :param value: value the column must match
168
+ :param case: should the value matching be case-sensitive (default: True)
169
+ :param regex: should the given `value` be interpreted as a regular expression
170
+ """
112
171
  return self._filter.not_contains(column=column, value=value, case=case, regex=regex)
113
172
 
114
173
  def isin(self, column: str, values: Union[Any, List[Any]]) -> PapyrusDataset:
174
+ """Keep samples whose value of the specified field is in the given options.
175
+
176
+ :param column: column to be checked for `values` in order for samples to be included
177
+ :param values: values the column must contain
178
+ """
115
179
  return self._filter.isin(column=column, values=values)
116
180
 
117
181
  def not_isin(self, column: str, values: Union[Any, List[Any]]) -> PapyrusDataset:
182
+ """Keep samples whose value of the specified field is not in the given options.
183
+
184
+ :param column: column to be checked for `values` in order for samples to be included
185
+ :param values: values the column must not contain
186
+ """
118
187
  return self._filter.not_isin(column=column, values=values)
119
188
 
120
189
  def keep_similar_molecules(self, smiles: Union[str, List[str]],
121
190
  fingerprint: fingerprint.Fingerprint = fingerprint.MorganFingerprint(),
122
191
  threshold: float = 0.7, cuda: bool = False) -> PapyrusDataset:
192
+ """Keep samples whose molecular structures are similar to any of the given SMILES.
193
+
194
+ :param smiles: SMILES the molecular structures must be similar to
195
+ :param fingerprint: type of fingerprint (subclass of `papyrus_scripts.fingerprint.Fingerprint`; default: `papyrus_scripts.fingerprint.MorganFingerprint`)
196
+ :param threshold: threshold of similarity to one of the given SMILES for a molecule to be considered a hit (default: 0.7)
197
+ :param cuda: should CUDA acceleration be used (default: False)
198
+ """
123
199
  return self._fpsubsim2.keep_similar_molecules(smiles=smiles, fingerprint=fingerprint, threshold=threshold,
124
200
  cuda=cuda)
125
201
 
126
202
  def keep_dissimilar_molecules(self, smiles: Union[str, List[str]],
127
203
  fingerprint: fingerprint.Fingerprint = fingerprint.MorganFingerprint(),
128
204
  threshold: float = 0.7, cuda: bool = False) -> PapyrusDataset:
205
+ """Keep samples whose molecular structures are not similar to any of the given SMILES.
206
+
207
+ :param smiles: SMILES the molecular structures must not be similar to
208
+ :param fingerprint: type of fingerprint (subclass of `papyrus_scripts.fingerprint.Fingerprint`; default: `papyrus_scripts.fingerprint.MorganFingerprint`)
209
+ :param threshold: threshold of similarity to one of the given SMILES for a molecule to be considered a hit (default: 0.7)
210
+ :param cuda: should CUDA acceleration be used (default: False)
211
+ """
129
212
  return self._fpsubsim2.keep_dissimilar_molecules(smiles=smiles, fingerprint=fingerprint, threshold=threshold,
130
213
  cuda=cuda)
131
214
 
132
215
  def keep_substructure_molecules(self, smiles: Union[str, List[str]]) -> PapyrusDataset:
216
+ """Keep samples whose molecular structures are substructures of any of the provided SMILES.
217
+
218
+ :param smiles: SMILES the molecular structures must not be substructures of
219
+ """
133
220
  return self._fpsubsim2.keep_substructure_molecules(smiles=smiles)
134
221
 
135
222
  def keep_not_substructure_molecules(self, smiles: Union[str, List[str]]) -> PapyrusDataset:
223
+ """Keep samples whose molecular structures are not substructures of any of the provided SMILES.
224
+
225
+ :param smiles: SMILES the molecular structures must not be substructures of
226
+ """
136
227
  return self._fpsubsim2.keep_not_substructure_molecules(smiles=smiles)
137
228
 
138
229
  def aggregate(self, progress: bool = False) -> pd.DataFrame:
230
+ """Aggregate the data in a PapyrusDataset to a pandas DataFrame.
231
+
232
+ :param progress: should filtering progress be shown
233
+ :return: a pandas DataFrame of the filtered data.
234
+ """
139
235
  total = (-(-self.papyrus_params['num_rows'] // self.papyrus_params['chunksize'])
140
236
  if self.papyrus_params['chunksize'] is not None
141
237
  else None)
@@ -145,15 +241,35 @@ class PapyrusDataset:
145
241
  progress=progress, total=total)
146
242
 
147
243
  def agg(self, progress: bool = False) -> pd.DataFrame:
244
+ """Aggregate the data in a PapyrusDataset to a pandas DataFrame (synonym of PapyrusDataset.aggregate).
245
+
246
+ :param progress: should filtering progress be shown
247
+ :return: a pandas DataFrame of the filtered data.
248
+ """
148
249
  return self.aggregate(progress=progress)
149
250
 
150
251
  def consume_chunks(self, progress: bool = False) -> pd.DataFrame:
252
+ """Aggregate the data in a PapyrusDataset to a pandas DataFrame (synonym of PapyrusDataset.aggregate).
253
+
254
+ :param progress: should filtering progress be shown
255
+ :return: a pandas DataFrame of the filtered data.
256
+ """
151
257
  return self.aggregate(progress=progress)
152
258
 
153
259
  def to_dataframe(self, progress: bool = False) -> pd.DataFrame:
260
+ """Aggregate the data in a PapyrusDataset to a pandas DataFrame (synonym of PapyrusDataset.aggregate).
261
+
262
+ :param progress: should filtering progress be shown
263
+ :return: a pandas DataFrame of the filtered data.
264
+ """
154
265
  return self.aggregate(progress=progress)
155
266
 
156
267
  def molecules(self, chunksize: Optional[int] = 1_000_000, progress: bool = False) -> PapyrusMoleculeSet:
268
+ """Get the structures of the molecules corresponding to the samples in the current PapyrusDataset.
269
+
270
+ :param chunksize: number of molecules to be loaded at once. To read without chunks (not recommended) set to None (default: 1_000_000).
271
+ :param progress: should progress of molecule aggregation be shown.
272
+ """
157
273
  ids = self.aggregate(progress=progress)['connectivity' if not self.papyrus_params['is3d'] else 'InChIKey'].unique()
158
274
  molecules = reader.read_molecular_structures(is3d=self.papyrus_params['is3d'],
159
275
  version=self.papyrus_params['version'],
@@ -163,12 +279,24 @@ class PapyrusDataset:
163
279
  return PapyrusMoleculeSet(molecules, {**self.papyrus_params, 'chunksize': chunksize})
164
280
 
165
281
  def proteins(self, progress: bool = False) -> PapyrusProteinSet:
282
+ """Get the protein targets corresponding to the samples in the current PapyrusDataset.
283
+
284
+ :param progress: should progress of molecule aggregation be shown.
285
+ """
166
286
  ids = self.aggregate(progress=progress)['target_id'].unique()
167
287
  proteins = self.papyrus_protein_data[self.papyrus_protein_data.target_id.isin(ids)]
168
288
  return PapyrusProteinSet(proteins, self.papyrus_params,
169
289
  len(proteins))
170
290
 
171
291
  def match_rcsb_pdb(self, update: bool = True, progress: bool = False) -> PapyrusPDBProteinSet:
292
+ """Get the protein 3D structures from the RCSB Protein Data Bank of both protein targets and molecules
293
+ corresponding to the samples in the current PapyrusDataset .
294
+
295
+
296
+ :param update: should the local cache of PDB identifiers be updated (default: False).
297
+ :param progress: should progress of molecule aggregation be shown
298
+ :return: should progress of 3D structure aggregation be shown.
299
+ """
172
300
  total = (-(-self.papyrus_params['num_rows'] // self.papyrus_params['chunksize'])
173
301
  if self.papyrus_params['chunksize'] is not None
174
302
  else None)
@@ -206,6 +334,23 @@ class PapyrusDataset:
206
334
  force: bool = False,
207
335
  progress: bool = True,
208
336
  source_path: Optional[str] = None) -> None:
337
+ """Remove the data of the Papyrus dataset with multiple levels of deletion.
338
+
339
+ :param version: version to delete
340
+ :param remove_papyruspp: should Papyrus++ be removed
341
+ :param remove_bioactivities: should bioactivity data be removed
342
+ :param remove_proteins: should protein data be removed
343
+ :param remove_nostereo: should the stereochemistry-agnostic data be removed
344
+ :param remove_stereo: should the stereochemistry-aware data be removed
345
+ :param remove_structures: should molecular structures be removed
346
+ :param remove_descriptors: should molecular descriptors be removed
347
+ :param remove_other_files: should any additional file be removed
348
+ :param remove_version_root: should all the data of that version be removed
349
+ :param remove_papyrus_root: should all data all versions combined be removed
350
+ :param force: avoid asking for confirmation
351
+ :param progress: should deletion progress
352
+ :param source_path: folder containing the bioactivity dataset (default: pystow's home folder)
353
+ """
209
354
  download.remove_papyrus(outdir=source_path, version=version, papyruspp=remove_papyruspp,
210
355
  bioactivities=remove_bioactivities, proteins=remove_proteins,
211
356
  nostereo=remove_nostereo, stereo=remove_stereo, structures=remove_structures,
@@ -213,6 +358,32 @@ class PapyrusDataset:
213
358
  version_root=remove_version_root, papyrus_root=remove_papyrus_root,
214
359
  force=force, progress=progress)
215
360
 
361
+ def molecular_descriptors(self, desc_type: str, progress: bool = False) -> pd.DataFrame | Iterator[pd.DataFrame]:
362
+ """Obtain the molecular descriptors of the molecules in the current PapyrusMoleculeSet.
363
+
364
+ :param desc_type: type of descriptor to be obtained. One of {'mold2', 'mordred', 'cddd', 'fingerprint', 'moe', 'all'}
365
+ :param progress: should aggregation progress be shown
366
+ :return: a pandas DataFrame of the molecular descriptors.
367
+ """
368
+ ids = self.aggregate(progress)['connectivity' if not self.papyrus_params['is3d'] else 'InChIKey'].unique()
369
+ # Handle descriptors not yet downloaded
370
+ try:
371
+ return reader.read_molecular_descriptors(desc_type=desc_type,
372
+ is3d=self.papyrus_params['is3d'],
373
+ version=self.papyrus_params['version'],
374
+ chunksize=self.papyrus_params['chunksize'],
375
+ source_path=self.papyrus_params['source_path'],
376
+ ids=ids,
377
+ verbose=progress)
378
+ except FileNotFoundError:
379
+ download.download_papyrus(outdir=self.papyrus_params['source_path'],
380
+ version=self.papyrus_params['version'],
381
+ nostereo=not self.papyrus_params['is3d'], stereo=self.papyrus_params['is3d'],
382
+ only_pp=self.papyrus_params['plusplus'], structures=False,
383
+ descriptors=desc_type, progress=self.papyrus_params['download_progress'],
384
+ disk_margin=0.0)
385
+ return self.molecular_descriptors(desc_type, progress)
386
+
216
387
  class PapyrusDataFilter:
217
388
  """Collection of filters to be applied on a PapyrusDataset instance."""
218
389
 
@@ -426,6 +597,11 @@ class PapyrusMoleculeSet:
426
597
  root_folder=self.papyrus_params['source_path'])
427
598
 
428
599
  def to_dataframe(self, progress: bool = False):
600
+ """Aggregate the data in a PapyrusMoleculeSet to a pandas DataFrame (synonym of `PapyrusMoleculeSet.aggregate`).
601
+
602
+ :param progress: should aggregation progress be shown
603
+ :return: a pandas DataFrame of the molecules.
604
+ """
429
605
  if isinstance(self.data, Iterator):
430
606
  return self.aggregate(progress=progress)
431
607
  return self.data
@@ -435,8 +611,14 @@ class PapyrusMoleculeSet:
435
611
  return f'{type(self).__name__}<iterator of molecules>'
436
612
  return f'{type(self).__name__}<{len(self.data)} molecules>'
437
613
 
438
- def molecular_descriptors(self, desc_type: str, progress: bool = False) -> pd.DataFrame:
439
- ids = self.aggregate(progress)['connectivity' if self.papyrus_params['is3d'] else 'InChIKey'].unique()
614
+ def molecular_descriptors(self, desc_type: str, progress: bool = False) -> pd.DataFrame | Iterator[pd.DataFrame]:
615
+ """Obtain the molecular descriptors of the molecules in the current PapyrusMoleculeSet.
616
+
617
+ :param desc_type: type of descriptor to be obtained. One of {'mold2', 'mordred', 'cddd', 'fingerprint', 'moe', 'all'}
618
+ :param progress: should aggregation progress be shown
619
+ :return: a pandas DataFrame of the molecular descriptors.
620
+ """
621
+ ids = self.aggregate(progress)['connectivity' if not self.papyrus_params['is3d'] else 'InChIKey'].unique()
440
622
  # Handle descriptors not yet downloaded
441
623
  try:
442
624
  return reader.read_molecular_descriptors(desc_type=desc_type,
@@ -456,6 +638,11 @@ class PapyrusMoleculeSet:
456
638
  return self.molecular_descriptors(desc_type, progress)
457
639
 
458
640
  def aggregate(self, progress: bool = False) -> pd.DataFrame:
641
+ """Aggregate the data in a PapyrusMoleculeSet to a pandas DataFrame.
642
+
643
+ :param progress: should aggregation progress be shown
644
+ :return: a pandas DataFrame of the molecules.
645
+ """
459
646
  total = (-(-self.num_rows // self.papyrus_params['chunksize'])
460
647
  if self.papyrus_params['chunksize'] is not None
461
648
  else None)
@@ -465,12 +652,56 @@ class PapyrusMoleculeSet:
465
652
  progress=progress, total=total)
466
653
 
467
654
  def agg(self, progress: bool = False) -> pd.DataFrame:
655
+ """Aggregate the data in a PapyrusMoleculeSet to a pandas DataFrame (synonym of `PapyrusMoleculeSet.aggregate`).
656
+
657
+ :param progress: should aggregation progress be shown
658
+ :return: a pandas DataFrame of the molecules.
659
+ """
468
660
  return self.aggregate(progress=progress)
469
661
 
470
662
  def consume_chunks(self, progress: bool = False) -> pd.DataFrame:
663
+ """Aggregate the data in a PapyrusMoleculeSet to a pandas DataFrame (synonym of `PapyrusMoleculeSet.aggregate`).
664
+
665
+ :param progress: should aggregation progress be shown
666
+ :return: a pandas DataFrame of the molecules.
667
+ """
471
668
  return self.aggregate(progress=progress)
472
669
 
473
- class PapyrusProteinSet:
670
+
671
+ class ProteinSet(ABC):
672
+ """Abstract class."""
673
+
674
+ def protein_descriptors(self,
675
+ desc_type: Union[str, prodec.Descriptor, prodec.Transform],
676
+ progress: bool = False
677
+ ) -> pd.DataFrame:
678
+ """Obtain the protein descriptors of the protein targets in the current PapyrusPDBProteinSet.
679
+
680
+ :param desc_type: type of protein descriptor to be obtained. Either 'unirep' or a `ProDEC.Descriptor` or `ProDEC.Transform`.
681
+ :param progress: should aggregation progress be shown
682
+ :return: a pandas DataFrame of the protein descriptors.
683
+ """
684
+ self.data = self.aggregate(progress)
685
+ ids = self.data['target_id'].unique()
686
+ try:
687
+ return reader.read_protein_descriptors(desc_type=desc_type,
688
+ is3d=self.papyrus_params['is3d'],
689
+ version=self.papyrus_params['version'],
690
+ chunksize=self.papyrus_params['chunksize'],
691
+ source_path=self.papyrus_params['source_path'],
692
+ ids=ids,
693
+ verbose=progress)
694
+ except FileNotFoundError:
695
+ download.download_papyrus(outdir=self.papyrus_params['source_path'],
696
+ version=self.papyrus_params['version'],
697
+ nostereo=not self.papyrus_params['is3d'], stereo=self.papyrus_params['is3d'],
698
+ only_pp=self.papyrus_params['plusplus'], structures=False,
699
+ descriptors=desc_type, progress=self.papyrus_params['download_progress'],
700
+ disk_margin=0.0)
701
+ return self.protein_descriptors(desc_type, progress)
702
+
703
+
704
+ class PapyrusProteinSet(ProteinSet):
474
705
  def __init__(self, df: Union[pd.DataFrame, Iterator], papyrus_params: Dict, num_proteins: int):
475
706
  self.data = df
476
707
  self.papyrus_params = papyrus_params
@@ -482,6 +713,11 @@ class PapyrusProteinSet:
482
713
  return f'{type(self).__name__}<{len(self.data)} proteins>'
483
714
 
484
715
  def to_dataframe(self, progress: bool = False) -> pd.DataFrame:
716
+ """Aggregate the data in a PapyrusProteinSet to a pandas DataFrame (synonym of `PapyrusProteinSet.aggregate`).
717
+
718
+ :param progress: should aggregation progress be shown
719
+ :return: a pandas DataFrame of the proteins.
720
+ """
485
721
  if isinstance(self.data, Iterator):
486
722
  total = (-(-self.num_rows // self.papyrus_params['chunksize'])
487
723
  if self.papyrus_params['chunksize'] is not None
@@ -490,15 +726,31 @@ class PapyrusProteinSet:
490
726
  return self.data
491
727
 
492
728
  def aggregate(self, progress: bool = False) -> pd.DataFrame:
729
+ """Aggregate the data in a PapyrusProteinSet to a pandas DataFrame.
730
+
731
+ :param progress: should aggregation progress be shown
732
+ :return: a pandas DataFrame of the proteins.
733
+ """
493
734
  return self.to_dataframe(progress)
494
735
 
495
736
  def agg(self, progress: bool = False) -> pd.DataFrame:
737
+ """Aggregate the data in a PapyrusProteinSet to a pandas DataFrame.
738
+
739
+ :param progress: should aggregation progress be shown
740
+ :return: a pandas DataFrame of the proteins.
741
+ """
496
742
  return self.to_dataframe(progress=progress)
497
743
 
498
744
  def consume_chunks(self, progress: bool = False) -> pd.DataFrame:
745
+ """Aggregate the data in a PapyrusProteinSet to a pandas DataFrame.
746
+
747
+ :param progress: should aggregation progress be shown
748
+ :return: a pandas DataFrame of the proteins.
749
+ """
499
750
  return self.to_dataframe(progress=progress)
500
751
 
501
- class PapyrusPDBProteinSet:
752
+
753
+ class PapyrusPDBProteinSet(ProteinSet):
502
754
 
503
755
  def __init__(self, df: Union[pd.DataFrame, Iterator], papyrus_params: Dict, num_proteins: int):
504
756
  self.data = df
@@ -506,6 +758,11 @@ class PapyrusPDBProteinSet:
506
758
  self.num_rows = num_proteins
507
759
 
508
760
  def to_dataframe(self, progress: bool = False) -> pd.DataFrame:
761
+ """Aggregate the data in a PapyrusPDBProteinSet to a pandas DataFrame.
762
+
763
+ :param progress: should aggregation progress be shown
764
+ :return: a pandas DataFrame of the RCSB Protein Data Bank protein 3D structures.
765
+ """
509
766
  if isinstance(self.data, Iterator):
510
767
  total = (-(-self.num_rows // self.papyrus_params['chunksize'])
511
768
  if self.papyrus_params['chunksize'] is not None
@@ -518,33 +775,27 @@ class PapyrusPDBProteinSet:
518
775
  return f'{type(self).__name__}<iterator of proteins structures>'
519
776
  return f'{type(self).__name__}<{len(self.data)} proteins structures>'
520
777
 
521
- def protein_descriptors(self,
522
- desc_type: Union[str, prodec.Descriptor, prodec.Transform],
523
- progress: bool = False
524
- ) -> pd.DataFrame:
525
- ids = self.aggregate(progress)['target_id'].unique()
526
- try:
527
- return reader.read_protein_descriptors(desc_type=desc_type,
528
- is3d=self.papyrus_params['is3d'],
529
- version=self.papyrus_params['version'],
530
- chunksize=self.papyrus_params['chunksize'],
531
- source_path=self.papyrus_params['source_path'],
532
- ids=ids,
533
- verbose=progress)
534
- except FileNotFoundError:
535
- download.download_papyrus(outdir=self.papyrus_params['source_path'],
536
- version=self.papyrus_params['version'],
537
- nostereo=not self.papyrus_params['is3d'], stereo=self.papyrus_params['is3d'],
538
- only_pp=self.papyrus_params['plusplus'], structures=False,
539
- descriptors=desc_type, progress=self.papyrus_params['download_progress'],
540
- disk_margin=0.0)
541
- return self.protein_descriptors(desc_type, progress)
542
778
 
543
779
  def aggregate(self, progress: bool = False) -> pd.DataFrame:
780
+ """Aggregate the data in a PapyrusPDBProteinSet to a pandas DataFrame.
781
+
782
+ :param progress: should aggregation progress be shown
783
+ :return: a pandas DataFrame of the RCSB Protein Data Bank protein 3D structures.
784
+ """
544
785
  return self.to_dataframe(progress)
545
786
 
546
787
  def agg(self, progress: bool = False) -> pd.DataFrame:
788
+ """Aggregate the data in a PapyrusPDBProteinSet to a pandas DataFrame.
789
+
790
+ :param progress: should aggregation progress be shown
791
+ :return: a pandas DataFrame of the RCSB Protein Data Bank protein 3D structures.
792
+ """
547
793
  return self.to_dataframe(progress=progress)
548
794
 
549
795
  def consume_chunks(self, progress: bool = False) -> pd.DataFrame:
796
+ """Aggregate the data in a PapyrusPDBProteinSet to a pandas DataFrame.
797
+
798
+ :param progress: should aggregation progress be shown
799
+ :return: a pandas DataFrame of the RCSB Protein Data Bank protein 3D structures.
800
+ """
550
801
  return self.to_dataframe(progress=progress)
@@ -94,11 +94,11 @@ def process_group(group, additional_columns: Optional[List[str]] = None):
94
94
  """Aggregate data from one group accordingly"""
95
95
  if (group.values[0] == group.values).all(): # If all values are equal, return first record
96
96
  group['pchembl_value_Mean'] = group['pchembl_value']
97
- group['pchembl_value_StdDev'] = np.NaN
98
- group['pchembl_value_SEM'] = np.NaN
97
+ group['pchembl_value_StdDev'] = np.nan
98
+ group['pchembl_value_SEM'] = np.nan
99
99
  group['pchembl_value_N'] = 1
100
100
  group['pchembl_value_Median'] = group['pchembl_value']
101
- group['pchembl_value_MAD'] = np.NaN
101
+ group['pchembl_value_MAD'] = np.nan
102
102
  return group.iloc[:1, :]
103
103
  # Lambda: Return one value if all are the same
104
104
  listvals = lambda x: ';'.join(set(str(y) for y in x)) if (x.values[0] == x.values).all() else ';'.join(
@@ -174,8 +174,8 @@ def keep_source(data: Union[pd.DataFrame, PandasTextFileReader, Iterator], sourc
174
174
  # Allow processing of Papyrus++
175
175
  papyruspp = 'Activity_class' not in data.columns
176
176
  if papyruspp:
177
- data['Activity_class'] = np.NaN
178
- data['type_other'] = np.NaN
177
+ data['Activity_class'] = np.nan
178
+ data['type_other'] = np.nan
179
179
  # Keep trace of order of columns
180
180
  ordered_columns = data.columns.tolist()
181
181
  # Keep binary data associated to source
@@ -307,8 +307,8 @@ def keep_type(data: Union[pd.DataFrame, PandasTextFileReader, Iterator], activit
307
307
  # Allow processing of Papyrus++
308
308
  papyruspp = 'Activity_class' not in data.columns
309
309
  if papyruspp:
310
- data['Activity_class'] = np.NaN
311
- data['type_other'] = np.NaN
310
+ data['Activity_class'] = np.nan
311
+ data['type_other'] = np.nan
312
312
  # Transform activity_types to column names
313
313
  activity_types = [f"type_{types[i]}" for i in range(len(types)) if types_[i] in activity_types]
314
314
  # Columns with optional multiple values
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: papyrus_scripts
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: A collection of scripts to handle the Papyrus bioactivity dataset
5
5
  Home-page: https://github.com/OlivierBeq/Papyrus-scripts
6
6
  Author: Olivier J. M. Béquignon - Brandon J. Bongers - Willem Jespers
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.7
17
17
  Classifier: Programming Language :: Python :: 3.6
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
- Requires-Dist: numpy
20
+ Requires-Dist: numpy>=2.0.0
21
21
  Requires-Dist: pandas
22
22
  Requires-Dist: rdkit
23
23
  Requires-Dist: requests
@@ -36,6 +36,7 @@ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
36
36
  Provides-Extra: testing
37
37
  Requires-Dist: pytest; extra == "testing"
38
38
  Requires-Dist: parameterized; extra == "testing"
39
+ Dynamic: license-file
39
40
 
40
41
  # Papyrus-scripts
41
42
 
@@ -168,10 +169,10 @@ Different online servers host the Papyrus data based on release and ChEMBL versi
168
169
 
169
170
  | Papyrus version | ChEMBL version | Zenodo | 4TU |
170
171
  |:---------------:|:--------------:|:---------------------------------------------------------:|:---------------------------------------------------------:|
171
- | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10944245) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
172
- | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/10943207) | :x: |
173
- | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7821775) | :x: |
174
- | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787634) | :x: |
172
+ | 05.4 | 29 | [:heavy_check_mark:](https://zenodo.org/records/10943992) | [:heavy_check_mark:](https://doi.org/10.4121/16896406.v2) |
173
+ | 05.5 | 30 | [:heavy_check_mark:](https://zenodo.org/records/7019873) | :x: |
174
+ | 05.6 | 31 | [:heavy_check_mark:](https://zenodo.org/records/7373213) | :x: |
175
+ | 05.7 | 34 | [:heavy_check_mark:](https://zenodo.org/records/13787633) | :x: |
175
176
 
176
177
  Precomputed molecular and protein descriptors along with molecular structures (2D for default set and 3D for low quality set with stereochemistry) are not available for version 05.4 from 4TU but are from Google Drive.
177
178
 
@@ -1,4 +1,4 @@
1
- numpy
1
+ numpy>=2.0.0
2
2
  pandas
3
3
  rdkit
4
4
  requests
@@ -181,8 +181,8 @@ class TestPapyrusDataset(unittest.TestCase):
181
181
  self.assertTrue(oop_data_agg.source.unique().item().lower().startswith('chembl'))
182
182
  self.assertTrue(oop_data_agg.type_IC50.dropna().astype(int).unique().item() == 0)
183
183
  self.assertTrue(oop_data_agg.type_EC50.dropna().astype(int).unique().item() == 0)
184
- self.assertTrue(oop_data_agg.type_other.replace({'NA': np.NaN, 'NaN': np.NaN, 'nan': np.NaN})
185
- .dropna().empty or (oop_data_agg.type_other.replace({'NA': np.NaN, 'NaN': np.NaN, 'nan': np.NaN})
184
+ self.assertTrue(oop_data_agg.type_other.replace({'NA': np.nan, 'NaN': np.nan, 'nan': np.nan})
185
+ .dropna().empty or (oop_data_agg.type_other.replace({'NA': np.nan, 'NaN': np.nan, 'nan': np.nan})
186
186
  .dropna().astype(int).unique().item() == 0))
187
187
  self.assertEqual((oop_data_agg[['type_KD', 'type_Ki']]
188
188
  .astype(int).
File without changes