1. 程式人生 > >python筆記之NUMPY(二)

python筆記之NUMPY(二)

NumPy 是 Numerical Python 的簡稱,是高效能運算和資料分析的基礎包。本書中幾乎所有高階工具都是建立在它的基礎之上,下面是它所能做的一些事情:

  • ndarray,快速和節省空間的多維陣列,提供陣列化的算術運算和高階的 廣播 功能。
  • 使用標準數學函式對整個陣列的資料進行快速運算,而不需要編寫迴圈。
  • 讀取/寫入磁碟上的陣列資料和操作儲存器映像檔案的工具。
  • 線性代數,隨機數生成,和傅立葉變換的能力。
  • 整合C,C++,Fortran程式碼的工具。

從生態系統的角度看,最後一點是最為重要的。因為NumPy 提供了易用的C API,它可以很容易的將資料傳遞到使用低階語言編寫的外部庫,也可以使外部庫返回NumPy陣列資料到Python。這一特性使得Python成為包裝傳統的C/C++/Fortran程式碼庫,並給它們一個動態的、易於使用的介面的首選語言。

雖然NumPy本身並沒有提供非常高階的資料分析功能,但是瞭解NumPy的陣列和麵向陣列的計算將會幫助你高效的使用類似於pandas這樣的工具。如果你是Python新手,並且只希望使用pandas來處理你手邊的資料,隨時可以略過這一章。更多的NumPy的特性例如廣播,請見第12章

對於大多數的資料分析應用來說,我關注的主要功能是:

  • 快速的向量化陣列操作:資料切割和清除,子集和過濾,轉化和任何其它型別的計算
  • 通用的陣列演算法,例如:sorting,unique和set操作
  • 有效的描述性統計和聚集/彙總資料
  • 資料對齊、關係資料的合併操作、異構資料的拼接操作
  • 使用陣列表示式來表示條件邏輯,而不是用帶有 if-elif-else
    分支的迴圈來表示
  • 組間資料的操作(聚合,轉換,功能應用)。關於這一點詳見 第5章

雖然NumPy提供了這些操作的計算功能,但你或許希望使用pandas作為大多數資料分析的基礎(特別是結構化或表格資料),因為它提供了一個豐富的,高階的介面使得常見的資料任務非常簡潔和簡單。pandas也提供了更多的一些特定領域的功能,如時間陣列操作,這是NumPy所沒有的。

在本章和全書,我始終使用 import numpy as np 。當然,你喜歡在程式碼中使用from numpy import * 來避免寫 np ,但我要提醒你反對這種習慣。

1.1. NumPy ndarray:多維陣列物件

NumPy的一個關鍵特性是它的N維陣列物件(ndarray),它在Python中是一個大型資料集的快速的,靈活的容器。陣列使你能夠在整個資料塊上進行數學運算,且與對應的純量元素間操作有相似的語法:

In [8]: data
Out[8]:
array([[ 0.9526, -0.246 , -0.8856],
       [ 0.5639, 0.2379, 0.9104]])
In [9]: data * 10                           In [10]: data + data
Out[9]:                                     Out[10]:
array([[ 9.5256, -2.4601, -8.8565],         array([[ 1.9051, -0.492 , -1.7713],
       [ 5.6385, 2.3794, 9.104 ]])                 [ 1.1277, 0.4759, 1.8208]])

ndarray是一個同種類資料的多維容器,也就是說,它的所有元素都是同類型的。每一個數組都有一個 shape (表示它每一維大小的元組)和dtype (一個描述陣列資料型別的物件):

In [11]: data.shape
Out[11]: (2, 3)
In [12]: data.dtype
Out[12]: dtype('float64')

本章將介紹ndarray的基礎知識,並足以應對本書剩下的部分。雖然對於許多的資料分析應用來說不必要對NumPy有深入的理解,但是精通面向陣列程式設計和思想是成為一名科學的Python大師的關鍵一步。

每當你在正文中看見“array”, “NumPy array”, or “ndarray”,除了很少的列外之外,它們都指的是同一個東西:ndarray物件。

1.1.1. 建立ndarray

最簡單的建立陣列的方式是使用 array 函式。它接受任何陣列物件(包括其它陣列),產生一個包含所傳遞的資料的新NumPy陣列。例如,列表就是一個很好的用於轉換的候選:

In [13]: data1 = [6, 7.5, 8, 0, 1]
In [14]: arr1 = np.array(data1)
In [15]: arr1
Out[15]: array([ 6. , 7.5, 8. , 0. , 1. ])

巢狀序列,如等長列表的列表,將會轉化為一個多維陣列:

In [16]: data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
In [17]: arr2 = np.array(data2)
In [18]: arr2
Out[18]:
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
In [19]: arr2.ndim
Out[19]: 2
In [20]: arr2.shape
Out[20]: (2, 4)

除非明確指定(在此以後會更多), np.array 試圖推斷一個好的資料型別給它所建立的陣列。資料型別儲存在一個特定的 dtype 的物件中;例如,在上面的兩個例子中,我們有:

In [21]: arr1.dtype
Out[21]: dtype('float64')
In [22]: arr2.dtype
Out[22]: dtype('int64')

np.array 之外,還有許多函式來建立新的陣列。例如, zeros ones 使用給定的長度或形狀分別的建立0s 和 1s陣列。 empty 會建立一個沒有使用特定值來初始化的陣列。給這些方法傳遞一個元組作為形狀來建立高維陣列:

In [23]: np.zeros(10)
Out[23]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
In [24]: np.zeros((3, 6))
Out[24]:
array([[ 0., 0., 0., 0., 0., 0.],
       [ 0., 0., 0., 0., 0., 0.],
       [ 0., 0., 0., 0., 0., 0.]])
In [25]: np.empty((2, 3, 2))
Out[25]:
array([[[ 4.94065646e-324, 4.94065646e-324],
        [ 3.87491056e-297, 2.46845796e-130],
        [ 4.94065646e-324, 4.94065646e-324]],
       [[ 1.90723115e+083, 5.73293533e-053],
        [ -2.33568637e+124, -6.70608105e-012],
        [ 4.42786966e+160, 1.27100354e+025]]])
