NumPy (Numerical Python) 簡(jiǎn)介:高性能科學(xué)計(jì)算基石
1. 什么是 NumPy?
NumPy (Numerical Python) 是 Python 語言中進(jìn)行科學(xué)計(jì)算的基礎(chǔ)庫。它提供了一個(gè)高性能的多維數(shù)組對(duì)象(ndarray)以及操作這些數(shù)組的工具。
在 Python 的數(shù)據(jù)科學(xué)生態(tài)系統(tǒng)中,NumPy 是基石。幾乎所有高級(jí)庫(如 Pandas、Matplotlib、SciPy、Scikit-learn、TensorFlow 和 PyTorch)都依賴于 NumPy 的數(shù)據(jù)結(jié)構(gòu)進(jìn)行高效的數(shù)據(jù)處理。它為處理大規(guī)模數(shù)據(jù)集提供了優(yōu)化的底層結(jié)構(gòu),是數(shù)據(jù)科學(xué)家和工程師不可或缺的工具。
2. 為什么選擇 NumPy?(核心優(yōu)勢(shì))
相比于 Python 原生的 list,NumPy 在處理大規(guī)模數(shù)值數(shù)據(jù)時(shí)具有顯著的優(yōu)勢(shì),這些優(yōu)勢(shì)源于其獨(dú)特的設(shè)計(jì)哲學(xué)和底層實(shí)現(xiàn):
-
速度與內(nèi)存效率:
內(nèi)存連續(xù)性與局部性:
ndarray數(shù)組將數(shù)據(jù)存儲(chǔ)在連續(xù)的內(nèi)存塊中。這種結(jié)構(gòu)確保了內(nèi)存局部性(Locality of Reference),使得 CPU 緩存(Cache)能夠高效地預(yù)取數(shù)據(jù),從而大大加快了數(shù)據(jù)讀取和處理速度。-
數(shù)據(jù)同構(gòu)性: 數(shù)組是同構(gòu)的(Homogeneous),意味著所有元素的數(shù)據(jù)類型相同(例如,都是 [圖片上傳中...(image-5474b3-1760165947694-16)]
位浮點(diǎn)數(shù),
float64)。這消除了 Python 在運(yùn)行時(shí)進(jìn)行頻繁類型檢查的開銷,使得內(nèi)存占用更小,計(jì)算效率更高。 C 語言加速與 SIMD: NumPy 的底層核心算法使用了高效的 C/C++/Fortran 語言實(shí)現(xiàn),并利用了現(xiàn)代 CPU 的 SIMD(Single Instruction, Multiple Data,單指令多數(shù)據(jù))指令集,能夠并行處理多個(gè)數(shù)據(jù)點(diǎn),使得在執(zhí)行復(fù)雜的數(shù)值運(yùn)算時(shí),其速度遠(yuǎn)超純 Python 循環(huán)。
-
向量化操作(Vectorization):
NumPy 允許您對(duì)整個(gè)數(shù)組執(zhí)行數(shù)學(xué)運(yùn)算,而無需編寫顯式的 Python 循環(huán)(例如
for循環(huán))。這種元素級(jí)(element-wise)的操作,例如數(shù)組相加或乘以標(biāo)量,直接映射到底層優(yōu)化的 C 函數(shù)調(diào)用,極大地簡(jiǎn)化了代碼結(jié)構(gòu),同時(shí)將性能優(yōu)化推到極致。
-
豐富的工具集:
- 提供了大量的通用函數(shù)(ufunc),它們是作用于
ndarray對(duì)象的快速函數(shù)。這些函數(shù)涵蓋了三角函數(shù)、指數(shù)、對(duì)數(shù)、數(shù)學(xué)、統(tǒng)計(jì)、線性代數(shù)等科學(xué)計(jì)算的必備功能,提供了對(duì)數(shù)組操作的高級(jí)抽象。
- 提供了大量的通用函數(shù)(ufunc),它們是作用于
3. NumPy 的核心:ndarray 對(duì)象深度解析
ndarray 是 NumPy 的核心數(shù)據(jù)結(jié)構(gòu),它是一個(gè)同構(gòu)且固定大小的多維數(shù)組。
3.1 數(shù)組的基本屬性與數(shù)據(jù)類型 (Dtype)
一個(gè) ndarray 實(shí)例的關(guān)鍵屬性,以及其數(shù)據(jù)類型的重要性:
| 屬性 | 描述 (中文) | 描述 (英文) | 示例 |
|
.ndim
|
數(shù)組的維數(shù)(或軸數(shù))。
|
[圖片上傳中...(image-668f84-1760165947694-15)]
-dimensions
|
三維張量為 [圖片上傳中...(image-ad954c-1760165947694-14)]
[圖片上傳中...(image-b363b6-1760165947694-13)]
|
|
.shape
|
數(shù)組的維度大?。烤S的長(zhǎng)度)。
|
Shape (e.g., [圖片上傳中...(image-21fea-1760165947694-12)]
)
|
[圖片上傳中...(image-8d500f-1760165947694-11)]
個(gè) [圖片上傳中...(image-d27834-1760165947694-10)]
矩陣的形狀
|
|
.size
|
數(shù)組中元素的總個(gè)數(shù)。
|
Total elements
|
[圖片上傳中...(image-d885ed-1760165947694-9)]
[圖片上傳中...(image-706db2-1760165947694-8)]
|
|
.dtype
|
數(shù)組中元素的類型。
|
Data Type
|
int32, float64, bool 等
|
數(shù)據(jù)類型 (dtype) 的重要性:
精確選擇 dtype 可以顯著影響內(nèi)存占用和計(jì)算速度。例如,存儲(chǔ) [圖片上傳中...(image-a48126-1760165947695-24)]
萬個(gè)整數(shù),使用 np.int32 比默認(rèn)的 np.int64 可以節(jié)省一半的內(nèi)存空間。常用的 dtype 包括 np.int64 (默認(rèn)整數(shù)), np.float64 (默認(rèn)浮點(diǎn)數(shù)), np.bool_, 和 np.complex128 (復(fù)數(shù))。在科學(xué)計(jì)算中,通常使用 np.float64 來保證高精度。
3.2 數(shù)組的創(chuàng)建與初始化
NumPy 提供了靈活的數(shù)組創(chuàng)建方式:
import numpy as np
# 1\. 指定數(shù)據(jù)類型創(chuàng)建數(shù)組 (使用 8 位無符號(hào)整數(shù))
matrix_a = np.array([[10, 20], [30, 40]], dtype=np.uint8)
# 2\. 創(chuàng)建全 0 數(shù)組 (3 維: 2個(gè) 2x3 的矩陣)
arr_b = np.zeros((2, 2, 3))
# 3\. 創(chuàng)建等間隔數(shù)組: log 空間等間隔
arr_c = np.logspace(0, 1, 5)
# arr_c 在 10^0 到 10^1 之間生成 5 個(gè)均勻間隔的數(shù)
# 4\. 創(chuàng)建對(duì)角矩陣 (對(duì)角線為 [1, 2, 3])
diag_matrix = np.diag([1, 2, 3])
# 5\. 創(chuàng)建隨機(jī)數(shù)組:從 [0, 10) 中抽取 9 個(gè)隨機(jī)整數(shù),并重塑為 3x3
random_integers = np.random.randint(0, 10, size=9).reshape(3, 3)
print(f"matrix_a 的數(shù)據(jù)類型: {matrix_a.dtype}") # uint8
print(f"diag_matrix:\n{diag_matrix}")
3.3 數(shù)組索引、切片與高級(jí)索引
NumPy 提供了三種主要的索引機(jī)制:
基本切片 (Basic Slicing): 類似于 Python 列表,使用
:來選擇連續(xù)的元素。切片操作返回的是視圖。布爾索引 (Boolean Indexing): 使用一個(gè)與原數(shù)組形狀相同(或可廣播)的布爾數(shù)組作為掩碼,來選擇所有值為
True的元素。花式索引(Fancy Indexing / 整數(shù)數(shù)組索引): 使用整數(shù)數(shù)組或列表來指定要選擇的行和列的索引。花式索引返回的是數(shù)據(jù)的副本。
A = np.arange(9).reshape(3, 3)
# A = [[0, 1, 2],
# [3, 4, 5],
# [6, 7, 8]]
# 1\. 基本切片
sub_matrix = A[:2, 1:]
# sub_matrix = [[1, 2], [4, 5]]
# 2\. 布爾索引:篩選出所有大于 5 的元素
mask = A > 5
filtered_values = A[mask] # [6, 7, 8]
# 3\. 花式索引:選擇 (0, 0), (1, 2), (2, 1) 這三個(gè)坐標(biāo)的元素
rows = np.array([0, 1, 2])
cols = np.array([0, 2, 1])
selected = A[rows, cols] # [0, 5, 7]
3.4 視圖 (View) 與副本 (Copy) 的區(qū)別
這是 NumPy 中最容易引起混淆的概念之一,因?yàn)樗苯雨P(guān)系到數(shù)據(jù)的修改。
-
視圖 (View):
定義: 切片操作(如
arr[:])和.reshape()、.T(轉(zhuǎn)置) 通常返回原始數(shù)組的一個(gè)視圖。影響: 視圖只是一個(gè)指向原始數(shù)據(jù)的新指針(共享內(nèi)存)。因此,對(duì)視圖的任何修改都會(huì)直接改變原始數(shù)組的數(shù)據(jù)。
-
副本 (Copy):
定義: 使用
.copy()方法或某些操作(如花式索引、flatten())會(huì)創(chuàng)建一個(gè)新的數(shù)組。影響: 副本在內(nèi)存中分配了新的數(shù)據(jù)空間。對(duì)副本的修改不會(huì)影響原始數(shù)組。
理解何時(shí)是視圖、何時(shí)是副本,對(duì)于避免意外修改原始數(shù)據(jù)至關(guān)重要。
original_arr = np.arange(4) # [0, 1, 2, 3]
# 示例 1: 切片 (View)
view_arr = original_arr[1:3] # [1, 2]
view_arr[0] = 99
# 原始數(shù)組被改變: original_arr 變?yōu)?[0, 99, 2, 3]
# 示例 2: 顯式復(fù)制 (Copy)
copy_arr = original_arr.copy()
copy_arr[0] = 100
# original_arr 不變,仍然是 [0, 99, 2, 3]
4. 通用函數(shù)(Universal Functions, UFuncs)
通用函數(shù)(UFuncs) 是 NumPy 的核心概念,它們是快速的、元素級(jí)的操作包裝器。所有基本的數(shù)學(xué)運(yùn)算(加、減、乘、除)和大多數(shù)數(shù)學(xué)函數(shù)都是 UFuncs。
4.1 數(shù)學(xué)與三角函數(shù) UFuncs
這些函數(shù)對(duì)數(shù)組中的每個(gè)元素獨(dú)立進(jìn)行計(jì)算,完美體現(xiàn)了向量化操作的威力。
| 函數(shù) | 描述 |
|
np.exp()
|
指數(shù)函數(shù) [圖片上傳中...(image-59a636-1760165947694-7)]
[圖片上傳中...(image-5a32b-1760165947694-6)]
|
|
np.sqrt()
|
平方根
|
|
np.sin() / np.cos()
|
三角函數(shù)
|
|
np.floor() / np.ceil()
|
向下/向上取整
|
|
np.maximum(A, B)
|
計(jì)算兩個(gè)數(shù)組(或數(shù)組和標(biāo)量)中對(duì)應(yīng)位置的最大值
|
4.2 比較與邏輯 UFuncs
UFuncs 也包括了元素級(jí)的比較操作,這些操作返回布爾數(shù)組,常用于創(chuàng)建布爾掩碼。
data_a = np.array([1, 5, 10])
data_b = np.array([2, 4, 8])
# 元素級(jí)比較
comparison = data_a > data_b
# comparison = [False, True, True]
# 邏輯操作(對(duì)布爾數(shù)組進(jìn)行操作)
is_large = (data_a > 4) & (data_a < 11)
# is_large = [False, True, True]
# 使用 np.maximum 找到兩個(gè)數(shù)組中更大的值
max_values = np.maximum(data_a, data_b) # [2, 5, 10]
5. 向量化運(yùn)算與廣播機(jī)制(Broadcasting)詳解
5.1 廣播機(jī)制 (Broadcasting)
廣播是 NumPy 允許不同形狀的數(shù)組在特定條件下進(jìn)行算術(shù)運(yùn)算的機(jī)制。它通過虛擬地?cái)U(kuò)展(不是實(shí)際復(fù)制內(nèi)存)較小數(shù)組的維度來匹配較大數(shù)組的形狀。
核心兼容性規(guī)則: 從數(shù)組的末尾維度開始比較,如果兩個(gè)數(shù)組的維度滿足以下任一條件,則兼容:
維度相等。
-
其中一個(gè)維度為 [圖片上傳中...(image-63c102-1760165947694-19)]
。
-
其中一個(gè)數(shù)組缺少該維度(NumPy 會(huì)在左側(cè)自動(dòng)添加維度 [圖片上傳中...(image-7b6976-1760165947694-18)]
)。
5.2 復(fù)雜廣播示例
A = np.arange(3).reshape(3, 1) # 形狀 (3, 1)
B = np.arange(3) # 形狀 (3,) -> 擴(kuò)展為 (1, 3)
# 形狀比較:
# A.shape: (3, 1)
# B.shape: (1, 3)
# 維度 1: 1 和 3 -> 兼容 (1 被擴(kuò)展到 3)
# 維度 2: 3 和 1 -> 兼容 (1 被擴(kuò)展到 3)
Result = A + B
# 結(jié)果形狀為 (3, 3)
# Result = [[0, 1, 2],
# [1, 2, 3],
# [2, 3, 4]]
print(f"A:\n{A}")
print(f"B:\n{B}")
print(f"A + B 的形狀: {Result.shape}")
print(f"廣播結(jié)果:\n{Result}")
6. 聚合函數(shù)與軸 (Axis) 的概念
6.1 軸 (Axis) 的直觀理解
軸 (Axis) 定義了聚合操作發(fā)生的方向。在 [圖片上傳中...(image-8baa12-1760165947695-23)]
維數(shù)組中,axis=k 意味著操作將沿著第 [圖片上傳中...(image-61160d-1760165947695-22)]
個(gè)軸進(jìn)行壓縮或計(jì)算,該維度將從結(jié)果的形狀中移除。
對(duì)于二維數(shù)組:
axis=0是列的方向,axis=1是行的方向。-
對(duì)于三維數(shù)組 (例如:圖像數(shù)據(jù) [圖片上傳中...(image-47e7fe-1760165947694-17)]
):
axis=0壓縮高度,axis=2壓縮顏色通道。
6.2 擴(kuò)展聚合函數(shù)
NumPy 提供了強(qiáng)大的統(tǒng)計(jì)工具:
| 函數(shù) | 描述 |
|
arr.mean()
|
均值
|
|
arr.std()
|
標(biāo)準(zhǔn)差
|
|
np.median(arr)
|
中位數(shù)
|
|
np.percentile(arr, q)
|
計(jì)算第 q
百分位數(shù)
|
|
arr.argmax(axis)
|
返回指定軸上的最大值索引
|
data_stats = np.array([[10, 20, 30],
[15, 25, 35]])
# 沿著 axis=0 (列) 計(jì)算均值
mean_cols = data_stats.mean(axis=0)
# mean_cols = [12.5, 22.5, 32.5]
# 沿著 axis=1 (行) 計(jì)算最大值索引
max_index_rows = data_stats.argmax(axis=1)
# max_index_rows = [2, 2] (每行最大值索引)
7. 案例應(yīng)用:數(shù)據(jù)標(biāo)準(zhǔn)化(Z-Score Normalization)
數(shù)據(jù)標(biāo)準(zhǔn)化([圖片上傳中...(image-8c62f9-1760165947695-21)]
-Score)是數(shù)據(jù)預(yù)處理中的常見步驟:[圖片上傳中...(image-56b576-1760165947695-20)]
。這個(gè)過程完美展示了 NumPy 的聚合、向量化和廣播機(jī)制。
# 假設(shè)有一個(gè) 100 行 5 列的特征矩陣 (100 個(gè)樣本, 5 個(gè)特征)
X = np.random.randn(100, 5) * 5 + 10 # 隨機(jī)數(shù)據(jù),均值約 10,標(biāo)準(zhǔn)差約 5
# 步驟 1: 沿著樣本軸 (axis=0) 計(jì)算每個(gè)特征的平均值 (μ)
mean = X.mean(axis=0) # 形狀 (5,)
# 步驟 2: 沿著樣本軸 (axis=0) 計(jì)算每個(gè)特征的標(biāo)準(zhǔn)差 (σ)
std = X.std(axis=0) # 形狀 (5,)
# 步驟 3: 使用廣播機(jī)制進(jìn)行 Z-Score 標(biāo)準(zhǔn)化
# X (100, 5) - mean (5,) -> mean 廣播到 (100, 5)
# (X - mean) / std -> std 廣播到 (100, 5)
X_standardized = (X - mean) / std
print(f"原始數(shù)據(jù)形狀: {X.shape}")
print(f"均值形狀: {mean.shape}")
print(f"標(biāo)準(zhǔn)化后的數(shù)據(jù)的均值 (接近 0):\n{X_standardized.mean(axis=0).round(4)}")
print(f"標(biāo)準(zhǔn)化后的數(shù)據(jù)的標(biāo)準(zhǔn)差 (接近 1):\n{X_standardized.std(axis=0).round(4)}")
8. 高級(jí)模塊:線性代數(shù)與隨機(jī)數(shù)
8.1 線性代數(shù) (np.linalg)
np.linalg 模塊提供了線性代數(shù)所需的核心工具,用于解決矩陣運(yùn)算和系統(tǒng)分析問題。
L = np.array([[4, 2], [1, 3]])
# 1\. 計(jì)算行列式
determinant = np.linalg.det(L) # 10.0
# 2\. 計(jì)算特征值和特征向量 (Eigenvalues and Eigenvectors)
eigen_values, eigen_vectors = np.linalg.eig(L)
print(f"行列式: {determinant}")
print(f"特征值: {eigen_values}")
8.2 隨機(jī)數(shù)生成與采樣 (np.random)
NumPy 的 np.random 模塊用于高效生成各種分布的隨機(jī)數(shù),是進(jìn)行數(shù)據(jù)模擬和模型初始化的基礎(chǔ)。
| 函數(shù) | 描述 |
|
np.random.uniform(low, high, size)
|
均勻分布 [圖片上傳中...(image-f33d9a-1760165947694-4)]
[圖片上傳中...(image-441b28-1760165947694-3)]
|
|
np.random.normal(loc, scale, size)
|
正態(tài)(高斯)分布,均值 [圖片上傳中...(image-b73d3a-1760165947694-2)]
,標(biāo)準(zhǔn)差 [圖片上傳中...(image-9f9aa3-1760165947694-1)]
[圖片上傳中...(image-1beb25-1760165947694-0)]
|
|
np.random.permutation(arr)
|
返回?cái)?shù)組的隨機(jī)排列(洗牌)
|
9. 數(shù)據(jù)持久化:數(shù)組的保存與加載
對(duì)于大型的 NumPy 數(shù)組,為了避免重復(fù)計(jì)算,我們需要將其保存到磁盤。
| 方法 | 文件格式 | 描述 |
|
np.save() / np.load()
|
.npy (NumPy 專用二進(jìn)制格式)
|
保存單個(gè)數(shù)組,速度最快,能夠精確地保留數(shù)組的 dtype 和形狀信息。
|
|
np.savetxt() / np.loadtxt()
|
.txt 或 .csv
|
保存和加載文本文件,雖然易于閱讀,但在處理大型數(shù)組時(shí)速度較慢且可能丟失精度。
|
|
np.savez()
|
.npz (壓縮的 NumPy 格式)
|
將多個(gè)數(shù)組保存到一個(gè)壓縮文件中,適合存儲(chǔ)整個(gè)數(shù)據(jù)集。
|