Example #1
0
    def tospectrum(self,
                   signal,
                   fwhm=0.1,
                   forceFwhm=True,
                   autoAlign=True,
                   iterLimit=None,
                   relThreshold=0.,
                   pickingHeight=0.90,
                   baseline=None):
        """Fit modeled profiles to spectrum using tmp peaklist.
            signal (numpy array) - m/z / intensity pairs
            fwhm (float) - defaut fwhm
            forceFwhm (bool) - use default fwhm
            autoAlign (bool) - automatic m/z shift
            iterLimit (int) - maximum number of iterations
            pickingHeight (float) - peak picking height for centroiding
            relThreshold (float) - relative intensity threshold
            baseline (numpy array) - signal baseline
        """

        # crop signal to relevant m/z range
        i1 = mod_signal.locate(signal, self.mzrange[0])
        i2 = mod_signal.locate(signal, self.mzrange[1])
        signal = signal[i1:i2]

        # get peaklist from signal
        peaklist = mod_peakpicking.labelscan(signal=signal,
                                             pickingHeight=pickingHeight,
                                             relThreshold=relThreshold,
                                             baseline=baseline)

        # remove shoulder peaks
        peaklist.remshoulders(fwhm=fwhm)

        # correct signal baseline
        if baseline != None:
            self.spectrum = mod_signal.subbase(signal, baseline)

        # fit to peaklist
        return self.topeaklist(
            peaklist=peaklist,
            fwhm=fwhm,
            forceFwhm=forceFwhm,
            autoAlign=autoAlign,
            iterLimit=iterLimit,
            relThreshold=relThreshold,
        )
Example #2
0
def labelpoint(signal, mz, baseline=None):
    """Return labeled peak at given x-value.
        signal (numpy array) - signal data points
        mz (float) - x-value to label
        baseline (numpy array) - signal baseline
    """

    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"

   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"

    # check signal data
    if len(signal) == 0:
        return None

    # check m/z value
    if mz <= 0:
        return None

    # get peak intensity
    ai = mod_signal.intensity(signal, mz)
    if not ai:
        return None

    # get peak baseline and s/n
    base = 0.0
    sn = None
    if baseline == None:
        base, noise = mod_signal.noise(signal, x=mz)
        if noise:
            sn = (ai - base) / noise
    else:
        idx = mod_signal.locate(baseline, mz)
        if (idx > 0) and (idx < len(baseline)):
            base = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][1]), (baseline[idx][0], baseline[idx][1]), x=mz)
            noise = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][2]), (baseline[idx][0], baseline[idx][2]), x=mz)
            if noise:
                sn = (ai - base) / noise

    # check peak intensity
    if ai <= base:
        return None

    # get peak fwhm
    height = base + (ai - base) * 0.5
    fwhm = mod_signal.width(signal, mz, height)

    # make peak object
    peak = obj_peak.peak(mz=mz, ai=ai, base=base, sn=sn, fwhm=fwhm)

    return peak
Example #3
0
def labelpoint(signal, mz, baseline=None):
    """Return labeled peak at given x-value.
        signal (numpy array) - signal data points
        mz (float) - x-value to label
        baseline (numpy array) - signal baseline
    """
    
    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"
    
   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"
    
    # check signal data
    if len(signal) == 0:
        return None
    
    # check m/z value
    if mz <= 0:
        return None
    
    # get peak intensity
    ai = mod_signal.intensity(signal, mz)
    if not ai:
        return None
    
    # get peak baseline and s/n
    base = 0.0
    sn = None
    if baseline == None:
        base, noise = mod_signal.noise(signal, x=mz)
        if noise:
            sn = (ai - base) / noise
    else:
        idx = mod_signal.locate(baseline, mz)
        if (idx > 0) and (idx < len(baseline)):
            base = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][1]), (baseline[idx][0], baseline[idx][1]), x=mz)
            noise = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][2]), (baseline[idx][0], baseline[idx][2]), x=mz)
            if noise:
                sn = (ai - base) / noise
    
    # check peak intensity
    if ai <= base:
        return None
    
    # get peak fwhm
    height = base + (ai - base) * 0.5
    fwhm = mod_signal.width(signal, mz, height)
    
    # make peak object
    peak = obj_peak.peak(mz=mz, ai=ai, base=base, sn=sn, fwhm=fwhm)
    
    return peak
