Scipy curve_fit для двух измерений не работает - объект слишком глубокий? - PullRequest
0 голосов
/ 13 ноября 2018

У меня есть массив данных 2400 на 2400, который выглядит примерно так:

data = [[-2.302670298082603040e-01 -2.304885241061924717e-01 -2.305029774024092148e-01 -2.304807100897505734e-01 -2.303702531336284665e-01 -2.307144352067780346e-01...
[-2.302670298082603040e-01 -2.304885241061924717e-01 -2.305029774024092148e-01 -2.304807100897505734e-01 -2.303702531336284665e-01 -2.307144352067780346e-01...
...

и я пытаюсь установить следующую 2D-функцию Гаусса:

def Gauss2D(x, mux, muy, sigmax, sigmay, amplitude, offset, rotation):
    assert len(x) == 2
    X = x[0]
    Y = x[1]
    A = (np.cos(rotation)**2)/(2*sigmax**2) + (np.sin(rotation)**2)/(2*sigmay**2)
    B = (np.sin(rotation*2))/(4*sigmay**2) - (np.sin(2*rotation))/(4*sigmax**2)
    C = (np.sin(rotation)**2)/(2*sigmax**2) + (np.cos(rotation)**2)/(2*sigmay**2)
    G = amplitude*np.exp(-((A * (X - mux) ** 2) + (2 * B * (X - mux) * (Y - muy)) + (C * (Y - muy) ** 2))) + offset
    return G

к этим данным, используя scipy curve_fit. Поэтому я определил область независимых переменных (координат) следующим образом:

vert = np.arange(2400, dtype=float)
horiz = np.arange(2400, dtype=float)
HORIZ, VERT = np.meshgrid(horiz, vert)

и в качестве начальной оценки параметров:

po = np.asarray([1200., 1200., 300., 300., 0.14, 0.22, 0.], dtype=float)

чтобы я мог выполнить следующую подгонку:

popt, pcov = curve_fit(Gauss2D, (HORIZ, VERT), data, p0=po)

Это возвращает следующее сообщение об ошибке, и я не имею ни малейшего понятия, почему:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: object too deep for desired array
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-11-ebba75332bfa> in <module>()
----> 1 curve_fit(Gauss2D, (HORIZ, VERT), data, p0=po)

/home/harrythegenius/anaconda3/lib/python3.6/site-packages/scipy/optimize/minpack.py in curve_fit(f, xdata, ydata, p0, sigma, absolute_sigma, check_finite, bounds, method, jac, **kwargs)
734         # Remove full_output from kwargs, otherwise we're passing it in twice.
735         return_full = kwargs.pop('full_output', False)
--> 736         res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs)
737         popt, pcov, infodict, errmsg, ier = res
738         cost = np.sum(infodict['fvec'] ** 2)

/home/harrythegenius/anaconda3/lib/python3.6/site-packages/scipy/optimize/minpack.py in leastsq(func, x0, args, Dfun, full_output, col_deriv, ftol, xtol, gtol, maxfev, epsfcn, factor, diag)
385             maxfev = 200*(n + 1)
386         retval = _minpack._lmdif(func, x0, args, full_output, ftol, xtol,
--> 387                                  gtol, maxfev, epsfcn, factor, diag)
388     else:
389         if col_deriv:

error: Result from function call is not a proper array of floats.

Я не понимаю сообщение "объект слишком глубокий для нужного массива". Я также видел несколько онлайн-решений для этого сообщения об ошибке, в котором можно было бы исправить это, обеспечив, чтобы все типы данных, которые были переданы в curve_fit, были плавающими, или проверив, чтобы измерения массивов были правильными. Я пробовал оба эти подхода снова и снова, но это не имеет значения. Так что не так с этим?

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

ОК, ребята, я сам решил проблему.Как я и подозревал, это проблема размерности.

Подходящие размеры для curve_fit, применяемые к 2D-массиву, следующие:

  • Функция - One Dimension, котораяв этом случае имеет те же измерения, что и набор данных, если не применяется
  • x data - (2, n * m), где n и m - измерения массива данных
  • y данные - (n * m)
  • Список начальных параметров - 1D массив, просто содержащий все параметры в том же порядке, как указано вfunction

Поэтому я оставил свой массив параметров без изменений, но внес следующие изменения в функцию:

def Gauss2D(x, mux, muy, sigmax, sigmay, amplitude, offset, rotation):
    assert len(x) == 2
    X = x[0]
    Y = x[1]
    A = (np.cos(rotation)**2)/(2*sigmax**2) + (np.sin(rotation)**2)/(2*sigmay**2)
    B = (np.sin(rotation*2))/(4*sigmay**2) - (np.sin(2*rotation))/(4*sigmax**2)
    C = (np.sin(rotation)**2)/(2*sigmax**2) + (np.cos(rotation)**2)/(2*sigmay**2)
    G = amplitude*np.exp(-((A * (X - mux) ** 2) + (2 * B * (X - mux) * (Y - muy)) + (C * (Y - muy) ** 2))) + offset
    return G.ravel()

и передал в аргумент x data следующее:

x = np.vstack((HORIZ.ravel(), VERT.ravel()))

и это для аргумента данных y:

y = data.ravel()

Таким образом, я оптимизировал его, используя:

curve_fit(Gauss2D, x, y, po)

, который работает просто отлично.

0 голосов
/ 14 ноября 2018

В комментариях приведен 3D-монтажник поверхностей с использованием curve_fit (), который имеет 3D-диаграмму рассеяния, 3D-график поверхности и контурный график.

import numpy, scipy, scipy.optimize
import matplotlib
from mpl_toolkits.mplot3d import  Axes3D
from matplotlib import cm # to colormap 3D surfaces from blue to red
import matplotlib.pyplot as plt

graphWidth = 800 # units are pixels
graphHeight = 600 # units are pixels

# 3D contour plot lines
numberOfContourLines = 16


def SurfacePlot(func, data, fittedParameters):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)

    matplotlib.pyplot.grid(True)
    axes = Axes3D(f)

    x_data = data[0]
    y_data = data[1]
    z_data = data[2]

    xModel = numpy.linspace(min(x_data), max(x_data), 20)
    yModel = numpy.linspace(min(y_data), max(y_data), 20)
    X, Y = numpy.meshgrid(xModel, yModel)

    Z = func(numpy.array([X, Y]), *fittedParameters)

    axes.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=1, antialiased=True)

    axes.scatter(x_data, y_data, z_data) # show data along with plotted surface

    axes.set_title('Surface Plot (click-drag with mouse)') # add a title for surface plot
    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label
    axes.set_zlabel('Z Data') # Z axis data label

    plt.show()
    plt.close('all') # clean up after using pyplot or else thaere can be memory and process problems


