Динамически определить функцию - PullRequest
6 голосов
/ 19 ноября 2011

Я пытаюсь написать функцию подбора кривой, которая возвращает оптимальные параметры a, b и c, вот упрощенный пример:

import numpy
import scipy
from scipy.optimize import curve_fit

def f(x, a, b, c):
    return x * 2*a + 4*b - 5*c

xdata = numpy.array([1,3,6,8,10])
ydata = numpy.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
popt, pcov = scipy.optimize.curve_fit(f, xdata, ydata)

Это работает нормально, но я хочу дать пользователю возможность предоставить некоторые (или ни один) из параметров a, b или c, и в этом случае они должны рассматриваться как константы и не оцениваться. Как я могу написать f, чтобы он соответствовал только параметрам, не предоставленным пользователем?

По сути, мне нужно определить f динамически с правильными аргументами. Например, если a был известен пользователю, f становится:

def f(x, b, c):
    a = global_version_of_a
    return x * 2*a + 4*b - 5*c

Ответы [ 5 ]

6 голосов
/ 20 ноября 2011

Взяв страницу из пьесы collection.namedtuple , вы можете использовать exec для "динамического" определения func:

import numpy as np
import scipy.optimize as optimize
import textwrap

funcstr=textwrap.dedent('''\
def func(x, {p}):
    return x * 2*a + 4*b - 5*c
''')
def make_model(**kwargs):
    params=set(('a','b','c')).difference(kwargs.keys())
    exec funcstr.format(p=','.join(params)) in kwargs
    return kwargs['func']

func=make_model(a=3, b=1)

xdata = np.array([1,3,6,8,10])
ydata = np.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
popt, pcov = optimize.curve_fit(func, xdata, ydata)
print(popt)
# [ 5.49682045]

Обратите внимание на строку

func=make_model(a=3, b=1)

Вы можете передать любые параметры, которые вам нравятся, make_model. Параметры, которые вы передаете make_model, становятся фиксированными константами в func. Все оставшиеся параметры становятся свободными параметрами, которые optimize.curve_fit будет пытаться найти.

Например, выше, a = 3 и b = 1 становятся фиксированными константами в func. Фактически, оператор exec помещает их в глобальное пространство имен func. Таким образом, func определяется как функция x и одного параметра c. Обратите внимание, что возвращаемое значение для popt является массивом длины 1, соответствующим оставшемуся свободному параметру c.


Относительно textwrap.dedent: В приведенном выше примере вызов textwrap.dedent не нужен. Но в «реальном» сценарии, где funcstr определяется внутри функции или на более глубоком уровне отступа, textwrap.dedent позволяет писать

def foo():
    funcstr=textwrap.dedent('''\
        def func(x, {p}):
            return x * 2*a + 4*b - 5*c
        ''')

вместо визуально непривлекательных

def foo():
    funcstr='''\
def func(x, {p}):
    return x * 2*a + 4*b - 5*c
'''

Некоторые люди предпочитают

def foo():
    funcstr=(
        'def func(x, {p}):\n'
        '    return x * 2*a + 4*b - 5*c'
        )

но я нахожу цитирование каждой строки отдельно и добавление явных символов EOL немного обременительным. Однако это спасет вас от вызова функции.

1 голос
/ 21 ноября 2011

Я обычно использую лямбду для этой цели.

user_b, user_c = get_user_vals()
opt_fun = lambda x, a: f(x, a, user_b, user_c)
popt, pcov = scipy.optimize.curve_fit(opt_fun, xdata, ydata)
0 голосов
/ 21 ноября 2011

Уже есть пакет, который делает это:

https://lmfit.github.io/lmfit-py/index.html

от README:

«LMfit-py обеспечивает метод минимизации наименьших квадратов и класс с простым, гибким подходом к параметризации модели для подгонка к данным. Именованные параметры могут быть фиксированными или свободными корректируется в соответствии или подержаны между нижней и верхней границей. В Кроме того, параметры могут быть ограничены как простые математические выражение других параметров. "

0 голосов
/ 20 ноября 2011

Если вы хотите простое решение, основанное на curve_fit, я бы посоветовал вам обернуть вашу функцию в класс.Минимальный пример:

import numpy
from scipy.optimize import curve_fit

class FitModel(object):
    def f(self, x, a, b, c):
        return x * 2*a + 4*b - 5*c

    def f_a(self, x, b, c):
        return self.f(x, self.a, b, c)

# user supplies a = 1.0
fitModel = FitModel()
fitModel.a = 1.0

xdata = numpy.array([1,3,6,8,10])
ydata = numpy.array([  0.91589774,   4.91589774,  10.91589774,  14.91589774,  18.91589774])
initial = (1.0,2.0)
popt, pconv = curve_fit(fitModel.f_a, xdata, ydata, initial)
0 голосов
/ 20 ноября 2011
def f(x, a = 10, b = 15, c = 25):
    return x * 2*a + 4*b - 5*c

Если пользователь не предоставил аргумент для рассматриваемого параметра, будет использовано все, что вы указали в правой части знака =:

например:
f(5, b = 20) оценивается в return 5 * 2*10 + 4*20 - 5*25, а
f(7) оценивается в return 7 * 2*10 + 4*15 - 5*25

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...