Python Numpy基礎教程
本文是一個關於Python numpy的基礎學習教程,其中,Python版本為Python 3.x
什麼是Numpy
Numpy = Numerical + Python,它是Python中科學計算的核心庫,可以高效的處理多維陣列的計算。並且,因為它的許多底層函式是用C語言編寫的,所以運算速度敲快。
基礎知識
ndarray
NumPy的主要物件是同型別的多維陣列ndarray。它是一個通用的同構資料多維容器,所有的元素必須是相同型別的,並通過正整數元組索引。利用該物件可以對整塊資料執行一些數學運算,語法和標量元素之間的運算一樣。在NumPy中,維度稱為軸,軸的數目為rank。
介紹一下ndarray常用的屬性:
- ndarray.shape:表示各個維度中陣列的大小,是一個整數的元組
- ndarray.dtype:描述陣列中元素型別的物件
- ndarray.ndim:陣列中軸的個數
- ndarray.size:陣列元素的總數
- ndarray.itemsize:陣列中每個元素的位元組大小
建立陣列
建立陣列通常有5種方式:
1. 由Python結構(list, tuple等)轉換
建立陣列最簡單的辦法就是使用array物件,它可以接受任何序列型的物件,然後產生一個新的含有傳入資料的numpy陣列(ndarray)。
舉個最簡單的例子:
import numpy as np
a = np.array([1, 2, 3])
print(a)
print(a.dtype)
print(a.shape)
2. 使用Numpy原生陣列建立(arange, ones,zeros等)
如:
b = np.zeros(10)
c = np.ones((1, 2))
3. 從磁碟讀取陣列
使用np.load方法讀取資料。
4. 使用字串或緩衝區從原始位元組建立陣列
5. 使用特殊庫函式(random等)
索引和切片
基礎操作
一維陣列中的索引表面看起來和Python list的功能差不多。
對於切片而言,當你將一個標量值賦值給一個切片時,該值會自動傳播到整個選區,跟Python list最重要的區別在於:Numpy中陣列的切片作用的是原始資料的檢視,也就是資料沒有被複制,所有的修改都會直接作用到源資料。這是因為,Numpy設計之初就是為了處理大資料,將資料複製來複制去自然會產生很多效能問題。如果你想要得到一份資料副本,就需要顯式的使用.copy()方法。
舉個例子:
arr = np.arange(10)
print(arr)
print(arr[0])
print(arr[1:6])
arr_slice = arr[1:6]
arr_slice[1:3] = 5
print(arr_slice)
print(arr)
arr_copy = arr[1:6].copy()
arr_copy[1:3] = 6
print(arr_copy)
print(arr)
對於多維陣列,各索引位置上的元素不再是標量,而是陣列,可以傳入一個以逗號隔開的索引列表來訪問單個元素。其它操作和一維陣列相同。
舉個例子:
arr_2d = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
print(arr_2d[0])
print(arr_2d[0, 1])
arr_2d_slice = arr_2d[1]
print(arr_2d_slice)
arr_2d_slice[0] = 1
print(arr_2d_slice)
print(arr_2d)
切片索引
ndarray的切片語法和Python list類似,對於高維物件,花樣比較多,可以在一個或者多個軸進行切片,也可以跟整數索引混合使用(降低維度)。
舉個例子:
arr_test = np.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
print(arr_test[:2])
print(arr_test[:2, 1:])
print(arr_test[1, :1])
arr_slice_test = arr_test[:2, 1:]
arr_slice_test[0] = 0
print( arr_slice_test)
print(arr_test)
布林型索引
通過布林型索引,可以方便我們根據指定條件快速的檢索陣列中的元素。如果進行變數或者標定量的大資料處理,這種篩選功能的使用肯定會給程式的設計帶來極大的便捷。
舉個簡單例子:
In [1]: import numpy as np
In [2]: x = np.array([[0, 1], [2, 3], [3, 4]])
In [3]: x
Out[3]:
array([[0, 1],
[2, 3],
[3, 4]])
In [4]: x > 2
Out[4]:
array([[False, False],
[False, True],
[ True, True]])
In [5]: x[ x > 2] = 0
In [6]: x
Out[6]:
array([[0, 1],
[2, 0],
[0, 0]])
並且,可以結合使用ndarray的統計方法來對布林型陣列中的True值進行計數,常見有三種方法:
- sum():對True值進行計數
- any():測試陣列中是否存在一個或者多個True
- all():檢查陣列中的所有值是否都是True
花式索引
花式索引(Fancy indexing)是一個Numpy的術語,指的是利用整數陣列進行索引。
花式索引根據索引陣列的值作為目標陣列的某個軸的下標來取值。對於使用一維整型陣列作為索引,如果目標是一維陣列,那麼索引的結果就是對應位置的元素;如果目標是二維陣列,那麼就是對應下標的行。
花式索引跟切片不一樣,它總是將資料複製到新陣列中。
舉個例子:
In [1]: import numpy as np
In [2]: array = np.empty((4, 3))
In [3]: for i in range(4):
...: array[i] = i
...:
In [4]: array
Out[4]:
array([[0., 0., 0.],
[1., 1., 1.],
[2., 2., 2.],
[3., 3., 3.]])
In [5]: array[[1, 3]]
Out[5]:
array([[1., 1., 1.],
[3., 3., 3.]])
In [6]: array[[-1, -3]]
Out[6]:
array([[3., 3., 3.],
[1., 1., 1.]])
In [7]: array[np.ix_([3, 0],[2, 1])]
Out[7]:
array([[3., 3.],
[0., 0.]])
形狀操作
形狀轉換
介紹幾個常見的修改陣列形狀的方法:
reshape():不改變原始資料的情況下修改陣列
flat():一個陣列元素的迭代器,可以處理陣列元素中的每個資料
flatten():返回一份陣列拷貝,對拷貝所做的處理不會影響原始陣列,格式為.flatten(order=''),其中order='C'表示按行展開,'F'表示按列,'A'表示原順序,'K'表示元素在記憶體中的出現順序。
ravel():展平的陣列元素,順序通常是"C風格",返回的是陣列檢視,修改會影響原始陣列。
該函式接收兩個引數:
舉個例子:
arr = np.arange(12)
print(arr)
arr1 = arr.reshape(3, 4)
for item in arr1:
print(item)
for item in arr1.flat:
print(item)
print(arr1.flatten())
print(arr1.flatten(order="K"))
arr.flatten()[10] = 0
print(arr)
print(arr.ravel())
arr.ravel()[10] = 0
print(arr)
轉置與軸對換
介紹常見的幾種方法:
- ndarray.T:轉置
- transpose: 對換陣列的維數
- rollaxis: 向後滾動指定的軸
- swapaxes:用於交換陣列的兩個軸
轉置是資料重塑的一種特殊形式,返回了源資料的檢視。簡單的轉置可以使用.T,也可以使用transpose方法和swapaxes。
舉個例子:
arr = np.arange(12).reshape((2, 2, 3))
print(arr)
print(arr.T)
print(arr.transpose((1, 0, 2)))
print(arr.swapaxes(1, 2))
通用函式:快速的元素級陣列函式
通用函式(ufunc)是一種對ndarray中的資料執行元素級運算的函式,可將其分為一元和二元進行說明。
一元func
一元func可看做是簡單的元素級變體,比如sqrt和cos,舉個例子:
arr = np.arange(10)
print(np.sqrt(arr))
print(np.square(arr))
二元func
接受2個陣列,然後返回一個結果陣列,比如add和mod,舉個例子:
arr1 = np.arange(10)
arr2 = np.arange(10)
print(np.add(arr1, arr2))
更多函式去官方文件查閱,哈哈,這裡就不贅述了。
陣列運算
基礎運算
在Numpy中,可以利用ndarray對整塊資料執行一些數學運算,語法和普通的標量元素之間的運算一樣。其中,陣列與標量的運算會將標量作用於各個陣列元素。
舉個例子:
i = np.array([[1, 2], [3, 4]])
j = np.array([[5, 6], [7, 8]])
print(i + j)
print(i - j)
print(i - 1)
print(i * j)
print( i / j)
以上,乘法並不同於矩陣乘法,若需進行矩陣相乘,可使用:
i = np.array([[1, 2], [3, 4]])
j = np.array([[5, 6], [7, 8]])
print(j.dot(i))
除此之外,Numpy還提供了以下常用統計方法:
- min():陣列最小值
- max():陣列最大值
- sum():陣列元素相加
- cumsum():計算軸向元素累加和,返回由中間結果組成的陣列
- cumprod():所有元素的累計積
陣列表示式
編寫陣列表示式處理多個陣列資料也是很便捷高效的,舉個例子:假設我們想要在一組值(網格型)上計算函式sqrt(x^2 + y^2),使用np.mashgrid函式接受兩個一維陣列,產生兩個二維矩陣:
In [1]: import numpy as np
In [2]: points = np.arange(-5, 5, 0.01)
In [3]: x, y = np.meshgrid(points, points)
In [4]: x
Out[4]:
array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
...,
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99],
[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]])
In [5]: z = np.sqrt(x ** 2 + y ** 2)
In [6]: z
Out[6]:
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]])
條件篩選
介紹幾個常見的篩選方法:
- where:返回輸入陣列中滿足給定條件的元素的索引
- .argmax() 和 numpy.argmin()函式分別沿給定軸返回最大和最小元素的索引
- nonzero() 函式返回輸入陣列中非零元素的索引。
例項
接下來,使用Numpy來模擬隨機漫步操作下陣列運算。
首先,實現一個很簡單的1000步的隨機漫步,從0開始,隨機生成1和-1,判斷隨機漫步過程中第一次到達某個值(暫定為8)的時間(步數),實現:
import numpy as np
nsteps = 1000
draws = np.random.randint(0, 2, size=nsteps)
steps = np.where(draws > 0, 1, -1)
# 各步的累計和
walk = steps.cumsum()
# 第一次到達8的時間
walk_8 = (np.abs(walk) >= 8).argmax()
print(walk_8)