<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    SciPy-數值計算庫?

                    SciPy函數庫在NumPy庫的基礎上增加了眾多的數學、科學以及工程計算中常用的庫函數。例如線性代數、常微分方程數值求解、信號處理、圖像處理、稀疏矩陣等等。由于其涉及的領域眾多、本書沒有能力對其一一的進行介紹。作為入門介紹,讓我們看看如何用SciPy進行插值處理、信號濾波以及用C語言加速計算。

                    最小二乘擬合?

                    假設有一組實驗數據(x[i], y[i]),我們知道它們之間的函數關系:y = f(x),通過這些已知信息,需要確定函數中的一些參數項。例如,如果f是一個線型函數f(x) = k*x+b,那么參數k和b就是我們需要確定的值。如果將這些參數用 p 表示的話,那么我們就是要找到一組 p 值使得如下公式中的S函數最小:

                    S(\mathbf{p}) = \sum_{i=1}^m [y_i - f(x_i, \mathbf{p}) ]^2

                    這種算法被稱之為最小二乘擬合(Least-square fitting)。

                    scipy中的子函數庫optimize已經提供了實現最小二乘擬合算法的函數leastsq。下面是用leastsq進行數據擬合的一個例子:

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    27
                    28
                    29
                    30
                    31
                    32
                    33
                    34
                    35
                    36
                    37
                    38
                    39
                    # -*- coding: 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。下面是程序的輸出:

                    >>> 真實參數: [10, 0.34000000000000002, 0.52359877559829882]
                    >>> 擬合參數 [-9.84152775  0.33829767 -2.68899335]
                    _images/scipy_intro_01.png

                    調用leastsq函數對噪聲正弦波數據進行曲線擬合

                    我們看到擬合參數雖然和真實參數完全不同,但是由于正弦函數具有周期性,實際上擬合參數得到的函數和真實參數對應的函數是一致的。

                    函數最小值?

                    optimize庫提供了幾個求函數最小值的算法:fmin, fmin_powell, fmin_cg, fmin_bfgs。下面的程序通過求解卷積的逆運算演示fmin的功能。

                    對于一個離散的線性時不變系統h, 如果它的輸入是x,那么其輸出y可以用x和h的卷積表示:

                    y = x * h

                    現在的問題是如果已知系統的輸入x和輸出y,如何計算系統的傳遞函數h;或者如果已知系統的傳遞函數h和系統的輸出y,如何計算系統的輸入x。這種運算被稱為反卷積運算,是十分困難的,特別是在實際的運用中,測量系統的輸出總是存在誤差的。

                    下面用fmin計算反卷積,這種方法只能用在很小規模的數列之上,因此沒有很大的實用價值,不過用來評價fmin函數的性能還是不錯的。

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    27
                    28
                    29
                    30
                    31
                    32
                    33
                    34
                    35
                    36
                    37
                    38
                    39
                    40
                    41
                    42
                    43
                    44
                    45
                    46
                    47
                    48
                    # -*- 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) 
                    

                    下面是程序的輸出:

                    fmin
                    ーーーーーーーーーーー
                    error of y: 0.00568756699607
                    error of h: 0.354083287918
                    
                    fmin_powell
                    ーーーーーーーーーーー
                    error of y: 0.000116114709857
                    error of h: 0.000258897894009
                    
                    fmin_cg
                    ーーーーーーーーーーー
                    error of y: 0.000111220299615
                    error of h: 0.000211404733439
                    
                    fmin_bfgs
                    ーーーーーーーーーーー
                    error of y: 0.000111220251551
                    error of h: 0.000211405138529
                    

                    非線性方程組求解?

                    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

                    程序如下:

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    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會自動計算方程組的雅可比矩陣,如果方程組中的未知數很多,而與每個方程有關的未知數較少時,即雅可比矩陣比較稀疏時,傳遞一個計算雅可比矩陣的函數將能大幅度提高運算速度。筆者在一個模擬計算的程序中需要大量求解近有50個未知數的非線性方程組的解。每個方程平均與6個未知數相關,通過傳遞雅可比矩陣的計算函數使計算速度提高了4倍。

                    雅可比矩陣

                    雅可比矩陣是一階偏導數以一定方式排列的矩陣,它給出了可微分方程與給定點的最優線性逼近,因此類似于多元函數的導數。例如前面的函數f1,f2,f3和未知數u1,u2,u3的雅可比矩陣如下:

                    \begin{bmatrix}
