nmrmetaproc 1.0.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.
- nmrmetaproc-1.0.0/LICENSE +21 -0
- nmrmetaproc-1.0.0/PKG-INFO +202 -0
- nmrmetaproc-1.0.0/README.md +175 -0
- nmrmetaproc-1.0.0/nmrmetaproc/__init__.py +32 -0
- nmrmetaproc-1.0.0/nmrmetaproc/__main__.py +5 -0
- nmrmetaproc-1.0.0/nmrmetaproc/alignment.py +137 -0
- nmrmetaproc-1.0.0/nmrmetaproc/cli.py +199 -0
- nmrmetaproc-1.0.0/nmrmetaproc/io.py +185 -0
- nmrmetaproc-1.0.0/nmrmetaproc/normalization.py +123 -0
- nmrmetaproc-1.0.0/nmrmetaproc/processing.py +387 -0
- nmrmetaproc-1.0.0/nmrmetaproc/processor.py +422 -0
- nmrmetaproc-1.0.0/nmrmetaproc/qc.py +218 -0
- nmrmetaproc-1.0.0/nmrmetaproc/utils.py +86 -0
- nmrmetaproc-1.0.0/nmrmetaproc/version.py +4 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/PKG-INFO +202 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/SOURCES.txt +25 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/dependency_links.txt +1 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/entry_points.txt +2 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/requires.txt +9 -0
- nmrmetaproc-1.0.0/nmrmetaproc.egg-info/top_level.txt +1 -0
- nmrmetaproc-1.0.0/pyproject.toml +52 -0
- nmrmetaproc-1.0.0/setup.cfg +4 -0
- nmrmetaproc-1.0.0/tests/test_integration.py +106 -0
- nmrmetaproc-1.0.0/tests/test_io.py +91 -0
- nmrmetaproc-1.0.0/tests/test_normalization.py +93 -0
- nmrmetaproc-1.0.0/tests/test_processing.py +208 -0
- nmrmetaproc-1.0.0/tests/test_qc.py +98 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Folorunsho Bright Omage
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nmrmetaproc
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: NMR Metabolomics Spectral Processor - raw Bruker FID to analysis-ready CSV
|
|
5
|
+
Author-email: Folorunsho Bright Omage <omagefolorunsho@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/omagebright/nmrmetaproc
|
|
8
|
+
Project-URL: Repository, https://github.com/omagebright/nmrmetaproc
|
|
9
|
+
Keywords: NMR,metabolomics,spectral processing,chemometrics,Bruker
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: nmrglue>=0.9
|
|
19
|
+
Requires-Dist: numpy>=1.21
|
|
20
|
+
Requires-Dist: scipy>=1.7
|
|
21
|
+
Requires-Dist: pandas>=1.3
|
|
22
|
+
Requires-Dist: tqdm>=4.60
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# nmrmetaproc
|
|
29
|
+
|
|
30
|
+
[](https://opensource.org/licenses/MIT)
|
|
31
|
+
[](https://www.python.org/)
|
|
32
|
+
[](https://doi.org/10.5281/zenodo.XXXXXXX)
|
|
33
|
+
|
|
34
|
+
**NMR Metabolomics Spectral Processor**
|
|
35
|
+
|
|
36
|
+
`nmrmetaproc` converts raw Bruker NMR FID files into clean, analysis-ready spectral matrices (CSV format) suitable for PCA, PLS-DA, pathway analysis, and other downstream metabolomics workflows. It implements a rigorous, reproducible processing pipeline with automatic phase correction, chemical-shift referencing, robust baseline correction, spectral alignment, and Probabilistic Quotient Normalization (PQN).
|
|
37
|
+
|
|
38
|
+
**Author:** Folorunsho Bright Omage, Ph.D.
|
|
39
|
+
**ORCID:** [0000-0002-9750-5034](https://orcid.org/0000-0002-9750-5034)
|
|
40
|
+
**Email:** omagefolorunsho@gmail.com
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Reads raw Bruker FID files (`fid` + `acqus`) directly, no conversion needed
|
|
47
|
+
- Full processing pipeline in correct order:
|
|
48
|
+
1. Exponential apodization (line broadening)
|
|
49
|
+
2. Zero-filling
|
|
50
|
+
3. Fast Fourier Transform
|
|
51
|
+
4. **Automatic phase correction** (ACME algorithm, no fixed phase values)
|
|
52
|
+
5. Chemical-shift referencing to TSP (0.00 ppm, auto-detected)
|
|
53
|
+
6. Asymmetric least-squares (ALS) baseline correction
|
|
54
|
+
7. Negative-value handling with per-sample logging
|
|
55
|
+
8. Water region exclusion (4.5-5.0 ppm)
|
|
56
|
+
9. Spectral alignment (icoshift-style cross-correlation or reference-peak)
|
|
57
|
+
10. Configurable region exclusion
|
|
58
|
+
11. Uniform binning
|
|
59
|
+
12. **PQN normalization** (default), or total area, TSP reference, none
|
|
60
|
+
- Per-sample quality control: SNR, TSP linewidth, water suppression score
|
|
61
|
+
- Clean CSV outputs ready for MetaboAnalyst, R, MATLAB
|
|
62
|
+
- Works on Windows, macOS, and Linux
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install nmrmetaproc
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or from source:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/omagebright/nmrmetaproc.git
|
|
76
|
+
cd nmrmetaproc
|
|
77
|
+
pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Dependencies:** `nmrglue`, `numpy`, `scipy`, `pandas`, `tqdm`
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Command-Line Usage
|
|
85
|
+
|
|
86
|
+
### Full Processing Pipeline
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
nmrmetaproc process /path/to/bruker/data --output ./results
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
nmrmetaproc process /path/to/data \
|
|
94
|
+
--output ./results \
|
|
95
|
+
--lb 0.5 \
|
|
96
|
+
--bin-width 0.005 \
|
|
97
|
+
--normalization pqn \
|
|
98
|
+
--snr-threshold 10 \
|
|
99
|
+
--exclude-regions "4.5-5.0,0.0-0.5"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### QC Scan Only
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
nmrmetaproc qc /path/to/data --output ./qc_results
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Inspect Available Samples
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
nmrmetaproc info /path/to/data
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Python API
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from nmrmetaproc import NMRProcessor
|
|
120
|
+
|
|
121
|
+
processor = NMRProcessor(
|
|
122
|
+
lb=0.3,
|
|
123
|
+
bin_width=0.01,
|
|
124
|
+
normalization="pqn",
|
|
125
|
+
ppm_range=(0.5, 9.5),
|
|
126
|
+
snr_threshold=10.0,
|
|
127
|
+
linewidth_threshold=2.5,
|
|
128
|
+
align="icoshift",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
results = processor.process("/path/to/bruker/data")
|
|
132
|
+
|
|
133
|
+
print(results.spectral_matrix) # rows=samples, columns=ppm bins
|
|
134
|
+
print(results.qc_report) # SNR, linewidth, pass/fail per sample
|
|
135
|
+
|
|
136
|
+
results.save("./output")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Output Files
|
|
142
|
+
|
|
143
|
+
| File | Description |
|
|
144
|
+
|------|-------------|
|
|
145
|
+
| `spectral_matrix.csv` | Rows = samples (passed QC), columns = ppm bin centres |
|
|
146
|
+
| `qc_report.csv` | SNR, linewidth (Hz), water suppression score, pass/fail per sample |
|
|
147
|
+
| `acquisition_parameters.csv` | SW, SFO1, TD, NS, RG, pulse program, temperature per sample |
|
|
148
|
+
| `processing_log.txt` | Full processing log with all parameters and per-sample status |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Data Format
|
|
153
|
+
|
|
154
|
+
Each sample must be in its own directory containing:
|
|
155
|
+
- `fid` - binary FID data (interleaved real/imaginary int32)
|
|
156
|
+
- `acqus` - acquisition parameter file
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
data_root/
|
|
160
|
+
|-- sample_001/
|
|
161
|
+
| |-- fid
|
|
162
|
+
| `-- acqus
|
|
163
|
+
`-- sample_002/
|
|
164
|
+
|-- fid
|
|
165
|
+
`-- acqus
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Nested layouts are also supported and discovered automatically.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Citing
|
|
173
|
+
|
|
174
|
+
If you use `nmrmetaproc` in your research, please cite:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
Omage, F. B. (2026). nmrmetaproc: NMR Metabolomics Spectral Processor (Version 1.0.0).
|
|
178
|
+
Zenodo. https://doi.org/10.5281/zenodo.XXXXXXX
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The PQN normalization method:
|
|
182
|
+
|
|
183
|
+
> Dieterle, F., Ross, A., Schlotterbeck, G., & Senn, H. (2006). Probabilistic quotient
|
|
184
|
+
> normalization as robust method to account for dilution of complex biological mixtures.
|
|
185
|
+
> *Analytical Chemistry*, 78(13), 4281-4290. https://doi.org/10.1021/ac051632c
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
git clone https://github.com/omagebright/nmrmetaproc.git
|
|
193
|
+
cd nmrmetaproc
|
|
194
|
+
pip install -e ".[dev]"
|
|
195
|
+
pytest tests/ -v
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# nmrmetaproc
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/)
|
|
5
|
+
[](https://doi.org/10.5281/zenodo.XXXXXXX)
|
|
6
|
+
|
|
7
|
+
**NMR Metabolomics Spectral Processor**
|
|
8
|
+
|
|
9
|
+
`nmrmetaproc` converts raw Bruker NMR FID files into clean, analysis-ready spectral matrices (CSV format) suitable for PCA, PLS-DA, pathway analysis, and other downstream metabolomics workflows. It implements a rigorous, reproducible processing pipeline with automatic phase correction, chemical-shift referencing, robust baseline correction, spectral alignment, and Probabilistic Quotient Normalization (PQN).
|
|
10
|
+
|
|
11
|
+
**Author:** Folorunsho Bright Omage, Ph.D.
|
|
12
|
+
**ORCID:** [0000-0002-9750-5034](https://orcid.org/0000-0002-9750-5034)
|
|
13
|
+
**Email:** omagefolorunsho@gmail.com
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Reads raw Bruker FID files (`fid` + `acqus`) directly, no conversion needed
|
|
20
|
+
- Full processing pipeline in correct order:
|
|
21
|
+
1. Exponential apodization (line broadening)
|
|
22
|
+
2. Zero-filling
|
|
23
|
+
3. Fast Fourier Transform
|
|
24
|
+
4. **Automatic phase correction** (ACME algorithm, no fixed phase values)
|
|
25
|
+
5. Chemical-shift referencing to TSP (0.00 ppm, auto-detected)
|
|
26
|
+
6. Asymmetric least-squares (ALS) baseline correction
|
|
27
|
+
7. Negative-value handling with per-sample logging
|
|
28
|
+
8. Water region exclusion (4.5-5.0 ppm)
|
|
29
|
+
9. Spectral alignment (icoshift-style cross-correlation or reference-peak)
|
|
30
|
+
10. Configurable region exclusion
|
|
31
|
+
11. Uniform binning
|
|
32
|
+
12. **PQN normalization** (default), or total area, TSP reference, none
|
|
33
|
+
- Per-sample quality control: SNR, TSP linewidth, water suppression score
|
|
34
|
+
- Clean CSV outputs ready for MetaboAnalyst, R, MATLAB
|
|
35
|
+
- Works on Windows, macOS, and Linux
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install nmrmetaproc
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or from source:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/omagebright/nmrmetaproc.git
|
|
49
|
+
cd nmrmetaproc
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Dependencies:** `nmrglue`, `numpy`, `scipy`, `pandas`, `tqdm`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Command-Line Usage
|
|
58
|
+
|
|
59
|
+
### Full Processing Pipeline
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
nmrmetaproc process /path/to/bruker/data --output ./results
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
nmrmetaproc process /path/to/data \
|
|
67
|
+
--output ./results \
|
|
68
|
+
--lb 0.5 \
|
|
69
|
+
--bin-width 0.005 \
|
|
70
|
+
--normalization pqn \
|
|
71
|
+
--snr-threshold 10 \
|
|
72
|
+
--exclude-regions "4.5-5.0,0.0-0.5"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### QC Scan Only
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
nmrmetaproc qc /path/to/data --output ./qc_results
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Inspect Available Samples
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
nmrmetaproc info /path/to/data
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Python API
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from nmrmetaproc import NMRProcessor
|
|
93
|
+
|
|
94
|
+
processor = NMRProcessor(
|
|
95
|
+
lb=0.3,
|
|
96
|
+
bin_width=0.01,
|
|
97
|
+
normalization="pqn",
|
|
98
|
+
ppm_range=(0.5, 9.5),
|
|
99
|
+
snr_threshold=10.0,
|
|
100
|
+
linewidth_threshold=2.5,
|
|
101
|
+
align="icoshift",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
results = processor.process("/path/to/bruker/data")
|
|
105
|
+
|
|
106
|
+
print(results.spectral_matrix) # rows=samples, columns=ppm bins
|
|
107
|
+
print(results.qc_report) # SNR, linewidth, pass/fail per sample
|
|
108
|
+
|
|
109
|
+
results.save("./output")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Output Files
|
|
115
|
+
|
|
116
|
+
| File | Description |
|
|
117
|
+
|------|-------------|
|
|
118
|
+
| `spectral_matrix.csv` | Rows = samples (passed QC), columns = ppm bin centres |
|
|
119
|
+
| `qc_report.csv` | SNR, linewidth (Hz), water suppression score, pass/fail per sample |
|
|
120
|
+
| `acquisition_parameters.csv` | SW, SFO1, TD, NS, RG, pulse program, temperature per sample |
|
|
121
|
+
| `processing_log.txt` | Full processing log with all parameters and per-sample status |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Data Format
|
|
126
|
+
|
|
127
|
+
Each sample must be in its own directory containing:
|
|
128
|
+
- `fid` - binary FID data (interleaved real/imaginary int32)
|
|
129
|
+
- `acqus` - acquisition parameter file
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
data_root/
|
|
133
|
+
|-- sample_001/
|
|
134
|
+
| |-- fid
|
|
135
|
+
| `-- acqus
|
|
136
|
+
`-- sample_002/
|
|
137
|
+
|-- fid
|
|
138
|
+
`-- acqus
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Nested layouts are also supported and discovered automatically.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Citing
|
|
146
|
+
|
|
147
|
+
If you use `nmrmetaproc` in your research, please cite:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Omage, F. B. (2026). nmrmetaproc: NMR Metabolomics Spectral Processor (Version 1.0.0).
|
|
151
|
+
Zenodo. https://doi.org/10.5281/zenodo.XXXXXXX
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The PQN normalization method:
|
|
155
|
+
|
|
156
|
+
> Dieterle, F., Ross, A., Schlotterbeck, G., & Senn, H. (2006). Probabilistic quotient
|
|
157
|
+
> normalization as robust method to account for dilution of complex biological mixtures.
|
|
158
|
+
> *Analytical Chemistry*, 78(13), 4281-4290. https://doi.org/10.1021/ac051632c
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
git clone https://github.com/omagebright/nmrmetaproc.git
|
|
166
|
+
cd nmrmetaproc
|
|
167
|
+
pip install -e ".[dev]"
|
|
168
|
+
pytest tests/ -v
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Silence scipy.optimize.fmin convergence output used internally by nmrglue
|
|
2
|
+
import scipy.optimize as _scipy_opt
|
|
3
|
+
_scipy_opt_fmin_orig = _scipy_opt.fmin
|
|
4
|
+
def _fmin_silent(fn, x0, *args, **kwargs):
|
|
5
|
+
kwargs.setdefault("disp", False)
|
|
6
|
+
return _scipy_opt_fmin_orig(fn, x0, *args, **kwargs)
|
|
7
|
+
_scipy_opt.fmin = _fmin_silent
|
|
8
|
+
del _scipy_opt # clean up namespace
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
nmrmetaproc: NMR Metabolomics Spectral Processor
|
|
12
|
+
=================================================
|
|
13
|
+
A Python package for processing raw Bruker NMR FID files into
|
|
14
|
+
analysis-ready spectral matrices for metabolomics studies.
|
|
15
|
+
|
|
16
|
+
Author: Folorunsho Bright Omage
|
|
17
|
+
ORCID: https://orcid.org/0000-0002-9750-5034
|
|
18
|
+
Email: omagefolorunsho@gmail.com
|
|
19
|
+
License: MIT
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from nmrmetaproc.version import __version__, __author__, __email__, __orcid__
|
|
23
|
+
from nmrmetaproc.processor import NMRProcessor, ProcessingResults
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"__version__",
|
|
27
|
+
"__author__",
|
|
28
|
+
"__email__",
|
|
29
|
+
"__orcid__",
|
|
30
|
+
"NMRProcessor",
|
|
31
|
+
"ProcessingResults",
|
|
32
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Spectral alignment algorithms.
|
|
3
|
+
|
|
4
|
+
Two strategies are available:
|
|
5
|
+
1. Reference-peak alignment: shift each spectrum so a chosen reference
|
|
6
|
+
peak (e.g. TSP) aligns exactly. Fast and interpretable.
|
|
7
|
+
2. Icoshift-style correlation-optimized shifting: maximise cross-
|
|
8
|
+
correlation with a reference (or mean) spectrum over a defined
|
|
9
|
+
shifting window. More robust for complex metabolite regions.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from typing import List, Optional, Tuple
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
# Maximum shift window (in number of bins) for icoshift-style alignment
|
|
22
|
+
DEFAULT_MAX_SHIFT: int = 50
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Reference-peak alignment
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def align_to_reference_peak(
|
|
30
|
+
spectra: np.ndarray,
|
|
31
|
+
ppm_axis: np.ndarray,
|
|
32
|
+
ref_ppm: float,
|
|
33
|
+
search_width: float = 0.1,
|
|
34
|
+
) -> Tuple[np.ndarray, List[int]]:
|
|
35
|
+
"""Shift each spectrum so the tallest peak near *ref_ppm* lands exactly there.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
spectra:
|
|
40
|
+
2-D array (n_samples x n_points).
|
|
41
|
+
ppm_axis:
|
|
42
|
+
1-D ppm axis.
|
|
43
|
+
ref_ppm:
|
|
44
|
+
Target chemical shift for the reference peak.
|
|
45
|
+
search_width:
|
|
46
|
+
Half-width of the search window in ppm.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
aligned:
|
|
51
|
+
Shifted spectra (same shape as input).
|
|
52
|
+
shifts:
|
|
53
|
+
Integer shift applied to each spectrum (in data-point units).
|
|
54
|
+
"""
|
|
55
|
+
from nmrmetaproc.utils import ppm_range_to_slice, ppm_to_index
|
|
56
|
+
|
|
57
|
+
lo = ref_ppm - search_width
|
|
58
|
+
hi = ref_ppm + search_width
|
|
59
|
+
start, stop = ppm_range_to_slice(ppm_axis, lo, hi)
|
|
60
|
+
target_idx = ppm_to_index(ppm_axis, ref_ppm)
|
|
61
|
+
|
|
62
|
+
aligned = np.zeros_like(spectra)
|
|
63
|
+
shifts: List[int] = []
|
|
64
|
+
|
|
65
|
+
for i, sp in enumerate(spectra):
|
|
66
|
+
region = sp[start:stop]
|
|
67
|
+
if region.size == 0:
|
|
68
|
+
aligned[i] = sp
|
|
69
|
+
shifts.append(0)
|
|
70
|
+
continue
|
|
71
|
+
peak_local = int(np.argmax(region))
|
|
72
|
+
peak_global = start + peak_local
|
|
73
|
+
shift = target_idx - peak_global
|
|
74
|
+
aligned[i] = np.roll(sp, shift)
|
|
75
|
+
shifts.append(int(shift))
|
|
76
|
+
|
|
77
|
+
return aligned, shifts
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# Icoshift-style cross-correlation alignment
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
def icoshift_align(
|
|
85
|
+
spectra: np.ndarray,
|
|
86
|
+
reference: Optional[np.ndarray] = None,
|
|
87
|
+
max_shift: int = DEFAULT_MAX_SHIFT,
|
|
88
|
+
) -> Tuple[np.ndarray, List[int]]:
|
|
89
|
+
"""Correlation-optimized spectral alignment inspired by icoshift.
|
|
90
|
+
|
|
91
|
+
Aligns each spectrum to *reference* (or the column-mean if None) by
|
|
92
|
+
finding the integer shift that maximises normalised cross-correlation,
|
|
93
|
+
within a window of [-max_shift, +max_shift] data points.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
spectra:
|
|
98
|
+
2-D array (n_samples x n_points).
|
|
99
|
+
reference:
|
|
100
|
+
Target spectrum. If None, the column-mean is used.
|
|
101
|
+
max_shift:
|
|
102
|
+
Maximum allowed shift in data points.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
aligned:
|
|
107
|
+
Aligned spectra.
|
|
108
|
+
shifts:
|
|
109
|
+
Integer shift applied to each spectrum.
|
|
110
|
+
"""
|
|
111
|
+
if reference is None:
|
|
112
|
+
reference = np.mean(spectra, axis=0)
|
|
113
|
+
|
|
114
|
+
ref_norm = _normalise_for_xcorr(reference)
|
|
115
|
+
aligned = np.zeros_like(spectra)
|
|
116
|
+
shifts: List[int] = []
|
|
117
|
+
|
|
118
|
+
for i, sp in enumerate(spectra):
|
|
119
|
+
sp_norm = _normalise_for_xcorr(sp)
|
|
120
|
+
xcorr = np.correlate(sp_norm, ref_norm, mode="full")
|
|
121
|
+
n = len(sp)
|
|
122
|
+
lags = np.arange(-(n - 1), n)
|
|
123
|
+
|
|
124
|
+
# Restrict to allowed window
|
|
125
|
+
mask = np.abs(lags) <= max_shift
|
|
126
|
+
best_lag = lags[mask][int(np.argmax(xcorr[mask]))]
|
|
127
|
+
aligned[i] = np.roll(sp, -int(best_lag))
|
|
128
|
+
shifts.append(int(-best_lag))
|
|
129
|
+
|
|
130
|
+
return aligned, shifts
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _normalise_for_xcorr(arr: np.ndarray) -> np.ndarray:
|
|
134
|
+
"""Zero-mean, unit-norm normalisation."""
|
|
135
|
+
a = arr - arr.mean()
|
|
136
|
+
norm = np.linalg.norm(a)
|
|
137
|
+
return a / norm if norm > 0 else a
|