在 pandas 数据帧上使用 scipy NonlinearConstraint 求解非线性方程

Solve non linear equations with scipy NonlinearConstraint over pandas dataframe

我正在尝试求解方程组:

其中 a、b 和 c 是来自 pandas 数据框的列。我曾经使用 Excel,其中我 运行 一个宏 通过更改其他列(函数)的值在一列(残差)中搜索,但我不知道如何在 Python 中执行此操作,我已经问过 here,但没有还没回答。

我试过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 argument #1 must support iteration

我什至试图最小化 pandas 数据帧:

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 中执行起来如此困难,我真的需要一些帮助。

我会忽略你问题的 pandas 部分,只会解决优化部分。忽略所有 pandas 噪声,您的问题基本上是如何求解具有简单变量边界的方程。这已被多次回答,例如参见here

这里有一些提示:

  • 你的边界没有意义,例如对数仅定义为正值,不定义为零。

  • 请熟悉np.ndarrays。使用 scipy.optimize.

    会让您的生活更轻松
  • 您的 NonlinearConstraint 也没有意义。这是一个可以传递给 scipy.optimize.minimize 的对象。它不可调用或优化方法。

  • 如果你想求解变量有界的方程,你需要将问题重新表述为非线性优化问题并通过scipy.optimize.minimize求解,见上面链接回答更多细节。请注意,我更改了您的 func,使其 returns 成为 np.ndarray。

以下是一个工作版本,可以解决数据框中每一行的方程式。随意重写它,以便您可以在 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

I never thought something so simple like go seak in excel would give such difficulty to perform in python

在不阅读文档的情况下混合几个不同的概念,使得在任何任意编程语言中的每项任务都变得困难。