NumPy - fromstring - fromfunction

NumPy - fromstring - fromfunction

標準安裝的 Python 中用列表 (list) 保存一組值,可以用來當作數組使用,不過由於列表的元素可以是任何對象,因此列表中所保存的是對象的指針。爲了保存一個簡單的 [1, 2, 3],需要有 3 個指針和三個整數對象。對於數值運算來說這種結構顯然比較浪費內存和 CPU 計算時間。

Python 還提供了一個 array 模塊,array 對象和列表不同,它直接保存數值,和 C 語言的一維數組比較類似。但是由於它不支持多維,也沒有各種運算函數,也不適合做數值運算。

NumPy 提供了兩種基本的對象:ndarray (N-dimensional array object) 和 ufunc (universal function object)。ndarray 是存儲單一數據類型的多維數組,而 ufunc 則是能夠對數組進行處理的函數。

1. ndarray 對象

NumPy 函數庫的導入

import numpy as np

1.1 創建

array 函數傳遞 Python 的序列對象創建數組。如果傳遞的是多層嵌套的序列,將創建多維數組。

(pt-1.4_py-3.6) yongqiang@yongqiang:~$ python
Python 3.6.10 |Anaconda, Inc.| (default, May  8 2020, 02:54:21)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>>
>>> a = np.array([1, 2, 3, 4])
>>> b = np.array((5, 6, 7, 8))
>>> c = np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]])
>>>
>>> a
array([1, 2, 3, 4])
>>> b
array([5, 6, 7, 8])
>>> c
array([[ 1,  2,  3,  4],
       [ 4,  5,  6,  7],
       [ 7,  8,  9, 10]])
>>> c.dtype
dtype('int64')
>>>

數組的大小可以通過其 shape 屬性獲得。

>>> a.shape
(4,)
>>>
>>> b.shape
(4,)
>>>
>>> c.shape
(3, 4)
>>>

數組 a 的 shape 只有一個元素,因此它是一維數組。而數組 c 的 shape 有兩個元素,因此它是二維數組,其中第 0 軸的長度爲 3,第 1 軸的長度爲 4。

可以通過修改數組的 shape 屬性,在保持數組元素個數不變的情況下,改變數組每個軸的長度。下面的例子將數組 c 的 shape 改爲 (4, 3),注意從 (3, 4) 改爲 (4, 3) 並不是對數組進行轉置,而只是改變每個軸的大小,數組元素在內存中的位置並沒有改變:

>>> c.shape
(3, 4)
>>>
>>> c
array([[ 1,  2,  3,  4],
       [ 4,  5,  6,  7],
       [ 7,  8,  9, 10]])
>>>
>>> c.shape = 4,3
>>>
>>> c
array([[ 1,  2,  3],
       [ 4,  4,  5],
       [ 6,  7,  7],
       [ 8,  9, 10]])
>>>

當某個軸的元素爲 -1 時,將根據數組元素的個數自動計算此軸的長度,因此下面的程序將數組 c 的 shape 改爲了 (2, 6)。

>>> c.shape
(4, 3)
>>>
>>> c
array([[ 1,  2,  3],
       [ 4,  4,  5],
       [ 6,  7,  7],
       [ 8,  9, 10]])
>>>
>>> c.shape = 2,-1
>>>
>>> c
array([[ 1,  2,  3,  4,  4,  5],
       [ 6,  7,  7,  8,  9, 10]])
>>>

使用數組的 reshape 方法,可以創建一個改變了尺寸的新數組,原數組的 shape 保持不變。

>>> a = np.array([1, 2, 3, 4])
>>> a
array([1, 2, 3, 4])
>>> a.shape
(4,)
>>>
>>> d = a.reshape((2, 2))
>>> d
array([[1, 2],
       [3, 4]])
>>> d.shape
(2, 2)
>>>
>>> a
array([1, 2, 3, 4])
>>>

數組 a 和 d 其實共享數據存儲內存區域,因此修改其中任意一個數組的元素都會同時修改另外一個數組的內容。

>>> a[1] = 100 # 將數組 a 的第一個元素改爲 100
>>> a
array([  1, 100,   3,   4])
>>> d # 注意數組 d 中的 2 也被改變了
array([[ 1, 100],
[ 3, 4]])

數組的元素類型可以通過 dtype 屬性獲得。上面例子中的參數序列的元素都是整數,因此所創建的數組的元素類型也是整數,並且是 64bit 的長整型。可以通過 dtype 參數在創建時指定元素類型:

>>> np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]], dtype=np.float)
array([[ 1.,  2.,  3.,  4.],
       [ 4.,  5.,  6.,  7.],
       [ 7.,  8.,  9., 10.]])
