示例#1
0
def rubberband(lbd, z):
    """
    method: https://dsp.stackexchange.com/questions/2725/how-to-perform-a-rubberband-correction-on-spectroscopic-data
    
    Arguments:
        lbd (nparray): wavelengths
        z (nparray): spectrum
        
    Returns:
        baseline (nparray): baseline
    """

    # 3rd party imports
    from scipy.spatial import ConvexHull
    import numpy as np

    v = ConvexHull([[X[0], X[1]] for X in zip(lbd, z)]).vertices
    v = np.roll(
        v, -v.argmin()
    )  # Rotate convex hull vertices until they start from the lowest one
    v = v[:v.argmax()]  # Leave only the ascending part

    baseline = np.interp(
        lbd, lbd[v],
        z[v])  # Create baseline using linear interpolation between vertices

    return baseline
    def rubberband(x, y):
        """ `rubberband` subtraction function """
        v = ConvexHull(np.column_stack([x, y])).vertices

        ascending = True if x[0] < x[1] else False

        if ascending:
            # rotate vertices until they start from the lowest one
            v = np.roll(v, -v.argmin())
            v = v[:v.argmax() + 1]
        else:
            # rotate vertices until they start from the highest one
            v = np.roll(v, -v.argmax())
            v = v[:v.argmin() + 1]

        # Create baseline using linear interpolation between vertices
        return y - np.interp(x, x[v], y[v])
示例#3
0
    def rb_test(self):
        """
        Background/baseline subtraction using 'rubberband correction' method
        """
        # get data from combo box
        curr_x = self.drop_down_x.itemText(self.drop_down_x.currentIndex())
        curr_y = self.drop_down_y.itemText(self.drop_down_y.currentIndex())

        # get vertex points
        curr_spec = None
        curr_spec_plt = None
        spectra_list = self.my_collec.spectra
        for i, spectrum in enumerate(spectra_list):
            if (spectrum.x == float(curr_x)) and (spectrum.y == float(curr_y)):
                curr_spec = self.my_collec.spectra[i]

        # Separate x and y values into two separate lists
        x = curr_spec.info[1]
        y = curr_spec.info[0]

        # Zip to create np array of (x, y) tuples
        zipped = np.column_stack((x, y))

        # Find the convex hull, where array v contains the indices of the convex hull
        # vertex points arranged in a counter clockwise direction
        v = ConvexHull(zipped).vertices

        # Rotate convex hull vertices until v starts from the lowest one
        v = np.roll(v, -v.argmin())

        # Leave only the ascending part
        v = v[:v.argmax()]

        # Create baseline using linear interpolation between vertices
        bsln = np.interp(x, x[v], y[v])

        # Find new y values using baseline
        new_y = []
        for i, point in enumerate(y):
            point -= bsln[i]
            new_y.append(point)

        # convert list of x and new y's into an np array of tuples
        x_y = np.column_stack((x, new_y))

        # plot the new spectrum
        self.ax.scatter(*zip(*x_y))

        self.ax.grid(which='both')

        # add left vert line
        self.vert_line = self.ax.axvline(x=0)

        # add right vert line
        self.vert_line_2 = self.ax.axvline(x=0)

        self.canvas.draw()
示例#4
0
 def rubberband_one(yy):
     # Find the convex hull
     v = ConvexHull(np.column_stack((x, yy))).vertices
     # Rotate convex hull vertices until they start from the lowest one
     v = np.roll(v, -v.argmin())
     # Leave only the ascending part
     v = v[:v.argmax() + 1]
     # Create baseline using linear interpolation between vertices
     b = np.interp(x, x[v], yy[v])
     return b