假定 np.array 會返回一個全零的陣列是不安全的。在許多情況下,如前所示,它將返回未初始化的垃圾值。

arange 是Python內建 range 函式的陣列版本:

In [26]: np.arange(15)
Out[26]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

表格4-1 是一個用於構建陣列的標準函式的清單。

陣列構建函式
函式 描述
array 轉換輸入資料(列表,陣列或其它序列型別)到一個ndarray,可以推斷一個dtype或明確的設定一個dtype。預設拷貝輸入資料。
asarray 轉換輸入為一個ndarray,當輸入已經是一個ndarray時就不拷貝。
arange 同內建的range函式,但不返回列表而是一個ndarray
ones, ones_like 根據提供的shape和dtype產生一個全1的陣列。ones_like使用另一歌陣列為入參,產生一個shape和dtype都相同的陣列。
zeros, zeros_like 同ones和ones_like,但是生成全0的陣列
empty, enpty_like 通過分配新記憶體來構造新的陣列,但不同與ones 和 zeros,不初始任何值。
eye, identity 生成一個NxN的單位方陣(對角線上為1,其它地方為0)

1.1.2. ndarray的資料型別

資料型別或dtype是一個特別的物件,儲存了ndarray如何解釋一塊記憶體為特定型別資料的資訊:

In [27]: arr1 = np.array([1, 2, 3], dtype=np.float64)
In [28]: arr2 = np.array([1, 2, 3], dtype=np.int32)
In [29]: arr1.dtype
Out[29]: dtype('float64')
In [30]: arr2.dtype
Out[30]: dtype('int32')

Dtypes是使NumPy如此強大和靈活的一部分。在大多數情況下,它們直接對映到底層的機器表示,這是的很容易地讀取和寫入二進位制流到磁碟上,也能連結低階語言,如C或Fortran編寫的程式碼。數值表示的dtypes以相同的方式命名:一個型別名,如foltint ,後面跟著一個表示數字有多少位的數字。一個標準的雙精度浮點值(它是Python的 float 物件的底層表示)佔據8位元組或64位。因此,這一型別在NumPy中被認為是 float64 。見 表格4-2 是一個NumPy支援的全部資料型別的清單。

不要為了記憶NumPy的dtypes而煩惱,尤其你是一個新使用者。通常只需要關心你所處理資料的普通型別(浮點、複數、整形、布林型、字元竄或一般的Python物件)。當你需要更多的控制資料如何儲存到記憶體和磁碟,特別是大的資料集,知道你所控制的儲存型別是很好的。
NumPy資料型別
型別 型別碼 描述
型別 型別碼 描述
int8, uint8 i1, u1 有符號和無符號8位(1位元組)整數型別
int16, uint16 i2, u2 有符號和無符號16位整數型別
int32, uint32 i4, u4 有符號和無符號32位整數型別
int64, uint64 i8, u8 有符號和無符號64位整數型別
float16 f2 半精度浮點型別
float32 f4 or f 標準精度浮點。與C的 float 相容
float64, float128 f8 or d 標準雙精度浮點。與C的 double 和Python 的folat 物件相容
float128 f16 or g 擴充套件精度浮點
complex64, complex128, complex256 c8, c16, c32 分別使用兩個32,64,128位浮點表示的複數
bool ? 布林值,儲存 True False
object O Python物件型別
string_ S 定長字元竄型別(每字元一位元組)。例如,為了生成長度為10的字元竄,使用 ‘S10’
unicode_ f16 or g 擴充套件精度浮點(位元組書依賴平臺)。同 string_ 有相同的語義規範(例如:“U10“ )

你可以使用ndarray的 astype 方法顯示的把一個數組的dtype轉換或 投射 到另外的型別:

In [31]: arr = np.array([1, 2, 3, 4, 5])
In [32]: arr.dtype
Out[32]: dtype('int64')
In [33]: float_arr = arr.astype(np.float64)
In [34]: float_arr.dtype
Out[34]: dtype('float64')

在這個例子中,整形被轉換到浮點型。如果把浮點數轉換到整形dtype,小數部分將會被截斷:

In [35]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In [36]: arr
Out[36]: array([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
In [37]: arr.astype(np.int32)
Out[37]: array([ 3, -1, -2, 0, 12, 10], dtype=int32)

你可能有一個字元竄陣列表示的數字,可以使用 astype 把它們轉換到數字的形式:

In [38]: numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_)
In [39]: numeric_strings.astype(float)
Out[39]: array([ 1.25, -9.6 , 42. ])

如果因為某些原因(如一個字元竄不能轉換到 float64 )轉換失敗了,將會引起一個 TypeError 。正如你所看見的,我有一些懶,使用float 而不是 np.float64 ;NumPy會足夠聰明的把Python的型別對應到等價的dtypes。

你也可以使用dtype的另一個屬性:

In [40]: int_array = np.arange(10)
In [41]: calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
In [42]: int_array.astype(calibers.dtype)
Out[42]: array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])

你也可以使用速記的型別碼字元竄來指定一個dtype:

In [43]: empty_uint32 = np.empty(8, dtype='u4')
In [44]: empty_uint32
Out[44]:
array([ 0, 0, 65904672, 0, 64856792, 0,
        39438163, 0], dtype=uint32)
呼叫 astype 總是會建立一個新的陣列(原資料的拷貝),即使是新的dtype和原來的dtype相同。 值得牢記的是浮點數,如那些是 float64float32 的陣列,是唯一能夠接近分數的。在複雜的計算中,可能會產生 浮點錯誤 ,計較時到了一定的小數位數時才有效。

1.1.3. 陣列和純量間的操作

陣列非常重要,因為它們使你不使用迴圈就可以在資料上進行一系列操作。這通常被叫做向量化。相同大小的陣列間的算術運算,其操作作用在對應的元素上:

In [45]: arr = np.array([[1., 2., 3.], [4., 5., 6.]])

In [46]: arr
Out[46]:
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

In [47]: arr * arr                 In [48]:arr - arr
Out[47]:                           Out[48]:
array([[  1.,   4.,   9.],        array([[ 0., 0., 0.],
       [ 16.,  25.,  36.]])              [ 0., 0., 0.]])

