《利用Python進行數據分析》第四章-numpy基礎

Numpy數組函數和數組數據處理

1.通用函數:快速的元素級數組函數

通用函數(即ufunc)是一種對ndarray中的數據執行元素級運算的函數。你可以將其看做簡單函數(接受一個或多個標量值,併產生一個或多個標量值)的矢量化包裝器。許多ufunc都是簡單的元素級變體,如sqrt和exp:

In [1]: import numpy as np

In [2]: arr = np.arange(10)

In [3]: arr
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [4]: np.sqrt(arr)
Out[4]: 
array([ 0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])

In [5]: np.exp(arr)
Out[5]: 
array([ 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])

這些都是一元(unary)ufunc。另外一些(如add或maximum)接受2個數組(因此也叫二元(binary)

In [6]: x=np.random.randn(9)

In [7]: x
Out[7]: 
array([-0.25250282, 1.21724418, 0.25989955, 0.34517506, -0.78180916,
-1.25297812, -0.40412865, 1.44120584, 1.06813696])

In [8]: y=np.random.randn(9)

In [9]: np.maximum(x,y)#元素級最大值
Out[9]: 
array([ 1.6500482 , 1.21724418, 0.92500696, 0.34517506, 0.07987384,
0.43910019, -0.40412865, 1.44120584, 1.06813696])

但有些ufunc的確可以返回多個數組。modf就是一個例子,它是Python內置函數divmod的矢量化版本,用於浮點數數組的小數和整數部分。

In [10]: arr=random.randn(7)*6
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-10-786d70de738f> in <module>()
----> 1 arr=random.randn(7)*6

NameError: name 'random' is not defined

In [11]: arr=np.random.randn(8)*5
In [12]: arr
Out[12]: 
array([ 6.47267866, 2.36813813, 3.12836 , -4.60901158, 3.74744856,
8.55320503, -3.00223264, 3.70719592])

In [13]: np.modf(arr)
Out[13]: 
(array([ 0.47267866, 0.36813813, 0.12836 , -0.60901158, 0.74744856,
0.55320503, -0.00223264, 0.70719592]),
array([ 6., 2., 3., -4., 3., 8., -3., 3.]))

In [14]: num=np.arange(6)

In [15]: num
Out[15]: array([0, 1, 2, 3, 4, 5])

In [16]: np.square(num)
Out[16]: array([ 0, 1, 4, 9, 16, 25], dtype=int32)

In [18]: num2=np.array([-9,16,-8,49,-4,6])

In [19]: num2
Out[19]: array([-9, 16, -8, 49, -4, 6])

In [20]: np.abs(num2)
Out[20]: array([ 9, 16, 8, 49, 4, 6])

表4-3和表4-4分別列出了一些一元和二元ufunc。
image.png
image.png

2.利用數組進行數據處理

NumPy數組使你可以將許多種數據處理任務表述爲簡潔的數組表達式(否則需要編寫循環)。用數組表達式代替循環的做法,通常被稱爲矢量化。一般來說,矢量化數組運算要比等價的純Python方式快上一兩個數量級(甚至更多),尤其是各種數值計算。
假設我們想要在一組值(網格型)上計算函數sqrt(x^2+y^2)。np.meshgrid函數接受兩個一維數組,併產生兩個二維矩陣(對應於兩個數組中所有的(x,y)對)

In [22]: point=np.arange(-5,5,0.01)#1000個間隔相等的點

In [23]: xs,ys=np.meshgrid(point,point)

In [24]: ys
Out[24]: 
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 [25]: import matplotlib.pyplot as plt

In [26]: z=np.sqrt(xs ** 2 + ys ** 2)

In [27]: z
Out[27]: 
array([[ 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]])

In [28]: plt.imshow(z,cmap=plt.cm.gray);plt.colorbar()
Out[28]: <matplotlib.colorbar.Colorbar at 0x8ede240>

In [29]: plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
Out[29]: <matplotlib.text.Text at 0x97c8d68>

函數值(一個二維數組)的圖形化結果如圖所示。這張圖我是用matplotlib的imshow函數創建的。根據網格對函數求值的結果:
image.png

3.將條件邏輯表述爲數組運算

numpy.where函數是三元表達式x if condition else y的矢量化版本。假設我們有一個布爾數組和兩個值數組:

In [30]: xarr=np.array([1.1,1.2,1.3,1.4,1.5])

In [31]: yarr=np.array([2.1, 2.2, 2.3, 2.4, 2.5])

In [32]: cond = np.array([True, False, True, True, False])

假設我們想要根據cond中的值選取xarr和yarr的值:當cond中的值爲True時,選取xarr的值,否則從yarr中選取。列表推導式的寫法應該如下所示:

In [34]: result = [(x if c else y)
    ...: for x,y,c in zip(xarr,yarr,cond)]

In [35]: result
Out[35]: [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]

這有幾個問題。第一,它對大數組的處理速度不是很快(因爲所有工作都是由純Python完成的)。第二,無法用於多維數組。若使用np.where,則可以將該功能寫得非常簡潔:

In [36]: result=np.where(cond,xarr,yarr)

In [37]: result
Out[37]: array([ 1.1, 2.2, 1.3, 1.4, 2.5])

np.where的第二個和第三個參數不必是數組,它們都可以是標量值。在數據分析工作中,where通常用於根據另一個數組而產生一個新的數組。假設有一個由隨機數據組成的矩陣,你希望將所有正值替換爲2,將所有負值替換爲-2。若利用np.where,則會非常簡單:

In [40]: arr=np.random.randn(4,4)

In [41]: arr
Out[41]: 
array([[ 3.05909033, -0.45293166, -2.17080782, 0.76203575],
[-0.59751362, -0.81526266, -0.23207938, -0.10778823],
[-1.11461048, 0.11587053, 0.06125199, 0.7875107 ],
[ 0.48923144, 1.45993583, 0.25710067, -0.59854274]])

In [42]: np.where(arr > 0,2,-2)
Out[42]: 
array([[ 2, -2, -2, 2],
[-2, -2, -2, -2],
[-2, 2, 2, 2],
[ 2, 2, 2, -2]])

In [43]: np.where(arr >0,2,arr) #只將正值設置爲2
Out[43]: 
array([[ 2. , -0.45293166, -2.17080782, 2. ],
[-0.59751362, -0.81526266, -0.23207938, -0.10778823],
[-1.11461048, 2. , 2. , 2. ],
[ 2. , 2. , 2. , -0.59854274]])

傳遞給where的數組大小可以不相等,甚至可以是標量值。

4.數學和統計方法

可以通過數組上的一組數學函數對整個數組或某個軸向的數據進行統計計算。sum、mean以及標準差std等聚合計算(aggregation,通常叫做約簡(reduction))既可以當做數組的實例方法調用,也可以當做頂級NumPy函數使用:

In [55]: arr=np.random.randn(5,4)#正態分佈數據

In [56]: arr.mean()
Out[56]: -0.049889604534816778

In [57]: np.mean(arr)
Out[57]: -0.049889604534816778

In [58]: arr.sum()
Out[58]: -0.99779209069633557

mean和sum這類的函數可以接受一個axis參數(用於計算該軸向上的統計值),最終結果是一個少一維的數組:

In [59]: arr.mean(axis=1)
Out[59]: array([ 0.61692875, -0.13910609, -0.00297005, -0.50647901, -0.21782161])

In [60]: arr
Out[60]: 
array([[-2.13861877, 2.29349781, 1.43112711, 0.88170883],
[-0.65024191, 0.4604094 , -0.56090264, 0.19431079],
[ 0.46188982, -1.09339898, 0.62626328, -0.00663433],
[ 0.44629915, -1.46862793, -0.06877831, -0.93480896],
[-0.24556897, 0.60528118, -0.18854913, -1.04244953]])
In [61]: arr.sum(0)
Out[61]: array([-2.12624069, 0.79716149, 1.23916031, -0.9078732 ])

其他如cumsum和cumprod之類的方法則不聚合,而是產生一個由中間結果組成的數組:

In [62]: arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])

