Curve_fit не оптимизирует один из параметров - PullRequest
0 голосов
/ 13 июня 2018

Мне нужно указать scipy.optimize.curve_fit некоторые данные, которые выглядят как точки на рисунке.Я использую функцию y(x) (см. Определение ниже), которая дает константу y(x)=c для x<x0, иначе - многочлен (например, вторая наклонная линия y1 = mx+q).

Я даю разумную начальную догадкудля параметров (x0, c, m, q), как показано на рисунке.Результат подбора показывает, что все параметры оптимизированы, кроме первого x0.

Почему так?Это как я определяю функцию testfit(x, *p), где x0 (= p[0]) появляется в другой функции?

enter image description here

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# generate some data:
x = np.linspace(0,100,1000)
y1 = np.repeat(0, 500)
y2 = x[500:] - 50
y = np.concatenate((y1,y2))
y = y + np.random.randn(len(y))


def testfit(x, *p):
    ''' piecewise function used to fit 
        it's a constant (=p[1]) for x < p[0]
        or a polynomial for x > p[0]     
    '''
    x = x.astype(float)
    y = np.piecewise(x, [x < p[0], x >= p[0]], [p[1], lambda x: np.poly1d(p[2:])(x)])
    return y

# initial guess, one horizontal and one tilted line:
p0_guess = (30, 5, 0.3, -10)

popt, pcov = curve_fit(testfit, x, y, p0=p0_guess)

print('params guessed  : '+str(p0_guess))
print('params from fit : '+str(popt))

plt.plot(x,y, '.')
plt.plot(x, testfit(x, *p0_guess), label='initial guess')
plt.plot(x, testfit(x, *popt), label='final fit')
plt.legend()

Выход

params guessed  : (30, 5, 0.3, -10) 
params from fit : [ 30. 0.04970411   0.80106256 -34.17194401] 

OptimizeWarning: Covariance of the parameters could not be estimated category=OptimizeWarning)

Ответы [ 2 ]

0 голосов
/ 17 июня 2018

Как предложил kazemakase, я решил проблему с плавным переходом между двумя функциями, которые я использую для подгонки (одна горизонтальная линия, за которой следует полином).Хитрость заключалась в том, чтобы умножить одну функцию на sigmoid(x), а другую на 1-sigmoid(x) (где sigmoid(x) определено ниже).

enter image description here

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

x = np.linspace(0,100,1000)
y1 = np.repeat(0, 500)
y2 = x[500:] - 50
y = np.concatenate((y1,y2))
y = y + np.random.randn(len(y))

def testfit(x, *p):
    ''' function to fit the indentation curve 
    p = [x0,c, poly1d_coeffs ]'''
    x = x.astype(float)
    y = p[1]*(1-sigmoid(x-p[0],k=1)) + np.poly1d(p[2:])(x) * sigmoid(x-p[0],k=1)
    return y

def sigmoid(x, k=1):
    return 1/(1+np.exp(-k*x))

p0_guess = (30, 5, 0.3, -10 )
popt, pcov = curve_fit(testfit, x, y, p0=p0_guess)
print('params guessed  : '+str(p0_guess))
print('params from fit : '+str(popt))


plt.figure(1)
plt.clf()
plt.plot(x,y, 'y.')
plt.plot(x, testfit(x, *p0_guess), label='initial guess')
plt.plot(x, testfit(x, *popt), 'k', label='final fit')
plt.legend()
0 голосов
/ 13 июня 2018

У меня была похожая проблема.В итоге я использовал np.gradient и свертку, чтобы сгладить кривую, а затем построить ее.Что-то вроде:

def mov_avg(n, data):
    return np.convolve(data, np.ones((n,))/n, mode='valid')

Если вы хотите более прямой подход, вы можете попробовать это:

def find_change(data):
    def test_flag(pos):
        grad = np.gradient(data) - np.gradient(data).mean()
        return (grad[:pos]<0).sum() + (grad[pos:]>0).sum()
    return np.vectorize(test_flag)(np.arange(len(data)-1)).argmax()

def find_gradient(pos, data):
    return np.gradient(data[:pos]).mean(), np.gradient(data[pos:]).mean()

pos=find_change(x2)
print(pos, find_gradient(pos, data))

Первая функция вычисляет точку, в которой изменение градиента, сравнивая градиент точкипротив среднего градиента, и находит точку, из которой градиенты "в основном положительные".

Надеюсь, что это помогает

...