def rubberband(x, y, opts):
    flipped = False
    if x[0] > x[-1]:
        if opts.debug:
            print "\tUnivariateSpline only accepts increasing x order. Flipping x and y arrays\n"
        x = np.flip(x, 0)
        y = np.flip(y, 0)
        flipped = True
    assert x[0] < x[-1]
    # Find the convex hull
    if opts.verbose: "Print using rubberband baseline correction method"

    v = ConvexHull(np.array(zip(x, y))).vertices
    if opts.debug:
        print "Vertices of Convex Hull: "
        for point in v:
            print "(%5i, %5i)" % (x[point], y[point])

    # Rotate convex hull vertices until they start from the lowest one
    v = np.roll(v, -v.argmin())

    if opts.doPlot:
        fig, axarr = plt.subplots(2, 1)  #,figsize=(6,12))
        axarr[0].scatter(x, y, s=1)
        for point in v:
            axarr[0].scatter(x[point], y[point], s=15, color='r')

    # Leave only the ascending part
    if opts.debug: print v
    v = v[:v.argmax() + 1]

    if opts.doPlot:
        for point in v:
            axarr[0].scatter(x[point], y[point], s=25, color='b')
        axarr[0].plot(x,
                      np.interp(x, x[v], y[v]),
                      'k--',
                      label="Convex hull (rubberband) fit")

        #        axarr[1].plot(x, y - np.interp(x, x[v], y[v]))
        spl = UnivariateSpline(x[v], y[v])
        axarr[1].plot(x, y - spl(x))

        basename = os.path.splitext(opts.inputFileName)[0]
        fig.savefig(basename + '.rubberband_fitting.pdf', format='pdf')

    ##Spline fit to vertices
    spl = UnivariateSpline(x[v], y[v])

    if flipped: x = np.flip(x)  ##Flip x values back to match original data
    return spl(x)