In [63]: arr.cumsum(0)
Out[63]: 
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)

In [64]: arr.cumprod(1)
Out[64]: 
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)


In [65]: np.max(arr)
Out[65]: 8

In [66]: np.argmax(arr)
Out[66]: 8

In [67]: np.sum(arr)
Out[67]: 36

image.png

5.用於布爾型數組的方法

在上面這些方法中,布爾值會被強制轉換爲1(True)和0(False)。因此,sum經常被用來對布爾型數組中的True值計數:

In [68]: arr=np.random.randn(100)

In [69]: (arr >0).sum() #正值的數量
Out[69]: 56

另外還有兩個方法any和all,它們對布爾型數組非常有用。any用於測試數組中是否存在一個或多個True,而all則檢查數組中所有值是否都是True:

In [70]: bools = np.array([False, False, True, False])

In [71]: bools.any()
Out[71]: True

In [72]: bools.all()
Out[72]: False

這兩個方法也能用於非布爾型數組,所有非0元素將會被當做True。

6.排序

跟Python內置的列表類型一樣,NumPy數組也可以通過sort方法就地排序:

In [73]: arr=np.random.randn(9)

In [74]: arr
Out[74]: 
array([-0.62889004, -1.81492359, 1.45620238, 1.51305779, 0.07868837,
-0.60457714, -1.60634838, -0.24632214, -0.2419341 ])

