Python package for parsing NMR IVDr data from Bruker instruments.
This is a Python migration of the R package nmr.parser (v0.3.4), preserving all functionality while adopting Python best practices and the scientific Python ecosystem (NumPy, pandas, SciPy).
- Binary spectrum reading with endianness handling and power factor scaling
- Parameter file parsing for acqus/procs files (xwin-nmr and TopSpin formats)
- XML parsing for quantification, lipoprotein, QC, PACS, and ERETIC data
- parseNMR migration with parquet export and DuckDB integration
- Smart logging system with 3 verbosity levels (prod/info/debug)
- Multiple format support with automatic version detection
- Spectrum processing with calibration, interpolation, and ERETIC correction
- Type hints for better IDE support and code clarity
- Modern Python packaging with pyproject.toml
# From source
pip install -e .
# With development dependencies
pip install -e ".[dev]"from nmr_parser import read_experiment
# Read all data from a single experiment
exp = read_experiment("data/HB-COVID0001/10")
# Access different data types
print(exp['acqus']) # Acquisition parameters
print(exp['procs']) # Processing parameters
print(exp['spec']) # Spectrum data
print(exp['quant']) # Quantification results
print(exp['lipo']) # Lipoprotein profiles
print(exp['qc']) # Quality control data
# Read multiple experiments
exps = read_experiment([
"data/HB-COVID0001/10",
"data/HB-COVID0001/11",
"data/HB-COVID0001/12"
])
# Read only specific components
exp = read_experiment(
"data/HB-COVID0001/10",
opts={"what": ["acqus", "spec", "quant"]}
)
# Read spectrum with custom options
opts = {
"what": ["spec"],
"specOpts": {
"fromTo": (-0.1, 10), # PPM range
"length_out": 44079, # Number of points
"uncalibrate": False, # Keep calibration
"eretic": 3808.27 # ERETIC factor (optional)
}
}
exp = read_experiment("data/HB-COVID0001/10", opts=opts)
# Access spectrum data
spec_df = exp['spec']['spec'].iloc[0]
x = spec_df['x'] # PPM axis
y = spec_df['y'] # IntensityThe parse_nmr function features a smart logging system with three verbosity levels:
Only shows final results and errors. Perfect for batch processing.
from nmr_parser import parse_nmr
result = parse_nmr("data/", opts={'verbosity': 'prod'})Output:
β Wrote 4 parquet files
β Created DuckDB database: run.duckdb
ββββββββββββββββββββββββββββββββββββββ
Parse Complete
Samples: 144
Variables: 44079
Data type: NMR
ββββββββββββββββββββββββββββββββββββββ
Shows major processing steps and findings. Best for interactive use.
result = parse_nmr("data/", opts={'verbosity': 'info'}) # or omit (default)Output:
βΆ Scanning folder for experiments
βΆ Processing 144 samples
βΆ Reading spectra
βΆ Calculating spcglyc biomarkers
IVDr QC data found
β Excluded 2 paths
β Wrote 4 parquet files
β Created DuckDB database
Shows all internal decisions and detailed progress. For debugging.
result = parse_nmr("data/", opts={'verbosity': 'debug'})Output:
βΆ Processing 144 samples
β’ Sample classification: {'sample': 138, 'qc': 4, 'ltr': 2}
βΆ Reading spectra
β’ Reading spectra from 144 paths
βΆ Calculating spcglyc biomarkers
β’ Trimming PPM regions: water, baseline, high
β’ Flipping 3 spectra (180Β° phase correction)
β’ Applying 3mm tube correction to 12 samples
β’ Wrote: run_data.parquet
β’ Wrote: run_metadata.parquet
...
# Production (minimal output)
python examples/parse_nmr_example.py data/ -v prod
# Info (default)
python examples/parse_nmr_example.py data/ -v info
# Debug (verbose)
python examples/parse_nmr_example.py data/ -v debugFeatures:
- π¨ Color-coded output (green=success, blue=progress, yellow=warning, red=error)
- β»οΈ Progress updates overwrite instead of spamming thousands of lines
- π Smart filtering - only shows what matters at each level
- β‘ Minimal overhead in PROD mode
The examples/ directory contains ready-to-use scripts with argument parsing and data export options.
# Read parameters with default test data
python examples/read_params_example.py
# Read from your own data
python examples/read_params_example.py /path/to/experiment/10
# Export all data to CSV
python examples/read_params_example.py -o output.csv
# Display all data in terminal
python examples/read_params_example.py --show-all
# Combine: use your data and export
python examples/read_params_example.py /path/to/exp/10 -o params.csvAll examples support --help, -o/--output for CSV export, and most support --show-all:
# Parameters
python examples/read_params_example.py [exp_path] [-o output.csv] [--show-all]
# Quantification
python examples/read_quant_example.py [xml_file] [-o output.csv] [--show-all]
# Lipoproteins
python examples/read_lipo_example.py [xml_file] [-o output.csv] [--show-all]
# Complete experiment
python examples/read_experiment_example.py [exp_path] [exp_path2...] [-o output.csv]
# Scan folders
python examples/scan_folder_example.py [folder_path] [-o output.csv]
# Process spectrum
python examples/process_spectrum_example.py [exp_path] [-o spectrum.csv]
# Batch processing
python examples/batch_processing_example.py [exp1 exp2 exp3...]All examples can export full datasets to CSV:
# Export parameters (all ~1,128 rows)
python examples/read_params_example.py -o my_params.csv
# Export quantification (~41-150 metabolites)
python examples/read_quant_example.py -o metabolites.csv
# Export lipoproteins (~112+ measurements)
python examples/read_lipo_example.py -o lipoproteins.csvNote: Examples show summaries by default (first 5-10 rows) for readability. Use -o to export complete data or --show-all to display everything.
from nmr_parser import (
read_spectrum,
read_param,
read_params,
read_quant,
read_lipo,
read_qc,
read_eretic
)
# Read single parameter
pulprog = read_param("experiment/acqus", "PULPROG")
# Read all parameters
params = read_params("experiment/acqus")
# Read spectrum
spec = read_spectrum(
"experiment/10",
procno=1,
options={'fromTo': (-0.1, 10), 'length_out': 44079}
)
# Read quantification data (handles multiple XML versions)
quant = read_quant("experiment/pdata/1/plasma_quant_report.xml")
print(quant['data']) # DataFrame with 41-150 compounds
print(quant['version']) # Version string
# Read lipoprotein profiles
lipo = read_lipo("experiment/pdata/1/lipo_results.xml")
print(lipo['data']) # DataFrame with 112 measurements
# Read QC data
qc = read_qc("experiment/pdata/1/plasma_qc_report.xml")
# Read ERETIC calibration
eretic = read_eretic("experiment/QuantFactorSample.xml")
print(eretic['ereticFactor'].iloc[0])Built-in reference data is bundled with the package and accessible without external files:
from nmr_parser.reference import get_lipo_table, get_qc_table, get_pacs_table, get_sm_table| Rows | Columns |
|---|---|
| 112 | fraction, name, abbr, id, type, value, unit, refMax, refMin, refUnit |
lipo = get_lipo_table() # 112 Γ 10
lipo_ext = get_lipo_table(extended=True) # adds 200+ calculated metricsReturns a dict keyed by version string, each containing tests and infos DataFrames.
| Version | tests rows | infos rows |
|---|---|---|
| BioBankQC PS 1.0.0 | 22 | 24 |
| BioBankQC PS 1.1.0 | 22 | 24 |
| Version | tests rows | infos rows |
|---|---|---|
| BioBankQC Urine | 28 | 27 |
qc_ser = get_qc_table("SER") # dict with 2 versions
qc_uri = get_qc_table("URI") # dict with 1 version
for version, tables in qc_ser.items():
print(version, len(tables['tests']), len(tables['infos']))| Rows | Columns |
|---|---|
| 16 | name, unit, refMax, refMin, refUnit |
pacs = get_pacs_table() # 16 Γ 5| Matrix | Rows | Description |
|---|---|---|
"PLA" / "SER" |
41 | Plasma metabolites |
"URI" |
150 | Urine metabolites |
sm_pla = get_sm_table("PLA") # 41 metabolites
sm_uri = get_sm_table("URI") # 150 metabolitesnmr_parser/
βββ core/ # Core I/O functions
β βββ experiment.py # Main orchestrator
β βββ spectrum.py # Spectrum reading
β βββ parameters.py # Parameter file parsing
βββ xml_parsers/ # XML parsing functions
β βββ quantification.py
β βββ lipoproteins.py
β βββ quality_control.py
β βββ pacs.py
β βββ eretic.py
βββ processing/ # Data processing utilities
βββ reference/ # Reference tables and data
- lxml (>=4.9.0) - XML parsing with XPath support
- pandas (>=2.0.0) - Data manipulation
- numpy (>=1.24.0) - Numerical operations
- scipy (>=1.10.0) - Signal processing (interpolation)
- rich (>=13.0.0) - Terminal output
This Python package maintains full compatibility with the R nmr.parser package (v0.3.4):
- Same function names (snake_case in Python vs camelCase in R)
- Same data structures (pandas DataFrames instead of R data.frames)
- Same logic for all parsers and calculations
- Same XML version detection and priority systems
| R Function | Python Function | Module |
|---|---|---|
readExperiment() |
read_experiment() |
nmr_parser |
readSpectrum() |
read_spectrum() |
nmr_parser.core |
readParam() |
read_param() |
nmr_parser.core |
readParams() |
read_params() |
nmr_parser.core |
readQuant() |
read_quant() |
nmr_parser.xml_parsers |
readLipo() |
read_lipo() |
nmr_parser.xml_parsers |
readQc() |
read_qc() |
nmr_parser.xml_parsers |
readEretic() |
read_eretic() |
nmr_parser.xml_parsers |
cleanNames() |
clean_names() |
nmr_parser.processing |
# Install development dependencies
pip install -e ".[dev]"
# Run tests (once implemented)
pytest
# Run with coverage
pytest --cov=nmr_parser
# Format code
black src/
# Lint code
ruff check src/
# Type check
mypy src/
# Build documentation
.venv/bin/sphinx-build -M clean docs docs/_build # Clean old builds
.venv/bin/sphinx-build -M html docs docs/_build # Build HTML docs
# Output will be in docs/_build/html/index.htmlMIT License - See LICENSE file for details
- Julien Wist ([email protected])
- Reika Masuda ([email protected])
If you use this package, please cite:
- Original R package: nmr.parser v0.3.4
- Python package: nmr-parser v0.4.2
This is a migration of an R package. Contributions should maintain compatibility with the original R implementation while following Python best practices.