aspect-data 0.0.1__py3-none-any.whl
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.
- aspect/__init__.py +5 -0
- aspect/checkpoint_utils.py +95 -0
- aspect/cli_module/__init__.py +0 -0
- aspect/cli_module/cli.py +129 -0
- aspect/cli_module/featurize.py +187 -0
- aspect/cli_module/io.py +52 -0
- aspect/data.py +577 -0
- aspect/io.py +189 -0
- aspect/package_data.py +35 -0
- aspect/transform/__init__.py +9 -0
- aspect/transform/base.py +182 -0
- aspect/transform/deep_functions.py +143 -0
- aspect/transform/functions.py +333 -0
- aspect/transform/registry.py +17 -0
- aspect/typing.py +31 -0
- aspect_data-0.0.1.dist-info/METADATA +917 -0
- aspect_data-0.0.1.dist-info/RECORD +21 -0
- aspect_data-0.0.1.dist-info/WHEEL +5 -0
- aspect_data-0.0.1.dist-info/entry_points.txt +2 -0
- aspect_data-0.0.1.dist-info/licenses/LICENSE +21 -0
- aspect_data-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,917 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aspect-data
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: General purpose, chemistry, and taxonomic featurization of tabular data with caching.
|
|
5
|
+
Author-email: Eachan Johnson <eachan.johnson@crick.ac.uk>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) [year] [fullname]
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
Project-URL: Homepage, https://github.com/scbirlab/aspect
|
|
28
|
+
Project-URL: Bug Tracker, https://github.com/scbirlab/aspect/issues
|
|
29
|
+
Keywords: ai,data,deep-learning,machine-learning
|
|
30
|
+
Classifier: Development Status :: 3 - Alpha
|
|
31
|
+
Classifier: Intended Audience :: Science/Research
|
|
32
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
38
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
39
|
+
Requires-Python: >=3.11
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
License-File: LICENSE
|
|
42
|
+
Requires-Dist: carabiner-tools[mpl,pd]>=0.0.5.post3
|
|
43
|
+
Requires-Dist: datasets>=3
|
|
44
|
+
Requires-Dist: filelock
|
|
45
|
+
Requires-Dist: huggingface_hub
|
|
46
|
+
Requires-Dist: numpy
|
|
47
|
+
Requires-Dist: platformdirs>=4.4.0
|
|
48
|
+
Requires-Dist: tqdm
|
|
49
|
+
Provides-Extra: chem
|
|
50
|
+
Requires-Dist: schemist>=0.0.6; extra == "chem"
|
|
51
|
+
Provides-Extra: chemprop
|
|
52
|
+
Requires-Dist: chemprop>=2.1.0; extra == "chemprop"
|
|
53
|
+
Provides-Extra: deep
|
|
54
|
+
Requires-Dist: transformers; extra == "deep"
|
|
55
|
+
Provides-Extra: bio
|
|
56
|
+
Requires-Dist: vectome>=0.0.4; extra == "bio"
|
|
57
|
+
Provides-Extra: dev
|
|
58
|
+
Requires-Dist: aspect-data[bio,chem]; extra == "dev"
|
|
59
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
60
|
+
Requires-Dist: flake8; extra == "dev"
|
|
61
|
+
Dynamic: license-file
|
|
62
|
+
|
|
63
|
+
# 🏔️ aspect
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+