In [75]: arr.sort()

In [76]: arr
Out[76]: 
array([-1.81492359, -1.60634838, -0.62889004, -0.60457714, -0.24632214,
-0.2419341 , 0.07868837, 1.45620238, 1.51305779])

多維數組可以在任何一個軸向上進行排序,只需將軸編號傳給sort即可

In [78]: arr3=np.random.randn(6,3)

In [79]: arr3
Out[79]: 
array([[-0.23956417, 0.84718833, 0.08335178],
[-0.55181716, 0.14174783, 0.02762056],
[-1.33281595, -0.22893889, -0.20462358],
[ 0.67883366, 0.9351854 , 1.31355628],
[ 0.42187437, -1.57850288, 0.39878659],
[-0.78516421, -0.00805874, 0.0210935 ]])
In [80]: arr3.sort(1)

In [81]: arr3
Out[81]: 
array([[-0.23956417, 0.08335178, 0.84718833],
[-0.55181716, 0.02762056, 0.14174783],
[-1.33281595, -0.22893889, -0.20462358],
[ 0.67883366, 0.9351854 , 1.31355628],
[-1.57850288, 0.39878659, 0.42187437],
[-0.78516421, -0.00805874, 0.0210935 ]])

頂級方法np.sort返回的是數組的已排序副本,而就地排序則會修改數組本身。計算數組分位數最簡單的辦法是對其進行排序,然後選取特定位置的值:

In [83]: large_arr = np.random.randn(1000)

In [84]: large_arr.sort()

In [85]: large_arr[int(0.05 * len(large_arr))] # 5%分位數
Out[85]: -1.603427813313238

7.唯一化以及其他的集合邏輯

NumPy提供了一些針對一維ndarray的基本集合運算。最常用的可能要數np.unique了,它用於找出數組中的唯一值並返回已排序的結果:

In [86]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])

In [87]: np.unique(names)
Out[87]: 
array(['Bob', 'Joe', 'Will'], 
dtype='<U4')

In [88]: ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])

In [89]: np.unique(ints)
Out[89]: array([1, 2, 3, 4])

拿跟np.unique等價的純Python代碼來對比一下

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

另一個函數np.in1d用於測試一個數組中的值在另一個數組中的成員資格,返回一個布爾型數組:

In [91]: values = np.array([6, 0, 0, 3, 2, 5, 6])

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

image.png

練習過程中涉及到數學和線性代數知識,需要去補充相關的知識,才能更好的理解。

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