如何在Python中任意扩展Symfit参数个数
How to extend the number of Symfit parameters arbitrarily in Python
我有以下优化代码,它由变量 n
参数化。
from symfit import parameters, Eq, Ge, Fit
import numpy as np
n = 3
xdata = np.sort(np.random.choice(range(1, 4*n), n)) # Make fake data
print(xdata)
p1, p2, p3 = parameters('p1, p2, p3')
model = p1*p2*p3
constraints = [
Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1),
Ge(p1, p2),
Ge(p2, p3),
Ge(p3, 0)
]
fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()
print(fit_result)
我想将它用于更大的 n
值,但我不知道如何更改行
p1, p2, p3 = parameters('p1, p2, p3')
model = p1*p2*p3
和constraints
来应对任意大的n
。
代码正在使用 symfit 库。 link 显示了如何使用 parameters
的示例以及文档的 link。
如何做到这一点?
需要从n
以动态方式计算参数字符串
paramstr = ', '.join(['p{}'.format(i) for i in range(1, n)])
# when n=1, paramstr = 'p1, p2, p3'
使用 paramstr
作为 parameters
函数的参数。
paramvals = parameters(paramstr)
model
可以通过减少其乘积的 paramvals
来重构。
from functools import reduce
model = reduce(lambda x, y: x * y, paramvals, 1)
现在进入有趣的部分! constraints
可以重构为:
eqs = xdata[0] * paramvals[0] + sum(
(xdata[i] - xdata[i-1]) * paramvals[i]
for i in range(1, n)
)
ges = [
Ge(paramvals[i-1], paramvals[i])
for i in range(1, n)
]
ges.append(
Ge(paramvals[-1], 0)
)
constraints = [
Eq(eqs, 1),
*ges
]
我对 Symfit 一无所知,但如果您只是想将上述代码泛化为任意 N,那么:
- 可以为任意N生成一个看起来像
"p1, p2, p3"
的字符串,解析成一个参数列表:
params_string = ", ".join("p{}".format(i + 1) for i in range(n))
params = parameters(params_string)
解析字符串以获得参数列表的想法对我来说听起来很臭,而且我确信有更好的方法来以编程方式声明一堆参数,但这将尽可能接近你的原代码在做。
编辑:查看 Symfit 文档,似乎 parameters(s)
只是一个快捷方式,您实际上可以这样做:
params = [Parameter("p{}".format(i + 1)) for i in range(n)]
这不需要您构建自己的所有参数名称的连接字符串,这样 Symfit 就可以将它们拆分回单独的参数名称。这还允许您为参数定义其他属性,例如它们的初始值或它们的 min/max 范围。
- 您可以概括
Eq
约束:
coeffs = [xdata[0]] + [(xdata[i+1] - xdata[i]) for i in range(n-1)]
eq_constraint = Eq(sum(param * coeff for param, coeff in zip(params, coeffs), 1)
或者,正如另一个答案那样,使用 numpy 操作:
coeffs = np.concat(xdata[:1], np.diff(xdata))
eq_constraint = Eq(np.sum(params * coeffs), 1)
- 您可以概括
Ge
个约束:
ge_constraints = [Ge(params[i + 1], params[i]) for i in range(n - 1)] + [Ge(params[-1], 0]
constraints = [eq_constraint] + ge_constraints
同样,这可以使用 numpy 操作来完成,但我会把它留给@user3483203 的回答。
- 您可以使用
reduce
乘以所有参数:
model = reduce(lambda x, y: x * y, params, 1)
或使用numpy.prod
:
model = np.prod(params)
这应该足以将上述概括为任意 N。
Numpy 真正 与 symfit
库交互良好。使用它时,您尝试概括的所有操作都相当微不足道。
设置
n = 3
_data = np.sort(np.random.choice(np.arange(1, 4 * n), n))
字符串格式
您可以使用简单的迭代器和 str.join
动态创建 tuple
个参数,然后您可以将其传递给 parameters
构造函数以获得 tuple
个你的参数。
params = parameters(', '.join(f'p{i}' for i in range(1, n+1)))
^^
# p1, p2, p3 = parameters('p1, p2, p3')
np.prod
这个操作非常简单。 np.prod
计算:
product of array elements over a given axis
当应用于 tuple
个 symfit
参数时,会生成您想要的 p1*p2*...pn
model = np.prod(params)
^^
# model = p1*p2*p3
np.concatenate
+ np.diff
可能是概括起来最复杂的一行,但仍然不太难理解。您想要将数据数组中连续元素的差异乘以您的参数,然后对结果求和。由于第一个元素与前一个元素没有区别,您可以使用 np.concatenate
将其添加回去。
u = np.concatenate((_data[:1], np.diff(_data)))
c1 = Eq(np.sum(params * u), 1)
^^
# Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1)
np.column_stack
您希望滚动查看参数作为约束:p1-p2
、p2-p3
、...pn, 0
。这只是用原始 tuple
参数堆叠一个零填充的一次性元组,然后使用列表理解解压到您的 Ge
构造函数中。
ges = [Ge(*group) for group in np.column_stack((params, params[1:] + (0,)))]
适合!
我这里什么都没改!
constraints = [c1, *ges]
fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()
我有以下优化代码,它由变量 n
参数化。
from symfit import parameters, Eq, Ge, Fit
import numpy as np
n = 3
xdata = np.sort(np.random.choice(range(1, 4*n), n)) # Make fake data
print(xdata)
p1, p2, p3 = parameters('p1, p2, p3')
model = p1*p2*p3
constraints = [
Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1),
Ge(p1, p2),
Ge(p2, p3),
Ge(p3, 0)
]
fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()
print(fit_result)
我想将它用于更大的 n
值,但我不知道如何更改行
p1, p2, p3 = parameters('p1, p2, p3')
model = p1*p2*p3
和constraints
来应对任意大的n
。
代码正在使用 symfit 库。 link 显示了如何使用 parameters
的示例以及文档的 link。
如何做到这一点?
需要从n
paramstr = ', '.join(['p{}'.format(i) for i in range(1, n)])
# when n=1, paramstr = 'p1, p2, p3'
使用 paramstr
作为 parameters
函数的参数。
paramvals = parameters(paramstr)
model
可以通过减少其乘积的 paramvals
来重构。
from functools import reduce
model = reduce(lambda x, y: x * y, paramvals, 1)
现在进入有趣的部分! constraints
可以重构为:
eqs = xdata[0] * paramvals[0] + sum(
(xdata[i] - xdata[i-1]) * paramvals[i]
for i in range(1, n)
)
ges = [
Ge(paramvals[i-1], paramvals[i])
for i in range(1, n)
]
ges.append(
Ge(paramvals[-1], 0)
)
constraints = [
Eq(eqs, 1),
*ges
]
我对 Symfit 一无所知,但如果您只是想将上述代码泛化为任意 N,那么:
- 可以为任意N生成一个看起来像
"p1, p2, p3"
的字符串,解析成一个参数列表:
params_string = ", ".join("p{}".format(i + 1) for i in range(n))
params = parameters(params_string)
解析字符串以获得参数列表的想法对我来说听起来很臭,而且我确信有更好的方法来以编程方式声明一堆参数,但这将尽可能接近你的原代码在做。
编辑:查看 Symfit 文档,似乎 parameters(s)
只是一个快捷方式,您实际上可以这样做:
params = [Parameter("p{}".format(i + 1)) for i in range(n)]
这不需要您构建自己的所有参数名称的连接字符串,这样 Symfit 就可以将它们拆分回单独的参数名称。这还允许您为参数定义其他属性,例如它们的初始值或它们的 min/max 范围。
- 您可以概括
Eq
约束:
coeffs = [xdata[0]] + [(xdata[i+1] - xdata[i]) for i in range(n-1)]
eq_constraint = Eq(sum(param * coeff for param, coeff in zip(params, coeffs), 1)
或者,正如另一个答案那样,使用 numpy 操作:
coeffs = np.concat(xdata[:1], np.diff(xdata))
eq_constraint = Eq(np.sum(params * coeffs), 1)
- 您可以概括
Ge
个约束:
ge_constraints = [Ge(params[i + 1], params[i]) for i in range(n - 1)] + [Ge(params[-1], 0]
constraints = [eq_constraint] + ge_constraints
同样,这可以使用 numpy 操作来完成,但我会把它留给@user3483203 的回答。
- 您可以使用
reduce
乘以所有参数:
model = reduce(lambda x, y: x * y, params, 1)
或使用numpy.prod
:
model = np.prod(params)
这应该足以将上述概括为任意 N。
Numpy 真正 与 symfit
库交互良好。使用它时,您尝试概括的所有操作都相当微不足道。
设置
n = 3
_data = np.sort(np.random.choice(np.arange(1, 4 * n), n))
字符串格式
您可以使用简单的迭代器和
str.join
动态创建tuple
个参数,然后您可以将其传递给parameters
构造函数以获得tuple
个你的参数。
params = parameters(', '.join(f'p{i}' for i in range(1, n+1)))
^^
# p1, p2, p3 = parameters('p1, p2, p3')
np.prod
这个操作非常简单。
np.prod
计算:product of array elements over a given axis
当应用于
tuple
个symfit
参数时,会生成您想要的p1*p2*...pn
model = np.prod(params)
^^
# model = p1*p2*p3
np.concatenate
+np.diff
可能是概括起来最复杂的一行,但仍然不太难理解。您想要将数据数组中连续元素的差异乘以您的参数,然后对结果求和。由于第一个元素与前一个元素没有区别,您可以使用
np.concatenate
将其添加回去。
u = np.concatenate((_data[:1], np.diff(_data)))
c1 = Eq(np.sum(params * u), 1)
^^
# Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1)
np.column_stack
您希望滚动查看参数作为约束:
p1-p2
、p2-p3
、...pn, 0
。这只是用原始tuple
参数堆叠一个零填充的一次性元组,然后使用列表理解解压到您的Ge
构造函数中。
ges = [Ge(*group) for group in np.column_stack((params, params[1:] + (0,)))]
适合!
我这里什么都没改!
constraints = [c1, *ges]
fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()