【Python】三、Scipy——《用Python做科学计算》

xiaoxiao2021-02-28  65

1.最小二乘拟合

假设有一组实验数据(x[i], y[i]),我们知道它们之间的函数关系:y = f(x),通过这些已知信息,需要确定函数中的一些参数项。例如,如果f是一个线型函数f(x) = k*x+b,那么参数k和b就是我们需要确定的值。如果将这些参数用 p 表示的话,那么我们就是要找到一组 p 值使得如下公式中的S函数最小: 这种算法被称之为最小二乘拟合(Least-square fitting)。 ------------------------------------------------------------------------------------------------------------------------------- 用已知数据集(x,y)去确定一组参数(k,b)使得实际的y值减去预测的y值的平方和最小。 ------------------------------------------------------------------------------------------------------------------------------- scipy中的子函数库optimize已经提供了实现最小二乘拟合算法的函数leastsq。下面是用leastsq进行数据拟合的一个例子: # -*- coding: utf-8 -*- # 注意如果代码里有中文,文件开头必须添加上面这句话,或者添加“#encoding:utf-8”。 import numpy as np from scipy.optimize import leastsq import pylab as pl def func(x, p): """ 数据拟合所用的函数: A*sin(2*pi*k*x + theta) """ A, k, theta = p return A*np.sin(2*np.pi*k*x+theta) def residuals(p, y, x): """ 实验数据x, y和拟合函数之间的差,p为拟合需要找到的系数 """ return y - func(x, p) x = np.linspace(0, -2*np.pi, 100) A, k, theta = 10, 0.34, np.pi/6 # 真实数据的函数参数 y0 = func(x, [A, k, theta]) # 真实数据 y1 = y0 + 2 * np.random.randn(len(x)) # 加入噪声之后的实验数据 p0 = [7, 0.2, 0] # 第一次猜测的函数拟合参数 # 调用leastsq进行数据拟合 # residuals为计算误差的函数 # p0为拟合参数的初始值 # args为需要拟合的实验数据 plsq = leastsq(residuals, p0, args=(y1, x)) print u"真实参数:", [A, k, theta] print u"拟合参数", plsq[0] # 实验数据拟合后的参数 pl.plot(x, y0, label=u"真实数据") pl.plot(x, y1, label=u"带噪声的实验数据") pl.plot(x, func(x, plsq[0]), label=u"拟合数据") pl.legend() pl.show() 这个例子中我们要拟合的函数是一个正弦波函数,它有三个参数 A, k, theta ,分别对应振幅、频率、相角。假设我们的实验数据是一组包含噪声的数据 x, y1,其中y1是在真实数据y0的基础上加入噪声的到了。 通过leastsq函数对带噪声的实验数据x, y1进行数据拟合,可以找到x和真实数据y0之间的正弦关系的三个参数: A, k, theta。 最后我们可以发现拟合参数虽然和真实参数完全不同,但是由于正弦函数具有周期性,实际上拟合参数得到的函数和真实参数对应的函数是一致的。

2. 函数最小值

optimize库提供了几个求函数最小值的算法:fmin, fmin_powell, fmin_cg, fmin_bfgs。下面的程序通过求解卷积的逆运算演示fmin的功能。 对于一个离散的线性时不变系统h, 如果它的输入是x,那么其输出y可以用x和h的卷积表示: 现在的问题是如果已知系统的输入x和输出y,如何计算系统的传递函数h;或者如果已知系统的传递函数h和系统的输出y,如何计算系统的输入x。这种运算被称为反卷积运算,是十分困难的,特别是在实际的运用中,测量系统的输出总是存在误差的。 下面用fmin计算反卷积,这种方法只能用在很小规模的数列之上,因此没有很大的实用价值,不过用来评价fmin函数的性能还是不错的。 # -*- coding: utf-8 -*- # 本程序用各种fmin函数求卷积的逆运算 import scipy.optimize as opt import numpy as np def test_fmin_convolve(fminfunc, x, h, y, yn, x0): """ x (*) h = y, (*)表示卷积 yn为在y的基础上添加一些干扰噪声的结果 x0为求解x的初始值 """ def convolve_func(h): """ 计算 yn - x (*) h 的power fmin将通过计算使得此power最小 """ return np.sum((yn - np.convolve(x, h))**2) # 调用fmin函数,以x0为初始值 h0 = fminfunc(convolve_func, x0) print fminfunc.__name__ print "---------------------" # 输出 x (*) h0 和 y 之间的相对误差 print "error of y:", np.sum((np.convolve(x, h0)-y)**2)/np.sum(y**2) # 输出 h0 和 h 之间的相对误差 print "error of h:", np.sum((h0-h)**2)/np.sum(h**2) print def test_n(m, n, nscale): """ 随机产生x, h, y, yn, x0等数列,调用各种fmin函数求解b m为x的长度, n为h的长度, nscale为干扰的强度 """ x = np.random.rand(m) h = np.random.rand(n) y = np.convolve(x, h) yn = y + np.random.rand(len(y)) * nscale x0 = np.random.rand(n) test_fmin_convolve(opt.fmin, x, h, y, yn, x0) test_fmin_convolve(opt.fmin_powell, x, h, y, yn, x0) test_fmin_convolve(opt.fmin_cg, x, h, y, yn, x0) test_fmin_convolve(opt.fmin_bfgs, x, h, y, yn, x0) if __name__ == "__main__": test_n(200, 20, 0.1)