純量的算術操作正如你期望的一樣,把操作值作用於每一個元素:

In [49]: 1 / arr                        In [50]: arr ** 0.5
Out[49]:                                Out[50]:
array([[ 1. , 0.5 , 0.3333],            array([[ 1. , 1.4142, 1.7321],
       [ 0.25 , 0.2 , 0.1667]])                [ 2. , 2.2361, 2.4495]])

在不同大小的陣列見的操作被叫做 broadcasting ,將在第12章 詳細討論。深入的瞭解broadcasting在本書的多數地方是不必要的。

1.1.4. 基本的索引和切片

NumPy的索引是一個內容豐富的主題,因為有許多方法可以使你在你的資料中選取一個子集或單個元素。一維的陣列很簡單,表面上它們的行為類似於Python的列表:

In [51]: arr = np.arange(10)
In [52]: arr
Out[52]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [53]: arr[5]
Out[53]: 5
In [54]: arr[5:8]
Out[54]: array([5, 6, 7])
In [55]: arr[5:8] = 12
In [56]: arr
Out[56]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

如你所見,當你給一個切片賦一純量值,如 arr[5:8]= 12 所示,該值被傳送(或 傳播 )到整個選擇區域。與列表的第一個重要的區別是陣列的切片在原來的陣列上(不生成新的陣列)。這意味著資料不會被拷貝,且對切片的任何修改都會影響源陣列:

In [57]: arr_slice = arr[5:8]
In [58]: arr_slice[1] = 12345
In [59]: arr
Out[59]: array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])
In [60]: arr_slice[:] = 64
In [61]: arr
Out[61]: array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])

如果你是使用NumPy的新手,這一點回事你感到驚訝,尤其當你使用過其它陣列程式語言,它們非常熱衷於拷貝資料。請記住,NumPy是設計用來處理大資料的情況,你可以想象如果NumPy堅持使用拷貝資料將會出現的效能和記憶體問題。

如果你想有陣列切片的一個拷貝,你需要明顯的拷貝陣列;例如 arr[5:8].copy()

對於高維陣列,你會有更多選項。在兩維的陣列,每一個索引的元素將不再是一個純量,而是一個一維陣列:

In [62]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
In [63]: arr2d[2]
Out[63]: array([7, 8, 9])

因此,單個元素可以遞迴的訪問,但是這會做多一點的工作。不過,你可以使用一個逗號分隔的索引列表來選擇單個元素。因此,下面的操作是等價的:

In [64]: arr2d[0][2]
Out[64]: 3
In [65]: arr2d[0, 2]
Out[65]: 3

NumPy陣列的索引,是在二維陣列上的索引圖例。

_images/index-2d.png

NumPy陣列的索引

在多維陣列中,如果你省略了後面的索引,返回的物件將會是一個較低維的ndarray,它包括較高維度的所有資料。因此,在 2*2*3 的陣列arr3d

In [66]: arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
In [67]: arr3d
Out[67]:
array([[[ 1, 2, 3],
        [ 4, 5, 6]],
       [[ 7, 8, 9],
        [10, 11, 12]]])

arr3d[0] 是一個 2*3 的陣列:

In [68]: arr3d[0]
Out[68]:
array([[1, 2, 3],
       [4, 5, 6]])

純量值和陣列都可以給 arr3d[0] 賦值:

In [69]: old_values = arr3d[0].copy()
In [70]: arr3d[0] = 42

In [71]: arr3d
Out[71]:
array([[[42, 42, 42],
        [42, 42, 42]],
       [[ 7, 8, 9],
        [10, 11, 12]]])

In [72]: arr3d[0] = old_values

In [73]: arr3d
Out[73]:
array([[[ 1, 2, 3],
        [ 4, 5, 6]],
       [[ 7, 8, 9],
        [10, 11, 12]]])

類似的, arr3d[1, 0] 給你那些索引以 (1, 0) 開始的值,形成了一個1維陣列:

In [74]: arr3d[1, 0]
Out[74]: array([7, 8, 9])

請注意,在所有的情況下,被選中的子節返回的陣列總是陣列視窗。

1.1.4.1. 帶切片的索引

如同一維物件,例如Python的列表,ndarrys可以使用熟悉的語法來切片:

In [75]: arr[1:6]
Out[75]: array([ 1, 2, 3, 4, 64])

較高維的物件給你更多的選擇,你可以切割一個或多個座標座標軸,並且可以混合整數。對上面的2維陣列, arr2d ,對它的切片有些不同:

In [76]: arr2d              In [77]: arr2d[:2]
Out[76]:                    Out[77]:
array([[1, 2, 3],           array([[1, 2, 3],
       [4, 5, 6],                  [4, 5, 6]])
       [7, 8, 9]])

正如你所見,它沿著 0 座標座標軸(第一個座標座標軸)切片。因此,一個切片沿著一個座標座標軸向選擇一個範圍的元素。你可以傳遞多個切片,就像你傳遞多個索引一樣:

In [78]: arr2d[:2, 1:]
Out[78]:
array([[2, 3],
       [5, 6]])

象這樣切片時,你得到的總是相同維數的陣列視窗。通過混合整形索引和切片,你可以得到較低維的切片:

In [79]: arr2d[1, :2]             In [80]: arr2d[2, :1]
Out[79]: array([4, 5])            Out[80]: array([7])

兩維陣列切片 圖解。注意,一個單一的冒號意味著取整個座標/座標軸,因此,你可以只切割更高維的座標軸,做法如下:

In [81]: arr2d[:, :1]
Out[81]:
array([[1],
       [4],
       [7]])

當然,給一個切片表示式賦值會對整個選擇賦值:

In [82]: arr2d[:2, 1:] = 0
_images/slicing-2d.png

兩維陣列切片


1.1.5. 布林索引

讓我們來考慮一個例子,我們有一些資料在一個數組中和一個有重複名字的陣列。我將會在這使用 numpy.random 中的 randn 函式來產生一些隨機的正態分佈的資料:

In [83]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [84]: data = randn(7, 4)
In [85]: names
Out[85]:
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
      dtype='|S4')