Example #4
0
 def tospectrum(self, signal, fwhm=0.1, forceFwhm=True, autoAlign=True, iterLimit=None, relThreshold=0., pickingHeight=0.90, baseline=None):
     """Fit modeled profiles to spectrum using tmp peaklist.
         signal (numpy array) - m/z / intensity pairs
         fwhm (float) - defaut fwhm
         forceFwhm (bool) - use default fwhm
         autoAlign (bool) - automatic m/z shift
         iterLimit (int) - maximum number of iterations
         pickingHeight (float) - peak picking height for centroiding
         relThreshold (float) - relative intensity threshold
         baseline (numpy array) - signal baseline
     """
     
     # crop signal to relevant m/z range
     i1 = mod_signal.locate(signal, self.mzrange[0])
     i2 = mod_signal.locate(signal, self.mzrange[1])
     signal = signal[i1:i2]
     
     # get peaklist from signal
     peaklist = mod_peakpicking.labelscan(
         signal = signal,
         pickingHeight = pickingHeight,
         relThreshold = relThreshold,
         baseline = baseline
     )
     
     # remove shoulder peaks
     peaklist.remshoulders(fwhm=fwhm)
     
     # correct signal baseline
     if baseline != None:
         self.spectrum = mod_signal.subbase(signal, baseline)
     
     # fit to peaklist
     return self.topeaklist(
         peaklist = peaklist,
         fwhm = fwhm,
         forceFwhm = forceFwhm,
         autoAlign = autoAlign,
         iterLimit = iterLimit,
         relThreshold = relThreshold,
     )
Example #5
0
def labelscan(signal, minX=None, maxX=None, pickingHeight=0.75, absThreshold=0., relThreshold=0., snThreshold=0., baseline=None):
    """Return centroided peaklist for given data points.
        signal (numpy array) - signal data points
        minX (float) - x-range start
        maxX (float) - x-range end
        pickingHeight (float) - centroiding height
        absThreshold (float) - absolute intensity threshold
        relThreshold (float) - relative intensity threshold
        snThreshold (float) - signal to noise threshold
        baseline (numpy array) - signal baseline
    """

    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"

   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"

    # crop data
    if minX != None and maxX != None:
        i1 = mod_signal.locate(signal, minX)
        i2 = mod_signal.locate(signal, maxX)
        signal = signal[i1:i2]

    # check data points
    if len(signal) == 0:
        return obj_peaklist.peaklist([])

    # get local maxima
    buff = []
    basepeak = mod_signal.basepeak(signal)
    threshold = max(signal[basepeak][1] * relThreshold, absThreshold)
    for peak in mod_signal.maxima(signal):
        if peak[1] >= threshold:
            buff.append( [peak[0], peak[1], 0., None, None] ) # mz, ai, base, sn, fwhm

    CHECK_FORCE_QUIT()

    # get peaks baseline and s/n
    basepeak = 0.0
    if baseline != None:
        for peak in buff:
            idx = mod_signal.locate(baseline, peak[0])
            if (idx > 0) and (idx < len(baseline)):
                p1 = baseline[idx-1]
                p2 = baseline[idx]
                peak[2] = mod_signal.interpolate( (p1[0], p1[1]), (p2[0], p2[1]), x=peak[0])
                noise = mod_signal.interpolate( (p1[0], p1[2]), (p2[0], p2[2]), x=peak[0])
                intens = peak[1] - peak[2]
                if noise:
                    peak[3] = intens / noise
                if intens > basepeak:
                    basepeak = intens

    CHECK_FORCE_QUIT()

    # remove peaks bellow threshold
    threshold = max(basepeak * relThreshold, absThreshold)
    candidates = []
    for peak in buff:
        if peak[0] > 0 and (peak[1] - peak[2]) >= threshold and (not peak[3] or peak[3] >= snThreshold):
            candidates.append(peak)

    # make centroides
    if pickingHeight < 1.:
        buff = []
        previous = None
        for peak in candidates:

            CHECK_FORCE_QUIT()

            # calc peak height
            h = ((peak[1]-peak[2]) * pickingHeight) + peak[2]

            # get centroid indexes
            idx = mod_signal.locate(signal, peak[0])
            if (idx == 0) or (idx == len(signal)):
                continue

            ileft = idx-1
            while (ileft > 0) and (signal[ileft][1] > h):
                ileft -= 1

            iright = idx
            while (iright < len(signal)-1) and (signal[iright][1] > h):
                iright += 1

            # calculate peak mz
            leftMZ = mod_signal.interpolate(signal[ileft], signal[ileft+1], y=h)
            rightMZ = mod_signal.interpolate(signal[iright-1], signal[iright], y=h)
            peak[0] = (leftMZ + rightMZ)/2.

            # get peak intensity
            intens = mod_signal.intensity(signal, peak[0])
            if intens and intens <= peak[1]:
                peak[1] = intens
            else:
                continue

            # try to group with previous peak
            if previous != None and leftMZ < previous:
                if peak[1] > buff[-1][1]:
                    buff[-1] = peak
                    previous = rightMZ
            else:
                buff.append(peak)
                previous = rightMZ

        # store as candidates
        candidates = buff

    CHECK_FORCE_QUIT()

    # get peaks baseline and s/n
    basepeak = 0.0
    if baseline != None:
        for peak in candidates:
            idx = mod_signal.locate(baseline, peak[0])
            if (idx > 0) and (idx < len(baseline)):
                p1 = baseline[idx-1]
                p2 = baseline[idx]
                peak[2] = mod_signal.interpolate( (p1[0], p1[1]), (p2[0], p2[1]), x=peak[0])
                noise = mod_signal.interpolate( (p1[0], p1[2]), (p2[0], p2[2]), x=peak[0])
                intens = peak[1] - peak[2]
                if noise:
                    peak[3] = intens / noise
                if intens > basepeak:
                    basepeak = intens

    CHECK_FORCE_QUIT()

    # remove peaks bellow threshold and calculate fwhm
    threshold = max(basepeak * relThreshold, absThreshold)
    centroides = []
    for peak in candidates:
        if peak[0] > 0 and (peak[1] - peak[2]) >= threshold and (not peak[3] or peak[3] >= snThreshold):
            peak[4] = mod_signal.width(signal, peak[0], (peak[2] + ((peak[1] - peak[2]) * 0.5)))
            centroides.append(obj_peak.peak(mz=peak[0], ai=peak[1], base=peak[2], sn=peak[3], fwhm=peak[4]))

    # return peaklist object
    return obj_peaklist.peaklist(centroides)
