MaldiAMRKit 0.18.0__tar.gz → 0.20.0__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 (108) hide show
  1. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0/MaldiAMRKit.egg-info}/PKG-INFO +25 -4
  2. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/MaldiAMRKit.egg-info/SOURCES.txt +3 -0
  3. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/MaldiAMRKit.egg-info/requires.txt +8 -1
  4. {maldiamrkit-0.18.0/MaldiAMRKit.egg-info → maldiamrkit-0.20.0}/PKG-INFO +25 -4
  5. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/README.md +19 -2
  6. maldiamrkit-0.20.0/maldiamrkit/__init__.py +38 -0
  7. maldiamrkit-0.20.0/maldiamrkit/_registry.py +124 -0
  8. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/alignment/raw_warping.py +5 -5
  9. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/__init__.py +5 -5
  10. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/example.yaml +1 -1
  11. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/dataset_layouts.py +29 -22
  12. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/input_layouts.py +23 -10
  13. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/dataset.py +15 -4
  14. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/plots.py +0 -3
  15. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/evaluation/mic_regression.py +1 -1
  16. maldiamrkit-0.20.0/maldiamrkit/io/readers.py +600 -0
  17. maldiamrkit-0.20.0/maldiamrkit/io/writers.py +139 -0
  18. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/__init__.py +10 -0
  19. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/binning.py +68 -37
  20. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/preprocessing_pipeline.py +106 -19
  21. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/transformers.py +178 -1
  22. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/similarity/__init__.py +12 -1
  23. maldiamrkit-0.20.0/maldiamrkit/similarity/metrics.py +389 -0
  24. maldiamrkit-0.20.0/maldiamrkit/similarity/pairwise.py +159 -0
  25. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/spectrum.py +8 -3
  26. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/susceptibility/breakpoint.py +5 -5
  27. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/pyproject.toml +1 -0
  28. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/requirements-docs.txt +0 -1
  29. maldiamrkit-0.20.0/requirements-formats.txt +5 -0
  30. maldiamrkit-0.18.0/maldiamrkit/__init__.py +0 -38
  31. maldiamrkit-0.18.0/maldiamrkit/io/readers.py +0 -343
  32. maldiamrkit-0.18.0/maldiamrkit/similarity/metrics.py +0 -222
  33. maldiamrkit-0.18.0/maldiamrkit/similarity/pairwise.py +0 -97
  34. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/LICENSE +0 -0
  35. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/MaldiAMRKit.egg-info/dependency_links.txt +0 -0
  36. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/MaldiAMRKit.egg-info/entry_points.txt +0 -0
  37. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/MaldiAMRKit.egg-info/top_level.txt +0 -0
  38. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/_compat.py +0 -0
  39. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/alignment/__init__.py +0 -0
  40. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/alignment/peak_align.py +0 -0
  41. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/alignment/strategies.py +0 -0
  42. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/alignment/warping.py +0 -0
  43. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/cli.py +0 -0
  44. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/README.md +0 -0
  45. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v1.0.yaml +0 -0
  46. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v1.1.yaml +0 -0
  47. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v1.2.yaml +0 -0
  48. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v1.3.yaml +0 -0
  49. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v10.0.yaml +0 -0
  50. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v11.0.yaml +0 -0
  51. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v12.0.yaml +0 -0
  52. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v13.1.yaml +0 -0
  53. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v14.0.yaml +0 -0
  54. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v15.0.yaml +0 -0
  55. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v16.0.yaml +0 -0
  56. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v2.0.yaml +0 -0
  57. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v4.0.yaml +0 -0
  58. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v5.0.yaml +0 -0
  59. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v6.0.yaml +0 -0
  60. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v7.1.yaml +0 -0
  61. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v8.0.yaml +0 -0
  62. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v8.1.yaml +0 -0
  63. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/breakpoints/eucast/eucast_v9.0.yaml +0 -0
  64. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/builder.py +0 -0
  65. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/duplicates.py +0 -0
  66. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/loader.py +0 -0
  67. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/data/site_info.py +0 -0
  68. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/detection/__init__.py +0 -0
  69. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/detection/peak_detector.py +0 -0
  70. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/detection/peaklist.py +0 -0
  71. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/differential/__init__.py +0 -0
  72. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/differential/analysis.py +0 -0
  73. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/differential/plots.py +0 -0
  74. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/differential/stats.py +0 -0
  75. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/__init__.py +0 -0
  76. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/monitor.py +0 -0
  77. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/pca_drift.py +0 -0
  78. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/peak_drift.py +0 -0
  79. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/drift/reference.py +0 -0
  80. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/evaluation/__init__.py +0 -0
  81. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/evaluation/metrics.py +0 -0
  82. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/evaluation/splitting.py +0 -0
  83. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/filters.py +0 -0
  84. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/io/__init__.py +0 -0
  85. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/io/mic.py +0 -0
  86. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/merging.py +0 -0
  87. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/pipeline.py +0 -0
  88. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/preprocessing/quality.py +0 -0
  89. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/py.typed +0 -0
  90. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/similarity/clustering.py +0 -0
  91. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/similarity/plots.py +0 -0
  92. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/susceptibility/__init__.py +0 -0
  93. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/susceptibility/label_encoder.py +0 -0
  94. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/susceptibility/mic_encoder.py +0 -0
  95. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/__init__.py +0 -0
  96. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/_common.py +0 -0
  97. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/alignment_plots.py +0 -0
  98. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/exploratory_plots.py +0 -0
  99. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/peak_plots.py +0 -0
  100. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/maldiamrkit/visualization/spectrum_plots.py +0 -0
  101. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/requirements-batch.txt +0 -0
  102. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/requirements-dev.txt +0 -0
  103. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/requirements.txt +0 -0
  104. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/setup.cfg +0 -0
  105. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/tests/test_compat.py +0 -0
  106. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/tests/test_numerical_edge_cases.py +0 -0
  107. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/tests/test_peak_detector.py +0 -0
  108. {maldiamrkit-0.18.0 → maldiamrkit-0.20.0}/tests/test_peaklist.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MaldiAMRKit