In [86]: data
Out[86]:
array([[-0.048 , 0.5433, -0.2349, 1.2792],
       [-0.268 , 0.5465, 0.0939, -2.0445],
       [-0.047 , -2.026 , 0.7719, 0.3103],
       [ 2.1452, 0.8799, -0.0523, 0.0672],
       [-1.0023, -0.1698, 1.1503, 1.7289],
       [ 0.1913, 0.4544, 0.4519, 0.5535],
       [ 0.5994, 0.8174, -0.9297, -1.2564]])

假設每一個名字都和 data 陣列中的一行對應。如果我們想要選擇與 ‘Bob’ 名字對應的所有行。象算術運算一樣,陣列的比較操作(例如== )也可以向量化。因此, namesBob 字元竄的比較會產生一個布林陣列:

In [87]: names == 'Bob'
Out[87]: array([ True, False, False, True, False, False, False], dtype=bool)

當索引陣列時可以傳遞這一布林陣列:

In [88]: data[names == 'Bob']
Out[88]:
array([[-0.048 , 0.5433, -0.2349, 1.2792],
       [ 2.1452, 0.8799, -0.0523, 0.0672]])

布林陣列必須和它索引的座標軸的長度相同。你甚至可以把布林陣列和切片或整數(或者整數序列,關於這一點後面會更多介紹)混合和匹配起來:

In [89]: data[names == 'Bob', 2:]
Out[89]:
array([[-0.2349, 1.2792],
       [-0.0523, 0.0672]])
In [90]: data[names == 'Bob', 3]
Out[90]: array([ 1.2792, 0.0672])

為了選擇除了 ‘Bob’ 之外的所有東西,你可以使用 != 或用 - 對條件表示式取反:

In [91]: names != 'Bob'
Out[91]: array([False, True, True, False, True, True, True], dtype=bool)

In [92]: data[-(names == 'Bob')]
Out[92]:
array([[-0.268 , 0.5465, 0.0939, -2.0445],
       [-0.047 , -2.026 , 0.7719, 0.3103],
       [-1.0023, -0.1698, 1.1503, 1.7289],
       [ 0.1913, 0.4544, 0.4519, 0.5535],
       [ 0.5994, 0.8174, -0.9297, -1.2564]])

使用布林算術操作符如 & (and) 和 | (or)來結合多個布林條件,下面是從三個名字中選取兩個的操作:

In [93]: mask = (names == 'Bob') | (names == 'Will')
In [94]: mask
Out[94]: array([True, False, True, True, True, False, False], dtype=bool)
In [95]: data[mask]
Out[95]:
array([[-0.048 , 0.5433, -0.2349, 1.2792],
       [-0.047 , -2.026 , 0.7719, 0.3103],
       [ 2.1452, 0.8799, -0.0523, 0.0672],
       [-1.0023, -0.1698, 1.1503, 1.7289]])

通過布林索引從一個數組中選取資料 總是 會建立資料的一份拷貝,即使是返回的陣列沒有改變。

Python的 andor 關鍵字不能與布林陣列一起工作。

通過布林陣列設定值工作於一種種常識性的方式。為了設定 data 中所有的負值為0,我們只需要:

In [96]: data[data < 0] = 0
In [97]: data
Out[97]:
array([[ 0. , 0.5433, 0. , 1.2792],
       [ 0. , 0.5465, 0.0939, 0. ],
       [ 0. , 0. , 0.7719, 0.3103],
       [ 2.1452, 0.8799, 0. , 0.0672],
       [ 0. , 0. , 1.1503, 1.7289],
       [ 0.1913, 0.4544, 0.4519, 0.5535],
       [ 0.5994, 0.8174, 0. , 0. ]])

使用一維布林陣列設定整行或列也非常簡單:

In [98]: data[names != 'Joe'] = 7
In [99]: data
Out[99]:
array([[ 7. , 7. , 7. , 7. ],
       [ 0. , 0.5465, 0.0939, 0. ],
       [ 7. , 7. , 7. , 7. ],
       [ 7. , 7. , 7. , 7. ],
       [ 7. , 7. , 7. , 7. ],
       [ 0.1913, 0.4544, 0.4519, 0.5535],
       [ 0.5994, 0.8174, 0. , 0. ]])

1.1.6. Fancy索引

Fancy 索引 是一個術語,被NumPy用來描述使用整形陣列索引。假如我們有一個 8*4 的陣列:

In [100]: arr = np.empty((8, 4))
In [101]: for i in range(8):
   .....:     arr[i] = i
In [102]: arr
Out[102]:
array([[ 0., 0., 0., 0.],
       [ 1., 1., 1., 1.],
       [ 2., 2., 2., 2.],
       [ 3., 3., 3., 3.],
       [ 4., 4., 4., 4.],
       [ 5., 5., 5., 5.],
       [ 6., 6., 6., 6.],
       [ 7., 7., 7., 7.]])

為了選出一個有特定順序行的子集,你可以傳遞一個列表或整形ndarray來指定想要的順序:

In [103]: arr[[4, 3, 0, 6]]
Out[103]:
array([[ 4., 4., 4., 4.],
       [ 3., 3., 3., 3.],
       [ 0., 0., 0., 0.],
       [ 6., 6., 6., 6.]])

很慶幸這個程式碼做了你所期望的!使用負的索引從結尾選擇行:

In [104]: arr[[-3, -5, -7]]
Out[104]:
array([[ 5., 5., 5., 5.],
       [ 3., 3., 3., 3.],
       [ 1., 1., 1., 1.]])

傳遞多個索引陣列有些微的不同;它選取一個一維陣列,元素對應與索引的每一個元組:

# 關於reshape在第12章會跟多介紹
In [105]: arr = np.arange(32).reshape((8, 4))
In [106]: arr
Out[106]:
array([[ 0, 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]])
In [107]: arr[[1, 5, 7, 2], [0, 3, 1, 2]]
Out[107]: array([ 4, 23, 29, 10])

花一點兒時間來看看剛剛發生了什麼:元素 (1, 0), (5, 3), (7, 1), 和(2, 2)被選擇了。 fancy索引的行為與一些使用者(也包括我自己)可能期望的有所不同,它因該是一個矩形區域,由選取的矩形的行和列組成。這裡有一個方法來得到它:

