示例#1
0
def substractBase(x,
                  y,
                  peakInterval,
                  baseFitInterval,
                  model,
                  usePositiveConstrains,
                  extrapolate=None,
                  useStartParams=None):
    """
    Fit base by Cauchy function and substract from y.
    :param x: argument
    :param y: function values
    :param peakInterval: interval of peak search (do not included in base fitting)
    :param baseFitInterval: interval of base fit. Usually it includes peakInterval
    :param model: 'cauchy' or 'bezier' or 'arctan'
    :param usePositiveConstrains: add constrain y_base <= y
    :param extrapolate: {'left':percent_dx_left, 'right':percent_dx_right}
    :return: x_peak, y_sub - peak with substracted base (on interval peakInterval); x_base, y_base - base on baseFitInterval; y_peak - peak part of original func; y_sub_full - y_sub expanded to baseFitInterval
    """
    assert model in ['cauchy', 'bezier', 'arctan']
    assert len(x) == len(y)
    if extrapolate is None: extrapolate = {}
    ind_peak = (x >= peakInterval[0]) & (x <= peakInterval[1])
    ind_base_full = (x >= baseFitInterval[0]) & (x <= baseFitInterval[1])
    ind_base = ind_base_full & ~ind_peak
    x_peak = x[ind_peak]
    y_peak = y[ind_peak]
    x_base = x[ind_base]
    y_base = y[ind_base]
    x_base_full = x[ind_base_full]
    y_base_full = y[ind_base_full]

    # make x_fit y_fit by extrapolating base inside peak interval (linear extrapolation from both ends)
    if usePositiveConstrains:
        ind_base_left = (x < peakInterval[0]) & ind_base_full
        b1, a1 = linearReg(x[ind_base_left], y[ind_base_left])
        ind_base_right = (x > peakInterval[1]) & ind_base_full
        b2, a2 = linearReg(x[ind_base_right], y[ind_base_right])
        y_gap = np.max([a1 * x_peak + b1, a2 * x_peak + b2],
                       axis=0).reshape(-1)
        assert len(y_gap) == len(x_peak)
        x_fit = x_base_full
        y_fit = np.concatenate((y[ind_base_left], y_gap, y[ind_base_right]))
        assert len(x_fit) == len(y_fit), str(len(x_fit)) + " " + str(
            len(y_fit))
    else:
        x_fit = x_base
        y_fit = y_base

    x1 = x_base[0]
    x2 = x_base[-1]
    y1 = y_base[0]
    y2 = y_base[-1]
    if 'left' in extrapolate:
        n = np.where(x_base <= x1 + (x2 - x1) / 10)[0][-1] + 1
        if n < 2: n = 2
        slope, intercept, _, _, _ = scipy.stats.linregress(
            x_base[:n], y_base[:n])
        percent = extrapolate['left']
        count = np.round(len(x_base) * percent)
        first = x1 - (x2 - x1) * percent
        last = x1 - (x2 - x1) / count
        new_x = np.linspace(first, last, count)
        x_base = np.insert(x_base, 0, new_x)
        y_base = np.insert(y_base, 0, new_x * slope + intercept)
    if 'right' in extrapolate:
        n = np.where(x_base >= x2 - (x2 - x1) / 10)[0][-1] + 1
        if n < 2: n = 2
        slope, intercept, _, _, _ = scipy.stats.linregress(
            x_base[-n:], y_base[-n:])
        percent = extrapolate['right']
        count = np.round(len(x_base) * percent)
        last = x2 + (x2 - x1) * percent
        first = x2 + (x2 - x1) / count
        new_x = np.linspace(first, last, count)
        x_base = np.append(x_base, new_x)
        y_base = np.append(y_base, new_x * slope + intercept)
    assert (len(x_peak) >= 2) and (len(x_base) >= 2), 'len(x_peak) = ' + str(
        len(x_peak)) + ' len(x_base) = ' + str(len(x_base))

    minx = np.min(x)
    maxx = np.max(x)
    maxy = np.max(y)
    if model == 'cauchy':
        fff = lambda x, a, b, g, d: a / ((x - b)**2 + g) + d
        mod = ExpressionModel('a/((x-b)**2+g) + d')
        b0 = x2 + x2 - x1
        g0 = 1
        a0 = (y2 - y1) / (1 / ((x2 - b0)**2 + g0) - 1 / ((x1 - b0)**2 + g0))
        d0 = y1 - a0 / ((x1 - b0)**2 + g0)
        params = mod.make_params(a=a0, b=b0, g=g0, d=d0)
        param_order = {'a': 0, 'b': 1, 'g': 2, 'd': 3}
        start0 = [
            params['a'].value, params['b'].value, params['g'].value,
            params['d'].value
        ]
        result = mod.fit(y_fit, params, x=x_fit)
        start = [
            result.params['a'].value, result.params['b'].value,
            result.params['g'].value, result.params['d'].value
        ]
        bounds = [[0, 1e3 * maxy], [minx, maxx + (maxx - minx) * 10],
                  [0, (maxx - minx) * 10], [-maxy, maxy]]
    elif model == 'arctan':
        fff = lambda x, a, b, c, x0, d: b / (1 + np.exp(-a * (x - x0))
                                             ) + c + d * (x - x_base[0])
        mod = ExpressionModel('b/(1+exp(-a*(x - x0)))+c+d*(x-' +
                              str(x_base[0]) + ')')
        efermi0, _, _ = findExpEfermi(x, y, 0.5 * np.mean(y[-5:]))
        if efermi0 < x_peak[0]: efermi0 = x_peak[0]
        a0 = 1
        b0 = y[-1] - y[0]
        c0 = y[0]
        x00 = efermi0
        d0 = (y_peak[0] - y_base[0]) / (x_peak[0] - x_base[0])
        params = mod.make_params(a=a0, b=b0, c=c0, x0=x00, d=d0)
        param_order = {'a': 0, 'b': 1, 'c': 2, 'x0': 3, 'd': 4}
        start0 = [
            params['a'].value, params['b'].value, params['c'].value,
            params['x0'].value, params['d'].value
        ]
        assert np.all(x[1:] - x[:-1] > 0), str(x)
        max_dy = np.max((y[1:] - y[:-1]) / (x[1:] - x[:-1]))
        params['a'].set(min=0)
        params['a'].set(max=max_dy / (np.max(y) - np.min(y)) * 10)
        params['b'].set(min=0)
        params['x0'].set(min=x_peak[0])
        params['d'].set(min=0)
        params['d'].set(max=3 * (y_peak[0] - y_base[0]) /
                        (x_peak[0] - x_base[0]))
        dist = np.max([abs(x00 - minx), abs(x00 - maxx), maxx - minx])
        bounds = [[0, a0 * 100], [0, maxy * 10], [-maxy, maxy],
                  [minx - dist, maxx + dist * 10],
                  [0, 3 * (y_peak[0] - y_base[0]) / (x_peak[0] - x_base[0])]]
        # TODO: remove lmfit, because scipy.optimize.minimize works better
        if useStartParams is None:
            result = mod.fit(y_fit, params, x=x_fit)
            # result.plot()
            # plt.show()
            # print(result.fit_report())
            start = [
                result.params['a'].value, result.params['b'].value,
                result.params['c'].value, result.params['x0'].value,
                result.params['d'].value
            ]
        else:
            start = useStartParams
    else:
        Mtk = lambda n, t, k: t**k * (1 - t)**(n - k) * scipy.misc.comb(n, k)
        BezierCoeff = lambda ts: [[Mtk(3, t, k) for k in range(4)] for t in ts]
        t = np.linspace(0, 1, len(x_base))
        Pseudoinverse = np.linalg.pinv(BezierCoeff(t))
        data = np.column_stack((x_base, y_base))
        control_points = Pseudoinverse.dot(data)
        Bezier = np.array(BezierCoeff(tPlot)).dot(control_points)
        assert not usePositiveConstrains

        return x_peak, y_peak - app_y_base_inside_peak, x_base_full, app_y_base_full, y_peak, y_base_full - app_y_base_full

    def func(params):
        y_app = fff(x_base, *params)
        return np.linalg.norm(y_app - y_base)

    if useStartParams is None:
        res = scipy.optimize.minimize(func, start0, bounds=bounds)
        # print(func(start), res.fun)
        if res.fun < func(start):
            for name in result.params:
                # print(f'Setting {name} = ',res.x[param_order[name]])
                result.params[name].set(res.x[param_order[name]])
            # print(result.params)
            start = res.x
    info = {'optimParam': start, 'optimVal': func(start)}
    if usePositiveConstrains:
        #while True:
        #if np.all(fff(x_peak,*start)<=y_peak): break
        #dx = np.max(x_peak)-np.min(x_peak)
        #dy = np.max(y_peak)-np.min(y_peak)
        #start[1] += dx*0.01
        #start[3] -= dy*0.01

        constrains = tuple()
        for i in range(len(x_peak)):
            cons_fun = lambda params, i=i: fff(x_peak[i], *params)
            constrains += (scipy.optimize.NonlinearConstraint(
                cons_fun, -maxy, y_peak[i]), )
        # print(bounds)
        res = scipy.optimize.minimize(func,
                                      start,
                                      bounds=bounds,
                                      constraints=constrains)
        params = res.x
        app_y_base_inside_peak = fff(x_peak, *params)
        app_y_base_full = fff(x_base_full, *params)
        info = {'optimParam': params, 'optimVal': res.fun}
    else:
        app_y_base_inside_peak = mod.eval(result.params, x=x_peak)
        app_y_base_full = mod.eval(result.params, x=x_base_full)
    return x_peak, y_peak - app_y_base_inside_peak, x_base_full, app_y_base_full, y_peak, y_base_full - app_y_base_full, info
示例#2
0
import matplotlib.pyplot as plt
import numpy as np

from lmfit.models import ExpressionModel

# Number of frames
nf = [2000, 1500, 1000, 500]
time = [6534.59, 4871.85, 3282.48, 1682.1186]

# Fitting
gmod = ExpressionModel("a*x +b")
result = gmod.fit(time, x=nf, a=10, b=0)

# Prediction
x = np.arange(0, 20000, 500)
y = gmod.eval(result.params, x=x)
print(result.params)
print(result.best_fit)
print(result.best_values)
print(result.fit_report())

test_nf = 200000
test_time = gmod.eval(result.params, x=test_nf)
print('{0:d} frames taks {1:f} s ({2:f} hours to finish)'.format(
    test_nf, test_time, test_time / 3600.0))

plt.plot(x, y, '.-')
plt.plot(nf, time, '*')
plt.show()