NumPy(Numerical Python 的簡稱)提供了高效存儲和操作密集數(shù)據(jù)緩存的接口合冀,可以理解是一個數(shù)組,與python列表相似业岁。Numpy的官網:https://numpy.org/
1.Numpy數(shù)據(jù)類型
1.1 基礎類型
首先鳞仙,需要先聊聊Python的基礎類型,與java不一致的是叨襟,Python的數(shù)據(jù)變量賦值是很隨意的繁扎,對于一個變量我可以給其賦值字符串,同時也可以為其賦值整型糊闽,從這一點發(fā)現(xiàn)Python 變量不僅是它們的值梳玫,還包括了關于值的類型的一些額外信息。
x = "jp Morgan"
x = 6
x = ["GE",16,4.23,True]
python的整型并不是單純的整型右犹,python是由C實現(xiàn)提澎,下邊是一個long的定義:
struct _longobject {
long ob_refcnt;
PyTypeObject *ob_type;
size_t ob_size;
long ob_digit[1];
};
除了實際存儲數(shù)值的ob_digit,還有很多其他信息念链,這意味著與 C 語言這樣的編譯語言中的整型相比盼忌,在 Python 中存儲一個整型會有一些額外開銷。
C 語言整型本質上是對應某個內存位置的標簽掂墓,里面存儲的字節(jié)會編碼成整型谦纱。而 Python 的整型其實是一個指針,指向包含這個 Python 對象所有信息的某個內存位置君编。
1.2 列表和Numpy
通過之前的基本理解跨嘉,對于Python的列表,列表的每一項都會包含各自的類型信息吃嘿、引用計數(shù)和其他信息祠乃,這樣效率很低。動態(tài)類型的列表和固定類型的(NumPy 式)數(shù)組間的區(qū)別如下:
每個列表元素是一個包含數(shù)據(jù)和類型信息的完整結構體兑燥,而且列表可以用任意類型的數(shù)據(jù)填充亮瓷。固定類型的 NumPy 式數(shù)組缺乏這種靈活性,但是能更有效地存儲和操作數(shù)據(jù)降瞳。
1.3 固定類型數(shù)組
Python也提供了固定類型數(shù)組的解決方案嘱支,可以使用array模塊下的方法創(chuàng)建,array.array有2個參數(shù)力崇,第一個是數(shù)組類型斗塘,后邊的數(shù)組必須是第一個參數(shù)指定的類型。
import array
y = array.array('c',list("ford"))
y[0]
更實用的是 NumPy 包中的 ndarray 對象亮靴。Python 的數(shù)組對象提供了數(shù)組型數(shù)據(jù)的有效存儲,而 NumPy 為該數(shù)據(jù)加上了高效的操作于置。下面介紹常見的numpy數(shù)組的創(chuàng)建方式:
不同于 Python 列表茧吊,NumPy 要求數(shù)組必須包含同一類型的數(shù)據(jù)贞岭。
1.可以使用np.array()來創(chuàng)建numpy數(shù)組,第一個參數(shù)是一個列表搓侄,第二個參數(shù)指定數(shù)組類型瞄桨。(不適合大數(shù)據(jù)量)
2.可以使用np.zeros()來創(chuàng)建全0數(shù)組。
3.可以使用np.ones()來創(chuàng)建全1數(shù)組讶踪。
4.可以使用np.full()來創(chuàng)建同值數(shù)組芯侥。
5.np.arange(x1,x2,x3) 創(chuàng)建一個序列,從x1~x2(不包含x2)乳讥,步長是x3柱查。
6.np.linspace(x1,x2,x3) 創(chuàng)建一個包含x3個元素的序列,均勻分布在x1~x2云石。
7.np.random.random(x1)生成在0~1均勻分布的隨機數(shù)組成的數(shù)組唉工。該方法只有1個參。
8.np.random.normal(x1,x2,x3)均值為x1汹忠、方差為x2的正態(tài)分布的隨機數(shù)數(shù)組淋硝,數(shù)組shape是x3的數(shù)組。
9.np.random.randint(x1,x2,x3)創(chuàng)建在[x1, x2)區(qū)間的隨機的shape為x3的整型數(shù)組宽菜。
10.np.eye(x1)創(chuàng)建一個x1 * x1維的單位陣谣膳。
11.np.empty(x1)創(chuàng)建一個由x1個整型數(shù)組成的未初始化的數(shù)組,數(shù)組的值是內存空間中的任意值铅乡。
y1 = np.array([range(i,i+5) for i in [2, 7, 9]],dtype='float')
y2 = np.zeros((4,3),dtype=int)
y3 = np.ones((2,5),dtype=float)
y4 = np.full([3,5],1986,dtype=float)
y5 = np.arange(0,21,3)
y6 = np.linspace(0,10,5)
y7 = np.random.random((3,6))
y8 = np.random.normal(0,1,(2,3))
y9 = np.random.randint(0,10,(4,2))
y10 = np.eye(6)
y11 = np.empty((4,3))
Numpy支持的數(shù)據(jù)類型可以參考官網:https://numpy.org/doc/1.14/user/basics.types.html
2.Numpy基礎
- Numpy的屬性:
Numpy的常用屬性包括:nidm(數(shù)組的維度)继谚、shape(數(shù)組每個維度的大小)隆判、size(數(shù)組的總大腥印)、dtype(數(shù)組的數(shù)據(jù)類型)侨嘀、itemsize(數(shù)組元素字節(jié)大谐敉臁)、nbytes(數(shù)組總字節(jié)大幸蟆)欢峰。 - Numpy的數(shù)據(jù)訪問:
訪問數(shù)組的某個元素和普通數(shù)組完全一致,a[i] 涨共, 多維數(shù)組采用a[i,j,k]指定不同維度纽帖。 - Numpy數(shù)組切片:
數(shù)組切片方法:x[start:stop:step],默認值:默認值 start=0举反、stop= 維度的大邪弥薄(size of dimension)和 step=1 。 切片的結果是包含start火鼻,不包含stop元素室囊。
關于數(shù)組切片有一點很重要也非常有用雕崩,那就是數(shù)組切片返回的是數(shù)組數(shù)據(jù)的視圖,而不是數(shù)值數(shù)據(jù)的副本融撞。這一點也是 NumPy 數(shù)組切片和 Python 列表切片的不同之處盼铁。也就是如果在切片時,改變了numpy的值尝偎,原數(shù)組也會改變饶火。
如果不想改變原數(shù)組,可以使用copy()方法創(chuàng)建一個副本致扯。 - 數(shù)據(jù)變形:
數(shù)組變形最靈活的實現(xiàn)方式是通過 reshape() 函數(shù)來實現(xiàn)肤寝。
另外一個常見的變形模式是將一個一維數(shù)組轉變?yōu)槎S的行或列的矩陣。你也可以通過 reshape 方法來實現(xiàn)急前,或者更簡單地在一個切片操作中利用 newaxis 關鍵字醒陆。 - 數(shù)組拼接和分裂:
np.concatenate可以拼接一維和多維數(shù)組。沿著固定維度處理數(shù)組時裆针,使用 np.vstack(垂直棧)和np.hstack(水平棧)函數(shù)會更簡潔刨摩。
分裂可以通過 np.split、np.hsplit和 np.vsplit 函數(shù)來實現(xiàn)世吨≡枭玻可以向以上函數(shù)傳遞一個索引列表作為參數(shù),索引列表記錄的是分裂點位置(這個是從1開始的耘婚,1表是第1一個元素)罢浇。N 分裂點會得到 N + 1 個子數(shù)組。
# numpy數(shù)組屬性和訪問
x1 = np.random.randint(0,10,size=(3,4,5))
print("x1的ndim屬性:{0}; x1的shape屬性:{1};".format(x1.ndim,x1.shape))
print("x1的size屬性:{0}; x1的dtype屬性:{1};".format(x1.size,x1.dtype))
print("x1的itemsize屬性:{0}; x1的nbytes屬性{1}".format(x1.itemsize,x1.nbytes))
x1[0,2,1] #數(shù)組的訪問方式
# numpy數(shù)組的切片
x2 = np.arange(0,15)
x3 = np.array([np.arange(i,i+3) for i in [3,6,9,1]])
print("切片包含左沐祷,不包含右:{0}".format(x2[4:6]))
x2[5::-1] #步長為負表示逆序嚷闭,從第5個元素開始逆序展示
x2[::-1]
x3
x3[1,:] # 拿到第2行
x3[:,2] # 拿到第3列
x3[0,2] = 1986 #切片改變了子numpy的值,原數(shù)組也會改變赖临。
x4 = x3[:,:].copy()
x4[0,2] = 1989 #改變的是副本copy的值胞锰,原numpy數(shù)組x3不變
x4
# numpy數(shù)組變形
x6 = np.arange(0,9)
x5 = x6.reshape(3,3) #將一維變?yōu)?*3維數(shù)組。
x6[:,np.newaxis] # 轉變?yōu)槎S列向量
x6[np.newaxis,:] # 轉變?yōu)槎S行向量
#數(shù)組拼接和分裂
y1 = np.array([1,2,3])
y2 = np.array([6,8,9])
np.concatenate([y1,y2])
y3 = np.array([[1,3,5],[4,5,2]])
y4 = np.array([[99],[98]])
np.concatenate([y3,y3]) # 默認按行來拼接
np.concatenate([y3,y3],axis = 1) #按列來拼接
np.vstack([y3,y1])
np.hstack([y4,y3])
y5 = np.arange(1,17).reshape([4,4])
y6 = np.arange(1,17)
z1,z2,z3 = np.split(y6,[7,12])
print("z1:{0} ;z2:{1} ;z3:{2}".format(z1,z2,z3))
z3,z4 = np.vsplit(y5,[3])
print("z3:{0} ;z4:{1}".format(z3,z4))
z5,z6 = np.hsplit(y5,[3])
print("z5:{0} ;z6:{1}".format(z5,z6))
3.通用函數(shù)
3.1 通用函數(shù)介紹和Numpy的通用函數(shù)種類
NumPy 數(shù)組的計算有時非尘ふィ快嗅榕,有時也非常慢。使 NumPy 變快的關鍵是利用向量化操作吵聪。NumPy 為很多類型的操作提供了非常方便的凌那、靜態(tài)類型的、可編譯程序的接口吟逝,也被稱作向量操作帽蝶。這種向量方法被用于將循環(huán)推送至 NumPy 之下的編譯層,這樣會取得更快的執(zhí)行效率块攒。通用函數(shù)的主要目的是對 NumPy 數(shù)組中的值執(zhí)行更快的重復操作嘲碱。
下表列舉了常用的通用函數(shù):
運算符 | 對應的通用函數(shù) | 描述 |
---|---|---|
+ | np.add | 加法運算(即 1 + 1 = 2) |
- | np.subtract | 減法運算(即 3 - 2 = 1) |
- | np.negative | 負數(shù)運算( 即 -2) |
* | np.multiply | 乘法運算(即 2 * 3 = 6) |
/ | np.divide | 除法運算(即 3 / 2 = 1.5) |
// | np.floor_divide | 地板除法運算(floor division金砍,即 3 // 2 = 1) |
** | np.power | 指數(shù)運算(即 2 ** 3 = 8) |
% | np.mod | 模 / 余數(shù)( 即 9 % 4 = 1) |
除了上邊經常使用的運算符局蚀,numpy還提供了絕對值:np.abs() 麦锯;三角函數(shù):np.sin(), np.cos() ;指數(shù)函數(shù): e^x , np.exp(x) 2^x , exp2(x) 3^x , power(3, x) 琅绅;對數(shù)函數(shù):ln(x), 以e為底x的對數(shù) log2(x), 以2為底x的對數(shù) log10(x), 以10為底x的對數(shù)扶欣。
更加專用,也更加晦澀的通用函數(shù)優(yōu)異來源是子模塊scipy.special千扶。
x1 = np.arange(1,7)
y1 = 1.0 / x1 #一元通用函數(shù)
x2 = np.linspace(0,12,6)
y2 = x2 / x1 #二元通用函數(shù)
x1,x2,y2
np.add(x1,3) #使用通用函數(shù)替代運算符
3.2 高級通用函數(shù)
- 指定輸出:
在進行大量運算時料祠,有時候指定一個用于存放運算結果的數(shù)組是非常有用的。但是對于較大的數(shù)組澎羞,通過慎重使用 out 參數(shù)將能夠有效節(jié)約內存髓绽。還有,需要注意的是out指定的數(shù)組必須事先有定義好(例如全0數(shù)組)妆绞。 - 聚合:
參照Hadoop的Map/Reduce思想顺呕,reduce 方法會對給定的元素和操作重復執(zhí)行,直至得到單個的結果括饶≈瓴瑁可以用任何通用函數(shù)的 reduce 方法。如果需要存儲每次計算的中間結果图焰,可以使用 accumulate启盛。 - 外積:
任何通用函數(shù)都可以用 outer 方法獲得兩個不同輸入數(shù)組所有元素對的函數(shù)運算結果。
# 輸出結果到數(shù)組變量
x3 = np.arange(5)
z3 = np.zeros(5)
p3 = np.ones(10)
np.multiply(x3,4,out=z3) #z3需要事先初始化一下技羔,例如全0數(shù)組
np.subtract(x3,6,out = p3[::2]) # 每隔2進行一次替換
# reduce方法
x4 = np.arange(6)
np.add.reduce(x4) #reduce 方法會對給定的元素和操作重復執(zhí)行
np.add.accumulate(x4) #需要存儲每次計算的中間結果
#外積
x5 = np.arange(1,6)
np.add.outer(x5,x5) # 對應元素[2,2] 表示第3個元素和第3個元素的和僵闯,即6
4.聚合
在python中,存在sum()藤滥、min()鳖粟、max()等聚合函數(shù),與之相對地超陆,在numpy中存在np.sum()牺弹、np.min()、np.max()等函數(shù)也功能類似时呀,但是執(zhí)行效率會更高张漂。默認情況下,每一個 NumPy 聚合函數(shù)將會返回對整個數(shù)組的聚合谨娜。
聚合函數(shù)還有一個參數(shù)航攒,用于指定沿著哪個軸的方向進行聚合。axis=0 :表示按列執(zhí)行趴梢。axis 關鍵字指定的是數(shù)組將會被折疊的維度漠畜,而不是將要返回的維度币他。
下邊列舉了一些常用的numpy的聚合函數(shù):
函數(shù)名稱 | NaN安全版本 | 描述 |
---|---|---|
np.sum | np.nansum | 計算元素的和 |
np.prod | np.nanprod | 計算元素的積 |
np.mean | np.nanmean | 計算元素的平均值 |
np.std | np.nanstd | 計算元素的標準差 |
np.var | np.nanvar | 計算元素的方差 |
np.min | np.nanmin | 找出最小值 |
np.max | np.nanmax | 找出最大值 |
np.argmin | np.nanargmin | 找出最小值的索引 |
np.argmax | np.nanargmax | 找出最大值的索引 |
np.median | np.nanmedian | 計算元素的中位數(shù) |
np.percentile | np.nanpercentile | 計算基于元素排序的統(tǒng)計值 |
np.any | N/A | 驗證任何一個元素是否為真 |
np.all | N/A | 驗證所有元素是否為真 |
x1 = np.random.random((4,3))
np.sum(x1) #默認所有元素的和
np.sum(x1,axis=0) #按列聚合
np.sum(x1,axis=1) #按行聚合
5.廣播計算
首先,說明下廣播計算的定義憔狞,簡單講來廣播計算就是不同維度的數(shù)組的計算蝴悉。也就是為用于不同大小數(shù)組的二進制通用函數(shù)(加、減瘾敢、乘等)的一組規(guī)則拍冠。
下邊是3個極為簡單的廣播示例:
d1 = np.arange(3)
d2 = np.ones((3,3))
d3 = np.ones((3,1))
d1+5
d1+d2
d1+d3
M = np.ones((2, 3))
a = np.arange(3)
M+a
a1 = np.arange(3).reshape((3, 1))
a2 = np.arange(3)
a1 + a2
a3 = np.ones((3, 2))
a4 = np.arange(3)
# a3 + a4 #could not be broadcast together with shapes (3,2) (3,)
np.newaxis
廣播的原則是,看相加(可以其他任何運算)的兩個數(shù)組的每個維度的最大值簇抵,例如(3,1) 和(1,3) 庆杜,很明顯是兩個維度的最大值都是3,那么最終的結果就是2個維度最大值的組合碟摆,也就是(3,3)晃财。
6.比較、掩碼和布爾邏輯
當你希望統(tǒng)計數(shù)組中有多少值大于某一個給定值典蜕,或者刪除所有超出某些門限值的異常點時断盛,布爾掩碼可以起到作用。
6.1 和通用函數(shù)類似的比較函數(shù)
除了第3部分介紹的通用函數(shù)嘉裤,NumPy 還實現(xiàn)了如 <(小于)和 >(大于)的逐元素比較的通用函數(shù)郑临,這些函數(shù)在計算后,會得到一個布爾數(shù)組屑宠。常見的比較操作符如下:
運算符 | 對應的通用函數(shù) |
---|---|
== | np.equal |
!= | np.not_equal |
< | np.less |
<= | np.less_equal |
> | np.greater |
>= | np.greater_equal |
x1 = np.arange(12).reshape(4,3)
x1>6 # 比較操作符得到布爾數(shù)組厢洞。
6.2 布爾數(shù)組的操作:
給定一個布爾數(shù)組,你可以實現(xiàn)很多有用的操作典奉。
- 統(tǒng)計記錄數(shù)
(1)np.count_nonzero:如果需要統(tǒng)計布爾數(shù)組中 True 記錄的個數(shù)躺翻,可以使用np.count_nonzero。
(2)np.sum():另外一種實現(xiàn)方式是利用np.sum卫玖,F(xiàn)alse 會被解釋成 0公你,True 會被解釋成1。
sum() 的好處是假瞬,和其他 NumPy 聚合函數(shù)一樣陕靠,這個求和也可以沿著行或列進行。
(3)np.any():數(shù)組中是否有任何一個元素符合布爾條件脱茉,也可以指定某個維度進行剪芥。
(4)np.all():數(shù)組中是否所有元素都符合布爾條件,也可以指定某個維度進行琴许。 - 布爾運算符
這部分很簡單税肪,實際就是與、或、非等邏輯條件益兄。通過&锻梳、|、^ 和 ~ 來實現(xiàn)净捅。
運算符 | 對應通用函數(shù)
-|-
& | np.bitwise_and
| | np.bitwise_or
^ | np.bitwise_xor
~ | np.bitwise_not
rainfall = pd.read_csv('data/Seattle2014.csv')['PRCP'].values #直接獲取數(shù)組
inches = rainfall / 254
inches.shape
#
x1 = np.arange(12).reshape(4,3)
x1>6 # 比較操作符得到布爾數(shù)組疑枯。
np.count_nonzero(x1>6) #布爾數(shù)組x1>6中True的個數(shù)是5個
np.sum(x1>6) #替代np.count_nonzero的一種方式
np.sum(x1>6,axis=0) #返回每一列大于6的元素的個數(shù)
np.sum(x1>6,axis=1) #返回每一行大于6的元素的個數(shù)
np.any(x1>6)
np.all(x1>6)
np.any(x1>6,axis=0) #按列維度,是否每一列都有大于6的元素
print("Number days without rain: ", np.sum(inches == 0))
print("Number days with rain: ", np.sum(inches != 0))
print("Days with more than 0.5 inches:", np.sum(inches > 0.5))
print("Rainy days with < 0.1 inches :", np.sum((inches > 0) & (inches < 0.2)))
6.3 布爾數(shù)組做掩碼:
一種更強大的模式是使用布爾數(shù)組作為掩碼灸叼,通過該掩碼選擇數(shù)據(jù)的子數(shù)據(jù)集神汹。為了將這些值從數(shù)組中選出于颖,可以進行簡單的索引适室,即掩碼操作轮蜕。掩碼操作在顯示的時候是以中括號來訪問值的。
and 和 or 對整個對象執(zhí)行單個布爾運算捉腥,而 & 和| 對一個對象的內容(單個比特或字節(jié))執(zhí)行多個布爾運算。對于NumPy 布爾數(shù)組你画,后者是常用的操作抵碟。
y1 = np.array([[5, 0, 3, 3],[7, 9, 3, 5],[2, 4, 7, 6]])
y1[y1>5] #所有的返回值是掩碼數(shù)組對應位置為 True 的值
np.arange(365) - 172 < 90
A = np.array([1, 0, 1, 0, 1, 0])
B = np.array([1, 1, 1, 0, 1, 1])
A | B #每個元素來進行或操作
7. 花哨索引
簡單的索引值(如 arr[0])、切片(如 arr[:5])和布爾掩碼(如 arr[arr > 0])獲得并修改部分數(shù)組坏匪∧獯花哨的索引的索引參數(shù)傳遞的是索引數(shù)組。
利用花哨的索引适滓,結果的形狀與索引數(shù)組的形狀一致敦迄,而不是與被索引數(shù)組的形狀一致。
同樣地凭迹,花哨的索引也對多個維度適用罚屋。和標準的索引方式一樣,第一個索引指的是行嗅绸,第二個索引指的是列脾猛。
花哨的索引可以和其他索引方案結合起來形成更強大的索引操作。
正如花哨的索引可以被用于獲取部分數(shù)組鱼鸠,它也可以被用于修改部分數(shù)組猛拴。操作中重復的索引會導致一些出乎意料的結果產生。at() 函數(shù)在這里對給定的操作蚀狰、給定的索引(這里是 i)以及給定的值(這里是 1)執(zhí)行的是就地操作
t1 = np.array([82, 74, 79, 85, 71, 87, 90, 97, 52, 4, 84, 62, 74, 50, 73])
t1[[3,8,9]]
i1 = np.array([[2,5],[9,1]])
t1[i1]
t2 = np.arange(12).reshape(3,4)
print("t2[[0,2],[1,2]]:{0}".format(t2[[0,2],[1,2]]))
print("t2[2, [2, 0, 1]]:{0}".format(t2[2, [2, 0, 1]]))
print("t2[1:, [2, 0, 1]]:{0}".format(t2[1:, [2, 0, 1]]))
t2
u1 = np.arange(12)
ui1 = np.array([2,1,4,6])
u1[ui1] = -99
u2 = np.zeros(12)
u2[[4,4]] = [12,4] #對于重復的索引愉昆,實際上是二次賦值。
ui2 = np.array([1,3,3,3,4,4,9,9,9,9])
u2[ui2] += 1 #并沒有完成累加的預期效果
u3 = np.zeros(12)
np.add.at(u3,ui2,1) #如果要完成累加造锅,可以使用np.add.at來實現(xiàn)撼唾。
u3
8. 數(shù)組排序
1.如果想在不修改原始輸入數(shù)組的基礎上返回一個排好序的數(shù)組,可以使用np.sort。
2.如果希望用排好序的數(shù)組替代原始數(shù)組倒谷,可以使用數(shù)組的 sort 方法蛛蒙。
3.另外一個相關的函數(shù)是argsort,該函數(shù)返回的是原始數(shù)組排好序的索引值渤愁。
4.NumPy排序算法的一個有用的功能是通過 axis 參數(shù)牵祟,沿著多維數(shù)組的行或列進行排序。
5.有時候不希望對整個數(shù)組進行排序抖格,僅僅希望找到數(shù)組中第K小的值诺苹,NumPy的np.partition函數(shù)提供了該功能。例如對于np.partition(x, 3)來說雹拄,結果數(shù)組中前三個值是數(shù)組中最小的三個值收奔,剩下的位置是原始數(shù)組剩下的值。在這兩個分隔區(qū)間中滓玖,元素都是任意排列的坪哄。
6.同樣地,np.partition也支持按照維度執(zhí)行势篡,同時也支持np.argpartition函數(shù)翩肌。
x1 = np.array([189, 148, 82, 140, 37, 142, 21, 174, 112, 53, 152, 163, 83, 71, 20])
x3 = np.array([189, 148, 82, 140, 37, 142, 21, 174, 112, 53, 152, 163, 83, 71, 20])
x2 = np.sort(x1) #該排序操作并不會修改x1的值
x3.sort() #該操作會修改x3的值
x4 = np.argsort(x1) #排序后的序號組成的數(shù)組
x1[x4] #利用花哨索引,返回了排序后的數(shù)組
x5 = np.array([[15, 17, 13],
[ 9, 6, 7],
[18, 10, 13],
[ 2, 3, 2]])
np.sort(x5,axis=0) #多維數(shù)據(jù)按列排序
np.sort(x5,axis=1) #多維數(shù)據(jù)按行排序
np.partition(x1,5)
np.partition(x5,2,axis=0)
np.partition(x5,1,axis=1)
np.argpartition(x1,5)