In [108]: arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
Out[108]:
array([[ 4, 7, 5, 6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11, 9, 10]])

另一種方法是使用 np.ix_ 函式,將兩個以為整形陣列轉換為位標,來選取一個正方形區域:

In [109]: arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
Out[109]:
array([[ 4, 7, 5, 6],
       [20, 23, 21, 22],
       [28, 31, 29, 30],
       [ 8, 11, 9, 10]])

注意,fancy索引,不像切片,它總是拷貝資料到一個新的陣列。

1.1.7. 轉置陣列和交換座標軸

轉置是一種特殊形式的變形,類似的它會返回基礎資料的一個視窗,而不會拷貝任何東西。陣列有 transpose 方法和專門的 T 屬性:

In [110]: arr = np.arange(15).reshape((3, 5))
In [111]: arr                          In [112]: arr.T
Out[111]:                              Out[112]:
array([[ 0, 1, 2, 3, 4],               array([[ 0, 5, 10],
       [ 5, 6, 7, 8, 9],                      [ 1, 6, 11],
       [10, 11, 12, 13, 14]])                 [ 2, 7, 12],
                                              [ 3, 8, 13],
                                              [ 4, 9, 14]])

當進行矩陣運算時,你常常會這樣做,像下面的例子一樣,使用 np.dot 計算內部矩陣來產生 XTX` :

In [113]: arr = np.random.randn(6, 3)
In [114]: np.dot(arr.T, arr)
Out[114]:
array([[ 2.584 , 1.8753, 0.8888],
       [ 1.8753, 6.6636, 0.3884],
       [ 0.8888, 0.3884, 3.9781]])

對於更高維的陣列, transpose 接受用於轉置的座標軸的號碼的一個元組(for extra mind bending):

In [115]: arr = np.arange(16).reshape((2, 2, 4))
In [116]: arr
Out[116]:
array([[[ 0, 1, 2, 3],
        [ 4, 5, 6, 7]],
       [[ 8, 9, 10, 11],
        [12, 13, 14, 15]]])

In [117]: arr.transpose((1, 0, 2))
Out[117]:
array([[[ 0, 1, 2, 3],
        [ 8, 9, 10, 11]],
       [[ 4, 5, 6, 7],
        [12, 13, 14, 15]]])

使用 .T 的轉置,僅僅是交換座標軸的一個特殊的情況:

In [118]: arr                             In [119]: arr.swapaxes(1, 2)
Out[118]:                                 Out[119]:
array([[[ 0, 1, 2, 3],                    array([[[ 0, 4],
        [ 4, 5, 6, 7]],                           [ 1, 5],
                                                  [ 2, 6],
       [[ 8, 9, 10, 11],                          [ 3, 7]],
        [12, 13, 14, 15]]])
                                                 [[ 8, 12],
                                                 [ 9, 13],
                                                 [10, 14],
                                                 [11, 15]]])

類似的 swapaxes 返回在資料上的一個視窗,而不進行拷貝。


1.2. 通用函式:快速的基於元素的陣列函式

一個通用的函式,或者 ufunc ,是一個在ndarrays的資料上進行基於元素的操作的函式。你可以認為它們是對簡單函式的一個快速向量化封裝,它們接受一個或多個標量值併產生一個或多個標量值。

許多 ufuncs 都是基於元素的簡單變換,像 sqrtexp

In [120]: arr = np.arange(10)
In [121]: np.sqrt(arr)
Out[121]:
array([ 0. , 1. , 1.4142, 1.7321, 2. , 2.2361, 2.4495,
        2.6458, 2.8284, 3. ])
In [122]: np.exp(arr)
Out[122]:
array([ 1. , 2.7183, 7.3891, 20.0855, 54.5982,
       148.4132, 403.4288, 1096.6332, 2980.958 , 8103.0839])

這些歸諸於 unary ufuncs。其它的,例如 addmaximum ,接受兩個陣列(因此,叫做binary ufuncs)且返回一個數組:

In [123]: x = randn(8)
In [124]: y = randn(8)
In [125]: x
Out[125]:
array([ 0.0749, 0.0974, 0.2002, -0.2551, 0.4655, 0.9222, 0.446 ,
       -0.9337])
In [126]: y
Out[126]:
array([ 0.267 , -1.1131, -0.3361, 0.6117, -1.2323, 0.4788, 0.4315,
       -0.7147])
In [127]: np.maximum(x, y) # element-wise maximum
Out[127]:
array([ 0.267 , 0.0974, 0.2002, 0.6117, 0.4655, 0.9222, 0.446 ,
       -0.7147])

雖然不常見,一個ufunc可以返回多個數組。 nodf 就是一個例子,它是Python內建 divmod 的向量化的版本:它返回一個副點數陣列的分數和整數部分:

In [128]: arr = randn(7) * 5
In [129]: np.modf(arr)
Out[129]:
(array([-0.6808, 0.0636, -0.386 , 0.1393, -0.8806, 0.9363, -0.883 ]),
 array([-2., 4., -3., 5., -3., 3., -6.]))

表格4-3 表格4-4 是可用的ufuncs的清單。

Unary ufuncs
函式 描述
abs, fabs 計算基於元素的整形,浮點或複數的絕對值。fabs對於沒有複數資料的快速版本
sqrt 計算每個元素的平方根。等價於 arr ** 0.5
square 計算每個元素的平方。等價於 arr ** 2
exp 計算每個元素的指數。
log, log10, log2, log1p 自然對數(基於e),基於10的對數,基於2的對數和 log(1+ x)
sign 計算每個元素的符號:1(positive),0(zero), -1(negative)
ceil 計算每個元素的天花板,即大於或等於每個元素的最小值
floor 計算每個元素的地板,即小於或等於每個元素的最大值
rint 圓整每個元素到最近的整數,保留dtype
modf 分別返回分數和整數部分的陣列
isnan 返回布林陣列標識哪些元素是 NaN (不是一個數)
isfinite, isinf 分別返回布林陣列標識哪些元素是有限的(non-inf, non-NaN)或無限的
cos, cosh, sin sinh, tan, tanh regular 和 hyperbolic 三角函式
arccos, arccosh, arcsin, arcsinh, arctan, arctanh 反三角函式
logical_not 計算基於元素的非x的真值。等價於 -arr
Binary universal funcitons
函式 描述
add 在陣列中新增相應的元素
substract 在第一個陣列中減去第二個陣列
multiply 對陣列元素相乘
divide, floor_divide 除和地板除(去掉餘數)
power 使用第二個陣列作為指數提升第一個陣列中的元素
maximum, fmax 基於元素的最大值。 fmax 忽略 NaN
minimum, fmin 基於元素的最小值。 fmin 忽略 NaN
mod 基於元素的模(取餘)
copysign 拷貝第二個引數的符號到第一個引數
greater, greater_equal, less, less_equal, not_equal 基於元素的比較,產生布爾陣列。等價於中綴操作符 >,>=, <, <=,==, !=
logical_and, logical_or, logical_xor 計算各個元素邏輯操作的真值。等價於中綴操作符 &,|, ^

1.3. 使用陣列進行資料處理

使用NumPy可以是你能夠使用簡明的陣列表示式而不是編寫迴圈表達許多種類的資料處理任務。這種使用陣列表示式代替顯示迴圈通常被成為“向量化”。在一般情況下,向量化陣列操作比與之等價的純Python運算元度快一到兩(或更多)個等級,這對任何種類的數值計算有最大的影響。稍後,在chp12index中,我會講解broadcasting ,一個向量化計算的強大方法。

作為一個簡單示例,假如我們希望研究函式 sqrt(x\:sup:`^`\ 2 +\ :sup:`^`\ 2) 穿過一個網格資料。np.meshgrid 函式接受兩個一維陣列併產生兩個二維矩陣,其值對於兩個陣列的所有(x, y) 對:

In [130]: points = np.arange(-5, 5, 0.01) # 1000個等間隔點
In [131]: xs, ys = np.meshgrid(points, points)
In [132]: ys
Out[132]:
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
       [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
       [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
       ...,
       [ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
       [ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
       [ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])

現在,研究這個函式是一個簡單的事情,編寫與你可能寫過的相同的表示式:

In [134]: import matplotlib.pyplot as plt
In [135]: z = np.sqrt(xs ** 2 + ys ** 2)
In [136]: z
Out[136]:
array([[ 7.0711, 7.064 , 7.0569, ..., 7.0499, 7.0569, 7.064 ],
       [ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569],
       [ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
       ...,
       [ 7.0499, 7.0428, 7.0357, ..., 7.0286, 7.0357, 7.0428],
       [ 7.0569, 7.0499, 7.0428, ..., 7.0357, 7.0428, 7.0499],
       [ 7.064 , 7.0569, 7.0499, ..., 7.0428, 7.0499, 7.0569]])
In [137]: plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
Out[137]: <matplotlib.colorbar.Colorbar instance at 0x4e46d40>
In [138]: plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
Out[138]: <matplotlib.text.Text at 0x4565790>

繪製在網格上的函式,我使用 matplotlib 函式 imshow 建立一個了一個影象,資料來源於上面的函式生成的二維陣列。

_images/plot-grid.png

繪製在網格上的函式

1.3.1. 用陣列操作來表達條件邏輯

函式 numpy.where 是三元表示式 x if condition else y 的向量化版本。假如我們有一個布林陣列和兩個值陣列:

In [140]: xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
In [141]: yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
In [142]: cond = np.array([True, False, True, True, False])

假如我們想要當對應的 cond 值為 True 時,從 xarr 中獲取一個值,否則從yarr 中獲取值。使用列表推到來做這件事,可能會像這樣:

In [143]: result = [(x if c else y)
   .....: for x, y, c in zip(xarr, yarr, cond)]
In [144]: result
Out[144]: [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]

這樣做會有許多問題。首先,對於大的陣列,它不會很快(因為所有的工作都是有純Python來做的)。其次,對於多維陣列,它不能工作。使用 np.where 你可以像這樣非常簡潔的編寫:

In [145]: result = np.where(cond, xarr, yarr)
In [146]: result
Out[146]: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

np.where 的第一個和第二個引數不需要是陣列;它們中的一個或兩個可以是純量。 在資料分析中 where 的典型使用是生成一個新的陣列,其值基於另一個數組。假如你有一個矩陣,其資料是隨機生成的,你想要把其中的正值替換為2,負值替換為-2,使用np.where 非常容易:

In [147]: arr = randn(4, 4)
In [148]: arr
Out[148]:
array([[ 0.6372, 2.2043, 1.7904, 0.0752],
       [-1.5926, -1.1536, 0.4413, 0.3483],
       [-0.1798, 0.3299, 0.7827, -0.7585],
       [ 0.5857, 0.1619, 1.3583, -1.3865]])
In [149]: np.where(arr > 0, 2, -2)
Out[149]:
array([[ 2, 2, 2, 2],
       [-2, -2, 2, 2],
       [-2, 2, 2, -2],
       [ 2, 2, 2, -2]])

In [150]: np.where(arr > 0, 2, arr) # 僅設定正值為 2
Out[150]:
array([[ 2. , 2. , 2. , 2. ],
       [-1.5926, -1.1536, 2. , 2. ],
       [-0.1798, 2. , 2. , -0.7585],
       [ 2. , 2. , 2. , -1.3865]])

傳遞到 where 的陣列不僅僅只是大小相等的陣列或純量。

使用一些小聰明,你可以使用 where 來表達更復雜的邏輯;考慮這個例子,我有兩個布林陣列, cond1cond2 ,並想根據4種布林值來賦值:

result = []
for i in range(n):
    if cond1[i] and cond2[i]:
        result.append(0)
    elif cond1[i]:
        result.append(1)
    elif cond2[i]:
        result.append(2)
    else:
        result.append(3)

也許可能不會很明顯,這個 for 迴圈可以轉換成一個巢狀的 where 表示式:

np.where(cond1 & cond2, 0,
         np.where(cond1, 1,
                  np.where(cond2, 2, 3)))

在這個特殊的例子中,我們還可以利用布林表示式在計算中被當作0或1這一事實,因此可以使用算術運算來表達:

result = 1 * cond1 + 2 * cond2 + 3 * -(cond1 | cond2)

1.3.2. 數學和統計方法

一組數學函式,計算整個陣列或一個軸向上資料的統計,和陣列函式一樣是容易訪問的。聚合(通常被稱為 reductions ),如 sunmean ,標準偏差 std 可以使用陣列例項的方法,也可以使用頂層NumPy的函式:

In [151]: arr = np.random.randn(5, 4) # 正態分佈資料
In [152]: arr.mean()
Out[152]: 0.062814911084854597
In [153]: np.mean(arr)
Out[153]: 0.062814911084854597
In [154]: arr.sum()
Out[154]: 1.2562982216970919

meansun 函式可以有一個可選的 axis 引數,它對給定座標軸進行統計,結果陣列將會減少一個維度:

In [155]: arr.mean(axis=1)
Out[155]: array([-1.2833, 0.2844, 0.6574, 0.6743, -0.0187])
In [156]: arr.sum(0)
Out[156]: array([-3.1003, -1.6189, 1.4044, 4.5712])

cumsumcumprod 這些函式並不聚集,而是產生一個 intermediate results 的陣列:

In [157]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
In [158]: arr.cumsum(0)         In [159]: arr.cumprod(1)
Out[158]:                       Out[159]:
array([[ 0, 1, 2],              array([[ 0, 0, 0],
[ 3, 5, 7],                            [ 3, 12, 60],
[ 9, 12, 15]])                         [ 6, 42, 336]])

表格4-5 是一個完整的清單。我們將在稍後的章節中看見關於這些函式的大量例子。

陣列構建函式
方法 描述
sum 對陣列的所有或一個軸向上的元素求和。零長度的陣列的和為靈。
mean 算術平均值。靈長度的陣列的均值為NaN。
std, var 標準差和方差,有可選的調整自由度(預設值為n)。
min, max 最大值和最小值
argmin, argmax 索引最小和最大元素。
cumsum 從0元素開始的累計和。
cumprod 從1元素開始的累計乘。

1.3.3. 布林陣列的方法

在上面的方法中布林值被強制為1( True )和0a( False )。因此, sum 經常被用來作為對一個布林陣列中的 True 計數的手段:

In [160]: arr = randn(100)
In [161]: (arr > 0).sum() # 正值的個數
Out[161]: 44

有兩個額外的方法, anyall ,對布林陣列尤其有用。 any 用來測試一個數組中是否有一個或更多的True ,而 all 用來測試所有的值是否為 True

In [162]: bools = np.array([False, False, True, False])
In [163]: bools.any()
Out[163]: True
In [164]: bools.all()
Out[164]: False

這些方法這些方法也可以工作在非不而陣列上,非零元素作為 True

1.3.4. 排序

像Python的內建列表一樣,NumPy陣列也可以使用 sort 方法就地排序:

In [165]: arr = randn(8)
In [166]: arr
Out[166]:
array([ 0.6903, 0.4678, 0.0968, -0.1349, 0.9879, 0.0185, -1.3147,
       -0.5425])
In [167]: arr.sort()
In [168]: arr
Out[168]:
array([-1.3147, -0.5425, -0.1349, 0.0185, 0.0968, 0.4678, 0.6903,
        0.9879])

多維陣列可以通過傳遞一個座標軸數到 sort ,對一維截面上的資料進行就地排序:

In [169]: arr = randn(5, 3)
In [170]: arr
Out[170]:
array([[-0.7139, -1.6331, -0.4959],
       [ 0.8236, -1.3132, -0.1935],
       [-1.6748, 3.0336, -0.863 ],
       [-0.3161, 0.5362, -2.468 ],
       [ 0.9058, 1.1184, -1.0516]])
In [171]: arr.sort(1)
In [172]: arr
Out[172]:
array([[-1.6331, -0.7139, -0.4959],
       [-1.3132, -0.1935, 0.8236],
       [-1.6748, -0.863 , 3.0336],
       [-2.468 , -0.3161, 0.5362],
       [-1.0516, 0.9058, 1.1184]])

頂層的 np.sort 函式返回一個經過排序後的陣列拷貝,而不是就地修改。一個快速和骯髒的計算一個數組的位數是對它排序並選擇一個特定階層值:

In [173]: large_arr = randn(1000)
In [174]: large_arr.sort()
In [175]: large_arr[int(0.05 * len(large_arr))] # 5% quantile
Out[175]: -1.5791023260896004

關於使用NumPy的排序方法和更高階的技術,如間接排序,請見第12章。其它幾種有關排序的資料操作(例如,通過一列或多列對資料表排序)也會在pandas 中找到。

1.3.5. Unique 和其它集合邏輯

Numpy有一些基本的針對一維ndarrays的集合操作。最常使用的一個可能是 np.unique ,它返回一個數組的經過排序的unique 值:

In [176]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
In [177]: np.unique(names)
Out[177]:
array(['Bob', 'Joe', 'Will'],
      dtype='|S4')
In [178]: ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
In [179]: np.unique(ints)
Out[179]: array([1, 2, 3, 4])

np.unique 與純Python版本比較:

In [180]: sorted(set(names))
Out[180]: ['Bob', 'Joe', 'Will']

另一個函式 np.in1d ,測試一個數組的值和另一個的關係,返回一個布林陣列:

In [181]: values = np.array([6, 0, 0, 3, 2, 5, 6])
In [182]: np.in1d(values, [2, 3, 6])
Out[182]: array([ True, False, False, True, True, False, True], dtype=bool)

表格4-6 是關於集合函式的清單。

陣列集合操作
unique(x) 計算x單一的元素,並對結果排序
intersect1d(x, y) 計算x和y相同的元素,並對結果排序
union1d 結合x和y的元素,並對結果排序
in1d(x, y) 得到一個布林陣列指示x中的每個元素是否在y中
setdiff1d(x, y) 差集,在x中但不再y中的集合
setxor1d(x, y) 對稱差集,不同時在兩個陣列中的元素

1.4. 關於陣列的檔案輸入和輸出

NumPy能夠儲存資料到磁碟和從磁碟載入資料,不論資料是文字或二進位制的。在後面的章節你可以學到使用pandas提供的工具來載入表格化的資料到記憶體。

1.4.1. 對磁碟上的二進位制格式陣列排序

np.savenp.load 是兩個主力功能,有效的儲存和載入磁碟資料。陣列預設儲存為未經過壓縮的原始二進位制資料,副檔名為.npy

In [183]: arr = np.arange(10)
In [184]: np.save('some_array', arr)

如果檔案路進並不是以 .npy 結尾,副檔名將會被自動加上。在磁碟上的陣列可以使用 np.load 載入:

In [185]: np.load('some_array.npy')
Out[185]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

你可以使用 np.savez 並以關鍵字引數傳遞陣列來儲存多個數組到一個zip的歸檔檔案中:

In [186]: np.savez('array_archive.npz', a=arr, b=arr)

當你載入一個 .npz 檔案時,會得到一個字典物件,它懶洋洋的載入單個數組:

In [187]: arch = np.load('array_archive.npz')
In [188]: arch['b']
Out[188]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

1.4.2. 儲存和載入文字檔案

從檔案載入文字是一個相當標準的任務。對一個新人來說,Python的檔案加讀取和寫入函式的景象可能有一點兒混亂,因此我將主要集中在pandas的 read_csvread_table 函式上。有時使用 np.loadtxt 或更專門的np.genfromtxt 對於載入資料到 vanilla NumPy 陣列是很有用的。

這些函式有許多選項,允許你指定不同的分割副,特定列的轉換函式,跳過某些行,和其它的事情。以這樣一個逗號分割檔案(CSV)作為一個簡單的例子:

In [191]: !cat array_ex.txt
0.580052,0.186730,1.040717,1.134411
0.194163,-0.636917,-0.938659,0.124094
-0.126410,0.268607,-0.695724,0.047428
-1.484413,0.004176,-0.744203,0.005487
2.302869,0.200131,1.670238,-1.881090
-0.193230,1.047233,0.482803,0.960334

它可以像這樣被載入到一個二維陣列:

In [192]: arr = np.loadtxt('array_ex.txt', delimiter=',')
In [193]: arr
Out[193]:
array([[ 0.5801, 0.1867, 1.0407, 1.1344],
[ 0.1942, -0.6369, -0.9387, 0.1241],
[-0.1264, 0.2686, -0.6957, 0.0474],
[-1.4844, 0.0042, -0.7442, 0.0055],
[ 2.3029, 0.2001, 1.6702, -1.8811],
[-0.1932, 1.0472, 0.4828, 0.9603]])

np.savatxt 執行相反的操作:寫入陣列到一個界定文字檔案中。 genfromtxtloadtxt 相似,但是她是面向結構陣列和缺失資料處理的;更多關於結構陣列請見第12章

更多有關讀取和寫入,特別是表格式的或類電子表格的資料,見後面涉及到pandas和DataFrame物件的章節。

1.5. 線性代數

線性代數,如矩陣乘法,分解,行列式和其它的方陣數學,對任何一個數組庫來說都是重要的部分。不像一些語言,如 MATLAB ,使用 * 來乘兩個二維陣列是基於元素的乘法,而不是矩陣點積。因此,有一個 dot 函式,是陣列的一個方法和 numpy 名稱空間中的一個函式,用來進行矩陣乘法運算:

In [194]: x = np.array([[1., 2., 3.], [4., 5., 6.]])
In [195]: y = np.array([[6., 23.], [-1, 7], [8, 9]])
In [196]: x                In [197]: y
Out[196]:                  Out[197]:
array([[ 1., 2., 3.],      array([[ 6., 23.],
       [ 4., 5., 6.]])            [ -1., 7.],
                                  [ 8., 9.]])
In [198]: x.dot(y) # equivalently np.dot(x, y)
Out[198]:
array([[ 28., 64.],
       [ 67., 181.]])

在一個二維陣列和合適大小的一維陣列間的矩陣乘積的結果是一個一維陣列:

In [199]: np.dot(x, np.ones(3))
Out[199]: array([ 6., 15.])

numpy.linalg 有一個關於矩陣分解和像轉置和行列式等的一個標準集合。它們和其它語言(如: MATLABR )一樣都是基於行業標準的 Fortran 庫,如 BLSALAPACK ,或可能的Intel MKL (依賴於你的NumPy的編譯)實現的:

In [201]: from numpy.linalg import inv, qr
In [202]: X = randn(5, 5)
In [203]: mat = X.T.dot(X)
In [204]: inv(mat)
Out[204]:
array([[ 3.0361, -0.1808, -0.6878, -2.8285, -1.1911],
       [-0.1808, 0.5035, 0.1215, 0.6702, 0.0956],
       [-0.6878, 0.1215, 0.2904, 0.8081, 0.3049],
       [-2.8285, 0.6702, 0.8081, 3.4152, 1.1557],
       [-1.1911, 0.0956, 0.3049, 1.1557, 0.6051]])
In [205]: mat.dot(inv(mat))
Out[205]:
array([[ 1., 0., 0., 0., -0.],
       [ 0., 1., -0., 0., 0.],
       [ 0., -0., 1., 0., 0.],
       [ 0., -0., -0., 1., -0.],
       [ 0., 0., 0., 0., 1.]])
In [206]: q, r = qr(mat)
In [207]: r
Out[207]:
array([[ -6.9271, 7.389 , 6.1227, -7.1163, -4.9215],
       [ 0. , -3.9735, -0.8671, 2.9747, -5.7402],
       [ 0. , 0. , -10.2681, 1.8909, 1.6079],
       [ 0. , 0. , 0. , -1.2996, 3.3577],
       [ 0. , 0. , 0. , 0. , 0.5571]])

表格4-7 是一些常用的線性代數常用的函式清單。

科學Python社群希望有一天可以實現矩陣乘法的中綴操作符,提供一個語法上更好的使用 np.dot 的替代。但是現在只能這樣做。
常用 numpy.linglg 函式
函式 描述
diag 返回一個方陣的對角線(或非對角線)元素為一個一維陣列,或者轉換一個一維陣列到一個方陣(非對角線元素為零)
dot 矩陣乘積
trace 計算對角線上元素的和