การใช้ scipy.optimize.curve_fit ภายในคลาส

ฉันมีชั้นเรียนที่อธิบายฟังก์ชันทางคณิตศาสตร์ คลาสจะต้องมีกำลังสองน้อยที่สุดที่พอดีกับตัวเองจึงจะส่งข้อมูลได้ นั่นคือคุณสามารถเรียกวิธีการดังนี้:

classinstance.Fit(x,y)

และจะปรับตัวแปรภายในให้เหมาะสมกับข้อมูลมากที่สุด ฉันกำลังพยายามใช้ scipy.optimize.curve_fit สำหรับสิ่งนี้ และมันต้องการให้ฉันผ่านฟังก์ชันโมเดล ปัญหาคือฟังก์ชันโมเดลอยู่ในคลาสและจำเป็นต้องเข้าถึงตัวแปรและสมาชิกของคลาสเพื่อคำนวณข้อมูล อย่างไรก็ตาม curve_fit ไม่สามารถเรียกใช้ฟังก์ชันที่มีพารามิเตอร์แรกเป็น self ได้ มีวิธีทำให้ curve_fit ใช้วิธีการของคลาสเนื่องจากเป็นฟังก์ชันโมเดลหรือไม่?

นี่คือตัวอย่างข้อมูลปฏิบัติการขั้นต่ำเพื่อแสดงปัญหา:

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)

และสิ่งนี้จะสร้างกราฟต่อไปนี้แล้วหยุดทำงานเนื่องจากฟังก์ชันโมเดลไม่ถูกต้องเมื่อพยายามทำให้พอดี

ป้อนคำอธิบายรูปภาพที่นี่

ขอบคุณล่วงหน้า!


person ZSG    schedule 07.05.2015    source แหล่งที่มา


คำตอบ (2)


ฉันใช้เวลาดูรหัสของคุณสักพักและพบว่าช้าไป 2 นาที อย่างไรก็ตาม เพื่อทำให้สิ่งต่างๆ น่าสนใจยิ่งขึ้นอีกเล็กน้อย ฉันได้แก้ไขชั้นเรียนของคุณเล็กน้อย นี่คือสิ่งที่ฉันปรุงขึ้น:

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**

ฉันเชื่อว่าคุณอาจพบว่านี่เป็นวิธีแก้ปัญหาที่น่าพอใจกว่านี้ใช่ไหม

ปัจจุบันพารามิเตอร์เกาส์ทั้งหมดของคุณเป็นแอตทริบิวต์ของคลาส ซึ่งหมายความว่าหากคุณพยายามสร้างอินสแตนซ์ที่สองของคลาสด้วยค่าพารามิเตอร์ที่แตกต่างกัน คุณจะเปลี่ยนค่าสำหรับคลาสแรกด้วย ด้วยการผลักพารามิเตอร์ทั้งหมดเป็นแอตทริบิวต์อินสแตนซ์ คุณจะกำจัดสิ่งนั้นออกไป นั่นคือเหตุผลที่เราจัดชั้นเรียนตั้งแต่แรก

ปัญหาของคุณเกี่ยวกับ self เกิดจากการที่คุณเขียน self ใน Formula ของคุณ ตอนนี้คุณไม่จำเป็นต้องอีกต่อไป ฉันคิดว่ามันสมเหตุสมผลกว่านี้เล็กน้อย เพราะเมื่อคุณสร้างอินสแตนซ์อ็อบเจ็กต์ของคลาส คุณสามารถเพิ่มพารามิเตอร์ได้มากหรือน้อยให้กับฟังก์ชันที่คุณประกาศตามที่คุณต้องการ ตอนนี้ไม่จำเป็นต้องเป็นแบบเกาส์ด้วยซ้ำ (ไม่เหมือนเมื่อก่อน)

เพียงโยนพารามิเตอร์ทั้งหมดลงในพจนานุกรม เช่นเดียวกับที่ curve_fit ทำ แล้วลืมมันไปซะ

การระบุอย่างชัดเจนว่า eval สามารถใช้อะไรได้บ้าง คุณช่วยให้แน่ใจว่าผู้กระทำความผิดจะทำลายโค้ดของคุณได้ยากขึ้น มันยังคงเป็นไปได้ แต่มันจะอยู่กับ eval เสมอ

โชคดี ถามว่าคุณต้องการอะไรชี้แจงหรือไม่

person ljetibo    schedule 08.05.2015

อ่า! จริงๆ แล้วมันเป็นข้อผิดพลาดในโค้ดของฉัน ถ้าฉันเปลี่ยนบรรทัดนี้:

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

to

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

ถ้าอย่างนั้นเราก็สบายดี ดังนั้น curve_fit จัดการพารามิเตอร์ self ได้อย่างถูกต้อง แต่ฟังก์ชันโมเดลของฉันไม่ควรรวม y

(อาย!)

person ZSG    schedule 08.05.2015
comment
สำหรับฉัน ไม่มีเบาะแสที่ curve_fit กำลังจัดการสิ่งนั้นอย่างเหมาะสม... - person Saullo G. P. Castro; 08.05.2015