MR data tends to arrive as a bare array plus a pile of numbers you have to keep in your head:
which axis is time, how wide the sweep was, what zero means. xmris keeps all of that on the
data itself. Your FID stays a plain xarray DataArray — axes named, time
in seconds, metadata riding along — and the physics is one accessor away.
spectrum = fid.xmr.to_spectrum().xmr.autophase().xmr.to_ppm()
There is no custom class to learn. Every xarray habit you have keeps working, a whole grid of
voxels goes through the same call as a single one, and the spectrum that comes out still knows its
ppm axis.
Start with the documentation
→ andrewendlinger.github.io/xmris
That is where the package really lives. Every tutorial page is a live notebook: the plots and
numbers you see come from the code right above them, re-run on every pull request. New here?
Basics walks the FID → spectrum → ppm round trip
from scratch; Concepts explains why xmris is
fussy about names and metadata.
Quick start
The shortest path to something you can look at: simulate_fid hands you a signal that already
carries the metadata the physics needs.
import xmris # importing registers the .xmr accessor on every xarray object
fid = xmris.simulate_fid(
amplitudes=[1.0, 0.4],
chemical_shifts=[0.0, 5.2], # ppm
reference_frequency=120.66, # MHz, the Larmor frequency of your nucleus
n_points=1024,
)
spectrum = (
fid
.xmr.apodize_exp(lb=5.0)
.xmr.zero_fill(target_points=2048)
.xmr.to_spectrum()
.xmr.autophase()
.xmr.to_ppm()
)
print(spectrum.dims) # ('chemical_shift',)
print(round(spectrum.attrs["phase_p0"], 2)) # 2.17 — autophase wrote down what it applied
Got your own data? Then you build the DataArray yourself. Four steps, no magic:
import numpy as np
import xarray as xr
import xmris
# Step 1 — the time axis. Space it by your dwell time (here: a 4000 Hz sweep,
# so 1/4000 s per point). This axis alone sets the frequency axis later on —
# there is no separate sweep-width setting to keep in sync.
time = np.arange(1024) / 4000.0 # seconds
# Step 2 — your samples. Any complex array works; here, one fake peak
# 250 Hz off centre, decaying. Stack three copies to play three voxels.
signal = np.exp(2j * np.pi * 250.0 * time - time / 0.05)
data = np.stack([signal, 0.5 * signal, 0.25 * signal]) # (voxel, time)
# Step 3 — wrap it up. Name the dims, attach the time axis, and add the
# two facts only you can know:
fid = xr.DataArray(
data,
dims=["voxel", "time"],
coords={"time": time},
attrs={
"reference_frequency": 120.66, # MHz — your Larmor frequency
"carrier_ppm": 0.0, # which ppm sits at 0 Hz (1H water: 4.7)
},
)
# Step 4 — done. The whole toolbox now works, all voxels at once:
spectrum = fid.xmr.to_spectrum()
print(spectrum.coords["frequency"].values[[0, -1]].tolist()) # [-2000.0, 1996.09375]
ppm = spectrum.xmr.to_ppm()
print(ppm.dims) # ('voxel', 'chemical_shift')
The 4000 Hz you put into the time axis is the 4000 Hz you get back. Forget
reference_frequency or carrier_ppm and to_ppm tells you, by name, before it does any maths —
Hz and ppm takes it from here.
What is in the box
- Processing — zero filling, exponential and Lorentz-to-Gauss apodization, manual and automatic phasing, asymmetric-least-squares baseline correction, FID ↔ spectrum, Hz ↔ ppm.
- Vendor data — Bruker ParaVision arrays and their parameter dicts become a fully labelled FID, digital-filter group delay included: the one that puts a phase roll through everything if you forget it.
- Fitting — AMARES quantification via pyAMARES, returning
a
Datasetwith your signal, the fit and the residual aligned. - Plots and widgets — matplotlib helpers, plus sliders you can drag to phase, apodize, or scroll through a stack of spectra.
And what is not: xmris is a 0.x package, the MRS side is ahead of the imaging side, Bruker is
the only vendor loader so far, and full MRSI grids — lazy, chunked, sitting on an anatomical
image — are still to come. Core xmris will not do image reconstruction. The
roadmap says what is shipped, what is moving,
and what is still being argued about.
Install
pip install xmris # or: uv add xmris
pip install "xmris[fitting]" # adds AMARES quantification
Fitting is an extra because deep in its dependencies sits hlsvdpro, which ships no arm64 wheel;
the pyamares-xmris repackage on PyPI adds the marker that skips it on Apple Silicon, so the
install works there too. All else is in the bare install. Python 3.10 – 3.13.
Contributing
Issues and pull requests are welcome. uv sync --all-extras --dev then uv run test gets you a
working checkout; the setup steps, the architecture contract and one page per kind of change are in
the contributor guide.
Changelog
Upgrading? The changelog records what
changed in each release. There is no CHANGELOG.md here — it is a rendered page, so every entry can
link the issue, the pull request, and the docs behind it.
License
xmris is BSD 3-Clause — see
LICENSE. Use it, build on it, ship
it, paid work or not; just keep the notice.
Metadata
Release files for xmris 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| xmris-0.7.0.tar.gz | 109.1 kB | Without provenance | Details for xmris-0.7.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| xmris-0.7.0-py3-none-any.whl | Python 3 | none | any | Details for xmris-0.7.0-py3-none-any.whl |
Total release size: 243.6 kB
Release files / xmris-0.7.0.tar.gz
| Download URL | xmris-0.7.0.tar.gz |
|---|---|
| Size | 109.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f62dd5ce3beef4c4ef09488975ee88ae1cf4cb7a34544da36f745b4c73fcc239
|
|
BLAKE2b-256 checksum How to use checksums |
a5b3016f27e7c7ceb20302e1e7a5bcc662af5267533cb41e177d43cd556a80d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / xmris-0.7.0-py3-none-any.whl
| Download URL | xmris-0.7.0-py3-none-any.whl |
|---|---|
| Size | 134.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
32185f99a12269df24147b8e998508521ab6d7a4c7c52abffc27a6c25f092fba
|
|
BLAKE2b-256 checksum How to use checksums |
c5e666abb9f962c06b4eff4a40e9c0bddd459ddd7c499bf477def6e9128c4940
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for xmris 0.7.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.