1 什么是numpy
numpy是一個在Python中做科學計算的基礎庫,重在數值計算,也是大部分Python科學計算庫的基礎庫,多用於大型、多維數據上執行數值計算。
在NumPy 中,最重要的對象是稱為 ndarray 的N維數組類型,它是描述相同類型的元素集合,numpy所有功能幾乎都以ndarray為核心展開。ndarray 中的每個元素都是數據類型對象(dtype)的對象。ndarray 中的每個元素在內存中使用相同大小的塊
2 numpy數組創建
創建Numpy數組一般有三種方法:
(1)通過傳入可待跌對象創建,我將之稱為基本方法
(2)使用Numpy內部功能函數,內部方法
(3)使用特殊的庫函數,特殊方法
2.1 基本方法:np.array()
基本方法是通過給numpy提供的一些函數中傳入可迭代對象來創建數組,這種方法通常是在已知所有元素的情況下使用。numpy中實現這種功能的函數包括:np.array()、np.arange()、np.line(),:
>>> np.array([0, 1, 2, 3, 4]) # 接收一個list作為參數 array([0, 1, 2, 3, 4]) >>> np.array([[11, 12, 13],[21, 22, 23]]) # 創建一個2*3的數組 array([[11, 12, 13], [21, 22, 23]]) >>> np.array((0, 1, 2, 3, 4)) # 接收一個tuple作為參數 array([0, 1, 2, 3, 4]) np.array()方法可以在創建數組的同時指定數據類型: >>> np.array([0, 1, 2, 3, 4], dtype=float) array([0., 1., 2., 3., 4.])
甚至還可以接受range()返回的可迭代對象作為參數:
>>> np.array(range(5)) array([0, 1, 2, 3, 4]) >>> np.array(range(10, 20, 2)) array([10, 12, 14, 16, 18])
2.2 通用方法:np.ones()、np.zeros()、np.eye()
通用方法指的是numpy中提供的arange()、ones()、zeros()、eye()、full()等方法,這些方法可以按照某種規則生成一個數組,並不需要傳入已知的可迭代對象。
(1)np.arange()
上面我們將range()函數結果傳遞給np.array(),np.arange()實現的就是這個功能,所以說,np.arange()就是numpy中的range()方法。
>>> np.arange(5) array([0, 1, 2, 3, 4]) >>> np.arange(10, 20, 2) array([10, 12, 14, 16, 18])
(2)np.linspace()
np.linspace()方法以等間距的形式對給定的兩數進行划分來創建數組:
>>> np.linspace(10, 20, 5) # 將10到20間的數等距取5個 array([10. , 12.5, 15. , 17.5, 20. ])
(3)np.ones()
創建一個元素值全為1的數組,接收一個list或者tuple作為參數
>>> np.ones([2]) # 創建一個一維數組 array([1., 1.]) >>> np.ones([2, 2]) # 創建一個2維數組 array([[1., 1.], [1., 1.]]) >>> np.ones([2, 2, 2]) array([[[1., 1.], [1., 1.]], [[1., 1.], [1., 1.]]])
(4)np.zeros()
創建一個元素值全為0的數組,接收一個list或者tuple作為參數
>>> np.zeros([3]) array([0., 0., 0.]) >>> np.zeros([3, 3]) array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
(5)np.random.random()
創建一個元素為0到1之間隨機數的數組,接收一個list或者tuple作為參數:
>>> np.random.random((3, 3)) array([[0.19414645, 0.2306415 , 0.08072019], [0.68814308, 0.48019088, 0.61438206], [0.5361477 , 0.33779769, 0.38549407]])
既然有random()方法,那么就會有randint()方法,也就是取隨機整數的方法,不過這個randint()方法參數形式更random()不太一樣,具體請看下面實例:
>>> np.random.randint(1, 10, 3) # 從1到10之間隨機取3個整數創建數組 array([6, 4, 6])
(6)np.eye()
創建一個從左上角到右下角的對角線上全為1,其余元素全為0的數組(單位矩陣)。注意,np.eye()的參數可不再是list或者tuple了。
>>> np.eye(3, 3) array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
(7) np.full()
np.full()函數可以創建一個填充給定數值的數組,第一個參數是定義數組形狀的list或tuple,第2個參數是需要填充的數值:
>>> np.full((2, 3), 3) # 創建一個2*3的數組,所有元素都填充3 array([[3, 3, 3], [3, 3, 3]])
2.3 讀取外部數據
numpy也支持從外部讀取數據來創建數組,例如從硬盤中讀取csv、txt等文件來創建數組。np.genfromtxt()是numpy中讀取文件的一個方法,例如在當前目錄下有一個data.csv文件,文件內容為:
id,height,length
1,100,101
2,200,230
3,300,350
通過numpy讀取:
>>> np.genfromtxt('data.csv',delimiter=',',skip_header=1) array([[ 1., 100., 101.], [ 2., 200., 230.], [ 3., 300., 350.]])
讀取外部數據的方法不止np.genfromtxt(),還有np.load(等,但numpy讀取外部數據的應用情況其實並不多,這里不再細說。
3 numpy中數組的數據類型
作為一個強大的科學計算庫,numpy中支持的數據類型遠不止Python原生的幾種數據類型。如下所示為numpy中支持的數據類型:
這些數據類型可以通過如np.bool_、np.float16等形式來調用,上文中提到過,創建數組時可以指定數據類型:
>>> a = np.array([0, 1, 0, 1], dtype=np.bool_) >>> a array([False, True, False, True])
可以通過numpy數組自帶的dtype屬性來查看數組的數據類型:
>>> a.dtype dtype('bool')
為什么輸出的類型是bool而不是bool_呢?因為numpy中后綴帶下划線“_”的數據類型指向的就是Python原生的數據類型,也就是說,np.bool_與Python中的bool數據類型等效,np.float_與Python中的float類型等效。
當一個數組已經被創建,但是想要改變其數據類型,那就可以通過np.asdtype()方法:
>>> a.astype(np.int) array([0, 1, 0, 1]) >>> a = np.random.random((2,2)) >>> a array([[0.02914317, 0.645534 ], [0.61839509, 0.64155607]]) >>> a.dtype dtype('float64') >>> a.astype(np.float16) array([[0.02914, 0.6455 ], [0.618 , 0.6416 ]], dtype=float16) >>> a.dtype dtype('float64')
4 numpy中數組的形狀
numpy中數組使用與存放多維數據,所以,所謂數組的形狀指的就是數據的維度大小,以及每一維度元素個數。我們可以通過數組自帶的shape屬性來查看形狀信息:
>>> a = np.array([[2, 3, 4], [5, 6, 7]]) >>> a array([[2, 3, 4], [5, 6, 7]]) >>> a.shape # 查看形狀屬性 (2, 3)
可以看到,查看形狀屬性時返回的是一個元組,元素的長度代表數組的維度,元組每一個屬性代表對應的維度的元素個數,(2,3)就表示第一個維度元素個數是2(兩行),第二個維度長度是3(3列)。
在數組創建之后,數組的形狀也是可以改變的。改變數組的形狀通過數組的reshape()方法:
>>> a = np.ones((2, 12)) >>> a array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]) >>> a.shape (2, 12) >>> b = a.reshape(2, 3, 4) >>> b array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> b.shape (2, 3, 4) >>> b = a.reshape((2,3,4)) # 元組作為參數 >>> b array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> b.shape (2, 3, 4)
可以看到,np.reshape()方法可以同時傳入多個描述形狀的數字,也可以傳入一個數組,當然,如果將形狀改變為一維數組時,必須傳入的是元組。另外需要注意,傳入reshape方法的多個參數的乘積必須與改變前數組總長度相等,否則會報錯。
numpy數組中專門提供了一個方法加你個數組轉換為以為數組,那就是flatten()方法,這個方法在執行數組運算是非常有用:
>>> a = np.ones((2, 3)) >>> b = a.flatten() >>> b array([1., 1., 1., 1., 1., 1.]) >>> b.shape (6,)
5 索引與切片
對數據使用時,不可避免要進行索引和切片,numpy在這一方面不可謂不強大。numpy數組中所有的索引都是從0開始的,我們可以根據索引來精確取數據。
5.1 按索引取值
下面所有實例都已下方數組a來展開:
>>> a = np.arange(36).reshape((4, 9)) >>> a 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, 32, 33, 34, 35]])
(1)取一行
>>> a[1] # 取第二行數據 array([ 9, 10, 11, 12, 13, 14, 15, 16, 17])
(2)取連續多行數據
>>> a[:2] # 取前兩行數據 array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8], [ 9, 10, 11, 12, 13, 14, 15, 16, 17]])
也可以加上步長:
>>> a[::2] # 每隔一行取一次 array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8], [18, 19, 20, 21, 22, 23, 24, 25, 26]]) >>> a[1:] # 取第2行后面所有行 array([[ 9, 10, 11, 12, 13, 14, 15, 16, 17], [18, 19, 20, 21, 22, 23, 24, 25, 26], [27, 28, 29, 30, 31, 32, 33, 34, 35]])
(3)取不連續多行數據
>>> a[[0,-1]] # 取第一行和最后一行 >>> a[[0,-1]] array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8], [27, 28, 29, 30, 31, 32, 33, 34, 35]])
可以看到,對numpy根據索引進行取值的方法與Python中list索引取值方法類似,都是通過方括號里面傳入索引取值,當需要對多維進行索引時,每一位數據之間用逗號隔開。
(4)取一列
>>> a[:,1] # 取第2列 array([ 1, 10, 19, 28])
(5)取連續多列
>>> a[:,1:3] # 取第2列到第3列 array([[ 1, 2], [10, 11], [19, 20], [28, 29]])
(6)取不連續多列
>>> a[:,[0,3]] # 取第1列和第4列 array([[ 0, 3], [ 9, 12], [18, 21], [27, 30]]))
(7)取連續多行多列
>>> a[1:3:,1:3] # 取第2、3行中的第2、3列 array([[10, 11], [19, 20]])
(8)取多個不連續位置數據
看到這里你應該也明白了取行、取列的規律了,如果取不連續的多行多列呢?例如取第1、3行與第2、4列,你可能認為是a[[0, 2], [1, 3]],我們來看看:
>>> a[[0, 2], [1, 3]]
array([ 1, 21])
可見,返回的並不是預期的數據,而是第1行第2列、第3行第4列的數據,也就是(0,1)和(2,3)位置的數據。
從而我們可以得出結論,如果取第3行中第3列、第5列,第4行中第1列、第7列數據方法如下:
>>> a[[2,2,3,3],[2,4,0,6]] # 第3行中第3列、第5列,第4行中第1列、第7列數據 array([20, 22, 27, 33])
(9)取單個數據
>>> b = a[3,3] >>> b 30 >>> type(b) # 取單個類型是返回的就是一個確切的numpy類型數值 <class 'numpy.int64'>
5.2 bool索引
(1)bool索引取值
numpy中提供了一些通用函數來實現通過bool條件判斷實現按條件取值,使用這些通用方法,與使用對應的符號時等效的,符號與numpy通用方法對應關系如下:
我們通過實例感受一下:
>>> a = np.arange(24).reshape((4,6)) >>> a 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]]) >>> b = a<5 # bool索引選取 >>> b array([[ True, True, True, True, True, False], [False, False, False, False, False, False], [False, False, False, False, False, False], [False, False, False, False, False, False]])
可以看到,在元素值小於5的位置上值為True,不滿足條件的為False。
也可以使用通用函數實現:
>>> b = np.less(a,5) # 通用函數選取 >>> b array([[ True, True, True, True, True, False], [False, False, False, False, False, False], [False, False, False, False, False, False], [False, False, False, False, False, False]])
對bool索引選取出來的結果全是True或者False,可能不是你想要的,可以進一步使用:
>>> a[b]
array([0, 1, 2, 3, 4])
所以我們可以直接刷選值:
>>> a[a<5]
array([0, 1, 2, 3, 4])
(2)三目元算
numpy中提供了一個where()方法來實現三目運算。where()方法接受三個參數,第一個參數是判斷條件,第二個參數時時判斷條件為真時數組中滿足條件的元素將要替換的值,第三個參數是判斷調價為假時不滿足條件元素將要替換的值。
例如,將數組中所有滿足元素值小於5的數值替換為0,不滿足的元素值替換為1:
>>> a = np.arange(24).reshape((4,6)) >>> a 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]]) >>> np.where(a<5, 0, 1) # 三目運算 array([[0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]])
6 numpy中賦值、視圖、深復制
(1)賦值
當對numpy數組進行賦值時,只是對同一個對象新建了一個引用,並不是建立新的對象,所以賦值前后的變量完全是同一對象,對其中一個引用修改時,所有引用都會生效:
>>> a = np.arange(12) >>> b = a # 賦值 >>> a is b True >>> b.shape = (3, 4) >>> b array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
(2)視圖(切片、淺復制)
numpy中允許不同數組間共享數據,這種機制在numpy中稱為視圖,對numpy數組的切片和淺復制都是通過視圖實現的。如果數組B是數組A的視圖(view),則稱A為B的base(除非A也是視圖)。視圖數組中的數據實際上保存在base數組中。
>>> a = np.arange(12) >>> b = a.view() # 使用視圖 >>> a is b False >>> b array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b.shape = (3, 4) # 改變b的形狀 >>> a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> b[0] = 0 >>> a array([ 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b array([[ 0, 0, 0, 0], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
從上面代碼中我們可以發現,a和b是不同的兩個數組,改變b的形狀對a不會有影響,但是改變b的數據之后,a的數據也會發現改變,說明a與b是共享數據的。
再來探索一些切片:
>>> a = np.arange(12) >>> b = a[:] # 切片 >>> a is b False >>> b.shape = (3, 4) >>> a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> b[0] = 0 >>> a array([ 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b array([[ 0, 0, 0, 0], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
果然,切片效果與視圖一致。
(3)深復制
深復制通過數組自帶的copy()方法實現,深復制產生的數組與原數組時完全不同的兩個數組對象,完全享有獨立的內存空間,所有操作都不會相互影響。
>>> a = np.arange(12) >>> b = a.copy() >>> a is b False >>> b.shape = (3, 4) >>> b[0] = 0 >>> a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b array([[ 0, 0, 0, 0], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])