python數學計算的工具:scipy和numpy
什麼是scipy、numpy、matplotlib?
Python是一種通用語言。它被解釋執行,是動態型別語言,並且非常適合互動工作和快速實現原型,然而又足夠強大用來寫大型應用。
NumPy是一個定義了數值陣列和矩陣型別和它們的基本運算的語言擴充套件。
SciPy是另一種使用NumPy來做高等數學、訊號處理、優化、統計和許多其它科學任務的語言擴充套件。
Matplotlib是一個幫助繪圖的語言擴充套件。
它們是用來幹什麼的?
SciPy和其它這些能用來完成許多工:
-
首先它們很適於用來進行嚴重依賴數學和數值運算的計算,它們可以原生地使用陣列、矩陣和對陣列和矩陣的運算,求出特徵根,計算積分,解決微分方程。
NumPy的陣列類(被用來實現矩陣類的基礎)是考慮速度的實現,所以存取NumPy陣列比存取Python列表速度更快。此外,NumPy實現一種陣列語言,以致於不需要大多數迴圈。例如,普通Python(C等等也是相似的):
a = range(10000000) b = range(10000000) c = [] for i in range(len(a)): c.append(a[i] + b[i])
這個迴圈將在幾GHz的處理器上耗時5到10秒,而NumPy:
import numpy as np a = np.arange(10000000) b = np.arange(10000000) c = a + b
不僅這個更加簡潔和易讀,而且相比幾乎是瞬間的,甚至NumPy的匯入都比普通Python中的迴圈更快。為什麼?Python是一種動態型別的解釋語言,這意味著在每次迴圈迭代時它都必須檢查運算物件a和b的型別來選則
+
正確的意義。(在python中+
被用到許多地方,比如連線字串、可以有不同元素型別的列表)當’+’的操作物件之一是一個NumPy陣列時,NumPy的add
函式將被Python自動選擇,僅僅檢測一次型別。然後它通過編譯C函式執行”真正的”加法迴圈。這和普通python中的解釋迴圈相比是非常快的。 -
有許多通用的或特定用途的數值程式碼使用了numpy和scipy。參考區域性軟體索引來檢視部分列表。Python有許多用來建立互動應用的高階的模組(例如
- 使用ipython使得互動工作更加簡單。資料處理,數值模型探索,在執行中嘗試運算可以很快地從一個想法得到結果(參考artical on ipython)。
- matplotlib模組製造高質量的繪圖,通過它你可以把你的資料和模型轉化為展示或文章用的影象。不必在一個程式中做數值計算,儲存資料,然後用另一個繪圖。
如何使用scipy工作
Python是一門語言,它有幾個使用者介面。沒有一個單獨的程式可以開始並且給一個整合的使用者體驗。取而代之的是各種使用python的方法。1
最普遍的方法是使用高階python互動shell來輸入命令和執行指令碼。指令碼可以用任何編輯器來寫,例如SPE,PyScripter,甚至notepad,emacs或者vi/vim。
scipy和numpy預設都沒有提供繪圖函式。它們僅僅是數值工具。推薦的繪圖工具包是matplotlib。
在Windows、Mac OS X和Linux下,所有這些工具都被Enthought Python發行版提供,獲取更多關於安裝這些的指導參考此站點安裝scipy部分。
學習使用scipy
最快的使用scipy工作的方法可能就是這個互動資料分析教程
示例會話
互動工作
讓我們看看在矩形窗函式的傅利葉變換。我們將使用一個互動python shell——ipython來做這個。因為我們想要通過互動繪圖呈現結果,我們將啟動使用--pylab
引數啟動ipython,這個引數允許使用matplotlib互動。
$ ipython --pylab
Python 2.5.1 (r251:54863, May 2 2007, 16:27:44)
Type "copyright", "credits" or "license" for more information.
IPython 0.7.3 -- An enhanced Interactive Python.
? -> Introduction to IPython's features.
%magic -> Information about IPython's 'magic' % functions.
help -> Python's own help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
Welcome to pylab, a matplotlib-based Python environment.
For more information, type 'help(pylab)'.
ipython提供了許多方便的特性,想tab補全python函式和一個優秀的幫助系統。
In [1]: %logstart
Activating auto-logging. Current session state plus future input saved.
Filename : ipython_log.py
Mode : rotate
Output logging : False
Raw input log : False
Timestamping : False
State : active
這個啟用一個登入會話到一個檔案。登入檔案格式允許在以後就像一個python指令碼一樣被簡單的執行,或編輯進一個程式。ipython也記錄所有的輸入輸出(並把它們儲存在叫In和Out的列表中),因此你可以啟動有追溯的登入。
In [2]: from scipy import *
因為numpy和scipy不是構建在python中的,你必須顯示地告訴python載入它們的特性。Scipy提供numpy所以當匯入scipy時匯入它是不必要的。
現在開始實際的數學:
In [3]: a = zeros(1000)
In [4]: a[:100]=1
第一行正如你所期望的那樣簡單地建立了一個有1000個0的陣列;numpy預設使這些0是雙精度浮點數,但是如果我想要單精度或複數,我可以指定zeors
的額外引數。第二行把一百個元素設定成-1.
然後我想要對這個陣列進行傅利葉變換,scipy提供fft
函式來完成這些:
b = fft(a)
為了看看b是什麼樣的,我將使用matplotlib庫。如果你使用”–pylab”啟動ipython將不需要匯入matplotlib。否則你應匯入它:from pylab import *
但是你將沒有互動功能(當你建立時自動繪圖)。
In [6]: plot(abs(b))
Out[6]: [<matplotlib.lines.Line2D instance at 0xb7b9144c>]
In [7]: show()
這將出現一個顯示b的影象的視窗,如果你啟動ipython時使用--pylab
的話show
命令是不必要的。
我注意到如果我把b的0頻移動到中間看起來更好。我可以通過連線b的後半部分和前半部分來實現,但是我記不清concatenate的語法了:
In [8]: concatenate?
Type: builtin_function_or_method
Base Class: <type 'builtin_function_or_method'>
String Form: <built-in function concatenate>
Namespace: Interactive
Docstring:
concatenate((a1, a2, ...), axis=0)
Join arrays together.
The tuple of sequences (a1, a2, ...) are joined along the given axis
(default is the first one) into a single numpy array.
Example:
>>> concatenate( ([0,1,2], [5,6,7]) )
array([0, 1, 2, 5, 6, 7])
In [9]: f=arange(-500,500,1)
In [10]: grid(True)
In [11]: plot(f,abs(concatenate((b[500:],b[:500]))))
Out[11]: [<matplotlib.lines.Line2D instance at 0xb360ca4c>]
In [12]: show()
這得到了我想要的影象。我可以使用互動控制上下移動和縮放圖片,並且為包含在出版物中產生postscript輸出(如果你想要學習更多繪圖知識,建議你閱讀matplotlib教程)。
執行指令碼
當你一遍又一遍重複做著相同的工作,把一些命令儲存在檔案中並把它們作為指令碼在ipython中執行將很有用。你可以使用”Ctrl-D”退出當前的ipython會話並且編輯ipython_log.py
檔案。當你想要執行這個檔案中的指令時,你可以開啟一個新的ipython會話輸入命令%run
-i ipython_log.py
。
當編輯一個指令碼檔案時,在ipython中嘗試一些命令也很方便。這將允許你在儲存和執行之前,對一些簡單的情況逐行嘗試你的指令碼。
一些關於匯入(import)的筆記
如果你僅僅初學scipy和與其伴侶,以下的東西對你並不那麼重要,不要太操心。但是當你開發一些大型的應用時最好記住它。
對互動工作(在ipython中)和一些小型的指令碼使用from scipy import *
沒什麼。這樣將會有個優點就是所有的功能在當前名稱空間都是立即可用的。然而,對大型的程式和軟體包來說,建議只匯入你真正需要的函式和模組。讓我們考慮這種情況:你(為了無論什麼理由)想要比較numpy和scipy的fft
函式。在你的指令碼中你應這樣寫:
# import from module numpy.fft
from numpy.fft import fft
# import scipy's fft implementation and rename it;
# Note: `from scipy import fft` actually imports numpy.fft.fft (check with
# `scipy.fft?` in Ipython or look at .../site-packages/scipy/__init__.py)
from scipy.fftpack import fft as scipy_fft
這個優勢就是,當你檢視程式碼時,你可以顯式的知道你在匯入什麼,程式碼便因此變得清晰和可讀。而且,這通常比通過import *
匯入所有東西更快,特別是如果你是從一個像scipy一樣特別大的庫中匯入。
然而,如果你使用許多不同的numpy函式,如果你顯式匯入每一個函式匯入宣告將變得非常長。但是你可以匯入整個包來代替使用import *
。
from numpy import * # bad
from numpy import abs, concatenate, sin, pi, dot, amin, amax, asarray, cov, diag, zeros, empty, exp, eye, kaiser # very long
import numpy # good
# use numpy.fft.fft() on array 'a'
b = numpy.fft.fft(a)
沒關係,因為通常import numpy
非常快。另一方面,scipy相當大(有很多子包)。因此from
scipy import *
第一次匯入可能非常慢(所有接下來的匯入宣告將會更迅速地執行,因為實際上沒有再次匯入)。這是為什麼當你匯入scipy時子包的匯入預設被禁止(像scipy.fft),這樣它才能像import
numpy
一樣快。如果你想使用比如說scipy.fft,你不得不顯式的匯入它(這無論如何是個好的想法)。如果你想要一次載入所有子包,你將不得不import scipy; scipy.pkgbuild()
。使用ipython的互動會話,你可以通過scipy
profile呼叫它(ipython -p scipy
),為你讀取scipy的配置檔案(通常在~/.ipython/ipythonrc-scipy)和載入所有scipy。對在即時互動環境使用scipy和matplotlib繪圖,你可以用像這樣的命令ipython
--pylab -p scipy
。