Example #6
0
def labelpeak(signal, mz=None, minX=None, maxX=None, pickingHeight=0.75, baseline=None):
    """Return labeled peak in given m/z range.
        signal (numpy array) - signal data points
        mz (float) - x-value to label
        minX (float) - x-range start
        maxX (float) - x-range end
        pickingHeight (float) - centroiding height
        baseline (numpy array) - signal baseline
    """

    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"

   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"

    # check m/z value or range
    if mz == None and minX == None and maxX == None:
        raise TypeError, "m/z value or range must be specified!"

    # check signal data
    if len(signal) == 0:
        return None

    # check m/z value
    if mz != None:
        minX = mz
    if minX <= 0:
        return False

    # get index of given m/z or range maximum
    if mz != None:
        imax = mod_signal.locate(signal, mz)
    else:
        i1 = mod_signal.locate(signal, minX)
        i2 = mod_signal.locate(signal, maxX)
        imax = i1
        if i1 != i2:
            imax += mod_signal.basepeak(signal[i1:i2])
    if (imax == 0) or (imax == len(signal)):
        return None

    # get centroid height
    h = signal[imax][1] * pickingHeight
    if baseline != None:
        idx = mod_signal.locate(baseline, signal[imax][0])
        if (idx > 0) and (idx < len(baseline)):
            base = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][1]), (baseline[idx][0], baseline[idx][1]), x=signal[imax][0])
            h = ((signal[imax][1] - base) * pickingHeight) + base

    # get centroid
    ileft = imax-1
    while (ileft > 0) and (signal[ileft][1] > h):
        ileft -= 1

    iright = imax
    while (iright < len(signal)-1) and (signal[iright][1] > h):
        iright += 1

    leftMZ = mod_signal.interpolate(signal[ileft], signal[ileft+1], y=h)
    rightMZ = mod_signal.interpolate(signal[iright-1], signal[iright], y=h)

    # check range
    if mz == None and (leftMZ < minX or rightMZ > maxX) and (leftMZ != rightMZ):
        return None

    # label peak in the newly found selection
    if mz != None and leftMZ != rightMZ:
        peak = labelpeak(
            signal = signal,
            minX = leftMZ,
            maxX = rightMZ,
            pickingHeight = pickingHeight,
            baseline = baseline
        )

    # label current point
    else:
        peak = labelpoint(
            signal = signal,
            mz = ((leftMZ + rightMZ)/2.),
            baseline = baseline
        )

    return peak
