我正在尝试求解方程组:
其中 a、b 和 c 是来自 pandas 数据框的列。我以前使用 Excel,通过更改其他列(函数)的值,在一列(残差)中使用 go seak 运行宏,但我不知道如何在 Python 中执行此操作,
我尝试过 fsolve,但结果总是我最初的猜测,所以,我猜它需要一些界限,但 fsolve 不允许引入界限,这就是我尝试 scipyn.NonlinearConstraint 的原因。下面是需要解决的数据集和函数:
import numpy as np import pandas as pd import scipy.optimize as opt from scipy.optimize import NonlinearConstraint a = np.linspace(300,400,30) b = np.random.randint(700,18000,30) c = np.random.uniform(1.4,4.0,30) df = pd.DataFrame({'A':a, 'B':b, 'C':c}) def func(zGuess,*Params): x,y,z = zGuess a,b,c = Params eq_1 = (((3.47-np.log10(y))**2+(np.log10(c)+1.22)**2)**0.5) - x eq_2 = ((a/101.32) * (101.32/b)** z) - y eq_3 = (0.381 * x + 0.05 * (b/101.32) -0.15) - z return eq_1,eq_2,eq_3 zGuess = np.array([2.6,20.2,0.92]) up_bound = (9,np.inf,1.1) low_bound = (0,-np.inf,0)
这里我尝试用 fsolve 来解决:
df['x'],df['y'],df['z'] = zip(*df.apply(lambda x: opt.fsolve(func,zGuess,args=(x['A'],x['B'],x['C'])),1) )
这里具有非线性约束:
df['x'],df['y'],df['z'] = zip(*df.apply(lambda x: NonlinearConstraint(func(zGuess,x['A'],x['B'],x['C']),low_bound,up_bound),1) )
但使用非线性约束时会出现错误:
TypeError: zip 参数 #1 必须支持迭代
我甚至尝试最小化熊猫数据框:
def func2(zGuess,*Params): x,y = zGuess a,b,c,n = Params eq_1 = (((3.47-np.log10(y))**2+(np.log10(c)+1.22)**2)**0.5) - x eq_2 = ((a/101.32) * (101.32/b)** n) - y return eq_1,eq_2,eq_3 zGuess2 = np.array([2.6,20.2]) n = np.linspace(0,1.,500) for i in n: df['x'],df['y'] = zip(*df.apply(lambda x: opt.fsolve(func2,zGuess2,args=(x['a'],x['b'],x['c'],i)),1)) df['n_calc'] = df.apply(lambda x: (0.381 * x['x'] + 0.05 * (x['b']/101.32) - 0.15),1) res = np.abs(df['n_calc'] - i) if res <= 1e-5: n_solver = i df['n_solver'].append(n_solver)
我从来没有想过像 excel 中的 go seak 这样简单的事情在 python 中执行会如此困难,我真的需要一些帮助,拜托。
你提出的问题是如何在 Python 中解决类似于 Excel 中 Goal Seek 的问题,其中目标是通过调整一组参数(如 a、b 和 c)来找到使某个公式的结果达到特定值的参数组合。
Goal Seek
a
b
c
在 Python 中,我们可以使用 scipy.optimize 模块来求解这样的非线性方程组,并且我们可以应用约束条件。你提到的 fsolve 是一个非常合适的工具,但它确实不会处理约束,因此我们需要其他方法来处理这个问题,特别是当你需要根据约束来寻找解时。
scipy.optimize
fsolve
我们可以采用以下两种方法来处理你的问题:
scipy.optimize.fsolve
fsolve 可以帮助我们求解方程组,但是它不会直接处理边界约束。如果不需要约束条件,那么你可以直接使用 fsolve。
使用 scipy.optimize.minimize 来解决带约束的优化问题:
scipy.optimize.minimize
minimize
我们先从不使用约束的 fsolve 开始,然后再展示如何使用 minimize 处理带有约束的优化问题。
import numpy as np import pandas as pd import scipy.optimize as opt # 示例数据 a = np.linspace(300, 400, 30) b = np.random.randint(700, 18000, 30) c = np.random.uniform(1.4, 4.0, 30) df = pd.DataFrame({'A': a, 'B': b, 'C': c}) # 定义方程组 def func(zGuess, a, b, c): x, y, z = zGuess eq_1 = (((3.47 - np.log10(y))**2 + (np.log10(c) + 1.22)**2) ** 0.5) - x eq_2 = ((a / 101.32) * (101.32 / b) ** z) - y eq_3 = (0.381 * x + 0.05 * (b / 101.32) - 0.15) - z return eq_1, eq_2, eq_3 # 初始猜测 zGuess = np.array([2.6, 20.2, 0.92]) # 使用 fsolve 解决方程组 df['x'], df['y'], df['z'] = zip(*df.apply(lambda row: opt.fsolve(func, zGuess, args=(row['A'], row['B'], row['C'])), axis=1)) # 查看结果 print(df[['x', 'y', 'z']])
如果你需要使用边界约束(如 x 在某个范围内),我们可以使用 scipy.optimize.minimize 来处理带有约束的优化问题。
x
import numpy as np import pandas as pd import scipy.optimize as opt # 示例数据 a = np.linspace(300, 400, 30) b = np.random.randint(700, 18000, 30) c = np.random.uniform(1.4, 4.0, 30) df = pd.DataFrame({'A': a, 'B': b, 'C': c}) # 定义方程组 def func(zGuess, a, b, c): x, y, z = zGuess eq_1 = (((3.47 - np.log10(y))**2 + (np.log10(c) + 1.22)**2) ** 0.5) - x eq_2 = ((a / 101.32) * (101.32 / b) ** z) - y eq_3 = (0.381 * x + 0.05 * (b / 101.32) - 0.15) - z return eq_1**2 + eq_2**2 + eq_3**2 # 目标函数:最小化误差的平方和 # 初始猜测 zGuess = np.array([2.6, 20.2, 0.92]) # 设置边界 bounds = [(0, 9), (0, np.inf), (0, 1.1)] # 使用 minimize 进行优化 def solve_for_row(row): result = opt.minimize(func, zGuess, args=(row['A'], row['B'], row['C']), bounds=bounds) if result.success: return result.x else: return [np.nan, np.nan, np.nan] # 对每一行数据应用优化 df['x'], df['y'], df['z'] = zip(*df.apply(solve_for_row, axis=1)) # 查看结果 print(df[['x', 'y', 'z']])
(0, 9)
(0, np.inf)
apply
这些方法可以帮助你在 Python 中实现类似 Excel Goal Seek 的功能。希望这能解决你的问题!
我将忽略您问题中的 pandas 部分,只讨论优化部分。忽略所有 pandas 噪音,您的问题基本上是如何求解具有简单变量界限的方程。这个问题已经回答过好几次了,例如请参见此处。
以下是一些提示:
func
以下是一个可解决数据框中每行方程的工作版本。您可以随意重写它,以便在里面使用它df.apply()。
df.apply()
from scipy.optimize import minimize def func(zz, *params): x,y,z = zz a,b,c = params eq_1 = (((3.47-np.log10(y))**2+(np.log10(c)+1.22)**2)**0.5) - x eq_2 = ((a/101.32) * (101.32/b)** z) - y eq_3 = (0.381 * x + 0.05 * (b/101.32) -0.15) - z return np.array((eq_1,eq_2,eq_3)) bounds = [(1e-6, None), (1e-6, None), (1e-6, None)] zGuess = np.array([2.6,20.2,0.92]) # create the new columns df[["x", "y", "z"]] = np.zeros((30, 3)) for i, row in df.iterrows(): params = row.values[:3] res = minimize(lambda x: np.sum(func(x, *params)**2), zGuess, bounds=bounds) # res.x contains your solution # let's write the values into the dataframe for k, val in zip(['x', 'y', 'z'], res.x): df.loc[i, k] = val
我从来没有想过像 excel 中的 go seek 这样简单的事情在 python 中执行会如此困难
在没有阅读文档的情况下混合几种不同的概念会使任何编程语言中的每项任务都变得困难。