\dfrac{\partial f1}{\partial u1} & \dfrac{\partial f1}{\partial u2} & \dfrac{\partial f1}{\partial u3} \\[9pt]
\dfrac{\partial f2}{\partial u1} & \dfrac{\partial f2}{\partial u2} & \dfrac{\partial f2}{\partial u3} \\[9pt]
\dfrac{\partial f3}{\partial u1} & \dfrac{\partial f3}{\partial u2} & \dfrac{\partial f3}{\partial u3} \\
\end{bmatrix}

                    使用雅可比矩陣的fsolve實例如下,計算雅可比矩陣的函數j通過fprime參數傳遞給fsolve,函數j和函數f一樣,有一個未知數的解矢量參數x,函數j計算非線性方程組在矢量x點上的雅可比矩陣。由于這個例子中未知數很少,因此程序計算雅可比矩陣并不能帶來計算速度的提升。

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    # -*- 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)
                    

                    B-Spline樣條曲線?

                    interpolate庫提供了許多對數據進行插值運算的函數。下面是使用直線和B-Spline對正弦波上的點進行插值的例子。

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    # -*- coding: utf-8 -*-
                    import numpy as np
                    import pylab as pl
                    from scipy import interpolate 
                    
                    x = np.linspace(0, 2*np.pi+np.pi/4, 10)
                    y = np.sin(x)
                    
                    x_new = np.linspace(0, 2*np.pi+np.pi/4, 100)
                    f_linear = interpolate.interp1d(x, y)
                    tck = interpolate.splrep(x, y)
                    y_bspline = interpolate.splev(x_new, tck)
                    
                    pl.plot(x, y, "o",  label=u"原始數據")
                    pl.plot(x_new, f_linear(x_new), label=u"線性插值")
                    pl.plot(x_new, y_bspline, label=u"B-spline插值")
                    pl.legend()
                    pl.show()
                    
                    _images/scipy_intro_02.png

                    使用interpolate庫對正弦波數據進行線性插值和B-Spline插值

                    在這段程序中,通過interp1d函數直接得到一個新的線性插值函數。而B-Spline插值運算需要先使用splrep函數計算出B-Spline曲線的參數,然后將參數傳遞給splev函數計算出各個取樣點的插值結果。

                    數值積分?

                    數值積分是對定積分的數值求解,例如可以利用數值積分計算某個形狀的面積。下面讓我們來考慮一下如何計算半徑為1的半圓的面積,根據圓的面積公式,其面積應該等于PI/2。單位半圓曲線可以用下面的函數表示:

                    def half_circle(x):
                        return (1-x**2)**0.5
                    

                    下面的程序使用經典的分小矩形計算面積總和的方式,計算出單位半圓的面積:

                    >>> N = 10000
                    >>> x = np.linspace(-1, 1, N)
                    >>> dx = 2.0/N
                    >>> y = half_circle(x)
                    >>> dx * np.sum(y[:-1] + y[1:]) # 面積的兩倍
                    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
                    

                    多重定積分的求值可以通過多次調用quad函數實現,為了調用方便,integrate庫提供了dblquad函數進行二重定積分,tplquad函數進行三重定積分。下面以計算單位半球體積為例說明dblquad函數的用法。

                    單位半球上的點(x,y,z)符合如下方程:

                    x^2 + y^2 + z^2 = 1

                    因此可以如下定義通過(x,y)坐標計算球面上點的z值的函數:

                    def half_sphere(x, y):
                        return (1-x**2-y**2)**0.5
                    

                    X-Y軸平面與此球體的交線為一個單位圓,因此積分區間為此單位圓,可以考慮為X軸坐標從-1到1進行積分,而Y軸從 -half_circle(x) 到 half_circle(x) 進行積分,于是可以調用dblquad函數:

                    >>> integrate.dblquad(half_sphere, -1, 1,
                        lambda x:-half_circle(x),
                        lambda x:half_circle(x))
                    >>> (2.0943951023931988, 2.3252456653390915e-14)
                    >>> np.pi*4/3/2 # 通過球體體積公式計算的半球體積
                    2.0943951023931953
                    

                    dblquad函數的調用方式為:

                    dblquad(func2d, a, b, gfun, hfun)
                    

                    對于func2d(x,y)函數進行二重積分,其中a,b為變量x的積分區間,而gfun(x)到hfun(x)為變量y的積分區間。

                    半球體積的積分的示意圖如下:

                    _images/mayavi2_sphere.png

                    半球體積的雙重定積分示意圖

                    X軸的積分區間為-1.0到1.0,對于X=x0時,通過對Y軸的積分計算出切面的面積,因此Y軸的積分區間如圖中紅色點線所示。

                    解常微分方程組?

                    scipy.integrate庫提供了數值積分和常微分方程組求解算法odeint。下面讓我們來看看如何用odeint計算洛侖茲吸引子的軌跡。洛侖茲吸引子由下面的三個微分方程定義:

                    \frac{dx}{dt} = \sigma (y - x)

                    \frac{dy}{dt} = x (\rho - z) - y

                    \frac{dz}{dt} = xy - \beta z

                    洛侖茲吸引子的詳細介紹: http://bzhang.lamost.org/website/archives/lorenz_attactor

                    這三個方程定義了三維空間中各個坐標點上的速度矢量。從某個坐標開始沿著速度矢量進行積分,就可以計算出無質量點在此空間中的運動軌跡。其中 \sigma, \rho, \beta 為三個常數,不同的參數可以計算出不同的運動軌跡: x(t), y(t), z(t)。 當參數為某些值時,軌跡出現餛飩現象:即微小的初值差別也會顯著地影響運動軌跡。下面是洛侖茲吸引子的軌跡計算和繪制程序:

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    # -*- coding: utf-8 -*-
                    from scipy.integrate import odeint 
                    import numpy as np 
                    
                    def lorenz(w, t, p, r, b): 
                        # 給出位置矢量w,和三個參數p, r, b計算出
                        # dx/dt, dy/dt, dz/dt的值
                        x, y, z = w
                        # 直接與lorenz的計算公式對應 
                        return np.array([p*(y-x), x*(r-z)-y, x*y-b*z]) 
                    
                    t = np.arange(0, 30, 0.01) # 創建時間點 
                    # 調用ode對lorenz進行求解, 用兩個不同的初始值 
                    track1 = odeint(lorenz, (0.0, 1.00, 0.0), t, args=(10.0, 28.0, 3.0)) 
                    track2 = odeint(lorenz, (0.0, 1.01, 0.0), t, args=(10.0, 28.0, 3.0)) 
                    
                    # 繪圖
                    from mpl_toolkits.mplot3d import Axes3D
                    import matplotlib.pyplot as plt 
                    
                    fig = plt.figure()
                    ax = Axes3D(fig)
                    ax.plot(track1[:,0], track1[:,1], track1[:,2])
                    ax.plot(track2[:,0], track2[:,1], track2[:,2])
                    plt.show()
                    
                    _images/scipy_intro_03.png

                    用odeint函數對洛侖茲吸引子微分方程進行數值求解所得到的運動軌跡

                    我們看到即使初始值只相差0.01,兩條運動軌跡也是完全不同的。

                    在程序中先定義一個lorenz函數,它的任務是計算出某個位置的各個方向的微分值,這個計算直接根據洛侖茲吸引子的公式得出。然后調用odeint,對微分方程求解,odeint有許多參數,這里用到的四個參數分別為:

                    1. lorenz, 它是計算某個位移上的各個方向的速度(位移的微分)
                    2. (0.0, 1.0, 0.0),位移初始值。計算常微分方程所需的各個變量的初始值
                    3. t, 表示時間的數組,odeint對于此數組中的每個時間點進行求解,得出所有時間點的位置
                    4. args, 這些參數直接傳遞給lorenz函數,因此它們都是常量

                    濾波器設計?

                    scipy.signal庫提供了許多信號處理方面的函數。在這一節,讓我們來看看如何利用signal庫設計濾波器,查看濾波器的頻率響應,以及如何使用濾波器對信號進行濾波。

                    假設如下導入signal庫:

                    >>> import scipy.signal as signal
                    

                    下面的程序設計一個帶通IIR濾波器:

                    >>> b, a = signal.iirdesign([0.2, 0.5], [0.1, 0.6], 2, 40)
                    

                    這個濾波器的通帶為0.2*f0到0.5*f0,阻帶為小于0.1*f0和大于0.6*f0,其中f0為1/2的信號取樣頻率,如果取樣頻率為8kHz的話,那么這個帶通濾波器的通帶為800Hz到2kHz。通帶的最大增益衰減為2dB,阻帶的最小增益衰減為40dB,即通帶的增益浮動在2dB之內,阻帶至少有40dB的衰減。

                    iirdesgin返回的兩個數組b和a, 它們分別是IIR濾波器的分子和分母部分的系數。其中a[0]恒等于1。

                    下面通過調用freqz計算所得到的濾波器的頻率響應:

                    >>> w, h = signal.freqz(b, a)
                    

                    freqz返回兩個數組w和h,其中w是圓頻率數組,通過w/pi*f0可以計算出其對應的實際頻率。h是w中的對應頻率點的響應,它是一個復數數組,其幅值為濾波器的增益,相角為濾波器的相位特性。

                    下面計算h的增益特性,并轉換為dB度量。由于h中存在幅值幾乎為0的值,因此先用clip函數對其裁剪之后,再調用對數函數,避免計算出錯。

                    >>> power = 20*np.log10(np.clip(np.abs(h), 1e-8, 1e100))
                    

                    通過下面的語句可以繪制出濾波器的增益特性圖,這里假設取樣頻率為8kHz:

                    >>> pl.plot(w/np.pi*4000, power)
                    

                    在實際運用中為了測量未知系統的頻率特性,經常將頻率掃描波輸入到系統中,觀察系統的輸出,從而計算其頻率特性。下面讓我們來模擬這一過程。

                    為了調用chirp函數以產生頻率掃描波形的數據,首先需要產生一個等差數組代表取樣時間,下面的語句產生2秒鐘取樣頻率為8kHz的取樣時間數組:

                    >>> t = np.arange(0, 2, 1/8000.0)
                    

                    然后調用chirp得到2秒鐘的頻率掃描波形的數據:

                    >>> sweep = signal.chirp(t, f0=0, t1 = 2, f1=4000.0)
                    

                    頻率掃描波的開始頻率f0為0Hz,結束頻率f1為4kHz,到達4kHz的時間為2秒,使用數組t作為取樣時間點。

                    下面通過調用lfilter函數計算sweep波形經過帶通濾波器之后的結果:

                    >>> out = signal.lfilter(b, a, sweep)
                    

                    lfilter內部通過如下算式計算IIR濾波器的輸出:

                    通過如下算式可以計算輸入為x時的濾波器的輸出,其中數組x代表輸入信號,y代表輸出信號:

                    y[n] & = b[0] x[n] + b[1] x[n-1] + \cdots + b[P] x[n-P] \\
     & - a[1] y[n-1] - a[2] y[n-2] - \cdots - a[Q] y[n-Q]

                    為了和系統的增益特性圖進行比較,需要獲取輸出波形的包絡,因此下面先將輸出波形數據轉換為能量值:

                    >>> out = 20*np.log10(np.abs(out))
                    

                    為了計算包絡,找到所有能量大于前后兩個取樣點(局部最大點)的下標:

                    >>> index = np.where(np.logical_and(out[1:-1] > out[:-2], out[1:-1] > out[2:]))[0] + 1
                    

                    最后將時間轉換為對應的頻率,繪制所有局部最大點的能量值:

                    >>> pl.plot(t[index]/2.0*4000, out[index] )
                    

                    下圖顯示freqz計算的頻譜和頻率掃描波得到的頻率特性,我們看到其結果是一致的。

                    _images/scipy_signal01.png

                    帶通IIR濾波器的頻率響應和頻率掃描波計算的結果比較

                    計算此圖的完整源程序請查看附錄中的 帶通濾波器設計

                    用Weave嵌入C語言?

                    Python作為動態語言其功能雖然強大,但是在數值計算方面有一個最大的缺點:速度不夠快。在Python級別的循環和計算的速度只有C語言程序的百分之一。因此才有了NumPy, SciPy這樣的函數庫,將高度優化的C、Fortran的函數庫進行包裝,以供Python程序調用。如果這些高度優化的函數庫無法實現我們的算法,必須從頭開始寫循環、計算的話,那么用Python來做顯然是不合適的。因此SciPy提供了快速調用C++語言程序的方法-- Weave。下面是對NumPy的數組求和的例子:

                     1
                     2
                     3
                     4
                     5
                     6
                     7
                     8
                     9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    20
                    21
                    22
                    23
                    24
                    25
                    26
                    27
                    28
                    29
                    30
                    31
                    32
                    33
                    34
                    35
                    36
                    37
                    38
                    39
                    40
                    41
                    42
                    # -*- coding: utf-8 -*-
                    import scipy.weave as weave
                    import numpy as np
                    import time
                    
                    def my_sum(a):
                        n=int(len(a))
                        code="""
                        int i;
                    
                        double counter;
                        counter =0;
                        for(i=0;i<n;i++){
                            counter=counter+a(i);
                        }
                        return_val=counter;
                        """
                    
                        err=weave.inline(
                            code,['a','n'],
                            type_converters=weave.converters.blitz,
                            compiler="gcc"
                        )
                        return err
                    
                    a = np.arange(0, 10000000, 1.0)
                    # 先調用一次my_sum,weave會自動對C語言進行編譯,此后直接運行編譯之后的代碼
                    my_sum(a)
                    
                    start = time.clock()
                    for i in xrange(100):
                        my_sum(a)  # 直接運行編譯之后的代碼
                    print "my_sum:", (time.clock() - start) / 100.0
                    
                    start = time.clock()
                    for i in xrange(100):
                        np.sum( a ) # numpy中的sum,其實現也是C語言級別
                    print "np.sum:", (time.clock() - start) / 100.0
                    
                    start = time.clock()
                    print sum(a) # Python內部函數sum通過數組a的迭代接口訪問其每個元素,因此速度很慢
                    print "sum:", time.clock() - start
                    

                    此例子在我的電腦上的運行結果為:

                    my_sum: 0.0294527349146
                    np.sum: 0.0527649547638
                    sum: 9.11022322669

                    可以看到用Weave編譯的C語言程序比numpy自帶的sum函數還要快。而Python的內部函數sum使用數組的迭代器接口進行運算,因此速度是Python語言級別的,只有Weave版本的1/300。

                    weave.inline函數的第一個參數為需要執行的C++語言代碼,第二個參數是一個列表,它告訴weave要把Python中的兩個變量a和n傳遞給C++程序,注意我們用字符串表示變量名。converters.blitz是一個類型轉換器,將numpy的數組類型轉換為C++的blitz類。C++程序中的變量a不是一個數組,而是blitz類的實例,因此它使用a(i)獲得其各個元素的值,而不是用a[i]。最后我們通過compiler參數告訴weave要采用gcc為C++編譯器。如果你安裝的是python(x,y)的話,gcc(mingw32)也一起安裝好了,否則你可能需要手工安裝gcc編譯器或者微軟的Visual C++。

                    Note

                    在我的電腦上,雖然安裝了Visual C++ 2008 Express,但仍然提示找不到合適的Visual C++編譯器。似乎必須使用編譯Python的編譯器版本。因此還是用gcc來的方便。

                    本書的進階部分還會對weave進行詳細介紹。這段程序先給了我們一個定心丸:你再也不必擔心Python的計算速度不夠快了。

                    首頁目錄

                    上一篇文章

                    NumPy-快速處理數據

                    下一篇文章

                    SymPy-符號運算好幫手

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频