
List (列表)是 Python 中最基本的數據結構。在用法上,它有點類似數組,因為每個列表都有一個下標,下標從 0 開始。因此,我們可以使用 list[1] 來獲取下標對應的值。如果我們深入下列表的底層原理,會發現列表是基于 PyListObject 實現的。PyListObject 是一個變長對象,所以列表的長度是隨著元素多少動態改變的。同時它還支持插入和刪除等操作,所以它還是一個可變對象。
可以簡單理解為,Python 的列表是長度可變的數組。一般而已,我們用于列表創建都是一維數組。那么問題來,我們如果創建多維數組呢?
01 列表能創建多維數組?
列表是支持操作符,如果一個列表與 ‘ * ’ 號結合使用,能達到重復列表的效果。比如
list_one = [0]
list_two = [0] * 3
print(list_one)
print(list_two)>>> 運行結果:[0]
[0, 0, 0]
那么利用這個重復特性,我們是否可以來創建一個二維數組呢?于是乎,我進行一頓猛操作,結果就被我折騰出來了。
list_one = [0]
list_two = [[0] * 3] * 3
print(list_one)
print(list_two)>>> 運行結果:[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
看起來很完美的操作,但是如果進行一些列表更新操作,問題就顯露出來了。比如我對 list_two 的更換中間位置的值,即對 list_two[1][1] 進行更換值。
list_two = [[0] * 3] * 3
print(list_two)list_two[1][1] = 2
print(list_two)
不難發現,運行結果有點不對勁,列表中有三個位置的值也改變了。
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 2, 0], [0, 2, 0], [0, 2, 0]]
為什么會出現在這種情況呢?原因是淺拷貝,我們以這種方式創建的列表,list_two 里面的三個列表的內存是指向同一塊,不管我們修改哪個列表,其他兩個列表也會跟著改變。
如果要使用列表創建一個二維數組,可以使用生成器來輔助實現。
list_three = [[0 for i in range(3)] for j in range(3)]
print(list_three)
list_three[1][1] = 3
print(list_three)
我們對 list_three 進行更新操作,這次就能正常更新了。
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 3, 0], [0, 0, 0]]
除了以上的方式,還有一種更加簡潔方便的方式,就是使用 NumPy 模塊。
02 相比 List,NumPy 數組的優勢
NumPy 全稱為 Numerical Python,是 Python 的一個以矩陣為主的用于科學計算的基礎軟件包。NumPy 和 Pandas、Matpotlib 經常結合一起使用,所以被人們合稱為數據分析三劍客。Numpy 中有功能強大的 ndarray 對象,能創建 N 維的數組,另外還提供很多通用函數,支持對數組的元素進行操作、支持對數組進行算法運算以及提供常用的統計函數。
相比 List 對象,NumPy 數組有以下優勢:
- 這是因為列表 list 的元素在系統內存中是分散存儲的,而 NumPy 數組存儲在一個均勻連續的內存塊中。這樣數組計算遍歷所有元素,不像列表 list 還需要對內存地址進行查找,從而節省了計算資源。
- Numpy數組能夠運用向量化運算來處理整個數組,速度較快;而 Python 的列表則通常需要借助循環語句遍歷列表,運行效率相對來說要差。
- NumPy 中的矩陣計算可以采用多線程的方式,充分利用多核 CPU 計算資源,大大提升了計算效率。
- Numpy 使用了優化過的 C API,運算速度較快。
03 創建數組
前面說到 NumPy 的主要對面是 ndarray 對象,它其實是一系列同類型數據的集合。因為 ndarray 支持創建多維數組,所以就有兩個行和列的概念。
創建 ndarray 的第一種方式是利用 array 方式。
import numpy as np
# 創建一維數組
nd_one = np.array([1, 2, 3])
# 創建二維數組
nd_two = np.array([[1, 2, 3], [4, 5, 6]])print(nd_one)
print(nd_two)
print('nd_two.shape =', nd_one.shape)
print('nd_two.shape =', nd_two.shape)>>> 運行結果:[1 2 3]
[[1 2 3][4 5 6]]
nd_two.shape = (3,)
nd_two.shape = (2, 3)
其中 shape 是數組的一個屬性,表示獲取數組大小(有多少行,有多少列),如果是一維數組,則只顯示(行,)。代碼中打印出 nd_two 的形狀,輸出為(2,3),表示數組中有 2 行 3 列。
第二種辦法則使用 Numpy 的內置函數
- 1 使用arange 或 linspace 創建連續數組。
```python
import numpy as np
# arange() 類似Python內置函數的 range()
# arange(初始值, 終值, 步長) 不包含終值
x0 = np.arange(1, 11, 2)
print(x0)# 創建一個 5x3 的數組
x1 = np.arange(15).reshape((5, 3))
print(x1)# linspace()線性等分向量
# linspace(初始值, 終值, 元素個數) 包含終值
x2 = np.linspace(1, 11, 6)
print(x2)>>> 運行結果:[1 3 5 7 9][[ 0 1 2][ 3 4 5][ 6 7 8][ 9 10 11][12 13 14]][ 1. 3. 5. 7. 9. 11.]
雖然 np.arange 和 np.linspace 起到的作用是一樣的,都是創建等差數組,但是創建的方式是不同的。
- 2 使用 zeros(),ones(),full() 創建數組
import numpy as np
# 創建一個 3x4 的數組且所有值全為 0
x3 = np.zeros((3, 4), dtype=int)
print(x3)
# 創建一個 3x4 的數組且所有元素值全為 1
x4 = np.ones((3, 4), dtype=int)
print(x4)
# 創建一個 3x4 的數組,然后將所有元素的值填充為 2
x5 = np.full((3, 4), 2, dtype=int)
print(x5)>>> 運行結果:[[0 0 0 0][0 0 0 0][0 0 0 0]][[1 1 1 1][1 1 1 1][1 1 1 1]][[2 2 2 2][2 2 2 2][2 2 2 2]]
- 3 使用 eye() 創建單位矩陣
eye() 創建的數組特點是行數和列數都是一樣。因為它創建出來的是單位矩陣,單位矩陣是正形矩陣,對角線的值均為 1,其他位置的值為 0。
import numpy as np
# 創建 3x3 的單位矩陣
x6 = np.eye(3, dtype=int)
print(x6)>>> 運行結果:[[1 0 0][0 1 0][0 0 1]]
- 4 使用 diag() 創建對角矩陣
diag() 是創建一個 NxN 的對角矩陣,對角矩陣是對角線上的主對角線之外的元素皆為 0 的矩陣。
import numpy as np
x7 = np.diag([1, 2, 3])
print(x7)>>> 運行結果:[[1 0 0][0 2 0][0 0 3]]
- 5 使用 random 創建隨機數組
numpy 中的 random 中有很多內置函數,能簡單介紹其中的幾種。
import numpy as np
# 創建 2x2 數組且所有值是隨機填充
x9 = np.random.random((2, 2))
print(x9)# 創建一個值在 [0, 10) 區間的 3x3 的隨機整數
x10 = np.random.randint(0, 10, (3, 3))
print(x10)>>> 運行結果:[[ 0.77233522 0.41516417][ 0.22350126 0.31611254]][[0 6 5][7 6 4][5 5 9]]
本文原創發布于微信公眾號「極客猴」,歡迎關注第一時間獲取更多原創分享
「極客猴」每周堅持分享 Python 原創干貨的公眾號。包括基礎入門,進階技巧,網絡爬蟲,數據分析, Web 應用開發等,歡迎關注。