def ContourPlot(func, data, fittedParameters):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)
    axes = f.add_subplot(111)

    x_data = data[0]
    y_data = data[1]
    z_data = data[2]

    xModel = numpy.linspace(min(x_data), max(x_data), 20)
    yModel = numpy.linspace(min(y_data), max(y_data), 20)
    X, Y = numpy.meshgrid(xModel, yModel)

    Z = func(numpy.array([X, Y]), *fittedParameters)

    axes.plot(x_data, y_data, 'o')

    axes.set_title('Contour Plot') # add a title for contour plot
    axes.set_xlabel('X Data') # X axis data label
    axes.set_ylabel('Y Data') # Y axis data label

    CS = matplotlib.pyplot.contour(X, Y, Z, numberOfContourLines, colors='k')
    matplotlib.pyplot.clabel(CS, inline=1, fontsize=10) # labels for contours

    plt.show()
    plt.close('all') # clean up after using pyplot or else thaere can be memory and process problems


def ScatterPlot(data):
    f = plt.figure(figsize=(graphWidth/100.0, graphHeight/100.0), dpi=100)

    matplotlib.pyplot.grid(True)
    axes = Axes3D(f)
    x_data = data[0]
    y_data = data[1]
    z_data = data[2]

    axes.scatter(x_data, y_data, z_data)

    axes.set_title('Scatter Plot (click-drag with mouse)')
    axes.set_xlabel('X Data')
    axes.set_ylabel('Y Data')
    axes.set_zlabel('Z Data')

    plt.show()
    plt.close('all') # clean up after using pyplot or else thaere can be memory and process problems


def func(data, a, alpha, beta):
    t = data[0]
    p_p = data[1]
    return a * (t**alpha) * (p_p**beta)


if __name__ == "__main__":
    xData = numpy.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
    yData = numpy.array([11.0, 12.1, 13.0, 14.1, 15.0, 16.1, 17.0, 18.1, 90.0])
    zData = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.0, 9.9])

    data = [xData, yData, zData]

    initialParameters = [1.0, 1.0, 1.0] # these are the same as scipy default values in this example

    # here a non-linear surface fit is made with scipy's curve_fit()
    fittedParameters, pcov = scipy.optimize.curve_fit(func, [xData, yData], zData, p0 = initialParameters)

    ScatterPlot(data)
    SurfacePlot(func, data, fittedParameters)
    ContourPlot(func, data, fittedParameters)

    print('fitted prameters', fittedParameters)

    modelPredictions = func(data, *fittedParameters) 

    absError = modelPredictions - zData

    SE = numpy.square(absError) # squared errors
    MSE = numpy.mean(SE) # mean squared errors
    RMSE = numpy.sqrt(MSE) # Root Mean Squared Error, RMSE
    Rsquared = 1.0 - (numpy.var(absError) / numpy.var(zData))
    print('RMSE:', RMSE)
    print('R-squared:', Rsquared)
...