Menggunakan scipy.optimize.curve_fit dalam kelas

Saya memiliki kelas yang menjelaskan fungsi matematika. Kelas harus mampu menyesuaikan kuadrat terkecil agar dapat meneruskan data. yaitu Anda dapat memanggil metode seperti ini:

classinstance.Fit(x,y)

dan menyesuaikan variabel internalnya agar sesuai dengan data. Saya mencoba menggunakan scipy.optimize.curve_fit untuk ini, dan saya perlu meneruskan fungsi model. Masalahnya adalah fungsi model berada di dalam kelas dan perlu mengakses variabel dan anggota kelas untuk menghitung data. Namun, curve_fit tidak dapat memanggil fungsi yang parameter pertamanya adalah self. Apakah ada cara untuk membuat curve_fit menggunakan metode kelas sebagai fungsi modelnya?

Berikut cuplikan minimum yang dapat dieksekusi untuk menunjukkan masalahnya:

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

# This is a class which encapsulates a gaussian and fits itself to data.
class GaussianComponent():
    # This is a formula string showing the exact code used to produce the gaussian.  I
    # It has to be printed for the user, and it can be used to compute values.
    Formula = 'self.Amp*np.exp(-((x-self.Center)**2/(self.FWHM**2*np.sqrt(2))))'

    # These parameters describe the gaussian.
    Center = 0
    Amp = 1
    FWHM = 1

    # HERE IS THE CONUNDRUM: IF I LEAVE SELF IN THE DECLARATION, CURVE_FIT
    # CANNOT CALL IT SINCE IT REQUIRES THE WRONG NUMBER OF PARAMETERS.
    # IF I REMOVE IT, FITFUNC CAN'T ACCESS THE CLASS VARIABLES.
    def FitFunc(self, x, y, Center, Amp, FWHM):
        eval('y - ' + self.Formula.replace('self.', ''))

    # This uses curve_fit to adjust the gaussian parameters to best match the
    # data passed in.
    def Fit(self, x, y):
        #FitFunc = lambda x, y, Center, Amp, FWHM: eval('y - ' + self.Formula.replace('self.', ''))
        FitParams, FitCov = curve_fit(self.FitFunc, x, y, (self.Center, self.Amp, self.FWHM))
        self.Center = FitParams[0]
        self.Amp = FitParams[1]
        self.FWHM = FitParams[2]

    # Give back a vector which describes what this gaussian looks like.
    def GetPlot(self, x):
        y = eval(self.Formula)
        return y

# Make a gausssian with default shape and position (height 1 at the origin, FWHM 1.
g = GaussianComponent()

# Make a space in which we can plot the gaussian.
x = np.linspace(-5,5,100)
y = g.GetPlot(x)

# Make some "experimental data" which is just the default shape, noisy, and
# moved up the y axis a tad so the best fit will be different.
ynoise = y + np.random.normal(loc=0.1, scale=0.1, size=len(x))

# Draw it
plt.plot(x,y, x,ynoise)
plt.show()

# Do the fit (but this doesn't work...)
g.Fit(x,y)

Dan ini menghasilkan grafik berikut dan kemudian crash karena fungsi model salah ketika mencoba melakukan penyesuaian.

masukkan deskripsi gambar di sini

Terima kasih sebelumnya!


person ZSG    schedule 07.05.2015    source sumber


Jawaban (2)


Saya menghabiskan beberapa waktu melihat kode Anda dan sayangnya terlambat 2 menit. Bagaimanapun, untuk membuatnya lebih menarik, saya telah mengedit kelas Anda sedikit, Inilah yang saya buat:

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

class GaussianComponent():

    def __init__(self, func, params=None):
        self.formula = func
        self.params = params

    def eval(self, x):
        allowed_locals = {key: self.params[key] for key in self.params}
        allowed_locals["x"] = x
        allowed_globals = {"np":np}
        return eval(self.formula, allowed_globals, allowed_locals)

    def Fit(self, x, y):
        FitParams, FitCov = curve_fit(self.eval, x, y, self.params)
        self.fitparams = fitParams


# Make a gausssian with default shape and position (height 1 at the origin, FWHM 1.
g = GaussianComponent("Amp*np.exp(-((x-Center)**2/(FWHM**2*np.sqrt(2))))", 
                      params={"Amp":1, "Center":0, "FWHM":1})

**SNIPPED FOR BREVITY**

Saya yakin Anda mungkin akan menganggap ini solusi yang lebih memuaskan?

Saat ini semua parameter gauss Anda adalah atribut kelas, itu berarti jika Anda mencoba membuat instance kedua dari kelas Anda dengan nilai parameter yang berbeda, Anda juga akan mengubah nilai untuk kelas pertama. Dengan memasukkan semua parameter sebagai atribut instan, Anda menghilangkannya. Itu sebabnya kami mengadakan kelas terlebih dahulu.

Masalah Anda dengan self berasal dari fakta Anda menulis self di Formula Anda. Sekarang Anda tidak perlu melakukannya lagi. Saya pikir ini lebih masuk akal seperti ini, karena ketika Anda membuat instance objek kelas Anda dapat menambahkan parameter sebanyak atau sesedikit mungkin ke fungsi yang Anda deklarasikan. Bahkan sekarang tidak harus gaussian (tidak seperti sebelumnya).

Masukkan saja semua param ke kamus, seperti yang dilakukan curve_fit dan lupakan saja.

Dengan secara eksplisit menyatakan apa yang eval dapat gunakan, Anda membantu memastikan bahwa pelaku kejahatan akan kesulitan memecahkan kode Anda. Namun itu masih mungkin, selalu dengan eval.

Semoga berhasil, tanyakan apakah Anda memerlukan sesuatu yang diklarifikasi.

person ljetibo    schedule 08.05.2015

Ahhh! Itu sebenarnya adalah bug dalam kode saya. Jika saya mengubah baris ini:

def FitFunc(self, x, y, Center, Amp, FWHM):

to

def FitFunc(self, x, Center, Amp, FWHM):

Kalau begitu kita baik-baik saja. Jadi curve_fit menangani parameter self dengan benar tetapi fungsi model saya tidak boleh menyertakan y.

(Malu!)

person ZSG    schedule 08.05.2015
comment
bagi saya tidak ada petunjuk bahwa curve_fit menanganinya dengan benar... - person Saullo G. P. Castro; 08.05.2015