Example #7
0
    def topoints(self, points, fwhm=0.1, autoAlign=True, iterLimit=None):
        """Fit modeled profiles to given points.
            points (numpy array or list) - m/z / intensity pairs
            fwhm (float) - defaut fwhm
            autoAlign (bool) - automatic m/z shift
            iterLimit (int) - maximum number of iterations
        """

        self.fwhm = fwhm

        # reset previous results
        self.composition = {}
        self.ncomposition = {}
        self.average = 0.
        self.model = numpy.array([])
        for x in self.models:
            self.models[x][2] = 0.0  # abs comp
            self.models[x][3] = 0.0  # rel comp

        # check points type
        if not isinstance(points, numpy.ndarray):
            points = numpy.array(points)

        # crop points to relevant m/z range
        i1 = mod_signal.locate(points, self.mzrange[0])
        i2 = mod_signal.locate(points, self.mzrange[1])
        self.data = numpy.array(points[i1:i2])

        # check data
        if len(self.data) == 0:
            return False

        # auto align data and theoretical pattern
        if autoAlign:
            self._alignData()

        # split data to raster and intensities
        xAxis, yAxis = numpy.hsplit(self.data, 2)
        raster = xAxis.flatten()
        intensities = yAxis.flatten()

        # model profiles
        models, exchanged = self._makeModels(raster, reset=False)

        # fit data to models
        fit = self._leastSquare(intensities, models, iterLimit=iterLimit)
        if not sum(fit):
            return False

        f = 1. / sum(fit)
        for i, abundance in enumerate(fit):
            self.models[exchanged[i]][2] = abundance
            self.models[exchanged[i]][3] = f * abundance

        # calc average exchange
        for x in self.models:
            self.average += x * self.models[x][3]

        # get compositions
        for x in sorted(self.models.keys()):
            self.composition[x] = self.models[x][2]
            self.ncomposition[x] = self.models[x][3]

        # get calculated points
        raster.shape = (-1, 1)
        intensities = numpy.sum(models * [[x] for x in fit], axis=0)
        intensities.shape = (-1, 1)
        self.model = numpy.concatenate((raster, intensities), axis=1).copy()

        return True
Example #8
0
 def topoints(self, points, fwhm=0.1, autoAlign=True, iterLimit=None):
     """Fit modeled profiles to given points.
         points (numpy array or list) - m/z / intensity pairs
         fwhm (float) - defaut fwhm
         autoAlign (bool) - automatic m/z shift
         iterLimit (int) - maximum number of iterations
     """
     
     self.fwhm = fwhm
     
     # reset previous results
     self.composition = {}
     self.ncomposition = {}
     self.average = 0.
     self.model = numpy.array([])
     for x in self.models:
         self.models[x][2] = 0.0 # abs comp
         self.models[x][3] = 0.0 # rel comp
     
     # check points type
     if not isinstance(points, numpy.ndarray):
         points = numpy.array(points)
     
     # crop points to relevant m/z range
     i1 = mod_signal.locate(points, self.mzrange[0])
     i2 = mod_signal.locate(points, self.mzrange[1])
     self.data = numpy.array(points[i1:i2])
     
     # check data
     if len(self.data) == 0:
         return False
     
     # auto align data and theoretical pattern
     if autoAlign:
         self._alignData()
     
     # split data to raster and intensities
     xAxis, yAxis = numpy.hsplit(self.data, 2)
     raster = xAxis.flatten()
     intensities = yAxis.flatten()
     
     # model profiles
     models, exchanged = self._makeModels(raster, reset=False)
     
     # fit data to models
     fit = self._leastSquare(intensities, models, iterLimit=iterLimit)
     if not sum(fit):
         return False
     
     f = 1./sum(fit)
     for i, abundance in enumerate(fit):
         self.models[exchanged[i]][2] = abundance
         self.models[exchanged[i]][3] = f*abundance
     
     # calc average exchange
     for x in self.models:
         self.average += x * self.models[x][3]
     
     # get compositions
     for x in sorted(self.models.keys()):
         self.composition[x] = self.models[x][2]
         self.ncomposition[x] = self.models[x][3]
     
     # get calculated points
     raster.shape = (-1,1)
     intensities = numpy.sum(models * [[x] for x in fit], axis=0)
     intensities.shape = (-1,1)
     self.model = numpy.concatenate((raster, intensities), axis=1).copy()
     
     return True
