
第 5 章NumPy 基础 — 数组与运算本章是数据分析教程的第 5 章。从本章开始我们正式进入数值计算的世界。NumPyNumerical Python是 Python 数据分析生态的基石几乎所有数据科学库Pandas、Scikit-learn、TensorFlow 等都建立在它之上。 摘要本章系统讲解 NumPy 的核心用法。首先介绍 NumPy 的定位及其相比 Python 列表的性能优势随后讲解 5 种创建数组的方式array、zeros、ones、arange、linspace与 5 个核心属性shape、ndim、size、dtype、itemsize接着介绍数据类型转换、算术/矩阵运算与通用函数以及统计函数和按轴统计最后通过学生成绩统计实战串联本章全部知识点。5.1 NumPy 是什么为什么要用它5.1.1 NumPy 的定位NumPy 是 Python 中用于科学计算的核心库它提供了一个高性能的多维数组对象ndarray以及大量用于操作这些数组的函数。简单来说Python 自带的列表list很灵活能装任何类型的数据但速度慢。NumPy 的数组ndarray只能装同一种类型的数据但速度快得惊人。5.1.2 NumPy 与 Python 列表的对比对比项Python 列表NumPy 数组元素类型可以混合不同类型必须同类型运算方式逐元素需写循环直接整体运算内存占用较大每个元素都是对象紧凑连续内存我们用一段代码直观感受性能差异对一百万个数字求平方。importnumpyasnpimporttime# 方式一用 Python 列表python_listlist(range(1_000_000))starttime.time()squared_list[x**2forxinpython_list]list_timetime.time()-start# 方式二用 NumPy 数组np_arraynp.arange(1_000_000)starttime.time()squared_arraynp_array**2array_timetime.time()-startprint(fPython 列表耗时{list_time:.4f}秒)print(fNumPy 数组耗时{array_time:.4f}秒)print(fNumPy 是列表的{list_time/array_time:.1f}倍快)输出Python 列表耗时0.1744 秒 NumPy 数组耗时0.0015 秒 NumPy 是列表的 116.3 倍快 提示实际倍数会因电脑配置、NumPy 版本不同而有差异但 NumPy 通常比纯 Python 快几十倍到上百倍。这就是数据分析必学 NumPy 的原因。 提示安装 NumPy 只需在终端运行pip install numpy。如果你安装了 AnacondaNumPy 已经自带了。5.2 创建数组多种方式NumPy 最核心的对象是ndarrayN-dimensional arrayN 维数组。创建数组有很多种方式我们逐一学习。5.2.1 从列表创建np.array()最直接的方式把一个 Python 列表或嵌套列表转换成数组。importnumpyasnp# 一维数组anp.array([1,2,3,4,5])print(一维数组,a)# 二维数组嵌套列表bnp.array([[1,2,3],[4,5,6]])print(二维数组\n,b)# 指定数据类型cnp.array([1,2,3],dtypefloat)print(浮点型数组,c)输出一维数组 [1 2 3 4 5] 二维数组 [[1 2 3] [4 5 6]] 浮点型数组 [1. 2. 3.]5.2.2 创建全零数组np.zeros(生成所有元素为 0 的数组常用于“初始化”一个容器。容器。importnumpyasnp z1np.zeros(5)# 一维5 个 0z2np.zeros((2,3))# 二维2 行 3 列print(z1:,z1)print(z2:\n,z2)输出z1: [0. 0. 0. 0. 0.] z2: [[0. 0. 0.] [0. 0. 0.]]5.2.3 创建全一数组np.ones()importnumpyasnp o1np.ones(4)o2np.ones((3,2),dtypeint)# 指定整型print(o1:,o1)print(o2:\n,o2)输出o1: [1. 1. 1. 1.] o2: [[1 1] [1 1] [1 1]]5.2.4 范围数组np.arange()类似 Python 的range()但返回的是数组且支持小数步长。importnumpyasnpprint(np.arange(0,10))# 0 到 9print(np.arange(0,10,2))# 步长 2print(np.arange(0,1,0.2))# 小数步长输出[0 1 2 3 4 5 6 7 8 9] [0 2 4 6 8] [0. 0.2 0.4 0.6 0.8]5.2.5 等差数列np.linspace()在指定区间内生成均匀分布的指定数量的点常用于绘制曲线坐标。importnumpyasnp# 0 到 1 之间等分 5 个点print(np.linspace(0,1,5))# 包含端点默认 Trueprint(np.linspace(0,10,3))输出[0. 0.25 0.5 0.75 1. ] [ 0. 5. 10.]函数作用典型参数np.array()从列表创建数据列表np.zeros()全零数组形状np.ones()全一数组形状np.arange()范围数组start, stop, stepnp.linspace()等差数列start, stop, num 提示arange和linspace的区别——arange指定步长linspace指定个数。当你想控制采样数量时用linspace想按固定间隔取数时用arange。5.3 数组属性shape, dtype, ndim, si创建数组后我们需要了解它的“形状”和“身份”。身份。这些属性是后续操作的基础。importnumpyasnp arrnp.array([[1,2,3],[4,5,6]])print(数组本身\n,arr)print(形状 shape :,arr.shape)# (2, 3) 2 行 3 列print(维度 ndim :,arr.ndim)# 2 维print(元素个数 size :,arr.size)# 6 个print(数据类型 dtype:,arr.dtype)# intprint(每项字节数 :,arr.itemsize)输出数组本身 [[1 2 3] [4 5 6]] 形状 shape : (2, 3) 维度 ndim : 2 元素个数 size : 6 数据类型 dtype: int32 每项字节数 : 4属性含义示例值shape各维度大小元组(2, 3)ndim维度数量2size元素总数6dtype元素数据类型int32itemsize每个元素占用字节数4 提示dtype显示为int32还是int64取决于操作系统。64 位系统通常默认int64或int32这是正常的。5.4 数组的数据类型与类型转换NumPy 数组要求所有元素同类型因此数据类型很重要。常见类型如下类型说明简写int8/16/32/64整数不同位数i1/i2/i4/i8uint8无符号整数0~255u1float16/32/64浮点数f2/f4/f8bool布尔型?complex复数c8/c16str字符串U5.4.1 创建时指定类型importnumpyasnp anp.array([1,2,3],dtypenp.float64)bnp.array([1.5,2.7],dtypenp.int32)# 小数会被截断print(a:,a,a.dtype)print(b:,b,b.dtype)输出a: [1. 2. 3.] float64 b: [1 2] int325.4.2 类型转换astype()转换已有数组类型用astype()它会返回新数组不修改原数组。importnumpyasnp arrnp.array([1.7,2.3,3.9])arr_intarr.astype(np.int32)# 截断小数print(原数组,arr)print(转换后,arr_int)输出原数组 [1.7 2.3 3.9] 转换后 [1 2 3] 提示astype做的是截断而非四舍五入。若想四舍五入先np.round()再转换。5.5 基本运算算术运算、矩阵运算、通用函数5.5.1 算术运算逐元素NumPy 的算术运算都是逐元素进行的这叫“向量化运算”。量化运算。importnumpyasnp anp.array([1,2,3])bnp.array([4,5,6])print(加法 a b :,ab)print(减法 a - b :,a-b)print(乘法 a * b :,a*b)# 注意这是逐元素相乘不是矩阵乘法print(除法 a / b :,a/b)print(幂运算 a ** 2:,a**2)print(标量运算 a * 10:,a*10)输出加法 a b : [5 7 9] 减法 a - b : [-3 -3 -3] 乘法 a * b : [ 4 10 18] 除法 a / b : [0.25 0.4 0.5 ] 幂运算 a ** 2: [1 4 9] 标量运算 a * 10: [10 20 30]**注意区分“逐元素乘法*”和“矩阵乘法或dot”dot这是初学者最容易混淆的地方。a * b是对应位置相乘a b才是线性代数里的矩阵乘法。5.5.2 矩阵运算二维数组的矩阵乘法用运算符或np.dot()。importnumpyasnp Anp.array([[1,2],[3,4]])Bnp.array([[5,6],[7,8]])print(逐元素相乘 A * B:\n,A*B)print(矩阵乘法 A B:\n,A B)print(点积 np.dot(A, B):\n,np.dot(A,B))输出逐元素相乘 A * B: [[ 5 12] [21 32]] 矩阵乘法 A B: [[19 22] [43 50]] 点积 np.dot(A, B): [[19 22] [43 50]]5.5.3 通用函数uNumPy 提供大量“通用函数”universal function对数组每个元素施加数学运算底层用 C 实现非常快。非常快**。importnumpyasnp anp.array([0,np.pi/2,np.pi])print(sin:,np.sin(a))print(cos:,np.cos(a))print(exp:,np.exp(np.array([0,1,2])))print(sqrt:,np.sqrt(np.array([1,4,9])))print(log:,np.log(np.array([1,np.e,np.e**2])))print(abs:,np.abs(np.array([-1,-2,3])))输出sin: [0.0000000e00 1.0000000e00 1.2246468e-16] cos: [ 1.000000e00 6.123234e-17 -1.000000e00] exp: [1. 2.71828183 7.3890561 ] sqrt: [1. 2. 3.] log: [0. 1. 2.] abs: [1 2 3] 提示sin(pi)理论上应为 0但输出是一个极小的数1.22e-16这是浮点数精度造成的属正常现象。5.6 统计函数sum, mean, std, min, max, argmin, argmax数据分析离不开统计。NumPy 提供了一组聚合函数。importnumpyasnp anp.array([[1,2,3],[4,5,6]])print(总和 sum :,a.sum())print(均值 mean :,a.mean())print(标准差 std:,a.std())print(方差 var :,a.var())print(最小 min :,a.min())print(最大 max :,a.max())print(最小索引 :,a.argmin())# 展平后的索引print(最大索引 :,a.argmax())输出总和 sum : 21 均值 mean : 3.5 标准差 std: 1.707825127659933 方差 var : 2.9166666666666665 最小 min : 1 最大 max : 6 最小索引 : 0 最大索引 : 55.6.1 按轴统计ax二维数组有“行”和“列”两个轴。和列两个轴。axis0沿列方向跨行统计axis1沿行方向跨列统计。importnumpyasnp anp.array([[1,2,3],[4,5,6]])print(每列之和 axis0:,a.sum(axis0))# [5 7 9]print(每行之和 axis1:,a.sum(axis1))# [6 15]print(每列均值 axis0:,a.mean(axis0))# [2.5 3.5 4.5]输出每列之和 axis0: [5 7 9] 每行之和 axis1: [ 6 15] 每列均值 axis0: [2.5 3.5 4.5]*记忆技巧——axis0会“消掉第 0 个维度”行被合并维度行被合并结果形状等于去掉第 0 维后的形状即列数。axis1同理消掉列结果形状是行数。5.7 数组变形reshape, flatten, transpose5.7.1 reshape改变形状reshape不改变数据只重新组织形状要求元素总数一致。importnumpyasnp anp.arange(12)print(原数组,a)print(变形为 3x4\n,a.reshape(3,4))print(变形为 2x2x3\n,a.reshape(2,2,3))输出原数组 [ 0 1 2 3 4 5 6 7 8 9 10 11] 变形为 3x4 [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] 变形为 2x2x3 [[[ 0 1 2] [ 3 4 5]] [[ 6 7 8] [ 9 10 11]]]5.7.2 flatten 与 ravel展平把多维数组压成一维。importnumpyasnp anp.array([[1,2,3],[4,5,6]])print(flatten:,a.flatten())print(ravel :,a.ravel())输出flatten: [1 2 3 4 5 6] ravel : [1 2 3 4 5 6] 提示flatten返回副本修改不影响原数组ravel返回视图修改可能影响原数组。一般只读用ravel更省内存。5.7.3 transpose转置行列交换常用于矩阵转置。importnumpyasnp anp.array([[1,2,3],[4,5,6]])print(原形状,a.shape)print(转置后形状,a.T.shape)print(转置结果\n,a.T)输出原形状 (2, 3) 转置后形状 (3, 2) 转置结果 [[1 4] [2 5] [3 6]]5.8 实战用 NumPy 计算学生成绩统计场景某班 5 名学生3 门科目语文、数学、英语我们用 NumPy 完成统计。importnumpyasnp# 行学生列科目scoresnp.array([[85,92,78],# 学生1[76,88,95],# 学生2[90,79,82],# 学生3[65,70,88],# 学生4[88,95,91],# 学生5])# 1. 每位学生的总分student_totalscores.sum(axis1)print(每位学生总分,student_total)# 2. 每位学生的平均分student_avgscores.mean(axis1)print(每位学生平均分,np.round(student_avg,2))# 3. 每门科目的平均分subject_avgscores.mean(axis0)print(每门科目平均分,np.round(subject_avg,2))# 4. 全班最高分与最低分print(全班最高分,scores.max())print(全班最低分,scores.min())# 5. 找出数学最高分的学生数学是第 1 列mathscores[:,1]top_student_idxmath.argmax()print(f数学最高分学生是第{top_student_idx1}位分数{math[top_student_idx]})# 6. 成绩标准化Z-score(x - 均值) / 标准差meanscores.mean(axis0)stdscores.std(axis0)normalized(scores-mean)/stdprint(标准化后\n,np.round(normalized,2))输出每位学生总分 [255 259 251 223 274] 每位学生平均分 [85. 86.33 83.67 74.33 91.33] 每门科目平均分 [80.8 84.8 86.8] 全班最高分 95 全班最低分 65 数学最高分学生是第 5 位分数 95 标准化后 [[ 0.45 0.79 -1.44] [-0.52 0.35 1.34] [ 1. -0.63 -0.79] [-1.71 -1.62 0.2 ] [ 0.78 1.11 0.69]] 提示标准化Z-score是数据预处理常用手段能消除量纲差异让不同科目的成绩可比较。本章小结本章我们从零认识了 NumPy掌握了NumPy 比 Python 列表快得多是数据分析的基础工具。5 种创建数组的方式array、zeros、ones、arange、linspace。数组的 5 个核心属性shape、ndim、size、dtype、itemsize。数据类型与astype转换。算术运算、矩阵运算/dot、通用函数ufunc。统计函数与按轴统计axis参数。变形reshape、flatten/ravel、transpose。