ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

50个Numpy核心函数实战指南:从数组创建到向量化运算

50个Numpy核心函数实战指南:从数组创建到向量化运算 1. 项目概述为什么你需要这50个Numpy函数如果你在用Python做数据分析、机器学习或者科学计算Numpy这个库你肯定绕不开。它就像是这些领域的“基建狂魔”几乎所有上层工具Pandas, Scikit-learn, TensorFlow, PyTorch都建立在它的基础之上。但很多朋友包括我刚开始的时候对Numpy的认知可能就停留在“一个能快速算数组的库”会用np.array、np.mean、np.sum就觉得差不多了。直到我在处理一个几百万行的数据集时因为一个简单的操作用了低效的Python循环程序跑了半个多小时还没出结果而同事用Numpy的一行向量化操作几秒钟就搞定了。那一刻我才真正意识到深度掌握Numpy的核心函数不是“锦上添花”而是“雪中送炭”。它能直接决定你代码的效率是“分钟级”还是“小时级”甚至影响整个项目的可行性。网上教程很多但要么太散要么太深。所以我想结合自己这些年踩过的坑和总结的经验为你梳理一份**“即查即用”的Numpy核心函数实战指南**。我不会像官方文档那样罗列所有参数而是聚焦在最常用、最能解决实际问题的50个函数上告诉你每个函数“是什么”、“什么时候用”、“怎么用最好”以及那些官方文档里不会写的“坑”和“骚操作”。无论你是刚入门的数据科学新手还是想优化代码性能的老手这份清单都能帮你把Numpy这个工具用得更加得心应手。我们直接进入正题从最基础的数组创建开始。2. 数组的诞生10个你必须掌握的创建与生成函数万事开头难在Numpy里万事开头是“创建数组”。这部分函数你用到的频率极高选对方法能让你的代码既简洁又高效。2.1 基础创建从列表到数组np.array()这是你的入门函数也是最常用的。它把Python的列表、元组等序列对象转换成Numpy的ndarray对象。import numpy as np list_data [1, 2, 3, 4, 5] arr np.array(list_data) print(arr) # 输出[1 2 3 4 5] print(type(arr)) # 输出class numpy.ndarray注意np.array()会尝试为所有元素推断一个统一的数据类型dtype。如果你的列表里混有整数和浮点数它会全部提升为浮点数float64。如果混有数字和字符串则会全部变成字符串U类型。在创建时显式指定dtype是个好习惯比如np.array([1, 2, 3], dtypenp.float32)。np.asarray()和array()功能几乎一样主要区别在于当输入已经是ndarray时asarray默认不会复制而是直接返回原数组的视图除非dtype不匹配而array默认总会复制一份。在确保输入是数组且不想产生额外拷贝时用asarray更高效。a np.array([1,2,3]) b np.asarray(a) # b是a的视图共享内存 c np.array(a) # c是a的副本不共享内存 a[0] 999 print(b) # 输出[999 2 3] print(c) # 输出[1 2 3]2.2 快速生成告别循环的模板数组当你需要一些有规律的“模板”数组时下面这些函数能让你一行代码搞定。np.arange(start, stop, step)类似于Python的range()但生成的是数组。它生成一个从start到stop不包括步长为step的均匀间隔数组。np.arange(0, 10, 2) # 输出[0 2 4 6 8] np.arange(5) # 输出[0 1 2 3 4]默认start0step1np.linspace(start, stop, num)这个函数我用的频率比arange还高。它生成在start和stop之间等间隔的num个点。关键在于它包含终点stop。这在绘制函数图形、进行数值积分时特别有用因为你通常需要包含区间端点。# 在0到1之间生成5个点 points np.linspace(0, 1, 5) print(points) # 输出[0. 0.25 0.5 0.75 1.]np.zeros(shape)和np.ones(shape)生成全0或全1的数组。shape可以是一个整数一维也可以是元组多维。初始化权重矩阵、掩码mask或者作为累加器的起点时它们是你的首选。np.zeros(3) # 输出[0. 0. 0.] np.ones((2, 3)) # 输出 2x3 的全1矩阵 # [[1. 1. 1.] # [1. 1. 1.]]np.full(shape, fill_value)生成一个所有元素都是指定填充值fill_value的数组。比先用ones再乘一个数更直观、更高效。np.full((2,2), 7) # 输出 2x2 的全7矩阵 # [[7 7] # [7 7]]np.eye(N)和np.identity(N)生成N x N的单位矩阵。eye可以生成非方阵的对角矩阵通过M参数指定行数而identity只能生成方阵。在线性代数运算、初始化某些变换矩阵时必不可少。np.eye(3) # [[1. 0. 0.] # [0. 1. 0.] # [0. 0. 1.]] np.eye(2, M3) # 2行3列对角线为1 # [[1. 0. 0.] # [0. 1. 0.]]np.random子模块 这是生成随机数的宝库虽然它是一个模块但里面的函数太重要了必须提。np.random.rand(d0, d1, ...)生成[0, 1)均匀分布的随机数。np.random.randn(d0, d1, ...)生成标准正态分布均值为0标准差为1的随机数。初始化神经网络权重最常用这个。np.random.randint(low, high, size)生成指定范围内的随机整数。np.random.seed()设置随机种子保证结果可复现。在机器学习中为了实验的可比性务必在开头设置随机种子。np.random.seed(42) # 生命、宇宙及万物的答案 data np.random.randn(100, 10) # 100个样本10个特征符合正态分布2.3 从已有数据构造np.copy(a)创建数组a的深拷贝副本。当你明确需要一份独立的数据不想修改原数组时使用。记住简单的赋值b a只是创建了一个新的引用它们指向同一块内存。np.empty(shape)分配指定形状的新数组但不会初始化它的值里面的值是内存的当前状态可能是任意值。它比zeros或ones稍快因为跳过了初始化步骤。但使用时必须非常小心确保在读取之前会填充所有值。我通常只在性能瓶颈明确且后续立刻全覆盖赋值时才用它。3. 数组的观察与洞察8个诊断与属性函数数组创建好了你得先“认识”它。这些函数帮你快速了解数组的“长相”和“内涵”。3.1 形状与结构洞察a.shape这不是函数是数组最重要的属性之一返回一个表示数组维度的元组。(5,)是一维5个元素(3,4)是3行4列(2,3,4)是2个3x4的矩阵。a.ndim属性返回数组的维数轴的数量。一个数字是0维标量一维数组ndim1矩阵ndim2以此类推。a.size属性返回数组的总元素个数等于shape各维度的乘积。len(a)Python内置函数对Numpy数组它返回第一维的长度shape[0]。对于一维数组len(a)等于a.size对于二维数组len(a)等于行数。arr_2d np.array([[1,2,3],[4,5,6]]) print(arr_2d.shape) # (2, 3) print(arr_2d.ndim) # 2 print(arr_2d.size) # 6 print(len(arr_2d)) # 23.2 数据类型与内存查看a.dtype属性返回数组元素的数据类型。这是Numpy高效的核心之一。常见的有np.int32,np.int64,np.float32,np.float64,np.bool_,U10长度小于10的字符串等。处理大型数据时使用float32代替float64可以节省一半内存在深度学习中很常见。a.itemsize属性返回数组中每个元素占用的字节数。例如float64的itemsize是8。a.nbytes属性返回整个数组数据部分占用的总字节数等于a.size * a.itemsize。监控内存占用时非常有用。arr np.array([1,2,3], dtypenp.float32) print(arr.dtype) # float32 print(arr.itemsize) # 4 print(arr.nbytes) # 12 (3个元素 * 4字节)np.info(object)一个不太常用但有时很有用的函数它会打印出关于该对象如函数、模块的详细信息包括文档字符串。当你忘记某个函数怎么用时在交互环境里np.info(np.convolve)比去浏览器查文档快。4. 数组的变形与手术12个重塑与操作函数数据很少一开始就是想要的形状你需要对它们进行“整形手术”。4.1 改变形状重塑与打平a.reshape(newshape)和np.reshape(a, newshape)改变数组形状但不改变数据。newshape中的-1是一个通配符表示“让Numpy自动计算这一维的大小”。这是我最爱的功能之一。arr np.arange(12) # [0 1 2 ... 11] arr_3x4 arr.reshape((3,4)) # 变成3行4列 arr_2xN arr.reshape((2, -1)) # 变成2行列数自动计算为6重要提示reshape通常返回一个视图view与原数组共享数据。修改视图会影响原数组。除非新的形状不连续需要通过拷贝才能实现否则不会发生数据复制。a.flatten()和a.ravel()都是将多维数组“拉平”成一维数组。flatten()总是返回一份拷贝copy。你对返回数组的修改不会影响原数组。ravel()尽可能返回一个视图view。如果可能它返回的数据和原数组共享内存修改会影响原数组。它更高效。arr np.array([[1,2],[3,4]]) flat_copy arr.flatten() flat_view arr.ravel() flat_copy[0] 99 flat_view[0] 100 print(arr) # [[100 2] [3 4]]只有ravel的修改影响了原数组实操心得如果你只是临时需要一维数据做计算且不想改变原数组用ravel()。如果你需要一份独立的、修改不影响原数组的一维数据副本用flatten()。a.resize(new_shape)与reshape不同resize会直接修改数组本身的形状。如果新形状需要的元素比原来多多出的部分会用0填充默认行为可改如果比原来少则截断。这是一个原地操作。a.T或np.transpose(a)数组的转置。对于二维数组就是行变列列变行。这是线性代数中的基本操作。arr np.array([[1,2,3],[4,5,6]]) print(arr.T) # [[1 4] # [2 5] # [3 6]]np.swapaxes(a, axis1, axis2)交换数组的两个轴。比transpose更通用。例如一个形状为(a, b, c)的数组swapaxes(0,2)会得到形状(c, b, a)。4.2 连接与分割数组的合并与拆分np.concatenate((a1, a2, ...), axis0)沿指定轴连接一系列形状相同除了连接轴的数组。这是最通用的连接函数。a np.array([[1,2],[3,4]]) b np.array([[5,6]]) # 沿第0轴行方向连接要求列数相同 c np.concatenate((a,b), axis0) # c.shape (3,2) # 沿第1轴列方向连接要求行数相同 d np.array([[7],[8]]) e np.concatenate((a,d), axis1) # e.shape (2,3)np.vstack((a1, a2, ...))和np.hstack((a1, a2, ...))分别是垂直堆叠沿行方向axis0和水平堆叠沿列方向axis1的便捷函数。它们实际上是concatenate的包装但更直观。np.split(ary, indices_or_sections, axis0)将数组沿指定轴分割成多个子数组。indices_or_sections可以是一个整数N表示分成N等份也可以是一个索引列表表示在哪些位置切分。arr np.arange(10) # [0 1 2 ... 9] result np.split(arr, [3, 5]) # 在索引3和5处切分 # 得到三个数组[0 1 2], [3 4], [5 6 7 8 9]np.vsplit(ary, indices_or_sections)和np.hsplit(ary, indices_or_sections)分别是垂直分割按行和水平分割按列的便捷函数。4.3 增加与删除维度np.newaxis或None这是一个别名用于在切片中增加一个新轴。在处理一维数组需要变成二维例如从特征向量变成样本矩阵时非常有用。arr np.array([1,2,3]) print(arr.shape) # (3,) row_vec arr[np.newaxis, :] # 或 arr[None, :] print(row_vec.shape) # (1, 3) 行向量 col_vec arr[:, np.newaxis] # 或 arr[:, None] print(col_vec.shape) # (3, 1) 列向量np.squeeze(a, axisNone)从数组形状中删除单维度条目。即把shape中为1的维度去掉。如果不指定axis则删除所有单维度。指定axis则只删除该轴的单维度。arr np.array([[[1,2,3]]]) # shape (1,1,3) squeezed np.squeeze(arr) print(squeezed.shape) # (3,) print(squeezed) # [1 2 3]5. 数组的智慧15个统计与数学函数Numpy的“内力”体现在这些数学和统计函数上。它们都是向量化操作速度快到飞起。5.1 基础统计描述你的数据np.sum(a, axisNone)计算数组元素的和。axis参数是核心axis0对每列求和压缩行axis1对每行求和压缩列axisNone对所有元素求和。记住指定的轴会被移除。np.mean(a, axisNone),np.median(a, axisNone),np.std(a, axisNone),np.var(a, axisNone)分别计算均值、中位数、标准差和方差。axis参数用法同上。std和var还有一个重要参数ddofDelta Degrees of Freedom在计算样本标准差/方差时通常设置ddof1无偏估计。data np.array([[1,2,3],[4,5,6]]) print(np.mean(data, axis0)) # 沿行压缩求每列的均值[2.5 3.5 4.5] print(np.std(data, axis1, ddof1)) # 沿列压缩求每行的样本标准差[1. 1.]np.min(a, axisNone),np.max(a, axisNone),np.argmin(a, axisNone),np.argmax(a, axisNone)求最小/最大值以及最小/最大值所在的索引。argmin/argmax在寻找最佳参数、分类结果时极其有用。np.percentile(a, q, axisNone)计算百分位数。q可以是0到100之间的数或数组。例如np.percentile(data, 50)就是中位数np.percentile(data, [25, 75])就是上下四分位数。np.unique(ar, return_countsFalse)返回数组中的唯一值已排序。如果return_countsTrue还会返回每个唯一值出现的次数。这是做数据清洗、分类统计的利器。arr np.array([2,1,2,3,1,4,3]) vals, counts np.unique(arr, return_countsTrue) print(vals) # [1 2 3 4] print(counts) # [2 2 2 1]5.2 基础数学运算这些函数对标Python的内置数学函数但是向量化的。np.sqrt(x),np.square(x),np.exp(x),np.log(x),np.log10(x),np.abs(x)平方根、平方、指数、自然对数、10为底对数、绝对值。都是逐元素element-wise操作。np.sin(x),np.cos(x),np.tan(x)三角函数输入是弧度制。np.add(x1, x2),np.subtract(x1, x2),np.multiply(x1, x2),np.divide(x1, x2),np.power(x1, x2)逐元素的加、减、乘、除、幂运算。通常我们直接用运算符,-,*,/,**但函数形式在特定场合如作为参数传递有用。np.dot(a, b)矩阵乘法或向量点积。对于二维数组就是标准的矩阵乘法。对于一维数组是向量内积。这是线性代数的核心。A np.array([[1,2],[3,4]]) B np.array([[5,6],[7,8]]) C np.dot(A, B) # 矩阵乘法 # 等价于 A B (Python 3.5 支持) v1 np.array([1,2,3]) v2 np.array([4,5,6]) dot_product np.dot(v1, v2) # 1*4 2*5 3*6 32踩坑记录np.dot和*或np.multiply有本质区别。*是逐元素相乘要求两个数组形状完全相同或满足广播规则。np.dot是矩阵乘法要求第一个数组的列数等于第二个数组的行数。混淆两者是新手常犯的错误。6. 高效索引与切片5个高级数据选取技巧Numpy的索引功能强大到令人发指是高效数据处理的基石。6.1 基础切片与布尔索引基础切片a[start:stop:step]和Python列表类似但支持多维a[行切片 列切片]并且返回的是视图。布尔索引Boolean Indexing是我认为最实用的功能之一。它允许你使用一个布尔值数组来索引目标数组选出所有对应布尔值为True的元素。arr np.array([1, -2, 3, -4, 5]) bool_idx arr 0 print(bool_idx) # [ True False True False True] print(arr[bool_idx]) # [1 3 5] # 更简洁的写法 print(arr[arr 0]) # [1 3 5] # 多条件组合选出大于0且是偶数的元素 print(arr[(arr 0) (arr % 2 0)]) # [] 注意条件要用括号括起来用、|、~代替and、or、not6.2 花式索引与组合整数数组索引Fancy Indexing使用整数数组来索引。它总是返回数据的副本而不是视图。arr np.array([10, 20, 30, 40, 50]) indices [0, 2, 4] print(arr[indices]) # [10 30 50] # 二维数组更强大 arr_2d np.array([[1,2,3],[4,5,6],[7,8,9]]) print(arr_2d[[0, 2], [0, 1]]) # [1 8] 取(0,0)和(2,1)两个位置的元素np.where(condition, [x, y])三元表达式。如果只传入condition返回满足条件非零元素的坐标索引数组。如果传入condition, x, y则根据条件从x或y中选择元素来组成新数组。arr np.array([1, 2, 3, 4, 5]) # 用法1找索引 indices np.where(arr 2) print(indices) # (array([2, 3, 4]),) 返回一个元组 print(arr[indices]) # [3 4 5] # 用法2三元替换 new_arr np.where(arr 2, arr, -1) # 大于2的元素保留否则替换为-1 print(new_arr) # [-1 -1 3 4 5]np.take(a, indices, axisNone)沿指定轴从数组中取出指定索引处的元素。可以看作是更安全、支持轴参数的a[indices]。特别是当indices可能越界时可以通过mode参数控制行为如‘wrap’、‘clip’。np.choose(a, choices)根据数组a中的索引0到n-1从choices序列中选取元素组成新数组。a和输出的形状一致choices可以是一个数组列表也可以是一个多维数组。choices [[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23]] a np.array([2, 1, 0, 2]) result np.choose(a, choices) print(result) # [20 11 2 23] 从choices的第2、1、0、2行分别取第0、1、2、3列7. 广播与向量化理解Numpy的灵魂这不是一个具体的函数但它是理解前面所有函数高效运行的基础必须单独拿出来讲。广播Broadcasting是Numpy对不同形状数组进行算术运算的一套规则。它的核心思想是将较小的数组“广播”到较大数组的形状以便它们具有兼容的形状进行逐元素运算。规则简述从后往前比对形状如果两个数组的维度数不同将维度较小的数组的形状前面补1直到维度数相同。对于每一个维度如果两个数组在该维度的大小相同或者其中一个为1则它们是兼容的。如果所有维度都兼容则可以广播。在运算时大小为1的维度会被“拉伸”以匹配另一个数组对应维度的大小。如果任何一个维度上大小既不相等也不为1则广播失败。# 例子1标量与数组标量被视为0维被广播到数组的每个元素 arr np.array([1,2,3]) result arr 5 # 相当于 [1,2,3] [5,5,5] print(result) # [6 7 8] # 例子2一维数组与二维数组常用 A np.array([[1,2,3],[4,5,6]]) # shape (2,3) b np.array([10, 20, 30]) # shape (3,) # b被广播为 [[10,20,30], [10,20,30]]shape (2,3) C A b print(C) # [[11 22 33] # [14 25 36]] # 例子3两个都需要广播 A np.array([[1],[2],[3]]) # shape (3,1) B np.array([10, 20, 30]) # shape (3,) # A被广播为 [[1,1,1], [2,2,2], [3,3,3]]shape (3,3) # B被广播为 [[10,20,30], [10,20,30], [10,20,30]]shape (3,3) C A B print(C) # [[11 21 31] # [12 22 32] # [13 23 33]]向量化Vectorization就是利用广播和Numpy的通用函数ufunc将本需要显式循环的操作转化为对整个数组的批量操作。这是Numpy比纯Python循环快成百上千倍的根源。# 低效的Python循环 python_list list(range(1000000)) squares [] for x in python_list: squares.append(x ** 2) # 高效的Numpy向量化 np_arr np.arange(1000000) squares_np np_arr ** 2 # 瞬间完成实操心得养成“向量化思维”。每当你想写一个for循环遍历数组元素时先停下来想想能不能用Numpy的广播和向量化操作来实现绝大多数情况下答案是肯定的。8. 实战避坑与性能优化5个函数与核心技巧掌握了函数还要知道怎么用得好、不出错。这部分分享一些血泪教训。8.1 视图与副本内存管理的陷阱这是Numpy新手最大的坑之一。很多操作如切片、reshape、ravel()、T返回的是视图view即与原数组共享数据内存。而有些操作如flatten()、copy()、花式索引返回的是副本copy。arr np.arange(10) view arr[3:7] # 切片是视图 view[0] 999 print(arr) # [0 1 2 999 4 5 6 7 8 9] 原数组被改了 arr2 np.arange(10) copy arr2[[3,4,5,6]] # 花式索引是副本 copy[0] 999 print(arr2) # [0 1 2 3 4 5 6 7 8 9] 原数组没变如何判断查看数组的base属性。如果a.base is None则a是数据的所有者可能是原始数组或副本。如果a.base是另一个数组则a是视图。黄金法则如果你不确定一个操作是否会产生副本并且你不想修改原数据就显式地使用.copy()。8.2 数据类型dtype导致的诡异错误Numpy是强类型的。混合类型的运算可能导致意想不到的结果或精度损失。int_arr np.array([1, 2, 3], dtypenp.int32) float_arr np.array([1.5, 2.5, 3.5]) result int_arr float_arr print(result.dtype) # float64int被提升为float了 # 更隐蔽的整数除法 arr np.array([1, 2, 3, 4]) print(arr / 2) # [0.5 1. 1.5 2.] 在Python3和Numpy中除法默认产生浮点 print(arr // 2) # [0 1 1 2] 地板除得到整数建议在创建数组和处理数据时时刻留意dtype。对于大型数据集使用float32代替float64可以显著节省内存和计算时间在精度允许的情况下。使用astype()方法进行类型转换但注意这会创建副本。8.3 性能杀手在Numpy中避免Python循环重申一遍但值得用函数来强调。np.vectorize(func)这个函数有点误导性。它并不是真正的向量化而是将一个接受标量输入、返回标量输出的Python函数转换成一个可以接受数组输入的“伪”向量化函数。底层它仍然是用Python循环实现的性能提升有限只是提供了便利的接口。真正的性能提升要靠Numpy内置的ufunc和广播。def my_func(x): return x ** 2 1 if x 0 else 0 vfunc np.vectorize(my_func) arr np.random.randn(10000) # 这种方式比纯Python循环稍好但远不如真正的向量化 result vfunc(arr) # 真正的向量化思路使用 np.where result_fast np.where(arr 0, arr ** 2 1, 0)8.4 内存布局np.ascontiguousarray(a)Numpy数组在内存中的存储顺序可以是C顺序行优先或F顺序列优先Fortran风格。大多数操作默认使用C顺序。但某些操作如某些切片、转置可能会产生不连续内存的数组。当需要将数据传递给某些底层是C/C写的库如某些图像处理、深度学习框架的接口时它们可能要求数据在内存中是连续的。这时可以用这个函数来确保。arr np.arange(12).reshape(3,4) slice arr[:, ::2] # 这个切片可能不是连续的 print(slice.flags[C_CONTIGUOUS]) # False contiguous_slice np.ascontiguousarray(slice) print(contiguous_slice.flags[C_CONTIGUOUS]) # True8.5 效率工具np.einsum(subscripts, *operands)爱因斯坦求和约定。这是一个非常强大但语法略显晦涩的函数用于表达多维数组的复杂线性运算如矩阵乘法、迹、对角化、张量收缩等。一旦掌握可以写出非常简洁高效的代码。对于简单的操作可能杀鸡用牛刀但对于复杂的张量运算它往往是最优选择。A np.random.rand(3,4) B np.random.rand(4,5) # 矩阵乘法 C_ij sum_k A_ik * B_kj C_einsum np.einsum(ik,kj-ij, A, B) C_dot np.dot(A, B) print(np.allclose(C_einsum, C_dot)) # True # 计算矩阵的迹 trace sum_i A_ii trace np.einsum(ii, A) # 对于非方阵只对共同维度求和学习einsum需要一些时间但它的回报是巨大的尤其是在深度学习等领域处理高维数据时。9. 融会贯通一个完整的数据处理小案例让我们用一个简单的例子串联使用多个函数。假设我们有一组学生的三门课成绩我们要进行一些分析。import numpy as np np.random.seed(123) # 固定随机种子 # 1. 创建数据20个学生3门课的成绩范围50-100 scores np.random.randint(50, 101, size(20, 3)) print(原始成绩表 (20x3):) print(scores[:5]) # 只看前5个学生 print(形状:, scores.shape) # 2. 计算每个学生的平均分和总分 student_avg np.mean(scores, axis1) # 沿列课程方向求平均 student_total np.sum(scores, axis1) print(\n前5个学生的平均分:, student_avg[:5]) print(前5个学生的总分:, student_total[:5]) # 3. 计算每门课的平均分、最高分、最低分 course_avg np.mean(scores, axis0) course_max np.max(scores, axis0) course_min np.min(scores, axis0) print(\n课程平均分 [课1, 课2, 课3]:, course_avg) print(课程最高分 [课1, 课2, 课3]:, course_max) print(课程最低分 [课1, 课2, 课3]:, course_min) # 4. 找出平均分高于85分的学生索引 high_achievers_idx np.where(student_avg 85)[0] print(f\n平均分高于85分的学生索引: {high_achievers_idx}) print(他们的成绩:) print(scores[high_achievers_idx]) # 5. 标准化处理将每门课的成绩转换为均值为0标准差为1的Z-score # Z (X - μ) / σ course_mean np.mean(scores, axis0, keepdimsTrue) # keepdims保持维度便于广播 course_std np.std(scores, axis0, keepdimsTrue, ddof1) # 样本标准差 scores_normalized (scores - course_mean) / course_std print(\n标准化后前5个学生的成绩 (Z-score):) print(scores_normalized[:5].round(2)) # 保留两位小数 print(标准化后每门课的均值:, np.mean(scores_normalized, axis0).round(2)) print(标准化后每门课的标准差:, np.std(scores_normalized, axis0, ddof1).round(2)) # 6. 找出三门课都及格60的学生 all_pass_mask np.all(scores 60, axis1) # 沿课程轴所有条件都为True all_pass_students scores[all_pass_mask] print(f\n三门课都及格的学生人数: {all_pass_students.shape[0]}) print(他们的成绩:) print(all_pass_students) # 7. 按总分对学生进行排名从高到低 # argsort返回的是从小到大的索引用[::-1]反转 rank_indices np.argsort(student_total)[::-1] sorted_scores_by_total scores[rank_indices] sorted_totals student_total[rank_indices] print(\n按总分排名前5的学生:) for i in range(5): print(f第{i1}名 (总分{sorted_totals[i]}): 成绩{sorted_scores_by_total[i]})这个案例几乎用到了我们前面讲到的大部分核心函数创建、统计、索引、布尔掩码、排序、广播。通过这样的串联你能真切感受到用Numpy进行数据处理的流畅与高效。记住关键是把问题分解成对数组的整体操作而不是想着去循环每一个元素。当你开始习惯这种“数组思维”你就真正入门了。
返回列表