NumPy之:ndarray多維陣列操作
簡介
NumPy一個非常重要的作用就是可以進行多維陣列的操作,多維陣列物件也叫做ndarray。我們可以在ndarray的基礎上進行一系列複雜的數學運算。
本文將會介紹一些基本常見的ndarray操作,大家可以在資料分析中使用。
建立ndarray
建立ndarray有很多種方法,我們可以使用np.random來隨機生成資料:
import numpy as np
# Generate some random data
data = np.random.randn(2, 3)
data
array([[ 0.0929, 0.2817, 0.769 ],
[ 1.2464, 1.0072, -1.2962]])
除了隨機建立之外,還可以從list中建立:
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)
array([6. , 7.5, 8. , 0. , 1. ])
從list中建立多維陣列:
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
使用np.zeros建立初始值為0的陣列:
np.zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
建立2維陣列:
np.zeros((3, 6))
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
使用empty建立3維陣列:
np.empty((2, 3, 2))
array([[[0., 0.],
[0., 0.],
[0., 0.]],
[[0., 0.],
[0., 0.],
[0., 0.]]])
注意,這裡我們看到empty建立的陣列值為0,其實並不是一定的,empty會從記憶體中隨機挑選空間來返回,並不能保證這些空間中沒有值。所以我們在使用empty建立陣列之後,在使用之前,還要記得初始化他們。
使用arange建立範圍類的陣列:
np.arange(15)
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
指定陣列中元素的dtype:
arr1 = np.array([1, 2, 3], dtype=np.float64)
arr2 = np.array([1, 2, 3], dtype=np.int32)
ndarray的屬性
可以通過data.shape獲得陣列的形狀。
data.shape
(2, 3)
通過ndim獲取維數資訊:
arr2.ndim
2
可以通過data.dtype獲得具體的資料型別。
data.dtype
dtype('float64')
ndarray中元素的型別轉換
在建立好一個型別的ndarray之後,還可以對其進行轉換:
arr = np.array([1, 2, 3, 4, 5])
arr.dtype
dtype('int64')
float_arr = arr.astype(np.float64)
float_arr.dtype
dtype('float64')
上面我們使用astype將int64型別的ndarray轉換成了float64型別的。
如果轉換型別的範圍不匹配,則會自動進行截斷操作:
arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
arr.astype(np.int32)
array([ 3, -1, -2, 0, 12, 10], dtype=int32)
注意,這裡是把小數截斷,並沒有向上或者向下取整。
ndarray的數學運算
陣列可以和常量進行運算,也可以和陣列進行運算:
arr = np.array([[1., 2., 3.], [4., 5., 6.]])
arr * arr
array([[ 1., 4., 9.],
[16., 25., 36.]])
arr + 10
array([[11., 12., 13.],
[14., 15., 16.]])
arr - arr
array([[0., 0., 0.],
[0., 0., 0.]])
1 / arr
array([[1. , 0.5 , 0.3333],
[0.25 , 0.2 , 0.1667]])
arr ** 0.5
array([[1. , 1.4142, 1.7321],
[2. , 2.2361, 2.4495]])
陣列之間還可以進行比較,比較的是陣列中每個元素的大小:
arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
arr2 > arr
array([[False, True, False],
[ True, False, True]])
index和切片
基本使用
先看下index和切片的基本使用,index基本上和普通陣列的使用方式是一樣的,用來訪問陣列中某一個元素。
切片要注意的是切片後返回的陣列中的元素是原陣列中元素的引用,修改切片的陣列會影響到原陣列。
# 構建一維陣列
arr = np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# index訪問
arr[5]
5
# 切片訪問
arr[5:8]
array([5, 6, 7])
# 切片修改
arr[5:8] = 12
array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
# 切片可以修改原陣列的值
arr_slice = arr[5:8]
arr_slice[1] = 12345
arr
array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8,
9])
# 構建二維陣列
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d[2]
array([7, 8, 9])
# index 二維陣列
arr2d[0][2]
3
# index二維陣列
arr2d[0, 2]
3
# 構建三維陣列
arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
# index三維陣列
arr3d[0]
array([[1, 2, 3],
[4, 5, 6]])
# copy是硬拷貝,和原陣列的值相互不影響
old_values = arr3d[0].copy()
arr3d[0] = 42
arr3d
array([[[42, 42, 42],
[42, 42, 42]],
[[ 7, 8, 9],
[10, 11, 12]]])
arr3d[0] = old_values
arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
# index 三維陣列
arr3d[1, 0]
array([7, 8, 9])
x = arr3d[1]
x
array([[ 7, 8, 9],
[10, 11, 12]])
x[0]
array([7, 8, 9])
index with slice
slice還可以作為index使用,作為index使用表示的就是一個index範圍值。
作為index表示的slice可以有多種形式。
有頭有尾的,表示index從1開始到6-1結束:
arr[1:6]
array([ 1, 2, 3, 4, 64])
無頭有尾的,表示index從0開始,到尾-1結束:
arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]])
有頭無尾的,表示從頭開始,到所有的資料結束:
arr2d[:2, 1:]
array([[2, 3],
[5, 6]])
arr2d[1, :2]
array([4, 5])
boolean index
index還可以使用boolean值,表示是否選擇這一個index的資料。
我們先看下怎麼構建一個boolean型別的陣列:
names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
names == 'Bob'
array([ True, False, False, True, False, False, False])
上面我們通過比較的方式返回了一個只包含True和False的陣列。
這個陣列可以作為index值來訪問陣列:
# 構建一個7 * 4 的陣列
data = np.random.randn(7, 4)
array([[ 0.275 , 0.2289, 1.3529, 0.8864],
[-2.0016, -0.3718, 1.669 , -0.4386],
[-0.5397, 0.477 , 3.2489, -1.0212],
[-0.5771, 0.1241, 0.3026, 0.5238],
[ 0.0009, 1.3438, -0.7135, -0.8312],
[-2.3702, -1.8608, -0.8608, 0.5601],
[-1.2659, 0.1198, -1.0635, 0.3329]])
# 通過boolean陣列來訪問:
data[names == 'Bob']
array([[ 0.275 , 0.2289, 1.3529, 0.8864],
[-0.5771, 0.1241, 0.3026, 0.5238]])
在索引行的時候,還可以索引列:
data[names == 'Bob', 3]
array([0.8864, 0.5238])
可以用 ~
符號來取反:
data[~(names == 'Bob')]
array([[-2.0016, -0.3718, 1.669 , -0.4386],
[-0.5397, 0.477 , 3.2489, -1.0212],
[ 0.0009, 1.3438, -0.7135, -0.8312],
[-2.3702, -1.8608, -0.8608, 0.5601],
[-1.2659, 0.1198, -1.0635, 0.3329]])
我們可以通過布林型陣列設定值,在實際的專案中非常有用:
data[data < 0] = 0
array([[0.275 , 0.2289, 1.3529, 0.8864],
[0. , 0. , 1.669 , 0. ],
[0. , 0.477 , 3.2489, 0. ],
[0. , 0.1241, 0.3026, 0.5238],
[0.0009, 1.3438, 0. , 0. ],
[0. , 0. , 0. , 0.5601],
[0. , 0.1198, 0. , 0.3329]])
data[names != 'Joe'] = 7
array([[7. , 7. , 7. , 7. ],
[0. , 0. , 1.669 , 0. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[0. , 0. , 0. , 0.5601],
[0. , 0.1198, 0. , 0.3329]])
Fancy indexing
Fancy indexing也叫做花式索引,它是指使用一個整數陣列來進行索引。
舉個例子,我們先建立一個 8 * 4的陣列:
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
arr
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.]])
然後使用一個整數陣列來索引,那麼將會以指定的順序來選擇行:
arr[[4, 3, 0, 6]]
array([[4., 4., 4., 4.],
[3., 3., 3., 3.],
[0., 0., 0., 0.],
[6., 6., 6., 6.]])
還可以使用負值來索引:
arr[[-3, -5, -7]]
array([[5., 5., 5., 5.],
[3., 3., 3., 3.],
[1., 1., 1., 1.]])
花式索引還可以組合來使用:
arr = np.arange(32).reshape((8, 4))
arr
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]])
上面我們構建了一個8 * 4的陣列。
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
array([ 4, 23, 29, 10])
然後取他們的第2列的第一個值,第6列的第三個值等等。最後得到一個1維的陣列。
陣列變換
我們可以在不同維度的陣列之間進行變換,還可以轉換陣列的軸。
reshape方法可以將陣列轉換成為任意的形狀:
arr = np.arange(15).reshape((3, 5))
arr
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
陣列還提供了一個T命令,可以將陣列的軸進行對調:
arr.T
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
對於高維陣列,可以使用transpose來進行軸的轉置:
arr = np.arange(16).reshape((2, 2, 4))
arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
arr.transpose((1, 0, 2))
array([[[ 0, 1, 2, 3],
[ 8, 9, 10, 11]],
[[ 4, 5, 6, 7],
[12, 13, 14, 15]]])
上面的transpose((1, 0, 2)) 怎麼理解呢?
其含義是將x,y軸對調,z軸保持不變。
上面我們通過使用reshape((2, 2, 4))方法建立了一個3維,也就是3個軸的陣列。 其shape是 2 * 2 * 4 。
先看下對應關係:
(0,0)-》 [ 0, 1, 2, 3]
(0,1)-》 [ 4, 5, 6, 7]
(1,0)-》 [ 8, 9, 10, 11]
(1,1)-》 [12, 13, 14, 15]
轉換之後:
(0,0)-》 [ 0, 1, 2, 3]
(0,1)-》 [ 8, 9, 10, 11]
(1,0)-》[ 4, 5, 6, 7]
(1,1)-》 [12, 13, 14, 15]
於是得到了我們上面的的結果。
多維陣列的軸轉換可能比較複雜,大家多多理解。
還可以使用 swapaxes 來交換兩個軸,上面的例子可以重寫為:
arr.swapaxes(0,1)
本文已收錄於 http://www.flydean.com/09-python-numpy-ndarray/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!