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.
@@ -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
+ ![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/scbirlab/aspect/python-publish.yml)
66
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/aspect-data)
67
+ ![PyPI](https://img.shields.io/pypi/v/aspect-data)
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).)