-------------------------------------------------------------------------------------------------------------------------------

if __name__ == "__main__"上面这句代码的功能是在当前文件下编译运行的时候,执行if下面的语句。如果在其他文件内通过import调用本文件,if内语句不执行。

-------------------------------------------------------------------------------------------------------------------------------

3.非线性方程组求解

optimize库中的fsolve函数可以用来对非线性方程组进行求解。它的基本调用形式如下: fsolve(func, x0) func(x)是计算方程组误差的函数,它的参数x是一个矢量,表示方程组的各个未知数的一组可能解,func返回将x代入方程组之后得到的误差;x0为未知数矢量的初始值。如果要对如下方程组进行求解的话: f1(u1,u2,u3) = 0 f2(u1,u2,u3) = 0 f3(u1,u2,u3) = 0 那么func可以如下定义: def func(x): u1,u2,u3 = x return [f1(u1,u2,u3), f2(u1,u2,u3), f3(u1,u2,u3)] 下面是一个实际的例子,求解如下方程组的解: 5*x1 + 3 = 0 4*x0*x0 - 2*sin(x1*x2) = 0 x1*x2 - 1.5 = 0 程序如下: from scipy.optimize import fsolve from math import sin,cos def f(x): x0 = float(x[0]) x1 = float(x[1]) x2 = float(x[2]) return [ 5*x1+3, 4*x0*x0 - 2*sin(x1*x2), x1*x2 - 1.5 ] result = fsolve(f, [1,1,1]) print result print f(result) 输出为: [-0.70622057 -0.6 -2.5 ] [0.0, -9.1260332624187868e-14, 5.3290705182007514e-15] 由于fsolve函数在调用函数f时,传递的参数为数组,因此如果直接使用数组中的元素计算的话,计算速度将会有所降低,因此这里先用float函数将数组中的元素转换为Python中的标准浮点数,然后调用标准math库中的函数进行运算。 在对方程组进行求解时,fsolve会自动计算方程组的雅可比矩阵,如果方程组中的未知数很多,而与每个方程有关的未知数较少时,即雅可比矩阵比较稀疏时,传递一个计算雅可比矩阵的函数将能大幅度提高运算速度。 雅可比矩阵 雅可比矩阵是一阶偏导数以一定方式排列的矩阵,它给出了可微分方程与给定点的最优线性逼近,因此类似于多元函数的导数。例如前面的函数f1,f2,f3和未知数u1,u2,u3的雅可比矩阵如下: 使用雅可比矩阵的fsolve实例如下,计算雅可比矩阵的函数j通过fprime参数传递给fsolve,函数j和函数f一样,有一个未知数的解矢量参数x,函数j计算非线性方程组在矢量x点上的雅可比矩阵。由于这个例子中未知数很少,因此程序计算雅可比矩阵并不能带来计算速度的提升。 # -*- coding: utf-8 -*- from scipy.optimize import fsolve from math import sin,cos def f(x): x0 = float(x[0]) x1 = float(x[1]) x2 = float(x[2]) return [ 5*x1+3, 4*x0*x0 - 2*sin(x1*x2), x1*x2 - 1.5 ] def j(x): x0 = float(x[0]) x1 = float(x[1]) x2 = float(x[2]) return [ [0, 5, 0], [8*x0, -2*x2*cos(x1*x2), -2*x1*cos(x1*x2)], [0, x2, x1] ] result = fsolve(f, [1,1,1], fprime=j) print result print f(result)

4.数值积分

数值积分是对定积分的数值求解,例如可以利用数值积分计算某个形状的面积。下面让我们来考虑一下如何计算半径为1的半圆的面积,根据圆的面积公式,其面积应该等于PI/2。单位半圆(X轴上面的半圆)曲线可以用下面的函数表示: def half_circle(x): return (1-x**2)**0.5 下面的程序使用经典的分小矩形计算面积总和的方式,计算出单位半圆的面积: >>> N = 10000 >>> x = np.linspace(-1, 1, N) >>> dx = 2.0/N # 这个值其实就是上面x每相邻两个点之间的距离,也就是举行的宽 >>> y = half_circle(x) # 每一点的纵坐标,每一点到下一点极端的看成高一致。这样每一点的y值乘以dx就是当前点到下一点之间矩形的面积。 >>> dx * np.sum(y) * 2 # 求面积的两倍方便看pi数值 3.1412751679988937 利用上述方式计算出的圆上一系列点的坐标,还可以用numpy.trapz进行数值积分: >>> import numpy as np >>> np.trapz(y, x) * 2 # 面积的两倍 3.1415893269316042 此函数计算的是以x,y为顶点坐标的折线与X轴所夹的面积。同样的分割点数,trapz函数的结果更加接近精确值一些。 如果我们调用scipy.integrate库中的quad函数的话,将会得到非常精确的结果: >>> from scipy import integrate >>> pi_half, err = integrate.quad(half_circle, -1, 1) >>> pi_half*2 3.1415926535897984 这一节我搬过来的内容少很多,想学更多内容的读者建议去原地址学习。 本文参考自:http://old.sebug.net/paper/books/scipydoc/scipy_intro.html#id1
转载请注明原文地址: https://www.6miu.com/read-66254.html

最新回复(0)