|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
**aspect** is a lightweight featurisation layer for tabular machine-learning data.
|
|
70
|
+
|
|
71
|
+
It lets you define independent feature pipelines for different input columns, then apply them to dictionaries, Pandas data frames, local files, Hugging Face datasets, or saved dataset checkpoints.
|
|
72
|
+
|
|
73
|
+
The core idea is:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
input table
|
|
77
|
+
├── numeric column → log / identity / custom transforms
|
|
78
|
+
├── categorical col → hash / one-hot / custom transforms
|
|
79
|
+
├── SMILES column → chemistry features, if installed
|
|
80
|
+
├── species column → taxonomic features, if installed
|
|
81
|
+
└── text column → deep embeddings, if installed
|
|
82
|
+
|
|
83
|
+
output dataset with named feature columns
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Contents
|
|
89
|
+
|
|
90
|
+
* [Quick start](#quick-start)
|
|
91
|
+
* [Installation](#installation)
|
|
92
|
+
* [Command-line interface](#command-line-interface)
|
|
93
|
+
* [Python API](#python-api)
|
|
94
|
+
* [Feature specifications](#feature-specifications)
|
|
95
|
+
* [Available transforms](#available-transforms)
|
|
96
|
+
* [Saving and loading pipelines](#saving-and-loading-pipelines)
|
|
97
|
+
* [Supported input and output formats](#supported-input-and-output-formats)
|
|
98
|
+
* [Custom transforms](#custom-transforms)
|
|
99
|
+
* [Optional chemistry, taxonomy, and deep-learning features](#optional-chemistry-taxonomy-and-deep-learning-features)
|
|
100
|
+
* [Development](#development)
|
|
101
|
+
* [Issues, problems, suggestions](#issues-problems-suggestions)
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Quick start
|
|
106
|
+
|
|
107
|
+
### Python API
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from aspect.data import DataPipeline
|
|
111
|
+
|
|
112
|
+
data = {
|
|
113
|
+
"compound_id": ["cmpd-1", "cmpd-2", "cmpd-3"],
|
|
114
|
+
"assay": ["MIC", "MIC", "IC50"],
|
|
115
|
+
"mwt": [46.07, 60.10, 78.11],
|
|
116
|
+
"pIC50": [5.0, 6.2, 4.8],
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pipe = DataPipeline(
|
|
120
|
+
column_transforms={
|
|
121
|
+
"log_mwt": ("mwt", "log"),
|
|
122
|
+
"assay_hash": (
|
|
123
|
+
"assay",
|
|
124
|
+
{"name": "hash", "kwargs": {"ndim": 8}},
|
|
125
|
+
),
|
|
126
|
+
"target": ("pIC50", "identity"),
|
|
127
|
+
},
|
|
128
|
+
columns_to_keep=["compound_id"],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
features = pipe(data, drop_unused_columns=True)
|
|
132
|
+
|
|
133
|
+
print(features.column_names)
|
|
134
|
+
print(pipe.data_out_shape)
|
|
135
|
+
print(features[:2])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Expected column structure:
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
['compound_id', 'log_mwt', 'assay_hash', 'target']
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`log_mwt` and `target` are `(n, 1)` arrays. `assay_hash` is an `(n, 8)` array. The output is a Hugging Face `Dataset`, so it can be saved, sliced, converted to Pandas, or passed into a downstream PyTorch/data-loader layer.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Command-line quick start
|
|
149
|
+
|
|
150
|
+
Create a small CSV:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
cat > compounds.csv <<'CSV'
|
|
154
|
+
compound_id,assay,mwt,pIC50
|
|
155
|
+
cmpd-1,MIC,46.07,5.0
|
|
156
|
+
cmpd-2,MIC,60.10,6.2
|
|
157
|
+
cmpd-3,IC50,78.11,4.8
|
|
158
|
+
CSV
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Featurise it:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
aspect featurize compounds.csv \
|
|
165
|
+
--features mwt:log@log_mwt assay:hash@assay_hash pIC50@target \
|
|
166
|
+
--extras compound_id \
|
|
167
|
+
--output features.parquet
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The feature specification means:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
mwt:log@log_mwt # read mwt, apply log, write column name `log_mwt`
|
|
174
|
+
assay:hash@assay_hash # read assay, apply deterministic hash vector, write column name `assay_hash`
|
|
175
|
+
pIC50@target # read pIC50, identity transform, write column name `target`
|
|
176
|
+
--extras compound_id # retain compound_id without transforming it
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### Reuse a saved feature specification
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
aspect serialize \
|
|
185
|
+
--features mwt:log@log_mwt assay:hash@assay_hash pIC50@target \
|
|
186
|
+
--extras compound_id \
|
|
187
|
+
--output feature-spec.as
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Then apply the same specification later:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
aspect featurize compounds.csv \
|
|
194
|
+
--config feature-spec.as \
|
|
195
|
+
--output features-from-config.parquet
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Installation
|
|
201
|
+
|
|
202
|
+
### Basic installation
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
pip install aspect-data
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Chemistry features
|
|
209
|
+
|
|
210
|
+
Install chemistry support if you want SMILES featurisation via `schemist`:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install "aspect-data[chem]"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Chemprop support
|
|
217
|
+
|
|
218
|
+
Install Chemprop support if you want to prepare Chemprop-style molecular data:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pip install "aspect-data[chemprop]"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Taxonomic features
|
|
225
|
+
|
|
226
|
+
Install taxonomy support if you want species or taxon-ID features via `vectome`:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install "aspect-data[bio]"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Deep-learning features
|
|
233
|
+
|
|
234
|
+
Install transformer support if you want Hugging Face model embeddings:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
pip install "aspect[deep]"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Development installation
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
git clone https://github.com/scbirlab/aspect.git
|
|
244
|
+
cd aspect
|
|
245
|
+
pip install -e ".[dev]"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Command-line interface
|
|
251
|
+
|
|
252
|
+
The CLI has two main commands:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
aspect serialize
|
|
256
|
+
aspect featurize
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Use help for the full option list:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
aspect --help
|
|
263
|
+
aspect serialize --help
|
|
264
|
+
aspect featurize --help
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### `aspect serialize`
|
|
268
|
+
|
|
269
|
+
Create a reusable pipeline checkpoint from a feature specification.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
aspect serialize \
|
|
273
|
+
--features mwt:log@log_mwt assay:hash@assay_hash \
|
|
274
|
+
--extras compound_id pIC50 \
|
|
275
|
+
--output feature-spec.as
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### `aspect featurize`
|
|
279
|
+
|
|
280
|
+
Apply a feature specification or saved config to a dataset.
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
aspect featurize compounds.csv \
|
|
284
|
+
--features mwt:log@log_mwt assay:hash@assay_hash \
|
|
285
|
+
--extras compound_id pIC50 \
|
|
286
|
+
--output features.parquet
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
or:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
aspect featurize compounds.csv \
|
|
293
|
+
--config feature-spec.as \
|
|
294
|
+
--output features.parquet
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Slice a dataset
|
|
298
|
+
|
|
299
|
+
This is useful for testing on large datasets.
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
aspect featurize large-table.csv \
|
|
303
|
+
--start 1000 \
|
|
304
|
+
--end 2000 \
|
|
305
|
+
--features assay:hash@assay_hash mwt:log@log_mwt \
|
|
306
|
+
--output slice.parquet
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Cache location
|
|
310
|
+
|
|
311
|
+
Use `--cache` to control where intermediate Hugging Face dataset files are cached.
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
aspect featurize compounds.csv \
|
|
315
|
+
--features assay:hash@assay_hash \
|
|
316
|
+
--cache .aspect-cache \
|
|
317
|
+
--output features.parquet
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Python API
|
|
323
|
+
|
|
324
|
+
The main class is `DataPipeline`.
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from aspect.data import DataPipeline
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
A `DataPipeline` maps one or more input columns to named output feature columns.
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
pipe = DataPipeline(
|
|
334
|
+
column_transforms={
|
|
335
|
+
"log_mwt": ("mwt", "log"),
|
|
336
|
+
"assay_hash": ("assay", "hash"),
|
|
337
|
+
},
|
|
338
|
+
)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Apply it to a dictionary:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
out = pipe({
|
|
345
|
+
"mwt": [46.07, 60.10, 78.11],
|
|
346
|
+
"assay": ["MIC", "MIC", "IC50"],
|
|
347
|
+
})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Apply it to a Pandas data frame:
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
import pandas as pd
|
|
354
|
+
from aspect.data import DataPipeline
|
|
355
|
+
|
|
356
|
+
df = pd.DataFrame({
|
|
357
|
+
"mwt": [46.07, 60.10, 78.11],
|
|
358
|
+
"assay": ["MIC", "MIC", "IC50"],
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
pipe = DataPipeline({
|
|
362
|
+
"log_mwt": ("mwt", "log"),
|
|
363
|
+
"assay_hash": ("assay", {"name": "hash", "kwargs": {"ndim": 16}}),
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
out = pipe(df)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Apply it to a local file:
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
pipe = DataPipeline({
|
|
373
|
+
"log_mwt": ("mwt", "log"),
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
out = pipe("compounds.csv")
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Apply it to a Hugging Face dataset reference:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
pipe = DataPipeline({
|
|
383
|
+
"assay_hash": ("assay", "hash"),
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
out = pipe("hf://datasets/scbirlab/fang-2023-biogen-adme~scaffold-split:train")
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Feature specifications
|
|
392
|
+
|
|
393
|
+
A feature specification defines:
|
|
394
|
+
|
|
395
|
+
1. the input column;
|
|
396
|
+
2. the transform or chain of transforms;
|
|
397
|
+
3. the output column name.
|
|
398
|
+
|
|
399
|
+
### Python feature specs
|
|
400
|
+
|
|
401
|
+
The recommended Python form is:
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
{
|
|
405
|
+
"output_column": ("input_column", "transform_name")
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Example:
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
pipe = DataPipeline({
|
|
413
|
+
"log_mwt": ("mwt", "log"),
|
|
414
|
+
})
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
For transform arguments, use a dictionary with a nested `kwargs` field:
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
pipe = DataPipeline({
|
|
421
|
+
"assay_hash": (
|
|
422
|
+
"assay",
|
|
423
|
+
{"name": "hash", "kwargs": {"ndim": 32, "seed": 123}},
|
|
424
|
+
),
|
|
425
|
+
})
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
For chained transforms, pass a list:
|
|
429
|
+
|
|
430
|
+
```python
|
|
431
|
+
pipe = DataPipeline({
|
|
432
|
+
"log_hashed_assay": (
|
|
433
|
+
"assay",
|
|
434
|
+
[
|
|
435
|
+
{"name": "hash", "kwargs": {"ndim": 16}},
|
|
436
|
+
"log",
|
|
437
|
+
],
|
|
438
|
+
),
|
|
439
|
+
})
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Only use transform chains when the output of one transform is mathematically valid input for the next. For example, `hash → log` is usually not sensible because hash values may be negative.
|
|
443
|
+
|
|
444
|
+
### CLI feature specs
|
|
445
|
+
|
|
446
|
+
CLI specs use a compact string form:
|
|
447
|
+
|
|
448
|
+
```text
|
|
449
|
+
input_column:transform@output_column
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Examples:
|
|
453
|
+
|
|
454
|
+
```text
|
|
455
|
+
mwt:log@log_mwt
|
|
456
|
+
assay:hash@assay_hash
|
|
457
|
+
pIC50@target
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Supported forms:
|
|
461
|
+
|
|
462
|
+
| Spec | Meaning |
|
|
463
|
+
| --------------------------- | ---------------------------------------------- |
|
|
464
|
+
| `column` | keep `column` as an extra column |
|
|
465
|
+
| `column@new_name` | identity transform from `column` to `new_name` |
|
|
466
|
+
| `column:transform` | transform `column`; auto-name the output |
|
|
467
|
+
| `column:transform@new_name` | transform `column`; write `new_name` |
|
|
468
|
+
| `column:t1:t2@new_name` | apply chained transforms |
|
|
469
|
+
|
|
470
|
+
For parameterised transforms, prefer the Python API. The Python API supports structured keyword arguments cleanly through `{"name": ..., "kwargs": ...}`.
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Available transforms
|
|
475
|
+
|
|
476
|
+
### Base transforms
|
|
477
|
+
|
|
478
|
+
These are available with the standard installation.
|
|
479
|
+
|
|
480
|
+
| Transform | Input | Output | Notes |
|
|
481
|
+
| ---------- | ----------: | ------------------: | ---------------------------------------------------------- |
|
|
482
|
+
| `identity` | any column | array | Pass-through transform |
|
|
483
|
+
| `log` | numeric | array | Natural logarithm, `np.log` |
|
|
484
|
+
| `hash` | string-like | fixed-length vector | Deterministic string hashing |
|
|
485
|
+
| `one-hot` | categorical | vector | Python API recommended because categories must be supplied |
|
|
486
|
+
|
|
487
|
+
Example:
|
|
488
|
+
|
|
489
|
+
```python
|
|
490
|
+
from aspect.data import DataPipeline
|
|
491
|
+
|
|
492
|
+
pipe = DataPipeline({
|
|
493
|
+
"log_mwt": ("mwt", "log"),
|
|
494
|
+
"assay_hash": (
|
|
495
|
+
"assay",
|
|
496
|
+
{"name": "hash", "kwargs": {"ndim": 16}},
|
|
497
|
+
),
|
|
498
|
+
"assay_onehot": (
|
|
499
|
+
"assay",
|
|
500
|
+
{"name": "one-hot", "kwargs": {"categories": ["MIC", "IC50"]}},
|
|
501
|
+
),
|
|
502
|
+
})
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Chemistry transforms
|
|
506
|
+
|
|
507
|
+
Require:
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
pip install "aspect[chem]"
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
| Transform | Input | Output |
|
|
514
|
+
| -------------------- | -----: | -----------------------: |
|
|
515
|
+
| `morgan-fingerprint` | SMILES | molecular fingerprint |
|
|
516
|
+
| `descriptors-2d` | SMILES | 2D molecular descriptors |
|
|
517
|
+
| `descriptors-3d` | SMILES | 3D molecular descriptors |
|
|
518
|
+
|
|
519
|
+
Example:
|
|
520
|
+
|
|
521
|
+
```python
|
|
522
|
+
from aspect.data import DataPipeline
|
|
523
|
+
|
|
524
|
+
data = {
|
|
525
|
+
"compound_id": ["ethanol", "ethylamine", "benzene"],
|
|
526
|
+
"smiles": ["CCO", "CCN", "c1ccccc1"],
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
pipe = DataPipeline(
|
|
530
|
+
{
|
|
531
|
+
"morgan": ("smiles", "morgan-fingerprint"),
|
|
532
|
+
"desc2d": ("smiles", "descriptors-2d"),
|
|
533
|
+
},
|
|
534
|
+
columns_to_keep=["compound_id"],
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
features = pipe(data, drop_unused_columns=True)
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Taxonomic transforms
|
|
541
|
+
|
|
542
|
+
Require:
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
pip install "aspect[bio]"
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
| Transform | Input | Output |
|
|
549
|
+
| --------------------- | -----------------------: | --------------------: |
|
|
550
|
+
| `vectome-fingerprint` | species name or taxon ID | taxonomic fingerprint |
|
|
551
|
+
|
|
552
|
+
Example:
|
|
553
|
+
|
|
554
|
+
```python
|
|
555
|
+
from aspect.data import DataPipeline
|
|
556
|
+
|
|
557
|
+
data = {
|
|
558
|
+
"species": [
|
|
559
|
+
"Mycobacterium tuberculosis",
|
|
560
|
+
"Escherichia coli",
|
|
561
|
+
"Staphylococcus aureus",
|
|
562
|
+
],
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
pipe = DataPipeline({
|
|
566
|
+
"species_fp": (
|
|
567
|
+
"species",
|
|
568
|
+
{"name": "vectome-fingerprint", "kwargs": {"ndim": 128}},
|
|
569
|
+
),
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
features = pipe(data)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Deep embedding transforms
|
|
576
|
+
|
|
577
|
+
Require:
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
pip install "aspect[deep]"
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
| Transform | Input | Output |
|
|
584
|
+
| --------- | ----: | ----------------------------------------: |
|
|
585
|
+
| `hf-bart` | text | aggregated BART encoder/decoder embedding |
|
|
586
|
+
|
|
587
|
+
Example:
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
from aspect.data import DataPipeline
|
|
591
|
+
|
|
592
|
+
data = {
|
|
593
|
+
"description": [
|
|
594
|
+
"cell wall inhibitor",
|
|
595
|
+
"protein synthesis inhibitor",
|
|
596
|
+
"DNA gyrase inhibitor",
|
|
597
|
+
],
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
pipe = DataPipeline({
|
|
601
|
+
"text_embedding": (
|
|
602
|
+
"description",
|
|
603
|
+
{
|
|
604
|
+
"name": "hf-bart",
|
|
605
|
+
"kwargs": {
|
|
606
|
+
"ref": "facebook/bart-base",
|
|
607
|
+
"aggregator": ["mean", "max"],
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
),
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
features = pipe(data)
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
The model will be loaded through Hugging Face `transformers`, so the first run may download model weights.
|
|
617
|
+
|
|
618
|
+
### Chemprop transform
|
|
619
|
+
|
|
620
|
+
Require:
|
|
621
|
+
|
|
622
|
+
```bash
|
|
623
|
+
pip install "aspect[chemprop]"
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
`chemprop-mol` is intended for converting SMILES into Chemprop-style molecular datapoints, with optional labels and extra dense features.
|
|
627
|
+
|
|
628
|
+
Because Chemprop models often need special collation and graph batching, use this transform together with a downstream model adapter or data-loader layer rather than assuming the output should be horizontally stacked with ordinary numeric arrays.
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Saving and loading pipelines
|
|
633
|
+
|
|
634
|
+
`DataPipeline` objects can be checkpointed.
|
|
635
|
+
|
|
636
|
+
```python
|
|
637
|
+
from aspect.data import DataPipeline
|
|
638
|
+
|
|
639
|
+
pipe = DataPipeline({
|
|
640
|
+
"log_mwt": ("mwt", "log"),
|
|
641
|
+
"assay_hash": (
|
|
642
|
+
"assay",
|
|
643
|
+
{"name": "hash", "kwargs": {"ndim": 16}},
|
|
644
|
+
),
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
pipe.save_checkpoint("feature-spec.as")
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
Load the checkpoint later:
|
|
651
|
+
|
|
652
|
+
```python
|
|
653
|
+
from aspect.data import DataPipeline
|
|
654
|
+
|
|
655
|
+
pipe = DataPipeline().load_checkpoint("feature-spec.as")
|
|
656
|
+
|
|
657
|
+
out = pipe({
|
|
658
|
+
"mwt": [46.07, 60.10, 78.11],
|
|
659
|
+
"assay": ["MIC", "MIC", "IC50"],
|
|
660
|
+
})
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
If the pipeline has already been applied to data, the checkpoint can also store input/output datasets unless skipped:
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
pipe.save_checkpoint(
|
|
667
|
+
"feature-spec-and-data.as",
|
|
668
|
+
skip_data_in=False,
|
|
669
|
+
skip_data_out=False,
|
|
670
|
+
)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
For reusable feature specifications, it is usually cleaner to save the pipeline before applying it to a dataset, or to skip data when checkpointing.
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Supported input and output formats
|
|
678
|
+
|
|
679
|
+
### Python inputs
|
|
680
|
+
|
|
681
|
+
`DataPipeline` accepts:
|
|
682
|
+
|
|
683
|
+
| Input type | Example |
|
|
684
|
+
| ------------------------------ | ------------------------------------------------------ |
|
|
685
|
+
| dictionary / mapping | `{"smiles": ["CCO", "CCN"]}` |
|
|
686
|
+
| Pandas `DataFrame` | `pipe(df)` |
|
|
687
|
+
| Hugging Face `Dataset` | `pipe(dataset)` |
|
|
688
|
+
| Hugging Face `IterableDataset` | `pipe(iterable_dataset)` |
|
|
689
|
+
| local file path | `pipe("data.csv")` |
|
|
690
|
+
| Hugging Face dataset ref | `pipe("hf://datasets/org/name~config:split@revision")` |
|
|
691
|
+
|
|
692
|
+
### Local file inputs
|
|
693
|
+
|
|
694
|
+
Supported local input extensions include:
|
|
695
|
+
|
|
696
|
+
* `.csv`, `.csv.gz`
|
|
697
|
+
* `.tsv`, `.tsv.gz`
|
|
698
|
+
* `.txt`, `.txt.gz`
|
|
699
|
+
* `.json`
|
|
700
|
+
* `.parquet`
|
|
701
|
+
* `.arrow`
|
|
702
|
+
* `.xml`
|
|
703
|
+
* `.hf` saved Hugging Face datasets
|
|
704
|
+
|
|
705
|
+
### CLI outputs
|
|
706
|
+
|
|
707
|
+
`aspect featurize --output` can write:
|
|
708
|
+
|
|
709
|
+
| Extension | Output |
|
|
710
|
+
| ---------------------------------- | ----------------------------------------- |
|
|
711
|
+
| `.csv`, `.csv.gz` | CSV |
|
|
712
|
+
| `.tsv`, `.txt`, optionally gzipped | delimited text |
|
|
713
|
+
| `.json` | JSON |
|
|
714
|
+
| `.parquet` | Parquet |
|
|
715
|
+
| `.sql` | SQL |
|
|
716
|
+
| `.hf` | Hugging Face dataset saved to disk |
|
|
717
|
+
| unknown extension | Hugging Face dataset, with `.hf` appended |
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## Working with output columns
|
|
722
|
+
|
|
723
|
+
By default, `DataPipeline` keeps the original input columns and appends feature columns.
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
out = pipe(data)
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
To keep only feature outputs and selected metadata:
|
|
730
|
+
|
|
731
|
+
```python
|
|
732
|
+
pipe = DataPipeline(
|
|
733
|
+
{
|
|
734
|
+
"log_mwt": ("mwt", "log"),
|
|
735
|
+
"assay_hash": ("assay", "hash"),
|
|
736
|
+
},
|
|
737
|
+
columns_to_keep=["compound_id"],
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
out = pipe(data, drop_unused_columns=True)
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
You can also provide extra columns at call time:
|
|
744
|
+
|
|
745
|
+
```python
|
|
746
|
+
out = pipe(
|
|
747
|
+
data,
|
|
748
|
+
drop_unused_columns=True,
|
|
749
|
+
keep_extra_columns=["compound_id", "target"],
|
|
750
|
+
)
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
After running a pipeline, inspect inferred output shapes:
|
|
754
|
+
|
|
755
|
+
```python
|
|
756
|
+
print(pipe.data_out_shape)
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
Example:
|
|
760
|
+
|
|
761
|
+
```text
|
|
762
|
+
{
|
|
763
|
+
"compound_id": (),
|
|
764
|
+
"log_mwt": (1,),
|
|
765
|
+
"assay_hash": (256,)
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## Custom transforms
|
|
772
|
+
|
|
773
|
+
Custom transforms can be registered using `register_function`.
|
|
774
|
+
|
|
775
|
+
A registered transform should be a factory: it receives configuration arguments and returns a callable with signature:
|
|
776
|
+
|
|
777
|
+
```python
|
|
778
|
+
fn(data, input_column) -> numpy.ndarray
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
Example:
|
|
782
|
+
|
|
783
|
+
```python
|
|
784
|
+
import numpy as np
|
|
785
|
+
|
|
786
|
+
from aspect.data import DataPipeline
|
|
787
|
+
from aspect.transform.registry import register_function
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
@register_function("square")
|
|
791
|
+
def Square():
|
|
792
|
+
def _square(data, input_column):
|
|
793
|
+
x = np.asarray(data[input_column], dtype=float)
|
|
794
|
+
return x ** 2
|
|
795
|
+
|
|
796
|
+
return _square
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
pipe = DataPipeline({
|
|
800
|
+
"mwt_squared": ("mwt", "square"),
|
|
801
|
+
})
|
|
802
|
+
|
|
803
|
+
out = pipe({
|
|
804
|
+
"mwt": [46.07, 60.10, 78.11],
|
|
805
|
+
})
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
With arguments:
|
|
809
|
+
|
|
810
|
+
```python
|
|
811
|
+
@register_function("scale")
|
|
812
|
+
def Scale(factor=1.0):
|
|
813
|
+
def _scale(data, input_column):
|
|
814
|
+
x = np.asarray(data[input_column], dtype=float)
|
|
815
|
+
return x * factor
|
|
816
|
+
|
|
817
|
+
return _scale
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
pipe = DataPipeline({
|
|
821
|
+
"scaled_mwt": (
|
|
822
|
+
"mwt",
|
|
823
|
+
{"name": "scale", "kwargs": {"factor": 0.001}},
|
|
824
|
+
),
|
|
825
|
+
})
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
If you save a pipeline using a custom transform, make sure the module that registers the transform is imported before loading the checkpoint.
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## Optional chemistry, taxonomy, and deep-learning examples
|
|
833
|
+
|
|
834
|
+
### Mixed ADME-style table
|
|
835
|
+
|
|
836
|
+
```python
|
|
837
|
+
from aspect.data import DataPipeline
|
|
838
|
+
|
|
839
|
+
data = {
|
|
840
|
+
"compound_id": ["cmpd-1", "cmpd-2", "cmpd-3"],
|
|
841
|
+
"smiles": ["CCO", "CCN", "c1ccccc1"],
|
|
842
|
+
"assay": ["solubility", "clearance", "microsome"],
|
|
843
|
+
"species": ["M. tuberculosis", "E. coli", "S. aureus"],
|
|
844
|
+
"mwt": [46.07, 45.08, 78.11],
|
|
845
|
+
"label": [0.1, 0.4, 0.8],
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
pipe = DataPipeline(
|
|
849
|
+
{
|
|
850
|
+
"morgan": ("smiles", "morgan-fingerprint"),
|
|
851
|
+
"desc2d": ("smiles", "descriptors-2d"),
|
|
852
|
+
"assay_hash": (
|
|
853
|
+
"assay",
|
|
854
|
+
{"name": "hash", "kwargs": {"ndim": 32}},
|
|
855
|
+
),
|
|
856
|
+
"species_fp": (
|
|
857
|
+
"species",
|
|
858
|
+
{"name": "vectome-fingerprint", "kwargs": {"ndim": 64}},
|
|
859
|
+
),
|
|
860
|
+
"log_mwt": ("mwt", "log"),
|
|
861
|
+
"y": ("label", "identity"),
|
|
862
|
+
},
|
|
863
|
+
columns_to_keep=["compound_id"],
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
features = pipe(data, drop_unused_columns=True)
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
This produces separate feature columns for chemical structure, descriptors, assay identity, species identity, molecular weight, and target label.
|
|
870
|
+
|
|
871
|
+
A downstream model adapter can decide whether to:
|
|
872
|
+
|
|
873
|
+
* concatenate dense arrays;
|
|
874
|
+
* route different columns into different neural-network towers;
|
|
875
|
+
* collate graph-like objects separately;
|
|
876
|
+
* keep labels and metadata separate from model inputs.
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## Development
|
|
881
|
+
|
|
882
|
+
Install development dependencies:
|
|
883
|
+
|
|
884
|
+
```bash
|
|
885
|
+
pip install -e ".[dev]"
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
Run tests:
|
|
889
|
+
|
|
890
|
+
```bash
|
|
891
|
+
pytest
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
Run the CLI smoke test script:
|
|
895
|
+
|
|
896
|
+
```bash
|
|
897
|
+
bash test/scripts/run-tests.sh
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Run selected doctests:
|
|
901
|
+
|
|
902
|
+
```bash
|
|
903
|
+
python -m doctest aspect/data.py
|
|
904
|
+
python -m doctest aspect/transform/base.py
|
|
905
|
+
python -m doctest aspect/transform/functions.py
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
## Issues, problems, suggestions
|
|
909
|
+
|
|
910
|
+
Please open an issue on GitHub:
|
|
911
|
+
|
|
912
|
+
[https://github.com/scbirlab/aspect/issues](https://github.com/scbirlab/aspect/issues)
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
## Documentation
|
|
916
|
+
|
|
917
|
+
(To come at [ReadTheDocs](https://aspect.readthedocs.org).)
|