示例#6
0
def baseline(x_input, y_input, bir, method, **kwargs):
    """Allows subtracting a baseline under a x y spectrum.

    Parameters
    ----------
    x_input : ndarray
        x values.
    y_input : ndarray
        y values.
    bir : ndarray
        Contain the regions of interest, organised per line. 
        For instance, roi = np.array([[100., 200.],[500.,600.]]) will 
        define roi between 100 and 200 as well as between 500 and 600.
        Note: This is NOT used by the "als" and "arPLS" algorithms, but still is a requirement when calling the function.
        bir and method probably will become args in a futur iteration of rampy to solve this.
    methods : str
        "poly": polynomial fitting, with splinesmooth the degree of the polynomial.
        "unispline": spline with the UnivariateSpline function of Scipy, splinesmooth is 
                     the spline smoothing factor (assume equal weight in the present case);
        "gcvspline": spline with the gcvspl.f algorythm, really robust. 
                     Spectra must have x, y, ese in it, and splinesmooth is the smoothing factor;
                     For gcvspline, if ese are not provided we assume ese = sqrt(y). 
                     Requires the installation of gcvspline with a "pip install gcvspline" call prior to use;
        "exp": exponential background;
        "log": logarythmic background;
        "rubberband": rubberband baseline fitting;
        "als": automatic least square fitting following Eilers and Boelens 2005;
        "arPLS": automatic baseline fit using the algorithm from Baek et al. 2015 
                 Baseline correction using asymmetrically reweighted penalized least squares smoothing, Analyst 140: 250-257.

    kwargs
    ------
    polynomial_order : Int
        The degree of the polynomial (0 for a constant), default = 1.
    s : Float
        spline smoothing coefficient for the unispline and gcvspline algorithms.
    lam : Float
        float, the lambda smoothness parameter for the ALS and ArPLS algorithms. Typical values are between 10**2 to 10**9, default = 10**5.
    p : Float
        float, for the ALS algorithm, advised value between 0.001 to 0.1, default = 0.01.
    ratio : float
        ratio parameter of the arPLS algorithm. default = 0.01.
    niter : Int
        number of iteration of the ALS algorithm, default = 10.
    p0_exp : List
        containg the starting parameter for the exp baseline fit with curve_fit. Default = [1.,1.,1.].
    p0_log : List
        containg the starting parameter for the log baseline fit with curve_fit. Default = [1.,1.,1.,1.].

    Returns
    -------
    out1 : ndarray
        Contain the corrected signal.
    out2 : ndarray
        Contain the baseline.

    """
    # we get the signals in the bir
    yafit_unscaled = get_portion_interest(x_input, y_input, bir)

    # signal standard standardization with sklearn
    # this helps for polynomial fitting
    X_scaler = preprocessing.StandardScaler().fit(x_input.reshape(-1, 1))
    Y_scaler = preprocessing.StandardScaler().fit(y_input.reshape(-1, 1))

    # transformation
    x = X_scaler.transform(x_input.reshape(-1, 1))
    y = Y_scaler.transform(y_input.reshape(-1, 1))

    yafit = np.copy(yafit_unscaled)
    yafit[:, 0] = X_scaler.transform(yafit_unscaled[:, 0].reshape(-1, 1))[:, 0]
    yafit[:, 1] = Y_scaler.transform(yafit_unscaled[:, 1].reshape(-1, 1))[:, 0]

    y = y.reshape(len(y_input))

    if method == 'poly':

        # optional parameters
        poly_order = kwargs.get('polynomial_order', 1)

        coeffs = np.polyfit(yafit[:, 0], yafit[:, 1], poly_order)

        baseline_fitted = np.polyval(coeffs, x)

    elif method == 'unispline':

        # optional parameters
        splinesmooth = kwargs.get('s', 2.0)

        # fit of the baseline
        coeffs = UnivariateSpline(yafit[:, 0], yafit[:, 1], s=splinesmooth)

        baseline_fitted = coeffs(x)

    elif method == 'gcvspline':

        try:
            from gcvspline import gcvspline, splderivative
        except ImportError:
            print(
                'ERROR: Install gcvspline to use this mode (needs a working FORTRAN compiler).'
            )

        # optional parameters
        splinesmooth = kwargs.get('s', 2.0)

        # Spline baseline with mode 1 of gcvspl.f, see gcvspline documentation
        c, wk, ier = gcvspline(
            yafit[:, 0],
            yafit[:, 1],
            np.sqrt(np.abs(yafit[:, 1])),
            splinesmooth,
            splmode=1)  # gcvspl with mode 1 and smooth factor

        baseline_fitted = splderivative(x, yafit[:, 0], c)

    elif method == 'gaussian':
        ### Baseline is of the type y = a*exp(-log(2)*((x-b)/c)**2)
        # optional parameters
        p0_gauss = kwargs.get('p0_gaussian', [1., 1., 1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.gaussian,
                                 yafit[:, 0],
                                 yafit[:, 1],
                                 p0=p0_gauss)

        baseline_fitted = rampy.gaussian(x, coeffs[0], coeffs[1], coeffs[2])

    elif method == 'exp':
        ### Baseline is of the type y = a*exp(b*(x-xo))
        # optional parameters
        p0_exp = kwargs.get('p0_exp', [1., 1., 1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.funexp,
                                 yafit[:, 0],
                                 yafit[:, 1],
                                 p0=p0_exp)

        baseline_fitted = rampy.funexp(x, coeffs[0], coeffs[1], coeffs[2])

    elif method == 'log':
        ### Baseline is of the type y = a*exp(b*(x-xo))
        # optional parameters
        p0_log = kwargs.get('p0_log', [1., 1., 1., 1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.funlog,
                                 yafit[:, 0],
                                 yafit[:, 1],
                                 p0=p0_log)

        baseline_fitted = rampy.funlog(x, coeffs[0], coeffs[1], coeffs[2],
                                       coeffs[3])

    elif method == 'rubberband':
        # code from this stack-exchange forum
        #https://dsp.stackexchange.com/questions/2725/how-to-perform-a-rubberband-correction-on-spectroscopic-data

        # Find the convex hull
        v = ConvexHull(np.array([x, y])).vertices

        # Rotate convex hull vertices until they start from the lowest one
        v = np.roll(v, -v.argmin())
        # Leave only the ascending part
        v = v[:v.argmax()]

        # Create baseline using linear interpolation between vertices
        baseline_fitted = np.interp(x, x[v], y[v])

    elif method == 'als':
        # Matlab code in Eilers et Boelens 2005
        # Python addaptation found on stackoverflow: https://stackoverflow.com/questions/29156532/python-baseline-correction-library

        # optional parameters
        lam = kwargs.get('lam', 1.0 * 10**5)
        p = kwargs.get('p', 0.01)
        niter = kwargs.get('niter', 10)

        # starting the algorithm
        L = len(y)
        D = sparse.csc_matrix(np.diff(np.eye(L), 2))
        w = np.ones(L)
        for i in range(niter):
            W = sparse.spdiags(w, 0, L, L)
            Z = W + lam * D.dot(D.transpose())
            z = sparse.linalg.spsolve(Z, w * y)
            w = p * (y > z) + (1 - p) * (y < z)

        baseline_fitted = z

    elif method == 'arPLS':
        # Adaptation of the Matlab code in Baek et al 2015

        # optional parameters
        lam = kwargs.get('lam', 1.0 * 10**5)
        ratio = kwargs.get('ratio', 0.01)

        N = len(y)
        D = sparse.csc_matrix(np.diff(np.eye(N), 2))
        w = np.ones(N)

        while True:
            W = sparse.spdiags(w, 0, N, N)
            Z = W + lam * D.dot(D.transpose())
            z = sparse.linalg.spsolve(Z, w * y)
            d = y - z
            # make d- and get w^t with m and s
            dn = d[d < 0]
            m = np.mean(dn)
            s = np.std(dn)
            wt = 1.0 / (1 + np.exp(2 * (d - (2 * s - m)) / s))
            # check exit condition and backup
            if norm(w - wt) / norm(w) < ratio:
                break
            w = wt

        baseline_fitted = z

    return y_input.reshape(-1, 1) - Y_scaler.inverse_transform(
        baseline_fitted.reshape(-1, 1)), Y_scaler.inverse_transform(
            baseline_fitted.reshape(-1, 1))
示例#7
0
def baseline(x_input,y_input,bir,method, **kwargs):
    """Allows subtracting a baseline under a x y spectrum.

    Parameters
    ----------
    x_input : ndarray
        x values.
    y_input : ndarray
        y values.
    bir : ndarray
        Contain the regions of interest, organised per line. 
        For instance, roi = np.array([[100., 200.],[500.,600.]]) will 
        define roi between 100 and 200 as well as between 500 and 600.
        Note: This is NOT used by the "als" and "arPLS" algorithms, but still is a requirement when calling the function.
        bir and method probably will become args in a futur iteration of rampy to solve this.
    methods : str
        "poly": polynomial fitting, with splinesmooth the degree of the polynomial.
        "unispline": spline with the UnivariateSpline function of Scipy, splinesmooth is 
                     the spline smoothing factor (assume equal weight in the present case);
        "gcvspline": spline with the gcvspl.f algorythm, really robust. 
                     Spectra must have x, y, ese in it, and splinesmooth is the smoothing factor;
                     For gcvspline, if ese are not provided we assume ese = sqrt(y). 
                     Requires the installation of gcvspline with a "pip install gcvspline" call prior to use;
        "exp": exponential background;
        "log": logarythmic background;
        "rubberband": rubberband baseline fitting;
        "als": automatic least square fitting following Eilers and Boelens 2005;
        "arPLS": automatic baseline fit using the algorithm from Baek et al. 2015 
                 Baseline correction using asymmetrically reweighted penalized least squares smoothing, Analyst 140: 250-257.

    kwargs
    ------
    polynomial_order : Int
        The degree of the polynomial (0 for a constant), default = 1.
    s : Float
        spline smoothing coefficient for the unispline and gcvspline algorithms.
    lam : Float
        float, the lambda smoothness parameter for the ALS and ArPLS algorithms. Typical values are between 10**2 to 10**9, default = 10**5.
    p : Float
        float, for the ALS algorithm, advised value between 0.001 to 0.1, default = 0.01.
    ratio : float
        ratio parameter of the arPLS algorithm. default = 0.01.
    niter : Int
        number of iteration of the ALS algorithm, default = 10.
    p0_exp : List
        containg the starting parameter for the exp baseline fit with curve_fit. Default = [1.,1.,1.].
    p0_log : List
        containg the starting parameter for the log baseline fit with curve_fit. Default = [1.,1.,1.,1.].

    Returns
    -------
    out1 : ndarray
        Contain the corrected signal.
    out2 : ndarray
        Contain the baseline.

    """
    # we get the signals in the bir
    yafit_unscaled = get_portion_interest(x_input,y_input,bir)

    # signal standard standardization with sklearn
    # this helps for polynomial fitting
    X_scaler = preprocessing.StandardScaler().fit(x_input.reshape(-1, 1))
    Y_scaler = preprocessing.StandardScaler().fit(y_input.reshape(-1, 1))

    # transformation
    x = X_scaler.transform(x_input.reshape(-1, 1))
    y = Y_scaler.transform(y_input.reshape(-1, 1))

    yafit = np.copy(yafit_unscaled)
    yafit[:,0] = X_scaler.transform(yafit_unscaled[:,0].reshape(-1, 1))[:,0]
    yafit[:,1] = Y_scaler.transform(yafit_unscaled[:,1].reshape(-1, 1))[:,0]

    y = y.reshape(len(y_input))

    if method == 'poly':

        # optional parameters
        poly_order = kwargs.get('polynomial_order',1)

        coeffs = np.polyfit(yafit[:,0],yafit[:,1],poly_order)

        baseline_fitted = np.polyval(coeffs,x)

    elif method == 'unispline':

        # optional parameters
        splinesmooth = kwargs.get('s',2.0)

        # fit of the baseline
        coeffs = UnivariateSpline(yafit[:,0],yafit[:,1], s=splinesmooth)

        baseline_fitted = coeffs(x)

    elif method == 'gcvspline':

        try:
            from gcvspline import gcvspline, splderivative
        except ImportError:
            print('ERROR: Install gcvspline to use this mode (needs a working FORTRAN compiler).')
            
        # optional parameters
        splinesmooth = kwargs.get('s',2.0)

        # Spline baseline with mode 1 of gcvspl.f, see gcvspline documentation
        c, wk, ier = gcvspline(yafit[:,0],yafit[:,1],np.sqrt(np.abs(yafit[:,1])),splinesmooth,splmode = 1) # gcvspl with mode 1 and smooth factor

        baseline_fitted = splderivative(x,yafit[:,0],c)

    elif method == 'gaussian':
        ### Baseline is of the type y = a*exp(-log(2)*((x-b)/c)**2)
        # optional parameters
        p0_gauss = kwargs.get('p0_gaussian',[1.,1.,1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.gaussian,yafit[:,0],yafit[:,1],p0 = p0_gauss)

        baseline_fitted = rampy.gaussian(x,coeffs[0],coeffs[1],coeffs[2])

    elif method == 'exp':
        ### Baseline is of the type y = a*exp(b*(x-xo))
        # optional parameters
        p0_exp = kwargs.get('p0_exp',[1.,1.,1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.funexp,yafit[:,0],yafit[:,1],p0 = p0_exp)

        baseline_fitted = rampy.funexp(x,coeffs[0],coeffs[1],coeffs[2])

    elif method == 'log':
        ### Baseline is of the type y = a*exp(b*(x-xo))
        # optional parameters
        p0_log = kwargs.get('p0_log',[1.,1.,1.,1.])
        ## fit of the baseline
        coeffs, pcov = curve_fit(rampy.funlog,yafit[:,0],yafit[:,1],p0 = p0_log)

        baseline_fitted = rampy.funlog(x,coeffs[0],coeffs[1],coeffs[2],coeffs[3])

    elif method == 'rubberband':
        # code from this stack-exchange forum
        #https://dsp.stackexchange.com/questions/2725/how-to-perform-a-rubberband-correction-on-spectroscopic-data

        # Find the convex hull
        v = ConvexHull(np.array([x, y])).vertices

        # Rotate convex hull vertices until they start from the lowest one
        v = np.roll(v, -v.argmin())
        # Leave only the ascending part
        v = v[:v.argmax()]

        # Create baseline using linear interpolation between vertices
        baseline_fitted = np.interp(x, x[v], y[v])

    elif method == 'als':
        # Matlab code in Eilers et Boelens 2005
        # Python addaptation found on stackoverflow: https://stackoverflow.com/questions/29156532/python-baseline-correction-library

        # optional parameters
        lam = kwargs.get('lam',1.0*10**5)
        p = kwargs.get('p',0.01)
        niter = kwargs.get('niter',10)

        # starting the algorithm
        L = len(y)
        D = sparse.csc_matrix(np.diff(np.eye(L), 2))
        w = np.ones(L)
        for i in range(niter):
            W = sparse.spdiags(w, 0, L, L)
            Z = W + lam * D.dot(D.transpose())
            z = sparse.linalg.spsolve(Z, w*y)
            w = p * (y > z) + (1-p) * (y < z)

        baseline_fitted = z

    elif method == 'arPLS':
        # Adaptation of the Matlab code in Baek et al 2015

        # optional parameters
        lam = kwargs.get('lam',1.0*10**5)
        ratio = kwargs.get('ratio',0.01)

        N = len(y)
        D = sparse.csc_matrix(np.diff(np.eye(N), 2))
        w = np.ones(N)

        while True:
            W = sparse.spdiags(w, 0, N, N)
            Z = W + lam * D.dot(D.transpose())
            z = sparse.linalg.spsolve(Z, w*y)
            d = y - z
            # make d- and get w^t with m and s
            dn = d[d<0]
            m = np.mean(dn)
            s = np.std(dn)
            wt = 1.0/(1 + np.exp( 2* (d-(2*s-m))/s ) )
            # check exit condition and backup
            if norm(w-wt)/norm(w) < ratio:
                break
            w = wt

        baseline_fitted = z

    return y_input.reshape(-1,1)-Y_scaler.inverse_transform(baseline_fitted.reshape(-1, 1)), Y_scaler.inverse_transform(baseline_fitted.reshape(-1, 1))