Example #9
0
def labelscan(signal, minX=None, maxX=None, pickingHeight=0.75, absThreshold=0., relThreshold=0., snThreshold=0., baseline=None):
    """Return centroided peaklist for given data points.
        signal (numpy array) - signal data points
        minX (float) - x-range start
        maxX (float) - x-range end
        pickingHeight (float) - centroiding height
        absThreshold (float) - absolute intensity threshold
        relThreshold (float) - relative intensity threshold
        snThreshold (float) - signal to noise threshold
        baseline (numpy array) - signal baseline
    """
    
    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"
    
   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"
    
    # crop data
    if minX != None and maxX != None:
        i1 = mod_signal.locate(signal, minX)
        i2 = mod_signal.locate(signal, maxX)
        signal = signal[i1:i2]
    
    # check data points
    if len(signal) == 0:
        return obj_peaklist.peaklist([])
    
    # get local maxima
    buff = []
    basepeak = mod_signal.basepeak(signal)
    threshold = max(signal[basepeak][1] * relThreshold, absThreshold)
    for peak in mod_signal.maxima(signal):
        if peak[1] >= threshold:
            buff.append( [peak[0], peak[1], 0., None, None] ) # mz, ai, base, sn, fwhm
    
    CHECK_FORCE_QUIT()
    
    # get peaks baseline and s/n
    basepeak = 0.0
    if baseline != None:
        for peak in buff:
            idx = mod_signal.locate(baseline, peak[0])
            if (idx > 0) and (idx < len(baseline)):
                p1 = baseline[idx-1]
                p2 = baseline[idx]
                peak[2] = mod_signal.interpolate( (p1[0], p1[1]), (p2[0], p2[1]), x=peak[0])
                noise = mod_signal.interpolate( (p1[0], p1[2]), (p2[0], p2[2]), x=peak[0])
                intens = peak[1] - peak[2]
                if noise:
                    peak[3] = intens / noise
                if intens > basepeak:
                    basepeak = intens
    
    CHECK_FORCE_QUIT()
    
    # remove peaks bellow threshold
    threshold = max(basepeak * relThreshold, absThreshold)
    candidates = []
    for peak in buff:
        if peak[0] > 0 and (peak[1] - peak[2]) >= threshold and (not peak[3] or peak[3] >= snThreshold):
            candidates.append(peak)
    
    # make centroides
    if pickingHeight < 1.:
        buff = []
        previous = None
        for peak in candidates:
            
            CHECK_FORCE_QUIT()
            
            # calc peak height
            h = ((peak[1]-peak[2]) * pickingHeight) + peak[2]
            
            # get centroid indexes
            idx = mod_signal.locate(signal, peak[0])
            if (idx == 0) or (idx == len(signal)):
                continue
            
            ileft = idx-1
            while (ileft > 0) and (signal[ileft][1] > h):
                ileft -= 1
            
            iright = idx
            while (iright < len(signal)-1) and (signal[iright][1] > h):
                iright += 1
            
            # calculate peak mz
            leftMZ = mod_signal.interpolate(signal[ileft], signal[ileft+1], y=h)
            rightMZ = mod_signal.interpolate(signal[iright-1], signal[iright], y=h)
            peak[0] = (leftMZ + rightMZ)/2.
            
            # get peak intensity
            intens = mod_signal.intensity(signal, peak[0])
            if intens and intens <= peak[1]:
                peak[1] = intens
            else:
                continue
            
            # try to group with previous peak
            if previous != None and leftMZ < previous:
                if peak[1] > buff[-1][1]:
                    buff[-1] = peak
                    previous = rightMZ
            else:
                buff.append(peak)
                previous = rightMZ
        
        # store as candidates
        candidates = buff
    
    CHECK_FORCE_QUIT()
    
    # get peaks baseline and s/n
    basepeak = 0.0
    if baseline != None:
        for peak in candidates:
            idx = mod_signal.locate(baseline, peak[0])
            if (idx > 0) and (idx < len(baseline)):
                p1 = baseline[idx-1]
                p2 = baseline[idx]
                peak[2] = mod_signal.interpolate( (p1[0], p1[1]), (p2[0], p2[1]), x=peak[0])
                noise = mod_signal.interpolate( (p1[0], p1[2]), (p2[0], p2[2]), x=peak[0])
                intens = peak[1] - peak[2]
                if noise:
                    peak[3] = intens / noise
                if intens > basepeak:
                    basepeak = intens
    
    CHECK_FORCE_QUIT()
    
    # remove peaks bellow threshold and calculate fwhm
    threshold = max(basepeak * relThreshold, absThreshold)
    centroides = []
    for peak in candidates:
        if peak[0] > 0 and (peak[1] - peak[2]) >= threshold and (not peak[3] or peak[3] >= snThreshold):
            peak[4] = mod_signal.width(signal, peak[0], (peak[2] + ((peak[1] - peak[2]) * 0.5)))
            centroides.append(obj_peak.peak(mz=peak[0], ai=peak[1], base=peak[2], sn=peak[3], fwhm=peak[4]))
    
    # return peaklist object
    return obj_peaklist.peaklist(centroides)