>>>
>>> np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]], dtype=np.complex)
array([[ 1.+0.j,  2.+0.j,  3.+0.j,  4.+0.j],
       [ 4.+0.j,  5.+0.j,  6.+0.j,  7.+0.j],
       [ 7.+0.j,  8.+0.j,  9.+0.j, 10.+0.j]])
>>>

上面的例子都是先創建一個 Python 序列,然後通過 array 函數將其轉換爲數組,這樣做顯然效率不高。因此NumPy 提供了很多專門用來創建數組的函數。

arrange 函數類似於 python 的 range 函數,通過指定開始值、終值和步長來創建一維數組,注意數組不包括終值。

>>> np.arange(0,1,0.1)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
>>>

linspace 函數通過指定開始值、終值和元素個數來創建一維數組,可以通過 endpoint 關鍵字指定是否包括終值,缺省設置是包括終值。

>>> np.linspace(0, 1, 12)
array([0.        , 0.09090909, 0.18181818, 0.27272727, 0.36363636,
       0.45454545, 0.54545455, 0.63636364, 0.72727273, 0.81818182,
       0.90909091, 1.        ])
>>>

logspace 函數和 linspace 類似,不過它創建等比數列,下面的例子產生 1 (10^0) 到 100 (10^2)、有 20 個元素的等比數列:

>>> np.logspace(0, 2, 20)
array([  1.        ,   1.27427499,   1.62377674,   2.06913808,
         2.6366509 ,   3.35981829,   4.2813324 ,   5.45559478,
         6.95192796,   8.8586679 ,  11.28837892,  14.38449888,
        18.32980711,  23.35721469,  29.76351442,  37.92690191,
        48.32930239,  61.58482111,  78.47599704, 100.        ])
>>>

使用 frombuffer, fromstring, fromfile 等函數可以從字節序列創建數組。

Python 的字符串實際上是字節序列,每個字符佔一個字節,因此如果從字符串 s 創建一個 8bit 的整數數組的話,所得到的數組正好就是字符串中每個字符的 ASCII 編碼。

>>> s = "abcdefgh"
>>> np.fromstring(s, dtype=np.int8)
array([ 97,  98,  99, 100, 101, 102, 103, 104], dtype=int8)
>>>

如果從字符串 s 創建 16bit 的整數數組,那麼兩個相鄰的字節就表示一個整數,把字節 98 和字節 97 當作一個16 位的整數,它的值就是 98 * 256 + 97 = 25185。可以看出內存中是以 little endian (低位字節在前) 方式保存數據的。

>>> s = "abcdefgh"
>>> np.fromstring(s, dtype=np.int8)
array([ 97,  98,  99, 100, 101, 102, 103, 104], dtype=int8)
>>>
>>> np.fromstring(s, dtype=np.int16)
array([25185, 25699, 26213, 26727], dtype=int16)
>>>

如果把整個字符串轉換爲一個 64 位的雙精度浮點數數組,那麼它的值是:

>>> np.fromstring(s, dtype=np.float)
array([8.54088322e+194])

顯然這個例子沒有什麼意義,但是可以想象如果我們用 C 語言的二進制方式寫了一組 double 類型的數值到某個文件中,那們可以從此文件讀取相應的數據,並通過 fromstring 函數將其轉換爲 float64 類型的數組。

我們可以寫一個 Python 的函數,它將數組下標轉換爲數組中對應的值,然後使用此函數創建數組:

>>> def func(i):
...     return i % 4 + 1
...
>>> np.fromfunction(func, (10,))
array([1., 2., 3., 4., 1., 2., 3., 4., 1., 2.])
>>>

fromfunction 函數的第一個參數爲計算每個數組元素的函數,第二個參數爲數組的大小 (shape),因爲它支持多維數組,所以第二個參數必須是一個序列,本例中用 (10,) 創建一個 10 元素的一維數組。

下面的例子創建一個二維數組表示九九乘法表,輸出的數組 a 中的每個元素 a[i, j] 都等於 func2(i, j):

>>> def func2(i, j):
...     return (i+1) * (j+1)
...
>>> a = np.fromfunction(func2, (9,9))
>>> a
array([[ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.],
       [ 2.,  4.,  6.,  8., 10., 12., 14., 16., 18.],
       [ 3.,  6.,  9., 12., 15., 18., 21., 24., 27.],
       [ 4.,  8., 12., 16., 20., 24., 28., 32., 36.],
       [ 5., 10., 15., 20., 25., 30., 35., 40., 45.],
       [ 6., 12., 18., 24., 30., 36., 42., 48., 54.],
       [ 7., 14., 21., 28., 35., 42., 49., 56., 63.],
       [ 8., 16., 24., 32., 40., 48., 56., 64., 72.],
       [ 9., 18., 27., 36., 45., 54., 63., 72., 81.]])
>>>

References

http://www.uml.org.cn/python/201811123.asp

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章