
【个人主页:玄同765】
大语言模型(LLM)开发工程师|中国传媒大学·数字媒体技术(智能交互与游戏设计) 深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调 技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️ 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案 专栏传送门:LLM大模型开发 项目实战指南、Python 从真零基础到纯文本 LLM 全栈实战、从零学 SQL + 大模型应用落地、大模型开发小白专属:从 0 入门 Linux&Shell 「让AI交互更智能,让技术落地更高效」 欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!

NumPy(Numerical Python)作为Python科学计算领域的核心库,其多维数组对象(ndarray)提供了高效、灵活的数据处理能力。NumPy数组运算不仅能显著提升计算效率,还能简化复杂的数据操作逻辑,是科学计算、数据分析和机器学习等领域的基础工具。本文将深入探讨NumPy数组运算的核心概念、操作方法和实际应用场景,帮助读者全面掌握这一强大工具。
NumPy数组(ndarray)是一种同构的多维数据结构,所有元素必须具有相同的数据类型。与Python原生列表相比,NumPy数组在性能、内存效率和功能丰富性方面具有显著优势 。
首先,NumPy数组采用C/C++底层实现,支持向量化操作,这意味着数组上的运算可以一次性应用于所有元素,无需显式循环,从而大幅提升计算效率 。根据实测数据,NumPy数组运算比Python列表循环快8-10倍,尤其在处理大型数据集时优势更为明显 。
其次,NumPy数组的内存布局是连续的,这使得数据访问和操作更加高效。Python列表存储的是对象的引用,这些引用可能分散在内存各处,而NumPy数组直接存储数据值,内存使用更为紧凑 。
此外,NumPy提供了一系列针对数组优化的数学函数和操作,包括基本算术运算、统计函数、线性代数、傅里叶变换等,这些功能在数据分析和科学计算中极为重要 。
创建NumPy数组的方法主要有两种:从Python列表转换和使用内置函数生成。例如:
import numpy as np
# 从Python列表创建数组
a_list = [1, 2, 3, 4]
a_np = np.array(a_list)
# 使用内置函数创建数组
zero_np = np.zeros((3, 3)) # 创建3x3全零数组
ones_np = np.ones((2, 4)) # 创建2x4全一数组
range_np = np.arange(0, 10, 2) # 创建0到10步长为2的数组数组的形状(shape)表示各维度的元素个数,如(3, 4)表示3行4列的二维数组。数组的维度(ndim)表示轴的数量,一维数组有1个轴,二维数组有2个轴,依此类推。数组的数据类型(dtype)决定了元素的存储方式和计算精度,常见的类型包括int32、float64和bool 。
NumPy数组的索引和切片操作是访问和操作数据的基础,它提供了比Python列表更灵活、强大的数据访问方式 。
基本索引允许通过下标访问单个元素,语法为数组名[行索引, 列索引](维度用逗号分隔) :
arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
print(arr[0, 1]) # 输出:20(第0行第1列元素)
print(arr[-1, -2]) # 输出:80(倒数第1行,倒数第2列元素)切片操作允许批量获取子集,语法为数组名[start:stop:step],其中start是起始索引(包含),stop是结束索引(不包含),step是步长 :
# 一维数组切片
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
print(a[2:7]) # 输出:[3 4 5 6 7]
print(a[::2]) # 输出:[1 3 5 7 9]
print(a[-3:]) # 输出:[7 8 9]
# 二维数组切片
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(b[0:2, 1:3]) # 输出:[[2 3] [5 6]]
print(b[::2, ::-1]) # 输出:[[1 2 3] [7 8 9]]切片操作返回的是原数组的视图(view),而非副本(copy),这意味着对视图的修改会影响原数组。这一特性在处理大型数据时非常有用,可以避免不必要的内存复制。
花式索引(Fancy Indexing)允许通过整数数组或列表获取多个位置的元素 :
arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])
print(arr[[0, 2], 1]) # 输出:[20 80](获取第0行和第2行的第1列元素)
print(arr[[0, 1, 2], [0, 1, 0]]) # 输出:[10 50 70](获取指定行和列的元素)花式索引返回的是原数组的副本,修改副本不会影响原数组 。
布尔索引(Boolean Indexing)允许通过条件筛选获取符合条件的元素 :
x = np.array([1, 2, 3, 4, 5])
print(x < 3) # 输出:[ True True False False False]
print(x[x > 3]) # 输出:[4 5](获取大于3的元素)
# 二维数组的布尔索引
nda1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
row_mask = nda1[:, -1] % 2 == 0 # 筛选最后一列元素为偶数的行
print(nda1[row_mask]) # 输出:[[ 4 5 6] [10 11 12]]布尔索引返回的是原数组的副本,修改副本不会影响原数组 。
下表总结了不同索引方式的特点和适用场景:
索引方式 | 语法示例 | 输出类型 | 内存特性 | 适用场景 |
|---|---|---|---|---|
基本索引 | arr[0,1] | 单个元素 | 视图 | 精确访问特定位置的元素 |
切片 | arr[0:2,1:3] | 子数组 | 视图 | 连续子集的批量访问 |
花式索引 | arr[[0,2],1] | 不连续元素 | 副本 | 非连续元素的批量访问 |
布尔索引 | arr[arr>5] | 条件筛选结果 | 副本 | 基于条件的元素筛选 |
NumPy的核心优势之一是其向量化运算能力,它允许对整个数组(或其子集)执行操作,而无需显式编写循环 。向量化运算通过C语言底层实现,能够充分利用现代CPU的并行计算能力,大幅提升计算效率 。
通用函数(Universal Functions,简称UFuncs)是NumPy实现向量化运算的基础工具。UFuncs可以分为一元函数(仅操作一个数组)和二元函数(操作两个数组) 。一元函数如np.sqrt、np.exp等,二元函数如np.add、npmaximum等 。
# 一元UFunc示例
a = np.array([1, 4, 9, 16])
print(np.sqrt(a)) # 输出:[1. 2. 3. 4.]
# 二元UFunc示例
b = np.array([2, 3, 4, 5])
print(np.add(a, b)) # 输出:[ 3 7 13 21]
print(npmaximum(a, b)) # 输出:[ 2 4 9 16]向量化运算与Python循环的性能对比:
操作 | Python循环 | NumPy向量化 | 执行时间(秒) |
|---|---|---|---|
计算马德隆常数 | 8-10倍 | 优化的C代码 | 循环:0.12 向量化:0.01 |
数组元素加法 | 逐个元素相加 | 批量操作 | 循环:0.05 向量化:0.002 |
数组元素平方 | 逐个元素平方 | 批量操作 | 循环:0.08 向量化:0.003 |
向量化运算比Python循环快8-10倍,尤其在处理大型数组时优势更为明显 。这是因为Python是解释型语言,循环执行效率较低,而NumPy调用的是底层优化的C或Fortran代码,可以并行处理大量元素。
统计函数是NumPy数组运算的重要组成部分,它们可以计算数组的各种统计指标:
# 创建一个3x4的数组
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# 计算基本统计指标
print("最小值:", np.min(arr)) # 输出:1
print("最大值:", np.max(arr)) # 输出:12
print("平均值:", np.mean(arr)) # 输出:6.5
print("标准差:", np.std(arr)) # 输出:3.415658002166863
print("中位数:", np.median(arr)) # 输出:6.5
print("方差:", np.var(arr)) # 输出:11.6875统计函数还可以通过axis参数指定沿哪个轴进行计算,这在处理多维数据时非常有用 :
# 沿列方向(axis=0)计算
print("每列平均值:", np.mean(arr, axis=0)) # 输出:[5. 6. 7. 8.]
print("每列标准差:", np.std(arr, axis=0)) # 输出:[4. 4. 4. 4.]
# 沿行方向(axis=1)计算
print("每行平均值:", np.mean(arr, axis=1)) # 输出:[2.5 6.5 10.5]
print("每行最大值:", np.max(arr, axis=1)) # 输出:[4 8 12]axis参数决定了统计计算的方向,axis=0表示沿列方向(垂直方向),axis=1表示沿行方向(水平方向) 。通过合理使用axis参数,可以轻松计算多维数据的统计指标。
此外,NumPy还提供了一些高级UFuncs,如累积函数和差分函数:
# 累积函数
arr = np.array([1, 2, 3, 4, 5])
print(np累加(arr)) # 输出:[ 1 3 6 10 15](逐个元素累积相加)
print(np累乘(arr)) # 输出:[ 1 2 6 24 120](逐个元素累积相乘)
# 差分函数
print(np diff(arr)) # 输出:[1 1 1 1](相邻元素的差)
print(np diff(arr, n=2))# 输出:[0 0 0](二阶差分)数组形状操作允许改变数组的维度和形状,而广播机制则使得不同形状的数组能够无缝运算 。
1. 数组形状操作
NumPy提供了多种数组形状操作函数,用于调整数组的维度和形状:
reshape():改变数组形状,元素总数必须不变transpose():交换数组的轴顺序ravel():将数组展平为一维,返回视图flatten():将数组展平为一维,返回副本newaxis:增加新维度这些函数在处理多维数据时非常有用。例如,在图像处理中,可能需要将形状为(height, width, channels)的三维数组转换为(channels, height, width)的格式 。
# reshape示例
arr = np.array([[1, 2, 3], [4, 5, 6]])
reshaped = arr.reshape(3, 2)
print(reshaped)
# 输出:
# [[1 2]
# [3 4]
# [5 6]]
# transpose示例
transposed = arr.transpose()
print(transposed)
# 输出:
# [[1 4]
# [2 5]
# [3 6]]
# ravel与flatten对比
raveled = arr.ravel() # 返回视图
flattened = arr.flatten() # 返回副本
raveled[0] = 100 # 修改raveled会影响原数组
print(arr)
# 输出:
# [[100 2]
# [ 4 5]
# [ 6 7]]
flattened[0] = 200 # 修改flattened不会影响原数组
print(arr)
# 输出:
# [[100 2]
# [ 4 5]
# [ 6 7]]下表对比了不同形状操作函数的特点:
函数 | 作用 | 输出类型 | 内存特性 | 适用场景 |
|---|---|---|---|---|
reshape() | 改变数组形状 | 视图(如果可能) | 通常共享内存 | 调整数据布局而不复制数据 |
transpose() | 交换轴顺序 | 视图 | 共享内存 | 矩阵转置或维度调整 |
ravel() | 展平为一维 | 视图(如果可能) | 通常共享内存 | 高效的一维数据处理 |
flatten() | 展平为一维 | 副本 | 独立内存 | 安全获取一维副本 |
newaxis | 增加新维度 | 视图 | 共享内存 | 广播或维度扩展 |
2. 广播机制(Broadcasting)
广播机制是NumPy处理不同形状数组运算的核心功能,它允许不同形状的数组进行元素级运算,而无需显式调整形状 。
广播遵循以下规则:
# 广播示例
A = np.array([[0], [10], [20], [30]]) # 形状(4,1)
B = np.array([0, 1, 2]) # 形状(3,)
C = A + B # 广播后形状(4,3)
print("A形状:", A.shape, "\n", A)
# 输出:
# A形状: (4, 1)
# [[ 0]
# [10]
# [20]
# [30]]
print("B形状:", B.shape, "\n", B)
# 输出:
# B形状: (3,)
# [0 1 2]
print("广播结果形状:", C.shape, "\n", C)
# 输出:
# 广播结果形状: (4, 3)
# [[ 0 1 2]
# [10 11 12]
# [20 21 22]
# [30 31 32]]广播过程的可视化示意图:
A = [[0], → 扩展为 → [[0, 0, 0],
[10], [10, 10, 10],
[20], [20, 20, 20],
[30]] [30, 30, 30]]
B = [0, 1, 2] → 扩展为 → [[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]]
相加结果:
[[ 0, 1, 2],
[10, 11, 12],
[20, 21, 22],
[30, 31, 32]]广播机制通过虚拟扩展而非实际内存复制,使得不同形状的数组能够高效运算 。这在处理高维数据时尤为重要,可以避免不必要的内存消耗和复制操作。
1. 数据准备与加载
# 生成模拟学生成绩数据
num_students = 100
num subjects = 5
subject_weights = np.array([0.3, 0.2, 0.2, 0.15, 0.15]) # 各科权重
# 生成随机成绩数据(范围40-100)
scores = np.random.randint(40, 100, (num_students, num Subjects))2. 基础统计分析
# 计算各科平均分和标准差
subject means = np.mean(scores, axis=0)
subject stds = np.std(scores, axis=0)
# 计算每个学生的加权总分
student_scores = np.dot(scores, subject_weights)
# 计算班级通过率
pass_rate = np.sum(student_scores >= 60) / num_students * 1003. 数据筛选与标记
# 标记不及格学生
scores[scores < 60] = 0 # 将不及格成绩置为0
# 筛选总分前10的学生
top_students = np.argsort(student_scores)[-10:]
print("总分前10的学生索引:", top_students)4. 数据可视化
import matplotlib.pyplot as plt
# 绘制成绩分布直方图
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.hist(scores, bins=20, edgecolor='black')
plt.xlabel('分数')
plt.ylabel('学生人数')
plt.title('各科成绩分布')
# 绘制班级通过率饼图
pass_counts = np.sum(scores >= 60, axis=0)
plt.subplot(1, 2, 2)
plt.pie(pass_counts, labels=['科目1', '科目2', '科目3', '科目4', '科目5'],
autopct='%1.1f%%', startangle=90)
plt.title('班级通过率')
plt.tight_layout()
plt.show()1. 数据生成与加载
# 生成模拟股票数据
num_stocks = 5
num_days = 100
initial_price = 100.0
volatility = 0.1
# 生成随机收益率
returns = np.random.normal(0, volatility, (num_stocks, num_days))
prices = initial_price * np.exp(np累加(returns, axis=1))2. 基础统计分析
# 计算各股票的平均收益率和标准差
mean Returns = np.mean(returns, axis=1)
volatility Returns = np.std(returns, axis=1)
# 计算年化波动率(假设250个交易日)
annual Volatility = volatility Returns * np.sqrt(250)
# 计算相关系数矩阵
correlation Matrix = np.corrcoeff(prices)3. 数据筛选与标记
# 标记表现不佳的股票(平均收益率低于0且波动率高于阈值)
poor_performers = np.where((mean Returns < 0) & (annual Volatility > 15))
print("表现不佳的股票索引:", poor_performers)
# 筛选高波动率股票(波动率高于20)
high_risk_stocks = np.where(annual Volatility > 20)
print("高波动率股票索引:", high_risk_stocks)4. 数据可视化
# 绘制股票价格趋势图
plt.figure(figsize=(12, 8))
for i in range(num_stocks):
plt.plot(prices[i], label=f'股票{i+1}')
plt.xlabel('交易日')
plt.ylabel('收盘价')
plt.title('股票价格趋势')
plt.legend()
plt.show()
# 绘制相关系数热力图
plt.figure(figsize=(8, 8))
plt.imshow(correlation Matrix, cmap='coolwarm')
plt.colorbar()
plt.title('股票相关系数矩阵')
plt.show()1. 图像处理中的数组运算
图像通常以三维数组表示(高度×宽度×通道),NumPy数组运算可以高效处理图像数据:
# 读取图像并转换为NumPy数组
from PIL import Image
import numpy as np
image = Image.open('image.jpg')
image_np = np.array(image)
# 调整亮度(广播机制应用)
brighten_factor = 1.2
image_brighten = image_np * brighten_factor
# 灰度化处理
gray_image = np.dot(image_np[..., :3], [0.299, 0.587, 0.114])
# 显示处理后的图像
plt.imshow(gray_image, cmap='gray')
plt.title('灰度化图像')
plt.show()2. 机器学习中的数组运算
在机器学习中,NumPy数组运算用于处理特征数据、计算损失函数和优化模型参数:
# 线性回归模型
def linear_regression(X, y):
# 添加一列全1,用于计算截距
X = np.hstack((X, np.ones((X.shape[0], 1))))
# 使用最小二乘法计算参数
theta = np.linalg.inv(X.T @ X) @ X.T @ y
return theta
# 生成模拟数据
X = np.random.rand(100, 5) # 100个样本,5个特征
true_theta = np.array([2.0, -1.5, 3.2, 0.8, -2.4, 1.0]) # 真实参数
y_true = X @ true_theta[:5] + true_theta[5] # 真实输出
y_noisy = y_true + np.random.normal(0, 1, 100) # 加入噪声
# 训练模型
theta Estimated = linear_regression(X, y_noisy)
print("估计参数:", theta Estimated)NumPy数组运算是科学计算和数据分析的基础,掌握其核心概念和操作方法对提高工作效率至关重要。NumPy数组的优势在于其高效的内存管理、强大的向量化运算能力和丰富的功能集,这些特性使其成为处理大型数据集的理想工具 。
在实际应用中,应遵循以下最佳实践:
通过结合NumPy数组运算和Matplotlib可视化,可以构建强大的数据分析流程,从数据加载、处理、分析到结果展示,全程无需离开Python环境。
NumPy数组运算是科学计算的基石,掌握这一工具将为数据分析、机器学习和科学仿真等领域的工作奠定坚实基础 。随着Python在科学计算领域的广泛应用,NumPy数组运算的重要性将持续提升,成为数据科学家和研究人员的必备技能。