Source code for crikit.measurement.peakamps

"""
Measurement methods to quantify peak relationships

Note
-----

For complex-valued measurements, all methods perform the math separately for real and imag
"""

import numpy as _np

[docs]class MeasurePeak: """ Meausure peak amplitude. Parameters ---------- f1 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1): self.output = None self.f1 = f1
[docs] def calculate(self, signal): self.output = self._calc(signal) return self.output
[docs] @classmethod def measure(cls, signal, f1): inst = cls(f1) return inst.calculate(signal)
def _calc(self, signal): output = signal[..., self.f1] return output
[docs]class AbstractMeasureTwo: """ Abstract class for measurements that take 2 things """ def __init__(self, f1, f2): """ Parameters ---------- f1 : int Frequency 1 entry f2 : int Frequency 2 entry Attributes ---------- output : ndarray Output of measurement operation """ self.output = None self.f1 = f1 self.f2 = f2
[docs] def calculate(self, signal): if _np.iscomplexobj(signal): self.output = self._calc(signal.real) + 1j*self._calc(signal.imag) else: self.output = self._calc(signal) return self.output
[docs] @classmethod def measure(cls, signal, f1, f2): inst = cls(f1, f2) return inst.calculate(signal)
def _calc(self, signal): raise NotImplementedError
[docs]class AbstractMeasureTwoOrdered(AbstractMeasureTwo): """ Abstract class for measurements that take 2 things, where f1 < f2 """ def __init__(self, f1, f2): if f1 <= f2: super().__init__(f1, f2) else: super().__init__(f2, f1)
[docs]class AbstractMeasureThree: """ Abstract class for measurements that take 3 things """ def __init__(self, f1, f2, f3): """ Parameters ---------- f1 : int Frequency 1 entry f2 : int Frequency 2 entry f3 : int Frequency 3 entry Attributes ---------- output : ndarray Output of measurement operation """ self.output = None self.f1 = f1 self.f2 = f2 self.f3 = f3
[docs] def calculate(self, signal): if _np.iscomplexobj(signal): self.output = self._calc(signal.real) + 1j*self._calc(signal.imag) else: self.output = self._calc(signal) return self.output
[docs] @classmethod def measure(cls, signal, f1, f2, f3): inst = cls(f1, f2, f3) return inst.calculate(signal)
def _calc(self, signal): raise NotImplementedError
[docs]class MeasurePeakMinus (AbstractMeasureTwo): """ Meausure the difference (subtraction) of two peaks (f1 - f2). Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = signal[..., self.f1] - signal[..., self.f2] return output
[docs]class MeasurePeakAdd(AbstractMeasureTwo): """ Meausure the addition of two peaks (f1 + f2). Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = signal[..., self.f1] + signal[..., self.f2] return output
[docs]class MeasurePeakMultiply(AbstractMeasureTwo): """ Meausure the multiplication of two peak. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = signal[..., self.f1] * signal[..., self.f2] return output
[docs]class MeasurePeakDivide(AbstractMeasureTwo): """ Meausure the ratio (division) of two peaks. f1/f2 Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = signal[..., self.f1] / signal[..., self.f2] return output
[docs]class MeasurePeakSum(AbstractMeasureTwoOrdered): """ Meausure the summation of all amplitudes between (inclusive) two peak locations. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.sum(signal[..., self.f1:self.f2+1], axis=-1) return output
[docs]class MeasurePeakSumAbsReImag(AbstractMeasureTwoOrdered): """ Meausure the summation of the absolute value of all amplitudes between (inclusive) two peak locations. Note that the absolute value is performed on the real and imaginary parts separately -- it's not a true absolute value. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.sum(_np.abs(signal[..., self.f1:self.f2+1]), axis=-1) return output
[docs]class MeasurePeakMax(AbstractMeasureTwoOrdered): """ Meausure the maximum across the range [f1,f2]. Note that real and imag are treated separately. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.max(signal[..., self.f1:self.f2+1], axis=-1) return output
[docs]class MeasurePeakMin(AbstractMeasureTwoOrdered): """ Meausure the minimum across the range [f1,f2]. Note that real and imag are treated separately. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.min(signal[..., self.f1:self.f2+1], axis=-1) return output
[docs]class MeasurePeakMaxAbs(AbstractMeasureTwoOrdered): """ Meausure the maximum across the absolute value across the range [f1,f2]. Note that real and imag are treated separately. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.max(_np.abs(signal[..., self.f1:self.f2+1]), axis=-1) return output
[docs]class MeasurePeakMinAbs(AbstractMeasureTwoOrdered): """ Meausure the summation of all amplitudes between (inclusive) two peak locations. Note that real and imag are treated separately. Parameters ---------- f1 : int Peak location in pixel coordinates f2 : int Peak location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, f1, f2): super().__init__(f1,f2) def _calc(self, signal): output = _np.min(_np.abs(signal[..., self.f1:self.f2+1]), axis=-1) return output
[docs]class MeasurePeakBWTroughs(AbstractMeasureThree): """ Meausure the amplitude of a peak between troughs. Parameters ---------- pk : int Peak location in pixel coordinates tr1 : int Trough 1 location in pixel coordinates tr2 : int Trough 2 location in pixel coordinates Attributes ---------- output : float or ndarray Amplitude of peak Methods ------- calculate : Calculate the amplitude Static Methods -------------- measure : Same as calculate but static (returns the amplitude directly) """ def __init__(self, pk, tr1, tr2): if tr1 <= tr2: super().__init__(pk, tr1, tr2) else: super().__init__(pk, tr2, tr1) def _calc(self, signal): slope = (signal[..., self.f3]-signal[..., self.f2])/ \ (self.f3 - self.f2) output = signal[..., self.f1] - (slope*(self.f1 - self.f2) + \ signal[..., self.f2]) return output
if __name__ == '__main__': data_m, data_n, data_p = [2, 3, 5] spectrum = _np.array([0,1,2,3,4]) hsi = _np.dot(_np.ones((data_m*data_n, 1)), spectrum[None,:]) hsi = hsi.reshape((data_m, data_n, data_p)) output = MeasurePeakBWTroughs.measure(hsi,2,0,4) print(output) m_inst = MeasurePeakBWTroughs(2,0,4) m_inst.calculate(hsi) print(m_inst.output) # import matplotlib.pyplot as _plt # print('\n\n--------- 1-Signal Test--------') # amp = 100 # pk = 50 # tr1 = 20 # tr2 = 80 # x = _np.arange(100) # signal = amp*_np.exp(-(x-pk)**2/(10**2)) # baseline = x # y = signal + baseline # _plt.plot(x, y.T, label='Signal') # _plt.plot(x,baseline, label='baseline') # _plt.plot(x, signal.T,label='Signal - Baseline') # # non-static method # #pbwt = MeasurePeakBWTroughs(pk=pk, tr1=tr1, tr2=tr2) # #out = pbwt.calculate(y) # # static method # out = MeasurePeakBWTroughs.measure(signal, pk, tr1, tr2) # _plt.plot((pk, pk), (0, out), 'k', lw=3, label='Calculated Amp') # _plt.xlabel('X') # _plt.ylabel('Amplitude (au)') # _plt.legend(loc='best') # _plt.show() # print('Actual peak amp: {:.2f}. Retrieved peak amp: {:.2f}.'.format(amp, out)) # print('Within 1% agreement: {}'.format(_np.isclose(amp, out, rtol=.01))) # print('\n\n--------- 2D Simple Test--------') # _plt.figure() # amp = 100 # pk = 50 # tr1 = 20 # tr2 = 80 # N=2 # x = _np.arange(100) # signal = amp*_np.exp(-(x-pk)**2/(10**2)) # baseline = x # y = signal + baseline # mask = _np.ones((N,N)) # y = _np.dot(mask[...,None], y[None,:]) # _plt.plot(y.reshape((-1,x.size)).T, label='Signal') # _plt.plot(x,baseline, label='baseline') # _plt.plot(x, signal.T,label='Signal - Baseline') # out = MeasurePeakBWTroughs.measure(signal, pk, tr1, tr2) # for out_pk in out.ravel(): # _plt.plot((pk, pk), (0, out_pk), 'k', lw=3, label='Calculated Amp') # _plt.xlabel('X') # _plt.ylabel('Amplitude (au)') # _plt.legend(loc='best') # print('Actual peak(s) amp: {:.2f}. Retrieved peak amps: {}.'.format(amp, out.ravel())) # print('Within 1% agreement: {}'.format(_np.isclose(amp, out.ravel(), rtol=.01))) # print('All agree within 1%: {}'.format(_np.allclose(amp, out.ravel(), rtol=.01))) # _plt.show() # print('\n\n--------- 2D More Complicated Test--------') # _plt.figure() # amp = 100 # pk = 50 # tr1 = 20 # tr2 = 80 # N=2 # x = _np.arange(100) # signal = amp*_np.exp(-(x-pk)**2/(10**2)) # mask = _np.ones((N,N)) # rndm = _np.random.randint(0,10,size=(N,N)) # baseline = _np.dot((rndm*mask)[...,None],x[None,:]) # y = signal[None,None,:] + baseline # #y = _np.dot(mask[...,None], y[None,:]) # _plt.plot(y.reshape((-1,x.size)).T, label='Signal') # _plt.plot(x,baseline.reshape((-1,x.size)).T, label='baseline') # _plt.plot(x, signal.T,label='Signal - Baseline') # #pbwt = MeasurePeakBWTroughs(pk=pk, tr1=tr1, tr2=tr2) # #out = pbwt.calculate(y) # out = MeasurePeakBWTroughs.measure(signal, pk, tr1, tr2) # for out_pk in out.ravel(): # _plt.plot((pk, pk), (0, out_pk), 'k', lw=3, label='Calculated Amp') # _plt.xlabel('X') # _plt.ylabel('Amplitude (au)') # _plt.legend(loc='best') # print('Actual peak(s) amp: {:.2f}. Retrieved peak amps: {}.'.format(amp, out.ravel())) # print('Within 1% agreement: {}'.format(_np.isclose(amp, out.ravel(), rtol=.01))) # print('All agree within 1%: {}'.format(_np.allclose(amp, out.ravel(), rtol=.01))) # _plt.show()