Source code for deimos.calibration

import numpy as np
from scipy.interpolate import interp1d
from scipy.stats import linregress

import deimos


[docs]class CCSCalibration: ''' Performs calibration and stores result to enable convenient application. Attributes ---------- buffer_mass : float Mass of the buffer gas used in ion mobility experiment. beta : float Slope of calibration curve. tfix : float Intercept of calibration curve. fit : dict of float Fit parameters of calibration curve. ''' def __init__(self): ''' Initializes :obj:`~deimos.calibration.CCSCalibration` object. ''' # Initialize variables self.buffer_mass = None self.beta = None self.tfix = None self.fit = {'r': None, 'p': None, 'se': None}
[docs] def _check(self): ''' Helper method to check for calibration parameters. ''' if (self.beta is None) or (self.tfix is None): raise ValueError('Must perform calibration to yield beta and ' 'tfix.')
[docs] def calibrate(self, mz=None, ta=None, ccs=None, q=None, beta=None, tfix=None, buffer_mass=28.013, power=False): ''' Performs calibration if `mz`, `ta`, `ccs`, and `q` arrays are provided, otherwise calibration parameters `beta` and `tfix` must be supplied directly. Parameters ---------- mz : :obj:`~numpy.array` Calibration mass-to-charge ratios. ta : :obj:`~numpy.array` Calibration arrival times. ccs : :obj:`~numpy.array` Calibration collision cross sections. q : :obj:`~numpy.array` Calibration nominal charges. beta : float Provide calibration parameter "beta" (slope) directly. tfix : float Provide calibration parameter "tfix" (intercept) directly. buffer_mass : float Mass of the buffer gas. power : bool Indicate whether to use linearize power function for calibration, i.e. in traveling wave ion moblility spectrometry. ''' # Buffer mass self.buffer_mass = buffer_mass # Power function indicator self.power = power # Calibrant arrays supplied if (mz is not None) and (ta is not None) and (ccs is not None) \ and (q is not None): self.mz = np.array(mz) self.ta = np.array(ta) self.ccs = np.array(ccs) self.q = np.array(q) # Derived variables self.gamma = np.sqrt( self.mz * self.q / (self.mz * self.q + self.buffer_mass)) / self.q self.reduced_ccs = self.ccs * self.gamma # Linear regression if self.power: beta, tfix, r, p, se = linregress(np.log(self.reduced_ccs), np.log(self.ta)) else: beta, tfix, r, p, se = linregress(self.reduced_ccs, self.ta) # Store params self.beta = beta self.tfix = tfix self.fit['r'] = r self.fit['p'] = p self.fit['se'] = se return # Beta and tfix supplied if (beta is not None) and (tfix is not None): # store params self.beta = beta self.tfix = tfix return raise ValueError('Must supply arrays for calibration or calibration ' 'parameters.')
[docs] def arrival2ccs(self, mz, ta, q=1): ''' Calculates collision cross section (CCS) from arrival time, m/z, and nominal charge, according to calibration parameters. Parameters ---------- mz : float or list of float Feature mass-to-charge ratio. ta : float or list of float Feature arrival time (ms). q : int or list of int Feature nominal charge. Returns ------- :obj:`~numpy.array` Feature collision cross section (A^2). ''' # Check for required attributes self._check() # Cast to numpy array mz = np.array(mz) ta = np.array(ta) q = np.array(q) # Derived variables gamma = np.sqrt(mz * q / (mz * q + self.buffer_mass)) / q # Power model if self.power: return np.exp((np.log(ta) - self.tfix) / self.beta) / gamma # Linear model return (ta - self.tfix) / (self.beta * gamma)
[docs] def ccs2arrival(self, mz, ccs, q=1): ''' Calculates arrival time from collsion cross section (CCS), m/z, and nominal charge, according to calibration parameters. Parameters ---------- mz : float or list of float Feature mass-to-charge ratio. ccs : float or list of float Feature collision cross section (A^2). q : int or list of int Feature nominal charge. Returns ------- :obj:`~numpy.array` Feature arrival time (ms). ''' # Check for required attributes self._check() # Cast to numpy array mz = np.array(mz) ccs = np.array(ccs) q = np.array(q) # Derived variables gamma = np.sqrt(mz * q / (mz * q + self.buffer_mass)) / q # Power model if self.power: return np.exp(self.beta * np.log(gamma * ccs) + self.tfix) # Linear model else: return self.beta * gamma * ccs + self.tfix
[docs]def calibrate_ccs(mz=None, ta=None, ccs=None, q=None, beta=None, tfix=None, buffer_mass=28.013, power=False): ''' Convenience function for :class:`~deimos.calibration.CCSCalibration`. Performs calibration if `mz`, `ta`, `ccs`, and `q` arrays are provided, otherwise calibration parameters `beta` and `tfix` must be supplied directly. Parameters ---------- mz : :obj:`~numpy.array` Calibration mass-to-charge ratios. ta : :obj:`~numpy.array` Calibration arrival times. ccs : :obj:`~numpy.array` Calibration collision cross sections. q : :obj:`~numpy.array` Calibration nominal charges. beta : float Provide calibration parameter "beta" (slope) directly. tfix : float Provide calibration parameter "tfix" (intercept) directly. buffer_mass : float Mass of the buffer gas. power : bool Indicate whether to use linearize power function for calibration, i.e. in traveling wave ion moblility spectrometry. Returns ------- :obj:`~deimos.calibration.CCSCalibration` Instance of calibrated `~deimos.calibration.CCSCalibration` object. ''' # Initialize calibration instance ccs_cal = CCSCalibration() # Perform calibration ccs_cal.calibrate(mz=mz, ta=ta, ccs=ccs, q=q, beta=beta, tfix=tfix, buffer_mass=buffer_mass, power=power) return ccs_cal
[docs]def tunemix(features, mz=[112.985587, 301.998139, 601.978977, 1033.988109, 1333.968947, 1633.949786], ccs=[108.4, 139.8, 179.9, 254.2, 283.6, 317.7], q=[1, 1, 1, 1, 1, 1], buffer_mass=28.013, mz_tol=200E-6, dt_tol=0.04, power=False): ''' Provided tune mix data with known calibration ions (i.e. known m/z, CCS, and nominal charge), determine the arrival time for each to define a CCS calibration. Parameters ---------- mz : :obj:`~numpy.array` Calibration mass-to-charge ratios. ccs : :obj:`~numpy.array` Calibration collision cross sections. q : :obj:`~numpy.array` Calibration nominal charges. buffer_mass : float Mass of the buffer gas. mz_tol : float Tolerance in ppm to isolate tune ion. dt_tol : float Fractional tolerance to define drift time window bounds. power : bool Indicate whether to use linearize power function for calibration, i.e. in traveling wave ion moblility spectrometry. Returns ------- :obj:`~deimos.calibration.CCSCalibration` Instance of calibrated `~deimos.calibration.CCSCalibration` object. ''' # Cast to numpy array mz = np.array(mz) ccs = np.array(ccs) q = np.array(q) # Check lengths deimos.utils.check_length([mz, ccs, q]) # Iterate tune ions ta = [] for mz_i, ccs_i, q_i in zip(mz, ccs, q): # Slice ms1 subset = deimos.slice(features, by='mz', low=mz_i - 0.1 * mz_tol, high=mz_i + mz_i * 0.9 * mz_tol) # Extract dt info dt_profile = deimos.collapse(subset, keep='drift_time') dt_i = dt_profile.sort_values(by='intensity', ascending=False)[ 'drift_time'].values[0] dt_profile = deimos.locate( dt_profile, by='drift_time', loc=dt_i, tol=dt_tol * dt_i).sort_values(by='drift_time') # X and Y arrays x = dt_profile['drift_time'].values y = dt_profile['intensity'].values # Interpolate spline spl = interp1d(x, y, kind='quadratic') # Higher resolution x-axis newx = np.arange(x.min(), x.max(), 0.001) # Evaluate newy = spl(newx) # Take argmax dt_j = newx[np.argmax(newy)] ta.append(dt_j) # Calibrate ta = np.array(ta) return deimos.calibration.calibrate_ccs(mz=mz, ta=ta, ccs=ccs, q=q, buffer_mass=buffer_mass, power=power)