Example #10
0
def labelpeak(signal, mz=None, minX=None, maxX=None, pickingHeight=0.75, baseline=None):
    """Return labeled peak in given m/z range.
        signal (numpy array) - signal data points
        mz (float) - x-value to label
        minX (float) - x-range start
        maxX (float) - x-range end
        pickingHeight (float) - centroiding height
        baseline (numpy array) - signal baseline
    """
    
    # check signal type
    if not isinstance(signal, numpy.ndarray):
        raise TypeError, "Signal must be NumPy array!"
    
   # check baseline type
    if baseline != None and not isinstance(baseline, numpy.ndarray):
        raise TypeError, "Baseline must be NumPy array!"
    
    # check m/z value or range
    if mz == None and minX == None and maxX == None:
        raise TypeError, "m/z value or range must be specified!"
    
    # check signal data
    if len(signal) == 0:
        return None
    
    # check m/z value
    if mz != None:
        minX = mz
    if minX <= 0:
        return False
    
    # get index of given m/z or range maximum
    if mz != None:
        imax = mod_signal.locate(signal, mz)
    else:
        i1 = mod_signal.locate(signal, minX)
        i2 = mod_signal.locate(signal, maxX)
        imax = i1
        if i1 != i2:
            imax += mod_signal.basepeak(signal[i1:i2])
    if (imax == 0) or (imax == len(signal)):
        return None
    
    # get centroid height
    h = signal[imax][1] * pickingHeight
    if baseline != None:
        idx = mod_signal.locate(baseline, signal[imax][0])
        if (idx > 0) and (idx < len(baseline)):
            base = mod_signal.interpolate( (baseline[idx-1][0], baseline[idx-1][1]), (baseline[idx][0], baseline[idx][1]), x=signal[imax][0])
            h = ((signal[imax][1] - base) * pickingHeight) + base
    
    # get centroid
    ileft = imax-1
    while (ileft > 0) and (signal[ileft][1] > h):
        ileft -= 1
    
    iright = imax
    while (iright < len(signal)-1) and (signal[iright][1] > h):
        iright += 1
    
    leftMZ = mod_signal.interpolate(signal[ileft], signal[ileft+1], y=h)
    rightMZ = mod_signal.interpolate(signal[iright-1], signal[iright], y=h)
    
    # check range
    if mz == None and (leftMZ < minX or rightMZ > maxX) and (leftMZ != rightMZ):
        return None
    
    # label peak in the newly found selection
    if mz != None and leftMZ != rightMZ:
        peak = labelpeak(
            signal = signal,
            minX = leftMZ,
            maxX = rightMZ,
            pickingHeight = pickingHeight,
            baseline = baseline
        )
    
    # label current point
    else:
        peak = labelpoint(
            signal = signal,
            mz = ((leftMZ + rightMZ)/2.),
            baseline = baseline
        )
    
    return peak