Spectrum1D
#
Spectrum1D(
frequencies: FloatArray | None = None,
amplitudes: ComplexArray | None = None,
frequency_unit: FrequencyUnit = HERTZ,
reference_frequency_hz: float | None = None,
metadata: dict[str, str] | None = None,
)
1D frequency-domain spectrum.
Attributes:
| Name | Type | Description |
|---|---|---|
frequencies |
FloatNdArray
|
Frequency coordinates |
amplitudes |
ComplexNdArray
|
Complex amplitudes |
frequency_unit |
Unit of frequency coordinates |
|
reference_frequency_hz |
Reference frequency for PPM calculations |
|
metadata |
dict[str, str]
|
Optional metadata dictionary |
processing_history
class-attribute
instance-attribute
#
processing_history: ProcessingHistory | None = (
betterproto2.field(
6, betterproto2.TYPE_MESSAGE, optional=True
)
)
Processing history - read-only audit log of all operations applied to this spectrum
history
property
#
Read-only access to processing history entries.
Returns:
| Type | Description |
|---|---|
Sequence[ProcessingHistoryEntry]
|
Immutable sequence of history entries. Returns empty tuple if |
Sequence[ProcessingHistoryEntry]
|
no history has been recorded yet. |
_frequency_axis
class-attribute
instance-attribute
#
Exactly one frequency axis (enforced by validation)
_amplitudes
class-attribute
instance-attribute
#
_amplitudes: NdComplexArray | None = betterproto2.field(
2, betterproto2.TYPE_MESSAGE, optional=True
)
1D complex amplitude data (shape = [N])
metadata
class-attribute
instance-attribute
#
metadata: dict[str, str] = betterproto2.field(
4,
betterproto2.TYPE_MAP,
map_meta=betterproto2.map_meta(
betterproto2.TYPE_STRING, betterproto2.TYPE_STRING
),
)
Optional metadata
solvent_reference_ppm
class-attribute
instance-attribute
#
solvent_reference_ppm: float | None = betterproto2.field(
5, betterproto2.TYPE_FLOAT, optional=True
)
Solvent reference PPM for calibration (set from sample's solvent info)
visualization_handler
property
writable
#
Plugin visualization handler ID (e.g., 'nanalysis:cosy_2d').
frequencies
instance-attribute
#
amplitudes
instance-attribute
#
frequency_range_hz
property
#
Frequency range of the spectrum in Hz.
_init_history
#
Initialize the processing history.
Called from init of Signal1D/Spectrum1D. Always creates a ProcessingHistory instance - history is always enabled.
_record_history_entry
#
_record_history_entry(
operation: str,
parameters: dict[str, str],
shape_before: tuple[int, ...],
shape_after: tuple[int, ...],
source: str = "tqt_nmr",
) -> None
Record a processing operation to the history.
This is an internal method - not exposed to users. Called by @track_operation decorator and TrackedArray.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operation
|
str
|
Name of the operation (e.g., "correct_phase_manual"). |
required |
parameters
|
dict[str, str]
|
Operation parameters as string key-value pairs. |
required |
shape_before
|
tuple[int, ...]
|
Shape of amplitudes before operation. |
required |
shape_after
|
tuple[int, ...]
|
Shape of amplitudes after operation. |
required |
source
|
str
|
Source of the operation (e.g., "tqt_nmr", "numpy"). |
'tqt_nmr'
|
_copy_history_to
#
Copy processing history to another object.
Used by copy() methods to preserve history lineage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Self
|
Object to copy history to. |
required |
suppress_region
#
suppress_region(
center: float,
width: float,
method: SuppressionMethod = SUPPRESS_INTERPOLATE,
) -> Self
Suppress a spectral region in-place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center
|
float
|
Center of the region to suppress (in spectrum frequency units). |
required |
width
|
float
|
Full width of the region to suppress (in spectrum frequency units). |
required |
method
|
SuppressionMethod
|
Suppression method enum. |
SUPPRESS_INTERPOLATE
|
Returns:
| Type | Description |
|---|---|
Self
|
Self (mutated in-place) for method chaining. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the region is entirely outside the spectrum range. |
reference_to_solvent
#
reference_to_solvent(
solvent: PredefinedSolvent,
nucleus: Nucleus | None = None,
search_window_ppm: float = 0.5,
) -> Self
Reference spectrum to a known solvent peak.
Finds the tallest peak within search_window_ppm of the expected solvent position and shifts the spectrum so that peak is at the correct PPM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
solvent
|
PredefinedSolvent
|
Predefined solvent enum value |
required |
nucleus
|
Nucleus | None
|
Target nucleus (defaults to 1H) |
None
|
search_window_ppm
|
float
|
PPM range to search for solvent peak |
0.5
|
Returns:
| Type | Description |
|---|---|
Self
|
Self with shifted frequencies |
Raises:
| Type | Description |
|---|---|
ValueError
|
If solvent/nucleus combination not found or no peak in window |
reference_to_ppm
#
reference_to_ppm(
target_ppm: float,
search_window_ppm: float = 0.5,
use_centroid: bool = False,
) -> Self
Shift spectrum so the tallest peak near target is at target_ppm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_ppm
|
float
|
The PPM value where the peak should be placed |
required |
search_window_ppm
|
float
|
PPM range around target to search for peak |
0.5
|
use_centroid
|
bool
|
If True, use weighted centroid instead of maximum |
False
|
Returns:
| Type | Description |
|---|---|
Self
|
Self with shifted frequencies |
Raises:
| Type | Description |
|---|---|
ValueError
|
If reference_frequency_hz is not set or no peak found |
reference_manual
#
Manually reference spectrum by specifying current and target PPM.
This is for UI-driven referencing where user clicks on a peak and specifies what PPM it should be shifted to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
click_ppm
|
float
|
The current PPM value of the clicked position |
required |
target_ppm
|
float
|
The PPM value it should be shifted to |
required |
Returns:
| Type | Description |
|---|---|
Self
|
Self with shifted frequencies |
Raises:
| Type | Description |
|---|---|
ValueError
|
If reference_frequency_hz is not set |
align_by_correlation
#
align_by_correlation(reference: Spectrum1D) -> Self
Align this spectrum to a reference using cross-correlation.
Computes the lag that maximizes the cross-correlation between the magnitude spectra, then shifts the frequency axis by that amount.
Both spectra must use the same frequency spacing (same number of points over the same spectral width). The reference spectrum is not modified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reference
|
Spectrum1D
|
Reference spectrum to align to. |
required |
Returns:
| Type | Description |
|---|---|
Self
|
Self with shifted frequencies. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the spectra have different lengths. |
apply_harmonics_mask
#
Masks out harmonics by a cosine wave.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frequency_hz
|
float
|
Frequency of the harmonics to mask. |
50
|
k
|
float
|
Cutoff threshold of the cosine wave (higher = thinner mask). Defaults to 0.99. |
0.99
|
remove_peak
#
Zero-out a small window around peak_frequency_hz.
halves_symmetry_score
#
Compute a simple symmetry score of |z| around central_frequency_hz.
1.0 is perfectly symmetric; 0.0 very asymmetric.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
central_frequency_hz
|
float
|
Central frequency to split the spectrum with. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
Inverse cosine distance. The lower the score the higher the spectrum's symmetry rate. |
find_peaks
#
find_peaks(
min_distance_hz: float | None = None,
min_height_ratio: float = 0.05,
prominence: float | None = None,
use_magnitude: bool = False,
adaptive: bool = False,
snr_threshold: float = 5.0,
) -> list[PeakInfo]
Find peaks in spectrum with advanced filtering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_distance_hz
|
float | None
|
Minimum peak distance (in Hz). If None, auto ~1% span. |
None
|
min_height_ratio
|
float
|
Minimum peak height as ratio of max height (0-1). Ignored if adaptive=True. |
0.05
|
prominence
|
float | None
|
Minimum prominence as ratio of max height (0-1). Filters noise. Ignored if adaptive=True. |
None
|
use_magnitude
|
bool
|
If True, work on |z| else on Re(z). |
False
|
adaptive
|
bool
|
If True, use noise-adaptive thresholds instead of fixed ratios. This detects all peaks above snr_threshold * noise_std. |
False
|
snr_threshold
|
float
|
When adaptive=True, minimum SNR for peak detection. Higher values = fewer false positives but may miss weak peaks. Recommended: 3-5 for clean spectra, 5-10 for noisy spectra. |
5.0
|
analyze_multiplet
#
analyze_multiplet(
center_frequency: float | None = None,
search_width: float = 50.0,
min_height_ratio: float = 0.1,
method: Literal[
"structure", "pascal", "deconvolution"
] = "deconvolution",
bootstrap_iterations: int | Literal["auto"] = "auto",
bic_threshold: float = 15.0,
peaks: list[PeakInfo] | None = None,
) -> MultipletAnalysis
Analyze peaks in a region to identify multiplet pattern and J-coupling.
Supports three methods: - "structure": NEW multiplet structure deconvolution using CWT detection and constrained J-pattern fitting (fastest + most accurate for clean spectra) - "deconvolution": BIC model selection with wild bootstrap (current default) - "pascal": Fast legacy approach using Pascal's triangle matching
Standard patterns identified: - Singlet (s): 1 peak - Doublet (d): 2 peaks, 1:1 ratio - Triplet (t): 3 peaks, 1:2:1 ratio - Quartet (q): 4 peaks, 1:3:3:1 ratio - Quintet (quint): 5 peaks, 1:4:6:4:1 ratio - Sextet (sext): 6 peaks, 1:5:10:10:5:1 ratio - Septet (sept): 7 peaks, 1:6:15:20:15:6:1 ratio - Doublet of doublets (dd): 4 peaks with two different J values - Doublet of triplets (dt): 6 peaks - Multiplet (m): complex/unresolved pattern
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center_frequency
|
float | None
|
Center of the search region (in spectrum units). If None and peaks provided, calculated from peaks. |
None
|
search_width
|
float
|
Width of search region on each side of center. |
50.0
|
min_height_ratio
|
float
|
Minimum peak height as ratio of tallest peak in region. |
0.1
|
method
|
Literal['structure', 'pascal', 'deconvolution']
|
Analysis method: - "structure" (fastest, uses CWT + constrained J-fitting) - "deconvolution" (current default, BIC model selection) - "pascal" (fast legacy heuristic) |
'deconvolution'
|
bootstrap_iterations
|
int | Literal['auto']
|
Number of bootstrap iterations for uncertainty estimation. Use "auto" for SNR-adaptive (default: SNR > 100 uses analytical, SNR 30-100 uses 50 iterations, SNR < 30 uses 200). |
'auto'
|
bic_threshold
|
float
|
BIC improvement threshold for peak count selection. |
15.0
|
peaks
|
list[PeakInfo] | None
|
Pre-detected peaks to use instead of re-detecting. When provided, the "pascal" method uses these directly. For "structure"/"deconvolution", center_frequency and search_width are derived from peaks if not specified. |
None
|
Returns:
| Type | Description |
|---|---|
MultipletAnalysis
|
MultipletAnalysis with pattern, coupling constants, and confidence. |
MultipletAnalysis
|
For deconvolution/structure methods, includes uncertainties and fit metrics. |
analyze_multiplet_groups
#
analyze_multiplet_groups(
min_height_ratio: float = 0.05,
min_distance_hz: float = 2.0,
linkage_method: Literal[
"single", "average", "complete", "ward"
] = "single",
method: Literal[
"structure", "pascal", "deconvolution"
] = "pascal",
) -> MultipletGroupsAnalysis
Detect and analyze all multiplet groups in the spectrum.
Uses hierarchical clustering with Jenks natural breaks optimization to automatically detect multiplet groups without fixed Hz thresholds.
Features: - Dynamic cut height using Jenks natural breaks (no magic constants) - Amplitude-aware splitting for overlapping multiplets - Pattern-based validation (spacing regularity + amplitude symmetry)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_height_ratio
|
float
|
Minimum peak height as ratio of tallest peak (default: 0.05). |
0.05
|
min_distance_hz
|
float
|
Minimum distance between peaks in Hz (default: 2.0). |
2.0
|
linkage_method
|
Literal['single', 'average', 'complete', 'ward']
|
Hierarchical clustering linkage method ('single', 'average', 'complete', 'ward'). Default: 'single'. |
'single'
|
method
|
Literal['structure', 'pascal', 'deconvolution']
|
Analysis method for pattern classification within each group: - "pascal" (default): Fast heuristic matching based on Pascal's triangle - "structure": CWT + constrained J-fitting (may re-detect peaks) - "deconvolution": BIC model selection (may re-detect peaks) |
'pascal'
|
Returns:
| Type | Description |
|---|---|
MultipletGroupsAnalysis
|
MultipletGroupsAnalysis with all detected groups. |
_analyze_multiplet_deconvolution
#
_analyze_multiplet_deconvolution(
center_frequency: float,
search_width: float = 50.0,
min_height_ratio: float = 0.1,
bootstrap_iterations: int | Literal["auto"] = 200,
bic_threshold: float = 15.0,
) -> MultipletAnalysis
Analyze multiplet using deconvolution with BIC model selection.
_analyze_multiplet_structure
#
_analyze_multiplet_structure(
center_frequency: float,
search_width: float = 50.0,
bootstrap_iterations: int | Literal["auto"] = "auto",
) -> MultipletAnalysis
Analyze multiplet using structure deconvolution with CWT detection.
integrate_peaks
#
integrate_peaks(
peaks: list[PeakInfo],
integration_width: float | None = None,
normalize_to: int
| Literal["largest", "smallest"]
| None = None,
use_magnitude: bool = False,
) -> list[IntegrationResult]
Calculate integrated areas for detected peaks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo]
|
List of peaks to integrate (from find_peaks()) |
required |
integration_width
|
float | None
|
Width of integration region on each side of peak (in spectrum units). If None, auto-calculates based on peak width at half-height. |
None
|
normalize_to
|
int | Literal['largest', 'smallest'] | None
|
Index of peak to normalize to 1.00. If None, no normalization. |
None
|
use_magnitude
|
bool
|
If True, integrate |z|, else integrate Re(z). |
False
|
Returns:
| Type | Description |
|---|---|
list[IntegrationResult]
|
List of IntegrationResult with area and bounds for each peak. |
integrate_shifts
#
integrate_shifts(
peaks: list[PeakInfo],
normalize_to: int
| Literal["largest", "smallest"]
| None = "largest",
use_magnitude: bool = False,
group_tolerance_hz: float = 20.0,
) -> list[ShiftIntegrationResult]
Integrate chemical shifts (multiplet groups) rather than individual peaks.
Groups peaks by their group_id (from multiplet analysis) or by proximity, then integrates the full region spanning each group. Uses integrate_peaks internally and sums per-peak areas within each group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
peaks
|
list[PeakInfo]
|
List of peaks (from find_peaks(), ideally with group_id set) |
required |
normalize_to
|
int | Literal['largest', 'smallest'] | None
|
Which group to normalize to 1.00. |
'largest'
|
use_magnitude
|
bool
|
If True, integrate |z|, else integrate Re(z). |
False
|
group_tolerance_hz
|
float
|
If peaks lack group_id, group by proximity (Hz). |
20.0
|
Returns:
| Type | Description |
|---|---|
list[ShiftIntegrationResult]
|
List of ShiftIntegrationResult, one per chemical shift group. |
deconvolve_region
#
deconvolve_region(
f_min: float,
f_max: float,
n_peaks: int | None = None,
bootstrap_iterations: int = 200,
) -> DeconvolutionResult
Full peak deconvolution for a spectral region.
Uses phased Lorentzian fitting with BIC model selection to automatically determine peak count and positions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f_min
|
float
|
Minimum frequency (Hz) |
required |
f_max
|
float
|
Maximum frequency (Hz) |
required |
n_peaks
|
int | None
|
Number of peaks to fit (auto-detect if None) |
None
|
bootstrap_iterations
|
int
|
Bootstrap iterations for uncertainty |
200
|
Returns:
| Type | Description |
|---|---|
DeconvolutionResult
|
DeconvolutionResult with fitted peaks and uncertainties |
estimate_snr
#
Estimate signal-to-noise ratio of the spectrum.
Uses Median Absolute Deviation (MAD) for robust noise estimation, which is insensitive to peaks in the spectrum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal_region
|
tuple[float, float] | None
|
Optional (f_min, f_max) to restrict signal calculation. If None, uses entire spectrum. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Signal-to-noise ratio (linear scale) |
correct_baseline
#
correct_baseline(
method: Literal[
"median", "polynomial", "als", "edge", "valley"
] = "polynomial",
edge_fraction: float = 0.1,
poly_order: int = 1,
als_lambda: float = 100000.0,
als_p: float = 0.01,
als_iterations: int = 10,
valley_prominence: float = 0.02,
valley_preserve_integral: bool = True,
**kwargs,
) -> Self
Apply baseline correction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Literal['median', 'polynomial', 'als', 'edge', 'valley']
|
Baseline correction method: - "polynomial": Fit polynomial to edge regions (default, recommended) - "als": Asymmetric least squares (robust for complex baselines) - "edge": Simple DC offset from edge regions - "median": Subtract median of edge regions - "valley": Interpolate baseline through spectrum valleys (minima between peaks) |
'polynomial'
|
edge_fraction
|
float
|
Fraction of spectrum edges to use for baseline estimation (default: 0.1) |
0.1
|
poly_order
|
int
|
Polynomial order for "polynomial" method (default: 1 = linear) |
1
|
als_lambda
|
float
|
Smoothness parameter for ALS (larger = smoother, default: 1e5) |
100000.0
|
als_p
|
float
|
Asymmetry parameter for ALS (smaller = more asymmetric, default: 0.01) |
0.01
|
als_iterations
|
int
|
Number of ALS iterations (default: 10) |
10
|
valley_prominence
|
float
|
Min prominence for valley detection as fraction of max (default: 0.02) |
0.02
|
valley_preserve_integral
|
bool
|
If True, rescale after correction to preserve integral (default: True) |
True
|
**kwargs
|
Additional parameters (ignored for compatibility) |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
correct_phase_auto
#
correct_phase_auto(
method: Literal["acme", "peak_minima"] = "acme",
p0_guess: float = 0,
p1_guess: float = 0,
peak_width: int = 100,
return_phases: bool = False,
verbose: bool = False,
multi_start: bool = True,
) -> Self | tuple[Self, tuple[float, float]]
Apply automatic phase correction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Literal['acme', 'peak_minima']
|
Automatic phase correction method |
'acme'
|
p0_guess
|
float
|
Initial zero order phase in degrees |
0
|
p1_guess
|
float
|
Initial first order phase in degrees |
0
|
peak_width
|
int
|
Width of the ROI for peak_minima optimization (number of surrounding indexes) |
100
|
return_phases
|
bool
|
Whether to return the optimized values of phases [p0, p1] in addition to the phased data |
False
|
verbose
|
bool
|
Whether to log the optimization process |
False
|
multi_start
|
bool
|
Try multiple starting p0 values to avoid local minima (default True) |
True
|
Returns:
| Type | Description |
|---|---|
Self | tuple[Self, tuple[float, float]]
|
Self if return_phases=False (default) or tuple(Self, optimization_result) otherwise |
correct_phase_manual
#
correct_phase_manual(
p0: float,
p1: float = 0.0,
method: Literal["linear", "exponential"] = "linear",
) -> Self
Apply manual phase correction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
p0
|
float
|
Zero-order phase correction (degrees) |
required |
p1
|
float
|
First-order phase correction (degrees) |
0.0
|
method
|
Literal['linear', 'exponential']
|
Manual phase correction method |
'linear'
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
to_ppm
#
Convert spectrum to PPM scale.
Returns:
| Type | Description |
|---|---|
Self
|
New spectrum in PPM units (or copy if already in PPM) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If reference frequency is not set |
smooth
#
Apply Gaussian smoothing to spectrum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float
|
Standard deviation for Gaussian kernel |
4.0
|
preserve_integral
|
bool
|
Whether to preserve total integral |
True
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
trim_frequency_range
#
Trim spectrum to specified frequency range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
f_min
|
float
|
Minimum frequency (in spectrum's current units) |
-inf
|
f_max
|
float
|
Maximum frequency (in spectrum's current units) |
inf
|
Returns:
| Type | Description |
|---|---|
Self
|
A new, trimmed spectrum (the original is left unchanged). |
center_spectrum
#
Center the spectrum by shifting the main peak to 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
Centering method ("max" or "centroid") |
'max'
|
Returns:
| Type | Description |
|---|---|
Self
|
Self |
convert_frequency_unit
#
convert_frequency_unit(
target_unit: FrequencyUnit,
reference_freq_hz: float | None = None,
) -> Self
Convert spectrum to different frequency unit.
shift
#
Shift the frequency axis by a constant amount.
If shift_unit is PPM, requires reference_frequency_hz.
interpolate_at
#
Interpolate spectrum at given frequencies.
normalize
#
Normalize spectrum amplitudes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
Literal['max', 'rms', 'unit']
|
Normalization method: - "max": Divide by maximum absolute value - "rms": Divide by RMS value - "unit": Divide by L2 norm |
'max'
|
Returns:
| Type | Description |
|---|---|
Self
|
Self for method chaining |
frequency_shift
#
Shift the frequency axis (used by center_spectrum).
apply_window_notch
#
Apply a notch filter around center_hz (used by remove_peak).
__getitem__
#
- slice/int -> Spectrum1D view copy using NumPy-first arrays
- array of frequencies -> complex amplitudes via linear interpolation
to_signal
#
to_signal(
time_unit: TimeUnit = MICROSECOND,
carrier_offset_hz: float | None = None,
half: bool = False,
) -> Signal1D
Convert spectrum back to time-domain signal via inverse FFT.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
time_unit
|
TimeUnit
|
Time unit for the output signal (default: MICROSECOND) |
MICROSECOND
|
carrier_offset_hz
|
float | None
|
Carrier offset in Hz. If provided, this offset is subtracted from frequencies before inverse FFT and stored in the output signal. |
None
|
half
|
bool
|
If True, return only the first half of the signal. Useful when the spectrum produces a mirrored/symmetric signal after IFFT. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
Signal1D |
Signal1D
|
Time-domain signal |