Migrating from 0.8 to 0.9¶
spxtacular 0.9 is a breaking release. It moves to the new majors of its sibling packages (tacular 2, peptacular 5, paftacular 2, tdfpy 5, mzmlpy 0.10), renames a few fields so each name says exactly what it holds, and makes optional parameters keyword-only. This page lists every rename and removal as old -> new.
Dependencies¶
| 0.8 | 0.9 |
|---|---|
peptacular>=4.2,<5 |
peptacular>=5,<6 |
paftacular>=1.4,<2 |
paftacular>=2,<3 |
(transitive) tacular |
tacular>=2,<3 (direct) |
tdfpy>=4.1,<5 ([bruker]) |
tdfpy>=5,<6 |
mzmlpy>=0.9,<0.10 ([mzml]) |
mzmlpy>=0.10,<0.11 |
peptacular 5 changes affect code that builds fragments to pass in: ion_types="by" is now
one ion type named "by" (an internal fragment), so write ion_types=("b", "y"); and
Fragment.losses is now Fragment.deltas.
tdfpy 5 changes some Bruker peak values. MS1 spectra from DReader can differ by one or two
peaks, with summed intensity about 40 ppm different. Some DDA MS2 peaks that 0.8 merged into one
now come out as separate peaks.
Precursor¶
Precursor is now its own frozen, slotted, keyword-only dataclass. It is no longer a Peak
subclass.
| 0.8 | 0.9 |
|---|---|
Precursor(mz=500.2, ...) |
Precursor(precursor_mz=500.2, ...) |
Precursor(500.2, 1e4, 2, ...) (positional) |
keyword-only: Precursor(precursor_mz=500.2, intensity=1e4, charge=2) |
precursor.mz |
precursor.precursor_mz |
is_monoisotopic required |
is_monoisotopic=None by default; intensity=0.0 by default |
isinstance(precursor, Peak) is True |
False |
| (none) | precursor.im_type: the kind of mobility stored in precursor.im |
Precursor.im keeps its name because the value is whatever the source recorded: 1/K0 for
Bruker and most mzML files, drift time for some mzML files. im_type (an IMType, or
None when unknown) says which. Readers fill it in: Bruker DDA and PRM set "ook0". mzML
takes it from the declared unit, for precursor values and for ion mobility arrays alike:
| mzML unit | im_type |
values |
|---|---|---|
| MS:1002814 (volt-second per square centimeter) | "ook0" |
as stored |
| UO:0000028 (millisecond) | "drift_time_ms" |
as stored |
| UO:0000010 (second) | "drift_time_ms" |
multiplied by 1000 |
| no unit | "im" (generic) |
as stored |
When a spectrum has several ion mobility arrays, the first whose length matches the peaks is used, with a warning.
MsnSpectrum¶
| 0.8 | 0.9 |
|---|---|
MsnSpectrum(isolation_im_range=...) |
MsnSpectrum(isolation_ook0_range=...) |
spec.isolation_im_range |
spec.isolation_ook0_range |
The isolation window is always 1/K0 (it comes from Bruker's quadrupole/TIMS isolation), so the name now says so.
| 0.8 | 0.9 |
|---|---|
Bruker isolation_im_range was (high, low), e.g. (1.3062, 1.29) |
isolation_ook0_range is (low, high): (1.29, 1.3062) |
0.8 JSON, npz files and spectrl tokens are read back as (low, high).
Spectrum and Peak constructors¶
Everything after intensity is keyword-only:
| 0.8 | 0.9 |
|---|---|
Spectrum(mz, intensity, charge) |
Spectrum(mz, intensity, charge=charge) |
Peak(500.0, 1e4, 2) |
Peak(500.0, 1e4, charge=2) |
Keyword-only parameters¶
Required parameters stay positional. Every parameter with a default is now keyword-only in the
public functions and methods below. spec.normalize("tic") becomes
spec.normalize(method="tic"); match_fragments(spec, frags, 10, "ppm") becomes
match_fragments(spec, frags, tolerance=10, tolerance_unit="ppm").
| Function or method | Now keyword-only |
|---|---|
Spectrum.filter |
all parameters |
Spectrum.normalize, Spectrum.denoise |
method, inplace |
Spectrum.centroid |
min_intensity, inplace |
Spectrum.merge |
mz_tolerance, mz_tolerance_unit, im_tolerance, im_tolerance_unit, inplace |
Spectrum.sort, Spectrum.top_peaks |
by, reverse (and inplace for sort) |
Spectrum.update, Spectrum.decharge |
inplace |
Spectrum.deconvolute |
all parameters |
Spectrum.remove_precursor_peak |
all parameters, including precursor_mz and precursor_charge |
Spectrum.scale_intensity |
method, degree, base, inplace |
Spectrum.round_mz |
decimals, combine, inplace |
Spectrum.has_peak, get_peak, get_peaks |
tolerance, tolerance_unit, target_charge, target_im, im_tolerance (and peak_selection for get_peak) |
Spectrum.match_fragments, match_fragments |
tolerance, tolerance_unit, peak_selection, is_monoisotopic |
Spectrum.score, score |
tolerance, tolerance_unit, peak_selection, predicted_intensities |
Spectrum.annotate, Spectrum.annot_plot_table |
tolerance, tolerance_unit, title, peak_selection, include_sequence |
Spectrum.plot, Spectrum.plot_table |
all parameters |
Spectrum.mass_error_plot, Spectrum.facet_plot |
all optional parameters, including fragments and mirror_spectrum for facet_plot |
Spectrum.to_spectrl_url, to_spectrl_url |
base |
Spectrum.from_usi, fetch_usi |
backend, timeout |
cosine, modified_cosine, entropy_similarity |
tolerance, tolerance_unit (and transform); the two precursor m/z of modified_cosine stay positional |
extract_chromatogram |
mode, mz_range |
extract_xic, plot_xic |
tolerance, tolerance_unit, im_window, aggregate (and plot options) |
brain_isotopic_distribution |
max_isotopes, isotope_abundances |
IsotopeModel.adaptive_distribution |
min_relative_abundance, max_isotopes |
estimate_noise_level |
method |
build_plot_table, build_annot_plot_table, plot_from_table, table_view |
all optional parameters |
plot_spectrum, annotate_spectrum, mirror_plot, plot_chromatogram |
all optional parameters |
profile_centroid_plot |
centroids, title, theme_mode, max_points |
sequence_coverage_plot, mass_error_plot, facet_plot |
all optional parameters, including fragments for facet_plot |
save_figure |
scale |
Reader, DReader |
centroid_config |
ThermoReader |
prefer_vendor_centroid |
CentroidConfig |
all fields |
MatchedFragment |
all fields (it is built by match_fragments, not by hand) |
spxtacular.decon.deconvolve_spectrum |
everything after intensity |
Chromatogram |
everything after intensity |
IsotopeModel |
everything after atoms_per_da |
IonizationModel |
carrier |
DeconvolutionProvenance |
everything from isotope_model_definition on |
ParsedUsi is a NamedTuple, so its fields stay positional: tuple unpacking depends on it.
IsotopeModel.distribution(mass, max_isotopes) and apex_index keep their signatures. Since 0.9.2 their max_isotopes defaults to None: 32 peaks, extended for high masses whose envelope needs more.
Renamed and removed parameters¶
| 0.8 | 0.9 |
|---|---|
has_peak/get_peak/get_peaks(im_tol=...) |
im_tolerance=..., the name used everywhere else |
get_peak(collision="largest"\|"closest") |
get_peak(peak_selection="largest"\|"closest"), the name match_fragments uses. Default is still "largest"; "all" raises (use get_peaks) |
Spectrum.plot(show_charges=...), plot_spectrum(show_charges=...) |
removed (deprecated in 0.8); use color="charge" or color=None |
Spectrum.plot_table(show_charges=...) |
removed; use color="charge" or color=None |
deconvolve_spectrum(..., is_ppm=True) |
tolerance_unit="ppm" (or "da"), as in Spectrum.deconvolute |
build_plot_table(show_charges=...) and mirror_plot(show_charges=...) keep their option: there
it is not a deprecated alias.
Removed names¶
| 0.8 | 0.9 |
|---|---|
spxtacular.core.JSON_SCHEMA_VERSION (1) |
spxtacular.serialization.SPECTRUM_SCHEMA_VERSION (now 2) for spectra. spxtacular.serialization.JSON_SCHEMA_VERSION still exists and stays 1: it is the chromatogram schema version |
spxtacular.reader.PeakListLookup, spxtacular.reader.ThermoScanLookup |
import from the package root: from spxtacular import PeakListLookup, ThermoScanLookup |
Tolerance units and polarity¶
Units and polarity are plain lowercase strings, typed by tacular. There are no enums or aliases for them in spxtacular.
| 0.8 | 0.9 |
|---|---|
tolerance_type= (every function, method and field) |
tolerance_unit= |
mz_tolerance_type=, im_tolerance_type= (merge, deconvolute, CentroidConfig) |
mz_tolerance_unit=, im_tolerance_unit= |
Chromatogram.tolerance_type |
Chromatogram.tolerance_unit |
DeconvolutionProvenance.tolerance_type, .im_tolerance_type |
.tolerance_unit, .im_tolerance_unit |
ToleranceType.DA, ToleranceType.PPM, "Da", "PPM" |
"da", "ppm" (other spellings raise SpxtacularError) |
ToleranceType, ToleranceLike |
from tacular.types import ToleranceUnit (Literal["da", "ppm"]) |
Polarity.POSITIVE, Polarity.NEGATIVE, "Positive" |
"positive", "negative" |
Polarity, PolarityLike |
from tacular.types import Polarity (Literal["positive", "negative"]) |
enums.DEFAULT_FRAGMENT_TOLERANCE_TYPE |
enums.DEFAULT_FRAGMENT_TOLERANCE_UNIT |
Chromatograms built by extract_xic store "da"/"ppm", where 0.8 stored "Da".
Readers¶
| 0.8 | 0.9 |
|---|---|
MzmlReader(..., extract_dir=...) |
removed (mzmlpy 0.10 no longer extracts gzip files to disk) |
Reader(..., mzml_extract_dir=...) |
removed |
gzip_mode="extract" |
removed; use "auto" (default), "indexed" or "stream" |
access_strategy == "extracted" |
no longer returned; "memory" or "stream" are new values |
mzML scan_number = 0-based spectrum index |
the number in the native id when it identifies the spectrum on its own: scan=19 or Thermo controllerType=0 controllerNumber=1 scan=19 -> 19, index=5 / spectrum=5 -> 5. Otherwise None (Bruker frame=… scan=…, Waters function=… scan=…, SCIEX cycle=…), because the scan value repeats or is missing |
write_ms2 wrote only S <scan> |
also writes the native id as I NativeID when it is not scan=<n> (for example a Bruker mzML id with no scan number, whose S line uses the 1-based position), and Ms2Reader reads it back. write_mgf is unchanged: SCANS only for a set scan_number, TITLE is the native id |
malformed mzML raised mzmlpy's MzmlParseError |
raises SpxtacularError, with the mzmlpy error as __cause__ (a missing spectrum is still KeyError) |
AcquisitionType.UNKNOWN == "UNKNOWN" (values "DDA", ...) |
AcquisitionType is tdfpy's enum; compare members, not strings |
lookup before open() raises RuntimeError |
raises SpxtacularError |
DReader spectra had native_id=None |
MS1 native_id="frame=F", DDA MS2 native_id="precursor=P" (DIA F@wI, PRM F@tT as before). write_mgf of Bruker spectra now writes a TITLE line, and get_by_native_id accepts these ids |
CentroidConfig applied to DDA MS2 |
DDA MS2 spectra use tdfpy's per-precursor merged peaks; CentroidConfig affects MS1, DIA and PRM |
New in 0.9: DReader MS1 spectra carry total_ion_current, and the reader lookup types
(DReaderMs1Lookup, DReaderMs2Lookup, MzmlSpectraLookup, ThermoScanLookup,
PeakListLookup) and a SpectrumLookup protocol are exported from the package root, so
Reader.ms1 / .ms2 can be type-annotated.
Transforms that are already done¶
In 0.8, calling normalize, denoise, centroid, deconvolute or decharge on a
spectrum that had already been through that step warned and returned it unchanged. In 0.9:
| Method | 0.9 behaviour |
|---|---|
normalize |
always rescales with the requested method (no warning) |
denoise, centroid, deconvolute, decharge |
silent no-op: returns a copy, or self when inplace=True |
Code that wrapped these calls in pytest.warns or warnings.catch_warnings can drop that.
Errors¶
| 0.8 | 0.9 |
|---|---|
ValueError for invalid input |
SpxtacularError, a ValueError subclass: except ValueError still works |
da_to_ppm(delta, 0) raises ValueError |
raises tacular's TacularError (also a ValueError) |
da_to_ppm(delta, mz) divides by mz |
divides by abs(mz), so a negative reference keeps the error's sign |
enum coercion (ToleranceType("foo")) raises plain ValueError |
raises SpxtacularError, listing the accepted values. Tolerance units and polarity are lowercase only ("PPM", "Da", "Positive" raise); IMType coercion is case-insensitive |
im_type, polarity accepted any value |
im_type coerced to IMType, polarity checked to be "positive"/"negative", on construction (Precursor, MsnSpectrum, JSON); anything else raises SpxtacularError |
activation_type, analyzer kept any value as given |
a member name in any case or a PSI-MS accession becomes the member ("MS:1002481" -> ActivationType.HCD, "TOF" or "MS:1000484" -> Analyzer.TOF / Analyzer.ORBITRAP); other non-blank strings are kept; non-strings and blanks raise |
a bad JSON payload (wrong types) raised TypeError |
raises SpxtacularError |
a corrupt or non-spectrum .npz raised numpy/zipfile/JSON errors |
raises SpxtacularError, chained to the original |
MatchedFragment¶
MatchedFragment is frozen, slotted and keyword-only. Its fields are unchanged. The new
annotation property returns the match as a paftacular PafAnnotation (with the mass error
in ppm), built on first access and cached.
JSON¶
| 0.8 | 0.9 |
|---|---|
spectrum schema_version 1 |
2 (0.9 still reads version 1) |
precursor key mz |
precursor_mz; new im_type key |
metadata key isolation_im_range |
isolation_ook0_range |
spxtacular/schemas/spectrum-v1.schema.json |
spxtacular/schemas/spectrum-v2.schema.json |
Deconvolution provenance (spectrum.deconvolution) is schema_version 3 with keys tolerance_unit and im_tolerance_unit (was 2 with tolerance_type, im_tolerance_type). Chromatogram JSON is schema version 2 with key tolerance_unit, and spxtacular/schemas/chromatogram-v1.schema.json is now chromatogram-v2.schema.json. 0.9 still reads the old provenance and chromatogram files, lowercasing "Da". spectrl tokens and URLs keep their wire keys, so
tokens written by 0.8 decode in 0.9.
Fragment labels¶
Plot labels come straight from paftacular 2's mzPAF writer, including negative charges
(y3^-2). The include_sequence option controls whether the peptide sequence is part of the
label.
Figures¶
Plots are drawn through a figure layer with a plotly and a matplotlib backend, and restyled for print. Code that only calls the plot functions and shows or saves the result keeps working; code that edits the returned plotly figure or the plot table may need changes.
| 0.8 | 0.9 |
|---|---|
plot functions return go.Figure |
return Any: a plotly Figure by default, a matplotlib Figure with backend="matplotlib", a FigureSpec with backend="spec" |
plot-table columns label_font, label_yshift, label_xanchor |
removed; the figure style sets fonts and label placement |
label_angle default -90 (vertical labels) |
0 (horizontal); label_size is NaN, meaning the style default |
plot_from_table required 13 columns |
requires mz, intensity, series, color, linewidth, opacity, hover, label, label_size, label_color |
axis titles m/z, Intensity, Error (ppm) |
italic m/z, Intensity (×10ⁿ) with absolute_axis=True, Mass error (ppm); the facet plot keeps Error (ppm) |
save_figure(fig, path, scale=2.0) |
save_figure(fig, path, *, scale=None, dpi=None): raster resolution defaults to the style's (192 dpi on screen, 600 for style="paper"); an unsupported suffix raises SpxtacularError |
label_size, label_angle, label_color per row |
still per row; label_color is kept as set, and rows that keep the series colour get the style's label colour |
plot-table linewidth in px |
relative to the default 1.6, scaled to the style's stick width |
no intensity_scale attr: axis from the data |
relative axis (0-100 ticks) when the y label starts with "Relative" and no intensity exceeds 100 |
a missing kaleido raises ImportError (pip install kaleido) |
still ImportError, not SpxtacularError; the message names spxtacular[plotly-export] |
| plotly figures always autosized | labelled "screen" figures keep their 900 px design width so labels stay clear of each other |
facet_plot(mirror_spectrum=) labels the mirror in full |
a label the top panel already shows on a matching peak is not repeated on the mirror; mirror_labels="both" keeps it |
| a default title on every figure | none with style="paper"; pass title= |
backend="matplotlib" n/a |
defaults to style="paper" |
| plotly fonts and sizes | the "screen" style (11 px text, 900 px wide); pass style="paper" or "talk", or layout_kwargs to override |
layout_kwargs still go to plotly's update_layout; with backend="matplotlib" they raise
SpxtacularError.