NumPy 經(jīng)典十例:從入門(mén)到精通
NumPy (Numerical Python) 是 Python 中科學(xué)計(jì)算的基礎(chǔ)庫(kù)。它提供了一個(gè)強(qiáng)大的多維數(shù)組對(duì)象ndarray,以及大量的數(shù)學(xué)函數(shù)來(lái)操作這些數(shù)組。NumPy 數(shù)組運(yùn)算效率高,語(yǔ)法簡(jiǎn)潔,是數(shù)據(jù)分析、機(jī)器學(xué)習(xí)等領(lǐng)域不可或缺的工具。
本文將通過(guò)常用案例,幫助你快速掌握 NumPy 的核心用法。每個(gè)案例都包含代碼示例和詳細(xì)解釋,確保代碼可以直接運(yùn)行。
準(zhǔn)備工作
首先,確保你已經(jīng)安裝了 NumPy。如果沒(méi)有安裝,可以使用 pip 進(jìn)行安裝:
pip install numpy
安裝完成后,在 Python 代碼中導(dǎo)入 NumPy 庫(kù),通常簡(jiǎn)寫(xiě)為 np:
import numpy as np
1. 創(chuàng)建NumPy數(shù)組(ndarray)
NumPy 的核心是 ndarray 對(duì)象,可以使用多種方式創(chuàng)建數(shù)組。
# 案例1:從 Python 列表創(chuàng)建數(shù)組
list1 = [1, 2, 3, 4, 5]
arr1 = np.array(list1)
print("案例1 - 從列表創(chuàng)建數(shù)組:", arr1)
print("數(shù)組類(lèi)型:", type(arr1)) # 查看數(shù)組類(lèi)型,是 numpy.ndarray
print("數(shù)組數(shù)據(jù)類(lèi)型:", arr1.dtype) # 查看數(shù)組元素的數(shù)據(jù)類(lèi)型,默認(rèn)是 int64 (或 int32 等,取決于系統(tǒng))
# 案例2:創(chuàng)建多維數(shù)組(矩陣)
list2 = [[1, 2, 3], [4, 5, 6]]
arr2 = np.array(list2)
print("\n案例2 - 創(chuàng)建多維數(shù)組:\n", arr2)
print("數(shù)組維度:", arr2.ndim) # 查看數(shù)組維度,二維數(shù)組是 2
print("數(shù)組形狀:", arr2.shape) # 查看數(shù)組形狀,(2, 3) 表示 2 行 3 列
print("數(shù)組元素個(gè)數(shù):", arr2.size) # 查看數(shù)組元素總數(shù),2 * 3 = 6
# 案例3:創(chuàng)建指定形狀的零數(shù)組
zeros_arr = np.zeros((3, 4)) # 創(chuàng)建 3 行 4 列的零數(shù)組
print("\n案例3 - 創(chuàng)建零數(shù)組:\n", zeros_arr)
# 案例4:創(chuàng)建指定形狀的單位數(shù)組
ones_arr = np.ones((2, 3), dtype=int) # 創(chuàng)建 2 行 3 列的單位數(shù)組,指定數(shù)據(jù)類(lèi)型為整數(shù)
print("\n案例4 - 創(chuàng)建單位數(shù)組:\n", ones_arr)
print("單位數(shù)組數(shù)據(jù)類(lèi)型:", ones_arr.dtype)
# 案例5:創(chuàng)建指定范圍的數(shù)組 (arange)
range_arr = np.arange(0, 10, 2) # 創(chuàng)建從 0 到 10 (不包含 10),步長(zhǎng)為 2 的數(shù)組
print("\n案例5 - 創(chuàng)建指定范圍數(shù)組 (arange):\n", range_arr)
# 案例6:創(chuàng)建指定數(shù)量的均勻間隔數(shù)組 (linspace)
linspace_arr = np.linspace(0, 1, 5) # 創(chuàng)建從 0 到 1 (包含 1),均勻間隔 5 個(gè)元素的數(shù)組
print("\n案例6 - 創(chuàng)建均勻間隔數(shù)組 (linspace):\n", linspace_arr)
# 案例7:創(chuàng)建隨機(jī)數(shù)數(shù)組 (random)
rand_arr = np.random.rand(2, 2) # 創(chuàng)建 2 行 2 列的 0-1 之間均勻分布的隨機(jī)數(shù)數(shù)組
print("\n案例7 - 創(chuàng)建隨機(jī)數(shù)數(shù)組 (均勻分布):\n", rand_arr)
randn_arr = np.random.randn(3) # 創(chuàng)建 3 個(gè)服從標(biāo)準(zhǔn)正態(tài)分布的隨機(jī)數(shù)數(shù)組
print("\n案例7 - 創(chuàng)建隨機(jī)數(shù)數(shù)組 (正態(tài)分布):\n", randn_arr)
randint_arr = np.random.randint(1, 10, size=(2, 3)) # 創(chuàng)建 2 行 3 列的 1-10 之間(不包含10)的隨機(jī)整數(shù)數(shù)組
print("\n案例7 - 創(chuàng)建隨機(jī)整數(shù)數(shù)組:\n", randint_arr)
2. 數(shù)組的基本操作
NumPy數(shù)組支持各種數(shù)學(xué)運(yùn)算,這些運(yùn)算通常是元素級(jí)別的。
arr_a = np.array([1, 2, 3])
arr_b = np.array([4, 5, 6])
# 案例8:數(shù)組的加法、減法、乘法、除法 (元素級(jí)別)
print("\n案例8 - 數(shù)組的基本運(yùn)算:")
print("數(shù)組 a:", arr_a)
print("數(shù)組 b:", arr_b)
print("加法 (a + b):", arr_a + arr_b)
print("減法 (a - b):", arr_a - arr_b)
print("乘法 (a * b):", arr_a * arr_b)
print("除法 (a / b):", arr_a / arr_b)
print("標(biāo)量乘法 (a * 2):", arr_a * 2) # 數(shù)組與標(biāo)量運(yùn)算
# 案例9:數(shù)組的平方、平方根、指數(shù)、對(duì)數(shù) (元素級(jí)別)
print("\n案例9 - 數(shù)組的數(shù)學(xué)函數(shù):")
print("平方 (a ** 2):", arr_a ** 2)
print("平方根 (np.sqrt(a)):", np.sqrt(arr_a))
print("指數(shù) (np.exp(a)):", np.exp(arr_a))
print("自然對(duì)數(shù) (np.log(a)):", np.log(arr_a))
print("正弦 (np.sin(a)):", np.sin(arr_a)) # 其他三角函數(shù) np.cos, np.tan 等
# 案例10:數(shù)組的比較運(yùn)算 (元素級(jí)別)
print("\n案例10 - 數(shù)組的比較運(yùn)算:")
print("a > 2:", arr_a > 2) # 返回布爾數(shù)組
print("a == b:", arr_a == arr_b)
3. 數(shù)組的索引和切片
NumPy 數(shù)組的索引和切片操作與 Python 列表類(lèi)似,但功能更強(qiáng)大。
arr_c = np.arange(10) # 創(chuàng)建 0-9 的數(shù)組
print("\n案例11 - 數(shù)組的索引和切片:")
print("數(shù)組 c:", arr_c)
# 索引
print("索引 0:", arr_c[0])
print("索引 -1 (最后一個(gè)元素):", arr_c[-1])
# 切片
print("切片 [2:5]:", arr_c[2:5]) # 索引 2, 3, 4 的元素
print("切片 [:5]:", arr_c[:5]) # 從開(kāi)始到索引 4 的元素
print("切片 [5:]:", arr_c[5:]) # 從索引 5 到結(jié)尾的元素
print("切片 [::2]:", arr_c[::2]) # 從開(kāi)始到結(jié)尾,步長(zhǎng)為 2 的元素
# 多維數(shù)組的索引和切片
arr_d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print("\n多維數(shù)組 d:\n", arr_d)
print("d[0, 0]:", arr_d[0, 0]) # 第一行第一列元素
print("d[1, :]:", arr_d[1, :]) # 第二行所有列 (或 d[1])
print("d[:, 2]:", arr_d[:, 2]) # 所有行第三列
print("d[0:2, 1:3]:\n", arr_d[0:2, 1:3]) # 切片子矩陣 (0,1行 和 1,2列)
# 布爾索引 (使用條件篩選元素)
bool_index = arr_c > 5
print("\n布爾索引 (arr_c > 5):\n", bool_index)
print("根據(jù)布爾索引篩選元素:", arr_c[bool_index]) # 只選擇 True 對(duì)應(yīng)的元素
print("更簡(jiǎn)潔的寫(xiě)法:", arr_c[arr_c > 5]) # 直接使用條件作為索引
4. 數(shù)組的形狀操作
可以改變數(shù)組的形狀,但不改變數(shù)據(jù)本身。
arr_e = np.arange(12)
print("\n案例12 - 數(shù)組的形狀操作:")
print("原始數(shù)組 e:", arr_e)
print("原始數(shù)組形狀:", arr_e.shape)
# reshape: 改變數(shù)組形狀 (返回新數(shù)組,不修改原數(shù)組)
reshaped_arr = arr_e.reshape(3, 4) # 變?yōu)?3 行 4 列
print("\nreshape 成 (3, 4):\n", reshaped_arr)
print("reshape 后的數(shù)組形狀:", reshaped_arr.shape)
print("原始數(shù)組 e (未改變):\n", arr_e)
# resize: 改變數(shù)組形狀 (修改原數(shù)組)
arr_e.resize(4, 3) # 變?yōu)?4 行 3 列,會(huì)修改 arr_e 本身
print("\nresize 成 (4, 3) 后,數(shù)組 e:\n", arr_e)
print("resize 后的數(shù)組形狀:", arr_e.shape)
# flatten: 展平數(shù)組 (返回一維數(shù)組)
flattened_arr = reshaped_arr.flatten() # 或使用 .ravel()
print("\n展平數(shù)組 (flatten):\n", flattened_arr)
print("展平后的數(shù)組形狀:", flattened_arr.shape)
# transpose: 轉(zhuǎn)置數(shù)組 (交換維度,對(duì)于二維數(shù)組就是行列互換)
arr_f = np.array([[1, 2, 3], [4, 5, 6]])
print("\n原始數(shù)組 f:\n", arr_f)
transposed_arr = arr_f.T # 或使用 np.transpose(arr_f)
print("\n轉(zhuǎn)置后的數(shù)組 f.T:\n", transposed_arr)
print("轉(zhuǎn)置后的數(shù)組形狀:", transposed_arr.shape)
5. 數(shù)組的聚合函數(shù)
NumPy 提供了很多聚合函數(shù),用于對(duì)數(shù)組進(jìn)行統(tǒng)計(jì)計(jì)算。
arr_g = np.array([[1, 2, 3], [4, 5, 6]])
print("\n案例13 - 數(shù)組的聚合函數(shù):")
print("數(shù)組 g:\n", arr_g)
# 求和
print("所有元素求和 (np.sum(g)):", np.sum(arr_g))
print("按列求和 (np.sum(g, axis=0)):", np.sum(arr_g, axis=0)) # axis=0 表示按列 (沿著行的方向求和)
print("按行求和 (np.sum(g, axis=1)):", np.sum(arr_g, axis=1)) # axis=1 表示按行 (沿著列的方向求和)
# 求均值
print("\n所有元素均值 (np.mean(g)):", np.mean(arr_g))
print("按列均值 (np.mean(g, axis=0)):", np.mean(arr_g, axis=0))
print("按行均值 (np.mean(g, axis=1)):", np.mean(arr_g, axis=1))
# 求最大值、最小值
print("\n最大值 (np.max(g)):", np.max(arr_g))
print("按列最大值 (np.max(g, axis=0)):", np.max(arr_g, axis=0))
print("最小值 (np.min(g)):", np.min(arr_g))
print("按行最小值 (np.min(g, axis=1)):", np.min(arr_g, axis=1))
# 其他聚合函數(shù):
print("\n標(biāo)準(zhǔn)差 (np.std(g)):", np.std(arr_g))
print("方差 (np.var(g)):", np.var(arr_g))
print("中位數(shù) (np.median(g)):", np.median(arr_g)) # 注意:中位數(shù)不是聚合函數(shù),但常用于統(tǒng)計(jì)
print("乘積 (np.prod(g)):", np.prod(arr_g)) # 所有元素乘積
6. 數(shù)組的合并與分割
可以將多個(gè)數(shù)組合并成一個(gè),也可以將一個(gè)數(shù)組分割成多個(gè)。
arr_h1 = np.array([[1, 2], [3, 4]])
arr_h2 = np.array([[5, 6], [7, 8]])
print("\n案例14 - 數(shù)組的合并:")
print("數(shù)組 h1:\n", arr_h1)
print("數(shù)組 h2:\n", arr_h2)
# concatenate: 沿指定軸連接數(shù)組
concat_axis0 = np.concatenate((arr_h1, arr_h2), axis=0) # 沿 axis=0 (垂直方向,行) 連接
print("\nconcatenate (axis=0):\n", concat_axis0)
concat_axis1 = np.concatenate((arr_h1, arr_h2), axis=1) # 沿 axis=1 (水平方向,列) 連接
print("\nconcatenate (axis=1):\n", concat_axis1)
# vstack: 垂直堆疊數(shù)組 (相當(dāng)于 concatenate axis=0)
vstack_arr = np.vstack((arr_h1, arr_h2))
print("\nvstack (垂直堆疊):\n", vstack_arr)
# hstack: 水平堆疊數(shù)組 (相當(dāng)于 concatenate axis=1)
hstack_arr = np.hstack((arr_h1, arr_h2))
print("\nhstack (水平堆疊):\n", hstack_arr)
# split: 分割數(shù)組
arr_i = np.arange(10)
print("\n案例15 - 數(shù)組的分割:")
print("數(shù)組 i:", arr_i)
split_arr = np.split(arr_i, 2) # 分割成 2 個(gè)子數(shù)組 (均勻分割)
print("\nsplit 成 2 個(gè)子數(shù)組:\n", split_arr)
split_arr_indices = np.split(arr_i, [3, 7]) # 在索引 3 和 7 的位置分割
print("\nsplit 在索引 [3, 7] 位置:\n", split_arr_indices) # 分割成 3 個(gè)子數(shù)組
vsplit_arr = np.vsplit(vstack_arr, 2) # 垂直分割多維數(shù)組
print("\nvsplit 多維數(shù)組 (vstack_arr):\n", vsplit_arr)
hsplit_arr = np.hsplit(hstack_arr, 2) # 水平分割多維數(shù)組
print("\nhsplit 多維數(shù)組 (hstack_arr):\n", hsplit_arr)
7. 數(shù)組的廣播 (Broadcasting)
NumPy 的廣播機(jī)制允許不同形狀的數(shù)組進(jìn)行算術(shù)運(yùn)算。
arr_j1 = np.array([1, 2, 3])
scalar = 3
print("\n案例16 - 數(shù)組的廣播 (Broadcasting):")
print("數(shù)組 j1:", arr_j1)
print("標(biāo)量:", scalar)
# 數(shù)組與標(biāo)量相加 (標(biāo)量廣播到數(shù)組的每個(gè)元素)
broadcast_add = arr_j1 + scalar
print("\n數(shù)組 + 標(biāo)量:", broadcast_add)
arr_j2 = np.array([[1, 2, 3], [4, 5, 6]])
arr_j3 = np.array([10, 20, 30]) # (3,) 形狀的數(shù)組
print("\n數(shù)組 j2:\n", arr_j2)
print("數(shù)組 j3:", arr_j3)
# 形狀不同的數(shù)組相加 (j3 廣播到 j2 的每一行)
broadcast_add_arr = arr_j2 + arr_j3
print("\n形狀不同的數(shù)組相加 (廣播):\n", broadcast_add_arr) # j3 會(huì)被廣播成 [[10, 20, 30], [10, 20, 30]] 再與 j2 相加
8. 線性代數(shù)運(yùn)算
NumPy 提供了基本的線性代數(shù)運(yùn)算功能。
arr_k1 = np.array([[1, 2], [3, 4]])
arr_k2 = np.array([[5, 6], [7, 8]])
print("\n案例17 - 線性代數(shù)運(yùn)算:")
print("數(shù)組 k1:\n", arr_k1)
print("數(shù)組 k2:\n", arr_k2)
# 矩陣乘法 (dot product)
matrix_multiply = np.dot(arr_k1, arr_k2) # 或使用 arr_k1 @ arr_k2 (Python 3.5+ 支持)
print("\n矩陣乘法 (k1 * k2):\n", matrix_multiply)
# 矩陣轉(zhuǎn)置 (前面案例已演示: .T 或 np.transpose())
# 矩陣的逆 (inverse)
from numpy.linalg import inv
try:
inv_k1 = inv(arr_k1)
print("\n矩陣 k1 的逆:\n", inv_k1)
except np.linalg.LinAlgError:
print("\n矩陣 k1 不可逆 (奇異矩陣)")
# 行列式 (determinant)
from numpy.linalg import det
det_k1 = det(arr_k1)
print("\n矩陣 k1 的行列式:", det_k1)
# 特征值和特征向量 (eigenvalues and eigenvectors)
from numpy.linalg import eig
eigenvalues, eigenvectors = eig(arr_k1)
print("\n矩陣 k1 的特征值:", eigenvalues)
print("\n矩陣 k1 的特征向量:\n", eigenvectors)
9. 文件 I/O
NumPy 可以將數(shù)組保存到文件,也可以從文件加載數(shù)組。
arr_l = np.arange(10)
print("\n案例18 - 文件 I/O:")
print("數(shù)組 l:", arr_l)
# 保存數(shù)組到文件 (二進(jìn)制格式 .npy)
np.save('my_array.npy', arr_l)
print("\n數(shù)組已保存到 my_array.npy")
# 從文件加載數(shù)組
loaded_arr = np.load('my_array.npy')
print("\n從 my_array.npy 加載的數(shù)組:", loaded_arr)
print("加載的數(shù)組與原始數(shù)組是否相等:", np.array_equal(arr_l, loaded_arr))
# 保存數(shù)組到文本文件 (.txt)
np.savetxt('my_array.txt', arr_l, fmt='%d') # fmt='%d' 指定保存為整數(shù)格式
print("\n數(shù)組已保存到 my_array.txt")
# 從文本文件加載數(shù)組
loaded_txt_arr = np.loadtxt('my_array.txt', dtype=int) # dtype=int 指定加載為整數(shù)類(lèi)型
print("\n從 my_array.txt 加載的數(shù)組:", loaded_txt_arr)
print("從文本文件加載的數(shù)組類(lèi)型:", loaded_txt_arr.dtype)
10. 性能優(yōu)勢(shì):向量化運(yùn)算
NumPy 的核心優(yōu)勢(shì)之一是其向量化運(yùn)算,可以顯著提高運(yùn)算效率,尤其是在處理大型數(shù)組時(shí)。
# 案例19 - 性能對(duì)比:向量化 vs 循環(huán)
size = 1000000
list_data = list(range(size))
numpy_arr = np.arange(size)
# 使用循環(huán)計(jì)算平方和 (Python 列表)
import time
start_time = time.time()
list_squared_sum = 0
for num in list_data:
list_squared_sum += num ** 2
list_time = time.time() - start_time
print("\n案例19 - 性能對(duì)比 (向量化 vs 循環(huán)):")
print("Python 列表循環(huán)計(jì)算平方和耗時(shí): {:.4f} 秒".format(list_time))
# 使用 NumPy 向量化運(yùn)算計(jì)算平方和 (NumPy 數(shù)組)
start_time = time.time()
numpy_squared_sum = np.sum(numpy_arr ** 2) # 向量化平方和運(yùn)算
numpy_time = time.time() - start_time
print("NumPy 向量化運(yùn)算計(jì)算平方和耗時(shí): {:.4f} 秒".format(numpy_time))
print("NumPy 向量化運(yùn)算速度是循環(huán)的約 {:.2f} 倍".format(list_time / numpy_time))
# 案例20 - 廣播的性能優(yōu)勢(shì)
arr_m = np.random.rand(1000, 1000)
scalar_m = 2
start_time = time.time()
result_broadcast = arr_m + scalar_m # 廣播運(yùn)算
broadcast_time = time.time() - start_time
start_time = time.time()
result_loop = np.zeros_like(arr_m)
for i in range(arr_m.shape[0]):
for j in range(arr_m.shape[1]):
result_loop[i, j] = arr_m[i, j] + scalar_m # 循環(huán)逐元素相加
loop_time = time.time() - start_time
print("\n案例20 - 廣播性能優(yōu)勢(shì):")
print("廣播運(yùn)算耗時(shí): {:.4f} 秒".format(broadcast_time))
print("循環(huán)逐元素相加耗時(shí): {:.4f} 秒".format(loop_time))
print("廣播運(yùn)算速度是循環(huán)的約 {:.2f} 倍".format(loop_time / broadcast_time))
總結(jié)
這20個(gè)案例涵蓋了 NumPy 最常用的功能,包括數(shù)組創(chuàng)建、基本操作、索引切片、形狀變換、聚合函數(shù)、合并分割、廣播、線性代數(shù)運(yùn)算以及性能優(yōu)勢(shì)。通過(guò)學(xué)習(xí)和實(shí)踐這些案例,你應(yīng)該能夠基本掌握 NumPy 的核心用法,并為后續(xù)深入學(xué)習(xí)數(shù)據(jù)分析、機(jī)器學(xué)習(xí)等領(lǐng)域打下堅(jiān)實(shí)的基礎(chǔ)。