3
- Version: 0.18.0
3
+ Version: 0.20.0
4
4
  Summary: A comprehensive toolkit for MALDI-TOF mass spectrometry data preprocessing for antimicrobial resistance (AMR) prediction purposes
5
5
  Author-email: Ettore Rocchi <ettore.rocchi3@unibo.it>
6
6
  License-Expression: MIT
@@ -39,7 +39,6 @@ Requires-Dist: tqdm>=4.60.0
39
39
  Provides-Extra: docs
40
40
  Requires-Dist: sphinx>=7.0.0; extra == "docs"
41
41
  Requires-Dist: pydata-sphinx-theme>=0.15.0; extra == "docs"
42
- Requires-Dist: sphinx-autodoc-typehints>=2.0.0; extra == "docs"
43
42
  Requires-Dist: sphinx-design>=0.6.0; extra == "docs"
44
43
  Requires-Dist: sphinxcontrib-typer>=0.5.0; extra == "docs"
45
44
  Requires-Dist: nbsphinx>=0.9.0; extra == "docs"
@@ -53,6 +52,11 @@ Requires-Dist: hypothesis>=6.0.0; extra == "dev"
53
52
  Provides-Extra: batch
54
53
  Requires-Dist: umap-learn>=0.5; extra == "batch"
55
54
  Requires-Dist: combatlearn>=1.3; extra == "batch"
55
+ Provides-Extra: formats
56
+ Requires-Dist: pyteomics>=4.0; extra == "formats"
57
+ Requires-Dist: lxml>=4.0; extra == "formats"
58
+ Requires-Dist: psims>=1.0; extra == "formats"
59
+ Requires-Dist: pynumpress>=0.1.5; python_version >= "3.11" and extra == "formats"
56
60
  Dynamic: license-file
57
61
 
58
62
  # MaldiAMRKit
@@ -109,6 +113,14 @@ pip install maldiamrkit[batch]
109
113
 
