查看全课大纲(第 32 / 101 节)
- 1.数据分析基本概念
- 2.学习数据分析的一般路线
- 3.数据分析的流程
- 4.数据类型
- 5.环境部署(1)
- 6.环境部署(2)
- 7.课程介绍
- 8.TXT文件操作
- 9.JSON文件操作
- 10.CSV文件操作
- 11.Excel文件操作
- 12.数据库及SQL常用语法
- 13.数据库基本操作
- 14.数据库多表连接
- 15.实战:欧洲职业足球数据库分析
- 16.爬虫简介
- 17.URL管理模块
- 18.网页下载模块
- 19.网页解析模块(1)
- 20.网页解析模块(2)
- 21.Scrapy简介
- 22.Scrapy使用步骤(1)
- 23.Scrapy使用步骤(2)
- 24.Scrapy使用步骤(3)
- 25.Scrapy使用步骤(4)
- 26.实战:获取国内城市空气质量指数数据
- 27.NumPy和SciPy介绍
- 28.多维数组
- 29.多维数组操作
- 30.NumPy的常用方法
- 31.向量化介绍
- 32.向量化及通用函数
- 33.实战:2016美国大选分析
- 34.数据结构-Series
- 35.数据结构-DataFrame
- 36.数据结构-Index
- 37.Series的索引操作
- 38.DataFrame的索引操作
- 39.索引操作总结
- 40.运算与对齐
- 41.函数应用操作(1) -- map
- 42.函数应用操作 (2) -- apply applymap
- 43.文件读写操作
- 44.排序操作
- 45.数据清洗--处理缺失数据
- 46.数据清洗--处理重复数据
- 47.数据清洗--替换数据
- 48.常用统计方法(1) -- describe quantile
- 49.常用统计方法(2) -- sum mean median count
- 50.常用统计方法(3) -- max min idxmax idxmin
- 51.常用统计方法(4) -- mad var std cumsum
- 52.实战:全球食品数据分析
- 53.层级索引
- 54.分组与聚合介绍
- 55.分组操作(1) -- GroupBy对象及常用聚合操作
- 56.分组操作(2) -- 自定义分组及聚合操作
- 57.透视表介绍
- 58.透视表操作
- 59.数据规整(1) -- 数据合并concat
- 60.数据规整(2) -- 数据连接merge
- 61.数据重构(3) -- 数据重构stack unstack
- 62.实战:互联网电影资料库分析
- 63.探索性数据分析EDA介绍
- 64.EDA的目的
- 65.EDA常用工具
- 66.Matplotlib绘图基本介绍
- 67.Matplotlib画布
- 68.散点图和柱状图的绘制
- 69.直方图的绘制
- 70.矩阵绘图
- 71.子图的使用
- 72.Matplotlib颜色、标记、线型
- 73.Matplotlib坐标刻度、标签、图例、标题
- 74.Seaborn介绍
- 75.数据集分布可视化(1) -- 单变量分布、双变量分布
- 76.数据集分布可视化(2) -- 变量关系可视化
- 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
- 78.交互式数据可视化工具Bokeh介绍
- 79.Bokeh绘制散点图、柱状图、盒子图、弦图
- 80.Bokeh绘制常用图形元素
- 81.3D绘图 -- mplot3d
- 82.3D曲线可视化
- 83.3D散点图可视化
- 84.3D柱状图可视化
- 85.Pandas绘图
- 86.实战:Lending Club借贷数据探索性分析及可视化
- 87.机器学习介绍及应用场景
- 88.机器学习建模介绍 (1) -- 分类
- 89.机器学习建模介绍 (2) -- 回归
- 90.机器学习建模介绍 (3) -- 聚类
- 91.机器学习分类
- 92.机器学习工具scikit-learn
- 93.使用scikit-learn的流程
- 94.数据集准备及划分
- 95.模型选择
- 96.数据预处理及特征工程
- 97.过拟合与欠拟合
- 98.模型调参介绍
- 99.模型调参方法
- 100.模型测试及评价
- 101.实战:通过移动设备行为数据预测性别和年龄
向量化及通用函数
约 7 分钟
向量化与通用函数
小象实战讲义 · Python数据分析实战
在上一节我们理解了向量化(Vectorization)和广播(Broadcasting)的概念,它们是NumPy高效运算的核心思想。本节我们将通过代码实战,直观感受向量化运算相比传统循环的巨大性能优势,并学习NumPy中另一类重要工具——通用函数(Universal functions, ufuncs),它们提供了丰富的元素级运算能力。
💡 核心导读
- 向量化实战对比:通过10万量级数组的乘法运算,对比循环与向量化两种实现方式的代码简洁度与执行效率。
- 广播操作验证:用代码复现上节课讲解的几种典型广播场景,验证NumPy自动处理形状兼容的能力。
- 通用函数入门:掌握
ceil、floor、rint、isnan等常用数学通用函数,理解其元素级操作的本质。 - 运算辨析:明确
multiply(元素相乘)与矩阵乘法是完全不同的操作,避免概念混淆。
向量化代码实战:效率的飞跃
我们通过一个具体的例子来感受向量化的威力:将两个长度均为10万的一维数组进行逐元素相乘。
方法一:使用循环(非向量化)
传统思路是使用for循环遍历每个索引,取出对应元素进行计算。这种方法直观,但效率较低。
import numpy as np
import time
# 创建两个长度为10万的数组(每个代码块自包含)
a = np.arange(100000, dtype=float) # 0, 1, 2, ..., 99999
b = np.arange(100000, 0, -1, dtype=float) # 100000, 99999, ..., 1
# 方法一:使用循环
begin = time.time()
results_loop = []
for i, j in zip(a, b):
results_loop.append(i * j)
end = time.time()
time_loop = end - begin # 循环耗时(秒数随机器/负载而变,属非确定值,不打印)
print('循环计算完成,前5个结果:', [float(x) for x in results_loop[:5]])循环计算完成,前5个结果: [0.0, 99999.0, 199996.0, 299991.0, 399984.0]方法二:使用向量化操作
NumPy的向量化允许我们直接对整个数组进行运算,无需显式循环。代码极其简洁。
import numpy as np
import time
# 创建两个长度为10万的数组(每个代码块自包含)
a = np.arange(100000, dtype=float)
b = np.arange(100000, 0, -1, dtype=float)
# 方法二:使用向量化操作
begin = time.time()
results_vectorized = a * b # 直接对整个数组进行乘法运算
end = time.time()
time_vectorized = end - begin # 向量化耗时(秒数随机器/负载而变,属非确定值,不打印)
# 验证两种方式的计算结果完全一致(确定性输出)
results_loop = [i * j for i, j in zip(a, b)]
print('向量化与循环的计算结果一致:', np.allclose(results_loop, results_vectorized))
print('向量化结果前5个元素:', results_vectorized[:5])向量化与循环的计算结果一致: True
向量化结果前5个元素: [ 0. 99999. 199996. 299991. 399984.]说明:耗时属于「每次运行都会变化」的测量值,不适合作为固定印刷结果展示。在典型笔记本电脑上,本例的向量化写法通常比循环快 20~50 倍;你可以在自己的机器上运行
print(time_loop / time_vectorized)亲自验证这个倍数。
对比分析:
- 代码简洁性:向量化仅需一行
a * b,而循环需要多行代码构造循环体。 - 执行效率:向量化运算由NumPy底层通过高度优化的C代码实现,并充分利用了现代CPU的SIMD(单指令多数据流)指令集实现并行计算,速度通常比Python循环快一到两个数量级(本节数据量下约20~50倍),且数据量越大优势越显著。
广播操作代码验证
广播是NumPy中另一个强大的机制,它允许不同形状的数组进行算术运算。我们来验证上节课提到的几个广播例子。
import numpy as np
# 示例1:一维数组与标量相加(标量被广播到数组的每个元素)
print("示例1 - 数组与标量相加:")
print(np.arange(3) + 5)
print()
# 示例2:二维数组与一维数组相加(一维数组被广播到每一行)
print("示例2 - 二维数组与一维数组相加:")
print(np.ones((3, 3)) + np.arange(3))
print()
# 示例3:列向量与行向量相加(两者都被广播,生成一个二维数组)
print("示例3 - 列向量与行向量相加:")
col_vector = np.arange(3).reshape((3, 1)) # 形状(3,1)
row_vector = np.arange(3) # 形状(3,)
print(col_vector + row_vector)示例1 - 数组与标量相加:
[5 6 7]
示例2 - 二维数组与一维数组相加:
[[1. 2. 3.]
[1. 2. 3.]
[1. 2. 3.]]
示例3 - 列向量与行向量相加:
[[0 1 2]
[1 2 3]
[2 3 4]]运行结果证实了广播规则的有效性。NumPy自动处理了形状扩展,使得这些运算能够顺利进行,无需我们手动复制数据。
通用函数:元素级运算工具
通用函数(ufunc)是一种对数组中的每个元素进行操作的函数。NumPy提供了大量的通用函数,涵盖数学、三角、逻辑比较等领域。
常用数学通用函数
我们首先看几个处理数值的通用函数:ceil(向上取整)、floor(向下取整)、rint(舍入到最近整数;遇到 .5 时取偶数,即“银行家舍入”,如 np.rint([0.5, 1.5, 2.5]) 得到 [0, 2, 2])和isnan(判断是否为非数字NaN)。
import numpy as np
# 一个2行3列的示例数组(硬编码字面量,保证每次运行输出一致)
arr = np.array([[ 0.01511223, 0.38603533, 1.10498912],
[ 1.04669897, -0.34327965, -0.40234461]])
print("原始数组:")
print(arr)
print()
print("向上取整 (np.ceil):")
print(np.ceil(arr))
print()
print("向下取整 (np.floor):")
print(np.floor(arr))
print()
print("舍入到最近整数 (np.rint, .5 取偶):")
print(np.rint(arr))
print()
print("判断是否为NaN (np.isnan):")
print(np.isnan(arr))原始数组:
[[ 0.01511223 0.38603533 1.10498912]
[ 1.04669897 -0.34327965 -0.40234461]]
向上取整 (np.ceil):
[[ 1. 1. 2.]
[ 2. -0. -0.]]
向下取整 (np.floor):
[[ 0. 0. 1.]
[ 1. -1. -1.]]
舍入到最近整数 (np.rint, .5 取偶):
[[ 0. 0. 1.]
[ 1. -0. -0.]]
判断是否为NaN (np.isnan):
[[False False False]
[False False False]]可以看到,每个函数都独立地作用于数组arr的每一个元素上,输出一个形状相同的新数组。
算术通用函数:multiply 与 divide
np.multiply 和 np.divide 是进行元素级乘法和除法的通用函数。
import numpy as np
# 元素乘法
arr = np.arange(3) # [0, 1, 2]
print("使用 np.multiply 进行元素乘法:")
print(np.multiply(arr, 5))
print()
print("使用数组的 * 运算符 (效果等同):")
print(arr * 5)
print()
# 元素除法
print("使用 np.divide 进行元素除法:")
print(np.divide(arr, 5))
print()
print("使用数组的 / 运算符 (效果等同):")
print(arr / 5)使用 np.multiply 进行元素乘法:
[ 0 5 10]
使用数组的 * 运算符 (效果等同):
[ 0 5 10]
使用 np.divide 进行元素除法:
[0. 0.2 0.4]
使用数组的 / 运算符 (效果等同):
[0. 0.2 0.4]重要辨析: np.multiply 执行的是元素对应相乘(Element-wise multiplication),它与Python的*运算符在NumPy数组上的行为完全一致。这与线性代数中的矩阵乘法(Matrix multiplication)是截然不同的操作。矩阵乘法在NumPy中通常使用 np.dot、@运算符或 np.matmul 函数来实现。初学者务必注意区分,避免混淆。
📝 动手练一练
- 概念辨析:假设有两个二维数组
A(形状 2x3) 和B(形状 3x2)。A * B和A @ B这两个操作,哪个能成功执行?它们分别代表什么类型的运算?
👉 点击查看参考答案
A * B:这个操作会失败。注意元素级乘法并非要求形状严格相同——只要满足广播规则即可(如本节示例中 (3,1) 数组与 (3,) 向量相乘就靠广播成功);但这一对形状恰好不兼容:按广播规则从末尾维度对齐时,A的末维 3 与B的末维 2 既不相等、也不是 1,无法广播到共同形状,因此报错。A @ B:这个操作可以成功执行。@运算符代表矩阵乘法。矩阵乘法要求第一个数组的列数(3)等于第二个数组的行数(3),因此A(2x3) @ B(3x2)是有效的,结果将是一个形状为 (2, 2) 的新数组。
- 代码实战:创建一个包含
[1.2, 2.7, 3.5, 4.1]的NumPy数组。请编写代码,分别计算:- 每个元素平方根(
np.sqrt)的结果。 - 每个元素以2为底的对数(
np.log2)的结果。
- 每个元素平方根(
👉 点击查看参考答案
import numpy as np
arr = np.array([1.2, 2.7, 3.5, 4.1])
print("原始数组:", arr)
print("平方根:", np.sqrt(arr))
print("以2为底的对数:", np.log2(arr))运行结果示例(具体小数位可能因环境略有差异):
原始数组: [1.2 2.7 3.5 4.1]
平方根: [1.09544512 1.64316767 1.87082869 2.02484567]
以2为底的对数: [0.26303441 1.43295941 1.80735492 2.03562391]本章小结
本节我们通过实战深刻体会了NumPy两大核心特性带来的好处:
- 向量化:用简洁的数组表达式替代冗长的循环,不仅提升了代码的可读性,更借助底层优化获得了数十倍乃至更高的性能提升,是数据分析中必须掌握的核心写法。
- 广播:让形状不同的数组进行算术运算成为可能,规则清晰且自动执行,极大地增加了代码的灵活性和表达力。
- 通用函数:提供了丰富的、高性能的元素级运算工具,从基本的四则运算到复杂的数学函数,是构建向量化计算的基础模块。
📋 行动清单
- 在遇到需要对数组每个元素进行操作时,第一反应应是“能否用向量化表达式或通用函数实现?”,尽量避免手动编写Python循环。
- 练习使用
np.ceil,np.floor,np.sqrt,np.log等常用通用函数处理小型示例数组,熟悉其输入输出。 - 明确区分
*(元素乘) 与@或np.dot(矩阵乘) 的使用场景,避免在后续线性代数运算中出错。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载本节课件:向量化及通用函数PDF · 260KB
- 下载全套课件打包(第1-5章)ZIP · 12.8MB
- 下载全套课件打包(第6-8章)ZIP · 15MB
- 下载实战数据集:AppleStore 应用商城分析ZIP · 329KB
- 下载实战数据集:女性服装电商分析ZIP · 2.8MB
- 下载Python 数据分析环境搭建指南PDF · 2MB
- 下载Scrapy 安装教程PDF · 12.7MB
- 下载附加实战项目:AppleStore 应用商城数据分析ZIP · 0.3MB · ipynb + CSV 数据
- 下载附加实战项目:银行电话营销数据分析ZIP · 0.4MB · ipynb + CSV 数据
- 下载附加实战项目:女性服装电商评论数据分析ZIP · 2.7MB · ipynb + CSV 数据
- 下载附加实战项目:美国化学学会杂志数据分析ZIP · 34.2MB · ipynb + SQLite 数据库