補充內容
來自《利用Python進行數據分析》,僅供自己學習使用,嚴禁轉載用於其他商業用途。
神奇的索引
arr = np.empty((8, 4))
for i in range(8):
arr[i] = i
print(arr)
print('------------')
print(arr[[4, 3, 0, 6]])
print('------------')
print(arr[[-3, -5, -7]])
#結果
[[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.]]
------------
[[4. 4. 4. 4.]
[3. 3. 3. 3.]
[0. 0. 0. 0.]
[6. 6. 6. 6.]]
------------
[[5. 5. 5. 5.]
[3. 3. 3. 3.]
[1. 1. 1. 1.]]
首先,我們生成一個8行4列的數組,然後根據索引進行切片,取出第4,3,0,6四行的數據,最後,我們根據索引,取出倒數第3、5、7行的數據。
傳遞多個索引數組時情況有些許不同,這樣會根據每個索引元組對應的元素選出一個一維數組:
arr = np.arange(32).reshape((8, 4))
print(arr)
#結果
[[ 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]]
print(arr[[1,5,7,2],[0,3,1,2]])
#
[ 4 23 29 10]
上述例子中,元素(1,0)、(5,3)、(7,1)、(2,2)被選中。如果不考慮數組的維數,索引的結果總是一維的。
數組轉置和換軸
轉置是一種特殊的數據重組形式,可以返回底層數據的視圖而不需要賦值任何內容。數組擁有transpose方法,也有特殊的T屬性。
import numpy as np
arr = np.arange(15).reshape((3, 5))
print(arr)
print('--------------')
print(arr.T)
#
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
--------------
[[ 0 5 10]
[ 1 6 11]
[ 2 7 12]
[ 3 8 13]
[ 4 9 14]]
可以發現,原數組的行和列進行了互換。
當進行矩陣操作時,有事還會用到一些特定操作。如計算矩陣內積會使用np.dot
arr = np.random.randn(6, 3)
print(arr)
print('--------')
print(np.dot(arr.T, arr))
#
[[ 2.36529252 1.45634028 -0.45027236]
[-1.85345472 1.50493214 -0.13627173]
[-1.05013824 -0.13728287 -0.24801649]
[-0.85897001 0.14457997 1.79092397]
[ 1.24476335 -1.34613385 -0.23993914]
[-0.77263369 0.04402339 -0.49494594]]
--------
[[13.0169215 -1.03430871 -2.00660628]
[-1.03430871 6.23951211 -0.26664834]
[-2.00660628 -0.26664834 3.79277832]]
對於更高維度的數組,transpose方法可以接受包含軸編號的元組,用於置換軸:
arr = np.arange(16).reshape((2, 2, 4))
print(arr)
print('--------')
print(arr.transpose((1, 0, 2)))
#
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
--------
[[[ 0 1 2 3]
[ 8 9 10 11]]
[[ 4 5 6 7]
[12 13 14 15]]]
這裏,軸已經被重新排序,使得原先的第二個軸變爲第一個,原先的第一個軸變成第二個,最後一個軸並沒有變。
通用函數:快速的逐元素數組函數
通用函數,也可以成爲ufunc,是一種在ndarray數據中進行逐元素操作的函數。某些簡單函數接受一個或多個標量數值,併產生一個或多個標量結果,而通用函數就是對這些簡單函數的向量化封裝。
arr = np.arange(10)
print(arr)
print('----------')
print(np.sqrt(arr))
print('----------')
print(np.exp(arr))
#
[0 1 2 3 4 5 6 7 8 9]
----------
[0. 1. 1.41421356 1.73205081 2. 2.23606798
2.44948974 2.64575131 2.82842712 3. ]
----------
[1.00000000e+00 2.71828183e+00 7.38905610e+00 2.00855369e+01
5.45981500e+01 1.48413159e+02 4.03428793e+02 1.09663316e+03
2.98095799e+03 8.10308393e+03]
使用sqrt()方法對數組內的元素開根號,exp()方法返回e的n次方,e是一個常數爲2.71828。
這是所謂的一元通用函數,還有一些通用函數,比如add或maximum則會接受兩個數組並返回一個數組作爲結果,因此成爲二元通用函數:
x = np.random.randn(8)
y = np.random.randn(8)
print(x)
print(y)
print(np.maximum(x, y))
#
[-2.01960726 0.51696239 0.77124305 1.20857829 -1.204375 1.4427587
0.12709071 0.29780411]
[-0.28531979 -0.32238757 -0.44377128 -0.3816275 1.75887677 0.98860426
-0.76177926 -0.84858413]
[-0.28531979 0.51696239 0.77124305 1.20857829 1.75887677 1.4427587
0.12709071 0.29780411]
numpy.maximum逐個元素地將x和y中元素最大的值計算出來。
也有一些返回多個數組的函數。比如modf,是python內建函數divmod的向量化版本,它返回一個浮點值數組的小數部分和整數部分:
arr = np.random.randn(7) * 5
print(arr)
remainder, whole_part = np.modf(arr)
print(remainder)
print(whole_part)
#
[ 8.11757348 -0.70141921 -0.41606526 0.55060716 6.30934265 -5.97310928
3.95041647]
[ 0.11757348 -0.70141921 -0.41606526 0.55060716 0.30934265 -0.97310928
0.95041647]
[ 8. -0. -0. 0. 6. -5. 3.]
賦值時,前面的是小數部分,後面的是整數部分。
函數名 | 描述 |
---|---|
abs、fabs | 逐元素地計算整數、浮點數或複數的絕對值 |
sqrt | 計算每個元素的平方根(與arr ** 0.5相等) |
square | 計算每個元素的平方(與arr**2相等) |
exp | 計算每個元素的自然指數值e**x |
log、log10、log2、log1p | 分別對應:自然對數(e爲底)、對數10爲底、對數2爲底、log(1+x) |
sign | 計算每個元素的符號值:1(正數)、0(0)、-1(負數) |
ceil | 計算每個元素的最高整數值(即大於等於給定數值的最小整數) |
floor | 計算每個元素的最小整數值(即小於等於給定數值的最大整數) |
modf | 分別將數組中的小數部分和整數部分按數組形式返回 |
isnan | 返回數組中的元素是否是一個NaN(不是一個數值),形式爲布爾值數組 |
isfinite、isinf | 分別返回數組中的元素是否有限(非inf、非NaN)、是否無限的,形式爲布爾值數組 |
cos、cosh、sin、sinh、tan、tanh | 常規的三角函數 |
arccos、arccosh、arcsin、 arcsinh、arctan、arctanh |
反三角函數 |
logical_not | 對數組的元素按位取反(與~arr效果一樣) |
函數名 | 描述 |
add | 將數組的對應元素增加 |
subtract | 在第二個數組中,將第一個數組中包含的元組去除 |
multiply | 將數組的對應元素相乘 |
divide, floor_divide | 除或整除(放棄餘數) |
power | 將第二個數組的元素作爲第一個數組對應元素的冪次方 |
maximum, fmax | 逐個元素計算最大值,fmax忽略NaN |
minimum, fmin | 逐個元素計算最小值,fmax忽略NaN |
mod | 按元素的求模計算(即求除法的餘數) |
copysign | 將第一個數組的符號值改爲第二個數組的符號值 |
greater, greater_equal, less, less_equal, equal, not_equal, logical_and, logical_or, logical_xor |
進行逐個元素的比較,返回布爾值數組(與數學操作符>、>=、<、<=、==、!= 效果一致)進行逐個元素的邏輯操作(與邏輯操作符&、|、^ 效果一致) |
使用數組進行面向數組編程
利用數組表達式來替代顯示循環的方法,稱爲向量化。
points = np.arange(-5, 5, 0.01)
xs, ys = np.meshgrid(points, points)
print(ys)
#
[[-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]]
z = np.sqrt(xs ** 2 + ys ** 2)
print(z)
#
[[7.07106781 7.06400028 7.05693985 ... 7.04988652 7.05693985 7.06400028]
[7.06400028 7.05692568 7.04985815 ... 7.04279774 7.04985815 7.05692568]
[7.05693985 7.04985815 7.04278354 ... 7.03571603 7.04278354 7.04985815]
...
[7.04988652 7.04279774 7.03571603 ... 7.0286414 7.03571603 7.04279774]
[7.05693985 7.04985815 7.04278354 ... 7.03571603 7.04278354 7.04985815]
[7.06400028 7.05692568 7.04985815 ... 7.04279774 7.04985815 7.05692568]]
可以求出根號下x**2 + y**2的值。
將條件邏輯作爲數組操作
numpy.where函數式三元表達式x if condition else y的向量化版本。
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])
result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)]
print(result)
#
[1.1, 2.2, 1.3, 1.4, 2.5]
假設cond中的元素爲True時,我們取axrr中的對應元素值,否則取yarr中的元素值,可以通過以上代碼完成。但如果數組很大,速度會很慢,而使用np.where時,就可以非常簡單地完成:
result = np.where(cond, xarr, yarr)
print(result)
#
[1.1 2.2 1.3 1.4 2.5]
np.where的第二個和第三個參數並不需要是數組,它們可以是標量。
arr = np.random.randn(4, 4)
print(arr)
print(arr > 0)
print(np.where(arr > 0, 2, -2))
#
[[ 0.61766413 0.05565645 0.17577733 -0.37404451]
[-0.13572482 -0.86114914 -0.07353302 -0.03741651]
[-0.27460935 0.41297701 0.27868873 -0.63462777]
[ 0.18022908 -0.54048348 0.43320103 0.31274484]]
[[ True True True False]
[False False False False]
[False True True False]
[ True False True True]]
[[ 2 2 2 -2]
[-2 -2 -2 -2]
[-2 2 2 -2]
[ 2 -2 2 2]]
例子中可以看到,我們將其中的正值都替換成2,負值都替換爲-2,使用np.where很容易實現。
如果僅將正值替換爲2,可以寫成:print(np.where(arr > 0, 2,arr))
數學和統計方法
arr = np.random.randn(5, 4)
print(arr)
print(arr.mean())
print(np.mean(arr))
print(arr.sum())
#
[[-2.80990453e-01 -7.58276181e-01 -1.14295903e+00 -1.38200808e+00]
[-1.99594965e-01 -1.38398991e+00 -1.19742730e+00 -3.04548718e-01]
[-2.27579601e-03 -1.31106338e-01 9.01807874e-01 5.98710554e-01]
[ 2.30283103e+00 2.25508761e+00 -2.11443469e-01 1.31561031e+00]
[ 1.34378629e+00 7.02711284e-01 1.58238066e+00 9.33821461e-01]]
0.24710634198581216
0.24710634198581216
4.942126839716243
像mean、sum等函數可以接受一個可選參數axis,這個參數可以用於計算給定軸向上的統計值,形成一個下降一維度的數組:
arr = np.random.randn(5, 4)
print(arr)
print(arr.mean(axis=1))
print(arr.sum(axis=0))
#
[[ 0.59696253 1.11121983 -0.1610959 -1.8332755 ]
[ 0.07409587 -2.14484861 0.50016975 0.76153254]
[-1.28510856 0.28761388 0.55825006 0.64558745]
[-0.38153078 0.83174068 -0.92845526 0.1239845 ]
[ 0.22249535 -0.68529482 -0.17869785 -0.7053025 ]]
[-0.07154726 -0.20226261 0.05158571 -0.08856521 -0.33669996]
[-0.77308559 -0.59956904 -0.20982921 -1.00747352]
arr.mean(1)表示“計算每一行的平均值”,而arr.sum(0)表示“計算列軸向的累和”。
其他的方法,例如cumsum和cumprod並不會聚合,它們會產生一箇中間結果:
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])
print(arr.cumsum())
#
[ 0 1 3 6 10 15 21 28]
在多維數組中,像cumsum這樣的累積函數返回相同長度的數組,但是可以在指定軸向上根據較低維度的切片進行部分聚合:
arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(arr)
print(arr.cumsum(axis=0))
print(arr.cumprod(axis=1))
#
[[0 1 2]
[3 4 5]
[6 7 8]]
[[ 0 1 2]
[ 3 5 7]
[ 9 12 15]]
[[ 0 0 0]
[ 3 12 60]
[ 6 42 336]]
方法 | 描述 |
sum | 沿着軸向計算所有元素的累和,0長度的數組,累和爲0 |
mean | 數學平均,0長度的數組平均值爲NaN |
std, var | 標準差和方差,可以選擇自由度調整(默認分母是n) |
min, max | 最大值和最小值 |
argmin, argmax | 最大值和最小值的位置 |
cumsum | 從0開始元素累積和 |
cumprod | 從1開始元素累積積 |