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.
@@ -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
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
32
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXXX.svg)](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
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
5
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXXX.svg)](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,5 @@
1
+ """Entry point for `python -m nmrmetaproc`."""
2
+ from nmrmetaproc.cli import main
3
+
4
+ if __name__ == "__main__":
5
+ main()
@@ -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