110
114
  Installs [`combatlearn`](https://github.com/EttoreRocchi/combatlearn) for ComBat-based batch effect correction and `umap-learn` for UMAP exploratory plots.
111
115
 
116
+ ### Optional: extended mzML/mzXML support
117
+
118
+ ```bash
119
+ pip install maldiamrkit[formats]
120
+ ```
121
+
122
+ `.mzML` and `.mzXML` files are read out of the box (zlib-compressed or uncompressed arrays). This extra installs [`pyteomics`](https://pyteomics.readthedocs.io/) with `lxml`, `psims`, and `pynumpress`, which additionally handle numpress-compressed arrays. `pynumpress` ships no wheels for Python 3.10, so numpress arrays need Python 3.11 or newer.
123
+
112
124
  ### Development Installation
113
125
 
114
126
  ```bash
@@ -130,6 +142,12 @@ pip install -e .[dev]
130
142
  ### Alignment & Detection
131
143
  - **Spectral Alignment**: Shift, linear, piecewise, DTW, quadratic, cubic, and LOWESS warping for both binned and raw full-resolution spectra
132
144
  - **Peak Detection**: Local maxima and persistent homology methods
145
+ - **Peak Sets**: `PeakSet` / `PeakList` represent each spectrum as a variable-length `(m/z, intensity)` set for peak-set models, via `MaldiPeakDetector.transform_peaklist()` on binned spectra or `create_peakset_input()` at full m/z resolution; extraction is stateless per spectrum, so results are cacheable without leakage
146
+ - **Fit-Free Peak Alignment**: `align_peaks()` warps a peak set onto caller-supplied reference peaks, so the reference stays fitted on the training split
147
+
148
+ ### Extensibility
149
+ - **Component Registries**: Plug your own components into the name-dispatched machinery - `register_spectral_metric()` for distance metrics (usable in `spectral_distance`, `pairwise_distances`, and `DriftMonitor`), `register_transformer()` to make `PreprocessingPipeline` configs holding custom transformers round-trip through JSON/YAML, and `register_binning_method()` for custom bin edges
150
+ - **Discovery & Safety**: `list_spectral_metrics()`, `list_transformers()`, `list_binning_methods()` report what is registered; built-ins are replaced only with an explicit `override=True`, and unregistering an overridden built-in restores the default
133
151
 
134
152
  ### Evaluation
135
153
  - **AMR Metrics**: VME, ME, sensitivity, specificity, categorical agreement, and `amr_classification_report` following EUCAST conventions
@@ -155,9 +173,10 @@ pip install -e .[dev]
155
173
  - **Dataset Building & Loading**: `DatasetBuilder` and `DatasetLoader` with pluggable layout adapters (`FlatLayout`, `BrukerTreeLayout`, `DRIAMSLayout`, `MARISMaLayout`)
156
174
  - **Replicate Collapsing**: `DRIAMSLayout(collapse_replicates=True)` merges technical replicates (`_MALDI<N>`) to one row per isolate at load time via the active `duplicate_strategy`
157
175
  - **Bruker Format Support**: Read Bruker flexAnalysis binary data (fid/1r + acqus) natively via `read_spectrum()` on directories
176
+ - **Open Formats**: Read `.mzML` and `.mzXML` files (optionally gzipped) via `read_spectrum()`, `MaldiSpectrum`, and `FlatLayout(spectrum_ext=".mzML")`; `pip install maldiamrkit[formats]` adds `pyteomics` for numpress-compressed arrays
158
177
  - **MIC Parsing**: `parse_mic_column()` for parsing MIC strings with qualifiers and European decimals
159
178
  - **Composable Filters**: `SpeciesFilter`, `DrugFilter`, `QualityFilter`, `MetadataFilter` combinable with `&`/`|`/`~` operators
160
- - **Spectrum Export**: Save spectra to CSV or TXT via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
179
+ - **Spectrum Export**: Save spectra to CSV, TXT, or mzML via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
161
180
 
162
181
  ### Visualization & Tools
163
182
  - **Exploratory Plots**: PCA, t-SNE, and UMAP scatter plots colored by species, resistance phenotype, or any metadata column
@@ -248,8 +267,10 @@ For more detailed examples, see the notebooks:
248
267
  - [Differential Analysis](notebooks/06_differential_analysis.ipynb) - R vs. S peak testing, volcano/Manhattan plots, and multi-drug comparison
249
268
  - [Drift Monitoring](notebooks/07_drift_monitoring.ipynb) - Baseline-anchored drift detection: reference similarity, PCA trajectory, peak stability, and effect-size drift
250
269
  - [Susceptibility](notebooks/08_susceptibility.ipynb) - `MICEncoder` + `BreakpointTable` for log2(MIC) regression targets, S/I/R categorisation with ATU, and `mic_regression_report` evaluation
270
+ - [Peak Sets](notebooks/09_peak_sets.ipynb) - `PeakSet` / `PeakList` extraction, ranking and padding, caching, and fit-free peak alignment
271
+ - [Custom Components](notebooks/10_custom_components.ipynb) - registering a custom distance metric and a custom transformer end-to-end
251
272
 
252
- Notebooks `01`-`03` and `08` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
273
+ Notebooks `01`-`03` and `08`-`10` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
253
274
 
254
275
  ## MaldiSuite Ecosystem
255
276
 
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  requirements-batch.txt
5
5
  requirements-dev.txt
6
6
  requirements-docs.txt
7
+ requirements-formats.txt
7
8
  requirements.txt
8
9
  MaldiAMRKit.egg-info/PKG-INFO
9
10
  MaldiAMRKit.egg-info/SOURCES.txt
@@ -13,6 +14,7 @@ MaldiAMRKit.egg-info/requires.txt
13
14
  MaldiAMRKit.egg-info/top_level.txt
14
15
  maldiamrkit/__init__.py
15
16
  maldiamrkit/_compat.py
17
+ maldiamrkit/_registry.py
16
18
  maldiamrkit/cli.py
17
19
  maldiamrkit/dataset.py
18
20
  maldiamrkit/filters.py
@@ -71,6 +73,7 @@ maldiamrkit/evaluation/splitting.py
71
73
  maldiamrkit/io/__init__.py
72
74
  maldiamrkit/io/mic.py
73
75
  maldiamrkit/io/readers.py
76
+ maldiamrkit/io/writers.py
74
77
  maldiamrkit/preprocessing/__init__.py
75
78
  maldiamrkit/preprocessing/binning.py
76
79
  maldiamrkit/preprocessing/merging.py
@@ -27,8 +27,15 @@ hypothesis>=6.0.0
27
27
  [docs]
28
28
  sphinx>=7.0.0
29
29
  pydata-sphinx-theme>=0.15.0
30
- sphinx-autodoc-typehints>=2.0.0
31
30
  sphinx-design>=0.6.0
32
31
  sphinxcontrib-typer>=0.5.0
33
32
  nbsphinx>=0.9.0
34
33
  ipykernel>=6.0.0
34
+
35
+ [formats]
36
+ pyteomics>=4.0
37
+ lxml>=4.0
38
+ psims>=1.0
39
+
40
+ [formats:python_version >= "3.11"]
41
+ pynumpress>=0.1.5
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MaldiAMRKit
3
- Version: 0.18.0
3
+ Version: 0.20.0
4
4
  Summary: A comprehensive toolkit for MALDI-TOF mass spectrometry data preprocessing for antimicrobial resistance (AMR) prediction purposes
5
5
  Author-email: Ettore Rocchi <ettore.rocchi3@unibo.it>
6
6
  License-Expression: MIT
@@ -39,7 +39,6 @@ Requires-Dist: tqdm>=4.60.0
39
39
  Provides-Extra: docs
40
40
  Requires-Dist: sphinx>=7.0.0; extra == "docs"
41
41
  Requires-Dist: pydata-sphinx-theme>=0.15.0; extra == "docs"
42
- Requires-Dist: sphinx-autodoc-typehints>=2.0.0; extra == "docs"
43
42
  Requires-Dist: sphinx-design>=0.6.0; extra == "docs"
44
43
  Requires-Dist: sphinxcontrib-typer>=0.5.0; extra == "docs"
45
44
  Requires-Dist: nbsphinx>=0.9.0; extra == "docs"
@@ -53,6 +52,11 @@ Requires-Dist: hypothesis>=6.0.0; extra == "dev"
53
52
  Provides-Extra: batch
54
53
  Requires-Dist: umap-learn>=0.5; extra == "batch"
55
54
  Requires-Dist: combatlearn>=1.3; extra == "batch"
55
+ Provides-Extra: formats
56
+ Requires-Dist: pyteomics>=4.0; extra == "formats"
57
+ Requires-Dist: lxml>=4.0; extra == "formats"
58
+ Requires-Dist: psims>=1.0; extra == "formats"
59
+ Requires-Dist: pynumpress>=0.1.5; python_version >= "3.11" and extra == "formats"
56
60
  Dynamic: license-file
57
61
 
58
62
  # MaldiAMRKit
@@ -109,6 +113,14 @@ pip install maldiamrkit[batch]
109
113
 
110
114
  Installs [`combatlearn`](https://github.com/EttoreRocchi/combatlearn) for ComBat-based batch effect correction and `umap-learn` for UMAP exploratory plots.
111
115
 
116
+ ### Optional: extended mzML/mzXML support
117
+
118
+ ```bash
119
+ pip install maldiamrkit[formats]
120
+ ```
121
+
122
+ `.mzML` and `.mzXML` files are read out of the box (zlib-compressed or uncompressed arrays). This extra installs [`pyteomics`](https://pyteomics.readthedocs.io/) with `lxml`, `psims`, and `pynumpress`, which additionally handle numpress-compressed arrays. `pynumpress` ships no wheels for Python 3.10, so numpress arrays need Python 3.11 or newer.
123
+
112
124
  ### Development Installation
113
125
 
114
126
  ```bash
@@ -130,6 +142,12 @@ pip install -e .[dev]
130
142
  ### Alignment & Detection
131
143
  - **Spectral Alignment**: Shift, linear, piecewise, DTW, quadratic, cubic, and LOWESS warping for both binned and raw full-resolution spectra
132
144
  - **Peak Detection**: Local maxima and persistent homology methods
145
+ - **Peak Sets**: `PeakSet` / `PeakList` represent each spectrum as a variable-length `(m/z, intensity)` set for peak-set models, via `MaldiPeakDetector.transform_peaklist()` on binned spectra or `create_peakset_input()` at full m/z resolution; extraction is stateless per spectrum, so results are cacheable without leakage
146
+ - **Fit-Free Peak Alignment**: `align_peaks()` warps a peak set onto caller-supplied reference peaks, so the reference stays fitted on the training split
147
+
148
+ ### Extensibility
149
+ - **Component Registries**: Plug your own components into the name-dispatched machinery - `register_spectral_metric()` for distance metrics (usable in `spectral_distance`, `pairwise_distances`, and `DriftMonitor`), `register_transformer()` to make `PreprocessingPipeline` configs holding custom transformers round-trip through JSON/YAML, and `register_binning_method()` for custom bin edges
150
+ - **Discovery & Safety**: `list_spectral_metrics()`, `list_transformers()`, `list_binning_methods()` report what is registered; built-ins are replaced only with an explicit `override=True`, and unregistering an overridden built-in restores the default
133
151
 
134
152
  ### Evaluation
135
153
  - **AMR Metrics**: VME, ME, sensitivity, specificity, categorical agreement, and `amr_classification_report` following EUCAST conventions
@@ -155,9 +173,10 @@ pip install -e .[dev]
155
173
  - **Dataset Building & Loading**: `DatasetBuilder` and `DatasetLoader` with pluggable layout adapters (`FlatLayout`, `BrukerTreeLayout`, `DRIAMSLayout`, `MARISMaLayout`)
156
174
  - **Replicate Collapsing**: `DRIAMSLayout(collapse_replicates=True)` merges technical replicates (`_MALDI<N>`) to one row per isolate at load time via the active `duplicate_strategy`
157
175
  - **Bruker Format Support**: Read Bruker flexAnalysis binary data (fid/1r + acqus) natively via `read_spectrum()` on directories
176
+ - **Open Formats**: Read `.mzML` and `.mzXML` files (optionally gzipped) via `read_spectrum()`, `MaldiSpectrum`, and `FlatLayout(spectrum_ext=".mzML")`; `pip install maldiamrkit[formats]` adds `pyteomics` for numpress-compressed arrays
158
177
  - **MIC Parsing**: `parse_mic_column()` for parsing MIC strings with qualifiers and European decimals
159
178
  - **Composable Filters**: `SpeciesFilter`, `DrugFilter`, `QualityFilter`, `MetadataFilter` combinable with `&`/`|`/`~` operators
160
- - **Spectrum Export**: Save spectra to CSV or TXT via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
179
+ - **Spectrum Export**: Save spectra to CSV, TXT, or mzML via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
161
180
 
162
181
  ### Visualization & Tools
163
182
  - **Exploratory Plots**: PCA, t-SNE, and UMAP scatter plots colored by species, resistance phenotype, or any metadata column
@@ -248,8 +267,10 @@ For more detailed examples, see the notebooks:
248
267
  - [Differential Analysis](notebooks/06_differential_analysis.ipynb) - R vs. S peak testing, volcano/Manhattan plots, and multi-drug comparison
249
268
  - [Drift Monitoring](notebooks/07_drift_monitoring.ipynb) - Baseline-anchored drift detection: reference similarity, PCA trajectory, peak stability, and effect-size drift
250
269
  - [Susceptibility](notebooks/08_susceptibility.ipynb) - `MICEncoder` + `BreakpointTable` for log2(MIC) regression targets, S/I/R categorisation with ATU, and `mic_regression_report` evaluation
270
+ - [Peak Sets](notebooks/09_peak_sets.ipynb) - `PeakSet` / `PeakList` extraction, ranking and padding, caching, and fit-free peak alignment
271
+ - [Custom Components](notebooks/10_custom_components.ipynb) - registering a custom distance metric and a custom transformer end-to-end
251
272
 
252
- Notebooks `01`-`03` and `08` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
273
+ Notebooks `01`-`03` and `08`-`10` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
253
274
 
254
275
  ## MaldiSuite Ecosystem
255
276
 
@@ -52,6 +52,14 @@ pip install maldiamrkit[batch]
52
52
 
53
53
  Installs [`combatlearn`](https://github.com/EttoreRocchi/combatlearn) for ComBat-based batch effect correction and `umap-learn` for UMAP exploratory plots.
54
54
 
55
+ ### Optional: extended mzML/mzXML support
56
+
57
+ ```bash
58
+ pip install maldiamrkit[formats]
59
+ ```
60
+
61
+ `.mzML` and `.mzXML` files are read out of the box (zlib-compressed or uncompressed arrays). This extra installs [`pyteomics`](https://pyteomics.readthedocs.io/) with `lxml`, `psims`, and `pynumpress`, which additionally handle numpress-compressed arrays. `pynumpress` ships no wheels for Python 3.10, so numpress arrays need Python 3.11 or newer.
62
+
55
63
  ### Development Installation
56
64
 
57
65
  ```bash
@@ -73,6 +81,12 @@ pip install -e .[dev]
73
81
  ### Alignment & Detection
74
82
  - **Spectral Alignment**: Shift, linear, piecewise, DTW, quadratic, cubic, and LOWESS warping for both binned and raw full-resolution spectra
75
83
  - **Peak Detection**: Local maxima and persistent homology methods
84
+ - **Peak Sets**: `PeakSet` / `PeakList` represent each spectrum as a variable-length `(m/z, intensity)` set for peak-set models, via `MaldiPeakDetector.transform_peaklist()` on binned spectra or `create_peakset_input()` at full m/z resolution; extraction is stateless per spectrum, so results are cacheable without leakage
85
+ - **Fit-Free Peak Alignment**: `align_peaks()` warps a peak set onto caller-supplied reference peaks, so the reference stays fitted on the training split
86
+
87
+ ### Extensibility
88
+ - **Component Registries**: Plug your own components into the name-dispatched machinery - `register_spectral_metric()` for distance metrics (usable in `spectral_distance`, `pairwise_distances`, and `DriftMonitor`), `register_transformer()` to make `PreprocessingPipeline` configs holding custom transformers round-trip through JSON/YAML, and `register_binning_method()` for custom bin edges
89
+ - **Discovery & Safety**: `list_spectral_metrics()`, `list_transformers()`, `list_binning_methods()` report what is registered; built-ins are replaced only with an explicit `override=True`, and unregistering an overridden built-in restores the default
76
90
 
77
91
  ### Evaluation
78
92
  - **AMR Metrics**: VME, ME, sensitivity, specificity, categorical agreement, and `amr_classification_report` following EUCAST conventions
@@ -98,9 +112,10 @@ pip install -e .[dev]
98
112
  - **Dataset Building & Loading**: `DatasetBuilder` and `DatasetLoader` with pluggable layout adapters (`FlatLayout`, `BrukerTreeLayout`, `DRIAMSLayout`, `MARISMaLayout`)
99
113
  - **Replicate Collapsing**: `DRIAMSLayout(collapse_replicates=True)` merges technical replicates (`_MALDI<N>`) to one row per isolate at load time via the active `duplicate_strategy`
100
114
  - **Bruker Format Support**: Read Bruker flexAnalysis binary data (fid/1r + acqus) natively via `read_spectrum()` on directories
115
+ - **Open Formats**: Read `.mzML` and `.mzXML` files (optionally gzipped) via `read_spectrum()`, `MaldiSpectrum`, and `FlatLayout(spectrum_ext=".mzML")`; `pip install maldiamrkit[formats]` adds `pyteomics` for numpress-compressed arrays
101
116
  - **MIC Parsing**: `parse_mic_column()` for parsing MIC strings with qualifiers and European decimals
102
117
  - **Composable Filters**: `SpeciesFilter`, `DrugFilter`, `QualityFilter`, `MetadataFilter` combinable with `&`/`|`/`~` operators
103
- - **Spectrum Export**: Save spectra to CSV or TXT via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
118
+ - **Spectrum Export**: Save spectra to CSV, TXT, or mzML via `MaldiSpectrum.save()` and `MaldiSet.save_spectra()`
104
119
 
105
120
  ### Visualization & Tools
106
121
  - **Exploratory Plots**: PCA, t-SNE, and UMAP scatter plots colored by species, resistance phenotype, or any metadata column
@@ -191,8 +206,10 @@ For more detailed examples, see the notebooks:
191
206
  - [Differential Analysis](notebooks/06_differential_analysis.ipynb) - R vs. S peak testing, volcano/Manhattan plots, and multi-drug comparison
192
207
  - [Drift Monitoring](notebooks/07_drift_monitoring.ipynb) - Baseline-anchored drift detection: reference similarity, PCA trajectory, peak stability, and effect-size drift
193
208
  - [Susceptibility](notebooks/08_susceptibility.ipynb) - `MICEncoder` + `BreakpointTable` for log2(MIC) regression targets, S/I/R categorisation with ATU, and `mic_regression_report` evaluation
209
+ - [Peak Sets](notebooks/09_peak_sets.ipynb) - `PeakSet` / `PeakList` extraction, ranking and padding, caching, and fit-free peak alignment
210
+ - [Custom Components](notebooks/10_custom_components.ipynb) - registering a custom distance metric and a custom transformer end-to-end
194
211
 
195
- Notebooks `01`-`03` and `08` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
212
+ Notebooks `01`-`03` and `08`-`10` run on the small example dataset bundled under [`data/`](data/) or are fully self-contained. Notebooks `04`-`07` need more samples and pull the real **MALDI-Kleb-AI** archive (Rocchi *et al.*, 2026; [Zenodo DOI 10.5281/zenodo.17405072](https://zenodo.org/records/17405072)) via the [`demo`](notebooks/_demo.py) helper. By default the helper restricts the dataset to the **Rome sub-cohort** (~470 spectra, single acquisition centre, no batch correction required); the 370 MB tarball is cached under `~/.cache/maldiamrkit/` on first use.
196
213
 
197
214
  ## MaldiSuite Ecosystem
198
215
 
@@ -0,0 +1,38 @@
1
+ """MaldiAMRKit: MALDI-TOF preprocessing toolkit for AMR prediction.
2
+
3
+ Subpackage guide
4
+ ----------------
5
+ - ``maldiamrkit.data``: DatasetBuilder, DatasetLoader, InputLayout, DatasetLayout
6
+ - ``maldiamrkit.filters``: SpeciesFilter, DrugFilter, QualityFilter, MetadataFilter
7
+ - ``maldiamrkit.preprocessing``: PreprocessingPipeline, transformers, binning, quality, merging
8
+ - ``maldiamrkit.alignment``: Warping, RawWarping, create_raw_input, align_peaks, AlignmentStrategy, AlignmentMethod
9
+ - ``maldiamrkit.detection``: MaldiPeakDetector, PeakSet, PeakList, create_peakset_input
10
+ - ``maldiamrkit.evaluation``: AMR metrics (binary + mic_regression_report), splitting (LabelEncoder moved to susceptibility)
11
+ - ``maldiamrkit.susceptibility``: MICEncoder, BreakpointTable, BreakpointResult, LabelEncoder
12
+ - ``maldiamrkit.similarity``: spectral_distance, pairwise_distances, clustering, plots
13
+ - ``maldiamrkit.differential``: DifferentialAnalysis, plot_volcano, plot_manhattan, plot_drug_comparison
14
+ - ``maldiamrkit.drift``: DriftMonitor, plot_reference_drift, plot_pca_drift, plot_peak_stability, plot_effect_size_drift
15
+ - ``maldiamrkit.visualization``: plot_spectrum, plot_pseudogel, plot_peaks, plot_alignment, plot_pca, plot_tsne, plot_umap
16
+ - ``maldiamrkit.io``: read_spectrum, parse_mic_column
17
+
18
+ Examples
19
+ --------
20
+ >>> from maldiamrkit import MaldiSpectrum, MaldiSet
21
+ >>> from maldiamrkit.preprocessing import PreprocessingPipeline
22
+ >>> from maldiamrkit.alignment import Warping
23
+ >>> from maldiamrkit.data import DatasetBuilder, DatasetLoader, FlatLayout, DRIAMSLayout
24
+ >>> from maldiamrkit.susceptibility import MICEncoder, BreakpointTable
25
+ """
26
+
27
+ from .dataset import MaldiSet
28
+ from .spectrum import MaldiSpectrum
29
+
30
+ __version__ = "0.20.0"
31
+ __author__ = "Ettore Rocchi"
32
+
33
+ __all__ = [
34
+ "MaldiSet",
35
+ "MaldiSpectrum",
36
+ "__version__",
37
+ "__author__",
38
+ ]
@@ -0,0 +1,124 @@
1
+ """Shared machinery for the package's name-dispatched component registries.
2
+
3
+ The public registration APIs (``register_spectral_metric``,
4
+ ``register_transformer``, ``register_binning_method`` and their
5
+ ``unregister_*`` / ``list_*`` companions) are thin wrappers around one
6
+ :class:`_ComponentRegistry` instance per component kind, so the three
7
+ registries share a single implementation of the built-in protection,
8
+ override, restore, and name-resolution semantics.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import Enum
14
+ from typing import Any, Callable
15
+
16
+
17
+ class _ComponentRegistry(dict):
18
+ """A ``name -> component`` mapping whose built-in entries are protected.
19
+
20
+ Behaves as a plain dict for lookups, while the mutation methods enforce
21
+ the registry contract:
22
+
23
+ - registering a new name is always allowed (re-registering a custom name
24
+ replaces it silently);
25
+ - replacing a built-in requires ``override=True``;
26
+ - unregistering an overridden built-in restores the default component;
27
+ - unregistering a pristine built-in is refused.
28
+
29
+ Parameters
30
+ ----------
31
+ kind : str
32
+ Full component noun used in error messages, e.g. ``"spectral
33
+ metric"``.
34
+ short : str
35
+ Short noun used where the messages already say the kind, e.g.
36
+ ``"metric"``.
37
+ register_fn : str
38
+ Name of the public registration function to point users at.
39
+ entries : dict
40
+ The built-in components. A copy is kept as the immutable set of
41
+ defaults that :meth:`unregister` can restore.
42
+ validate : callable, optional
43
+ Called with the candidate component before registration; should
44
+ raise ``TypeError`` for unacceptable components.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ kind: str,
50
+ short: str,
51
+ register_fn: str,
52
+ entries: dict[str, Any],
53
+ validate: Callable[[Any], None] | None = None,
54
+ ) -> None:
55
+ super().__init__(entries)
56
+ self._kind = kind
57
+ self._short = short
58
+ self._register_fn = register_fn
59
+ self._validate = validate
60
+ self._defaults: dict[str, Any] = dict(entries)
61
+
62
+ @property
63
+ def defaults(self) -> dict[str, Any]:
64
+ """The built-in components (``name -> component``), never mutated."""
65
+ return self._defaults
66
+
67
+ def register(self, name: str, component: Any, *, override: bool = False) -> None:
68
+ if self._validate is not None:
69
+ self._validate(component)
70
+ if name in self._defaults and not override:
71
+ raise ValueError(
72
+ f"{name!r} is a built-in {self._kind}. "
73
+ "Pass override=True to replace it."
74
+ )
75
+ self[name] = component
76
+
77
+ def unregister(self, name: str) -> None:
78
+ if name in self._defaults:
79
+ default = self._defaults[name]
80
+ if self.get(name) is default:
81
+ raise ValueError(
82
+ f"Cannot unregister built-in {self._kind} {name!r}. "
83
+ f"Built-in {self._short}s are {sorted(self._defaults)}."
84
+ )
85
+ self[name] = default
86
+ return
87
+ try:
88
+ del self[name]
89
+ except KeyError:
90
+ raise KeyError(f"No {self._kind} named {name!r} is registered.") from None
91
+
92
+ def names(self) -> list[str]:
93
+ return sorted(self)
94
+
95
+ def resolve_key(
96
+ self,
97
+ value: Any,
98
+ enum_cls: type[Enum] | None = None,
99
+ *,
100
+ not_a: str = "valid",
101
+ ) -> str:
102
+ """Return the registry key for *value*, an enum member or a name.
103
+
104
+ Raises
105
+ ------
106
+ ValueError
107
+ If *value* names no registered component. The message lists the
108
+ registered names and points at the registration function.
109
+ """
110
+ if enum_cls is not None and isinstance(value, enum_cls):
111
+ key = value.value
112
+ else:
113
+ key = value
114
+ try:
115
+ known = key in self
116
+ except TypeError:
117
+ known = False
118
+ if not known:
119
+ raise ValueError(
120
+ f"{value!r} is not a {not_a} {self._kind}. Use one of "
121
+ f"{self.names()} or register a custom {self._short} with "
122
+ f"{self._register_fn}()."
123
+ )
124
+ return key
@@ -45,11 +45,11 @@ def create_raw_input(
45
45
  How to handle duplicate sample IDs (e.g. the same sample
46
46
  appearing in multiple year subdirectories):
47
47
 
48
- * ``"first"`` -- keep the first occurrence (default).
49
- * ``"last"`` -- keep the last occurrence.
50
- * ``"drop"`` -- remove all duplicates.
51
- * ``"keep_all"`` -- keep every replicate with ``_repN`` suffixes.
52
- * ``"average"`` -- keep all replicates and add an
48
+ * ``"first"``: keep the first occurrence (default).
49
+ * ``"last"``: keep the last occurrence.
50
+ * ``"drop"``: remove all duplicates.
51
+ * ``"keep_all"``: keep every replicate with ``_repN`` suffixes.
52
+ * ``"average"``: keep all replicates and add an
53
53
  ``_original_id`` column so that
54
54
  :meth:`RawWarping.transform` can group and average them.
55
55
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  Submodules
4
4
  ----------
5
- - ``input_layouts`` -- :class:`InputLayout` adapters for :class:`DatasetBuilder`
6
- - ``dataset_layouts`` -- :class:`DatasetLayout` adapters for :class:`DatasetLoader`
7
- - ``builder`` -- :class:`DatasetBuilder`, :class:`ProcessingHandler`, :class:`BuildReport`
8
- - ``loader`` -- :class:`DatasetLoader`
9
- - ``site_info`` -- :class:`SiteInfo`, :class:`BuildInfo`, ``read_site_info``,
5
+ - ``input_layouts``: :class:`InputLayout` adapters for :class:`DatasetBuilder`
6
+ - ``dataset_layouts``: :class:`DatasetLayout` adapters for :class:`DatasetLoader`
7
+ - ``builder``: :class:`DatasetBuilder`, :class:`ProcessingHandler`, :class:`BuildReport`
8
+ - ``loader``: :class:`DatasetLoader`
9
+ - ``site_info``: :class:`SiteInfo`, :class:`BuildInfo`, ``read_site_info``,
10
10
  ``write_site_info`` (self-describing dataset manifest written at build time
11
11
  and consulted by :class:`DRIAMSLayout` at load time)
12
12
  """
@@ -1,4 +1,4 @@
1
- # Synthetic example breakpoint table -- NOT FOR CLINICAL USE.
1
+ # Synthetic example breakpoint table: NOT FOR CLINICAL USE.
2
2
  #
3
3
  # Used by tests, docs, and notebooks for shape demonstration only. The
4
4
  # values below are illustrative and have been deliberately simplified;
@@ -169,25 +169,28 @@ class DRIAMSLayout(DatasetLayout):
169
169
  The column is renamed to ``'Species'`` for downstream use.
170
170
  year : str, int, or None
171
171
  Restrict to a single year.
172
- metadata_dir : str, default="id"
173
- Subdirectory name containing metadata CSV files.
174
- metadata_suffix : str, default="_clean.csv"
175
- Filename suffix for metadata CSV files.
176
- spectrum_ext : str, default=".txt"
177
- File extension for spectrum files (including the dot).
172
+ metadata_dir : str, optional
173
+ Subdirectory name containing metadata CSV files. Taken from
174
+ ``site_info.json`` when the dataset has one, else ``"id"``.
175
+ metadata_suffix : str, optional
176
+ Filename suffix for metadata CSV files. Taken from
177
+ ``site_info.json`` when the dataset has one, else ``"_clean.csv"``.
178
+ spectrum_ext : str, optional
179
+ File extension for spectrum files (including the dot). Taken from
180
+ ``site_info.json`` when the dataset has one, else ``".txt"``.
178
181
  duplicate_strategy : str or DuplicateStrategy, default ``"first"``
179
182
  How to handle duplicate spectrum IDs (e.g. the same sample
180
183
  appearing in multiple year subdirectories):
181
184
 
182
- * ``"first"`` -- keep the first occurrence (default).
183
- * ``"last"`` -- keep the last occurrence.
184
- * ``"drop"`` -- remove all duplicates.
185
- * ``"keep_all"`` -- keep every replicate with ``_repN`` suffixes.
186
- * ``"average"`` -- tag replicates for downstream averaging.
185
+ * ``"first"``: keep the first occurrence (default).
186
+ * ``"last"``: keep the last occurrence.
187
+ * ``"drop"``: remove all duplicates.
188
+ * ``"keep_all"``: keep every replicate with ``_repN`` suffixes.
189
+ * ``"average"``: tag replicates for downstream averaging.
187
190
  id_transform : callable, optional
188
191
  Function mapping raw ``ID`` strings to a canonical *sample*
189
192
  identifier. When set, duplicates are detected on the
190
- transformed identifier rather than the raw one -- so
193
+ transformed identifier rather than the raw one, so
191
194
  technical-replicate files that share an underlying sample
192
195
  (e.g. DRIAMS ``UUID_MALDI1`` / ``UUID_MALDI2``) are
193
196
  recognized as duplicates by ``duplicate_strategy``. The raw
@@ -212,11 +215,15 @@ class DRIAMSLayout(DatasetLayout):
212
215
  collapse DRIAMS technical replicates (``_MALDI<N>``) to one row per
213
216
  underlying isolate via the active ``duplicate_strategy``. Ignored when
214
217
  an explicit ``id_transform`` is given (that always takes precedence).
215
- mz_min : float, default=2000.0
218
+ mz_min : float, optional
216
219
  Lower m/z edge to assign to bin index 0 when a ``binned_N/`` stage
217
- is loaded. Only consulted by :meth:`postprocess_spectrum`.
218
- mz_max : float, default=19997.0
219
- Upper m/z edge assigned to bin index ``N-1``.
220
+ is loaded. Only consulted by :meth:`postprocess_spectrum`. Taken
221
+ from the ``mz_range`` in ``site_info.json`` when the dataset has one,
222
+ else ``2000.0``.
223
+ mz_max : float, optional
224
+ Upper m/z edge assigned to bin index ``N-1``. Taken from the
225
+ ``mz_range`` in ``site_info.json`` when the dataset has one, else
226
+ ``19997.0``.
220
227
  normalize_tic : bool, default=False
221
228
  When ``True``, re-apply a TIC normalization
222
229
  (``intensity <- intensity / sum(intensity)``) to every loaded
@@ -224,7 +231,7 @@ class DRIAMSLayout(DatasetLayout):
224
231
  published DRIAMS / MS-UMG ``binned_6000/`` files do not sum
225
232
  to 1.0 on disk (empirically ~1.29 and ~1.36 respectively),
226
233
  despite the DRIAMS preprocessing script calling
227
- ``calibrateIntensity(method="TIC")`` before trimming -- the
234
+ ``calibrateIntensity(method="TIC")`` before trimming; the
228
235
  cause is somewhere in the upstream pipeline (MALDIquant version
229
236
  or an implicit scaling step) and has not been reproduced here.
230
237
  Enabling this kwarg gives sum=1.0 per spectrum, aligning DRIAMS
@@ -531,13 +538,13 @@ class MARISMaLayout(DatasetLayout):
531
538
  How to handle duplicate specimen identifiers (e.g. the same
532
539
  sample measured at multiple MALDI target positions):
533
540
 
534
- * ``"first"`` -- keep the first occurrence (default).
535
- * ``"last"`` -- keep the last occurrence.
536
- * ``"drop"`` -- remove all duplicates.
537
- * ``"keep_all"`` -- keep every replicate, appending the
541
+ * ``"first"``: keep the first occurrence (default).
542
+ * ``"last"``: keep the last occurrence.
543
+ * ``"drop"``: remove all duplicates.
544
+ * ``"keep_all"``: keep every replicate, appending the
538
545
  target-position value to the ID
539
546
  (``{identifier}_{target_position}``).
540
- * ``"average"`` -- tag replicates for downstream averaging
547
+ * ``"average"``: tag replicates for downstream averaging
541
548
  (adds ``_original_id`` column).
542
549
  id_transform : callable, optional
543
550
  Function mapping raw ``ID`` strings to a canonical *sample*
@@ -80,6 +80,10 @@ class FlatLayout(InputLayout):
80
80
  ``spectra_dir`` is itself flat but the years are known from another
81
81
  source. Takes precedence over the inferred subfolder year,
82
82
  but not over ``year_column``.
83
+ spectrum_ext : str, default=".txt"
84
+ File extension of the spectrum files. Any format understood by
85
+ :func:`~maldiamrkit.io.read_spectrum` works, e.g. ``".mzML"`` or
86
+ ``".mzXML"`` for open-format exports.
83
87
  """
84
88
 
85
89
  def __init__(
@@ -90,30 +94,39 @@ class FlatLayout(InputLayout):
90
94
  id_column: str = "ID",
91
95
  year_column: str | None = None,
92
96
  year_overrides: dict[str, str] | None = None,
97
+ spectrum_ext: str = ".txt",
93
98
  ) -> None:
94
99
  self.spectra_dir = Path(spectra_dir)
95
100
  self.metadata_csv = Path(metadata_csv)
96
101
  self.id_column = id_column
97
102
  self.year_column = year_column
98
103
  self.year_overrides = year_overrides
104
+ self.spectrum_ext = (
105
+ spectrum_ext if spectrum_ext.startswith(".") else f".{spectrum_ext}"
106
+ )
99
107
  self._year_map: dict[str, str] | None = None
100
108
  self._folder_year_map: dict[str, str] = {}
101
109
 
102
110
  def discover_spectra(self) -> list[Path]:
103
- """Find ``.txt`` files, whether flat or in (possibly nested) subfolders.
111
+ """Find spectrum files, whether flat or in (possibly nested) subfolders.
112
+
113
+ The extension is ``spectrum_ext`` (``.txt`` by default).
104
114
 
105
115
  Files are searched flat first, then one level down, then
106
116
  recursively. Whenever a spectrum sits under a four-digit-year
107
117
  folder, that year is recorded so it can serve as a fallback when
108
118
  no ``year_column`` is supplied (see :meth:`get_year`).
109
119
  """
110
- files = sorted(self.spectra_dir.glob("*.txt"))
120
+ pattern = f"*{self.spectrum_ext}"
121
+ files = sorted(self.spectra_dir.glob(pattern))
111
122
  if not files:
112
- files = sorted(self.spectra_dir.glob("*/*.txt"))
123
+ files = sorted(self.spectra_dir.glob(f"*/{pattern}"))
113
124
  if not files:
114
- files = sorted(self.spectra_dir.rglob("*.txt"))
125
+ files = sorted(self.spectra_dir.rglob(pattern))
115
126
  if not files:
116
- raise ValueError(f"No .txt spectrum files found in {self.spectra_dir}")
127
+ raise ValueError(
128
+ f"No {self.spectrum_ext} spectrum files found in {self.spectra_dir}"
129
+ )
117
130
 
118
131
  self._folder_year_map = {}
119
132
  for f in files:
@@ -214,13 +227,13 @@ class BrukerTreeLayout(InputLayout):
214
227
  How to handle duplicate specimen identifiers (e.g. the same
215
228
  sample measured at multiple MALDI target positions):
216
229
 
217
- * ``"first"`` -- keep the first occurrence (default).
218
- * ``"last"`` -- keep the last occurrence.
219
- * ``"drop"`` -- remove all duplicates.
220
- * ``"keep_all"`` -- keep every replicate, appending the
230
+ * ``"first"``: keep the first occurrence (default).
231
+ * ``"last"``: keep the last occurrence.
232
+ * ``"drop"``: remove all duplicates.
233
+ * ``"keep_all"``: keep every replicate, appending the
221
234
  target-position value to the ID
222
235
  (``{identifier}_{target_position}``).
223
- * ``"average"`` -- tag replicates for downstream averaging
236
+ * ``"average"``: tag replicates for downstream averaging
224
237
  (adds ``_original_id`` column).
225
238
  validate : bool, default=True
226
239
  If ``True``, skip empty spectra (all-zero ``fid``) and warn