--- slide1.xml --- 授课教师:李 老师 第3章 Numpy数值分析库 --- slide2.xml --- 本章学习目标 01 了解Numpy数值分析库的基本概念,以及数组对象、数据类型和广播机制。 03 熟悉Numpy中的索引、切片和布尔索引技术,以及如何进行数组的排序和元素筛选。 05 学习Numpy中聚合函数和逐元素操作函数的使用,以及如何进行数组的统计分析和数学变换。 02 掌握利用Numpy进行数组创建、操作和数学计算的基础理论及编程实现。 04 理解Numpy中特殊数值(如NaN和Inf)的概念和处理方法,以及它们在数值计算中的影响。 06 培养使用Numpy解决实际科学计算问题的能力。 --- slide3.xml --- 目录 3.2 Numpy数据类型 3.3 Numpy数组广播机制 3.4 特殊数值处理 3.1 Numpy数组创建与操作 --- slide4.xml --- 3.1 Numpy数组创建与操作 Numpy数组基本操作 3.1.3 Numpy函数 3.1.4 Numpy数组创建 3.1.1 Numpy数组属性 3.1.2 --- slide5.xml --- 3.1 Numpy数组创建与操作 import numpy as np 导入惯例如下: 01 对于一维数组,只有一个轴,这条轴通常称为轴0,它沿着数组的长度方向操作。 对于二维数组,有两个轴:轴0和轴1,轴0是沿着行的方向,轴1是沿着列的方向。 数组的轴 02 --- slide6.xml --- 3.1.1 Numpy数组创建 1.使用np.array()方法 实例演示 使用np.array()方法将现有的列表或元组转换为NumPy数组。 当array()方法的参数是一维列表或者元组时,创建的是一维数组。 当array()方法的参数是二维列表或者元组时,创建的是二维数组。 可以通过数组对象的ndim属性查看数组的维度。 In: a = np.array([1, 2, 3, 4]) a Out: array([1, 2, 3, 4]) # 一维列表转成一维数组 In: b = np.array([[1, 2, 3], [4, 5, 6]]) b Out: array([[1, 2, 3], [4, 5, 6]]) # 二维列表转成二维数组 In: a.ndim Out: 1 In: b.ndim Out: 2 --- slide7.xml --- 3.1.1 Numpy数组创建 2. 使用np.arange()方法 实例演示 arange()方法的用法与Python内置函数range()的用法相同,需要3个参数start、stop、step。不同之处在于,arange()方法生成的是一个numpy数组。 结合arange()方法和数组对象的reshape()方法可以灵活创建各种维度和形状的数组。 In: c = np.arange(0, 10, 2) c Out: array([0, 2, 4, 6, 8]) In: d = np.arange(12).reshape( (2,6) ) Out: array([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]]) In: a = np.arange(12).reshape(2,-1) #维度大小为-1 Out: array([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]]) --- slide8.xml --- 3.1.1 Numpy数组创建 3. 生成随机数组 实例演示 用法: np.random.randint(x, y, size):生成[x,y)范围内的形状为size的随机整数数组。 In: np.random.randint(1, 10) #未指定size参数,生成一个随机整数 Out: 2 In: np.random.randint(1, 10, size=(5,)) #生成一维数组 Out: array([6, 1, 2, 7, 8]) In: np.random.randint(1, 10, size=(2,5)) 生成二维数组 Out: array([[3, 8, 3, 3, 8], [3, 7, 5, 8, 2]]) --- slide9.xml --- 3.1.1 Numpy数组创建 3. 生成随机数组 实例演示 用法: np.random.rand(shape):生成[0,1)范围内均匀分布的随机数数组,数组的形状通过参数数量指定。 In: np.random.rand() #未指定参数,生成一个随机小数 Out: 0.45799666392103 In: np.random.rand(3) #指定一个参数,生成一个一维数组 Out: array([0.46840468, 0.22441032, 0.7115226 ]) In: np.random.rand(2, 4) #指定两个参数,生成一个二维数组 Out: array([[0.48789605, 0.62403898, 0.09199842, 0.62880645], [0.88644542, 0.90503952, 0.00590543, 0.65129496]]) --- slide10.xml --- 3.1.2 Numpy数组属性 实例演示 数组的常用属性如表3-1所示 In: a = np.arange(8).reshape(2, 4) a.ndim Out: 2 In: a.size Out: 8 In: a.shape Out: (2, 4) In: a.dtype In: dtype('int32') In: a.T Out: array([[0, 4], [1, 5], [2, 6], [3, 7]]) --- slide11.xml --- 3.1.2 Numpy数组属性 实例演示 一维数组的转置还是一维数组。 In: a = np.arange(4) a Out: array([0, 1, 2, 3]) In: a.T Out: array([0, 1, 2, 3]) --- slide12.xml --- 3.1.3 Numpy数组基本操作 实例演示 通过索引访问数组元素的方法与Python访问可迭代对象的方法相同,有正索引和负索引两种。 In: a = np.arange(8).reshape(2, 4) a[0][-1] Out: 3 1. 索引 --- slide13.xml --- 3.1.3 Numpy数组基本操作 实例演示 Numpy数组的切片与Python可迭代对象切片有以下不同之处。 (1)对于二维及以上,Numpy数组对每个轴都可以切片。 In: a = np.arange(12).reshape(3, 4) a Out: array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) In: a[1:3] #按行切片 Out: array([[ 4, 5, 6, 7], [ 8, 9, 10, 11]]) In: a[:,1:3] #按列切片,用冒号指定所有行 Out: array([[ 1, 2], [ 5, 6], [ 9, 10]]) In: a[1:3,1:3] #同时对行和列切片 Out: array([[ 5, 6], [ 9, 10]]) In: a[1,2] #切片可以简化为对一个元素的访问 Out: 6 2. 切片 --- slide14.xml --- 3.1.3 Numpy数组基本操作 实例演示 Numpy数组的切片与Python可迭代对象切片有以下不同之处。 (2)支持布尔索引。 可以用一个元素是布尔值的数组或列表对数组进行索引,选出索引值为真的数组元素,生成一个新数组。 通过布尔索引,选出数组中满足运算条件的元素 In: a = np.arange(6) b = [True, False, True, True, False, True] c = a[b] #用元素是布尔值的列表进行索引 c Out: array([0, 2, 3, 5]) #选出索引值为真的元素 In: d = np.array([True, False, True,False, False, True]) e = a[d] #用元素是布尔值的数组进行索引 e Out: array([0, 2, 5]) 2. 切片 In: a = np.arange(6) a Out: array([0, 1, 2, 3, 4, 5]) In: a[a>3] #用数组运算进行布尔索引,选出大于3的数组元素 Out: array([4, 5]) --- slide15.xml --- 3.1.3 Numpy数组基本操作 实例演示 Numpy数组的切片与Python可迭代对象切片有以下不同之处。 (3)numpy引入了视图 概念:视图是指共享相同数据的不同数组对象。 作用:避免了不必要的数据复制,节省了内存。 效果:对视图中元素的修改会直接影响到原数组,反之亦然。 In: a = np.arange(6) a Out: array([0, 1, 2, 3, 4, 5]) In: b = a[1:4] #对a切片的结果为数组b b Out: array([1, 2, 3]) In: b[0] = -1 #改变数组b的索引为0的元素值 b Out: array([-1, 2, 3]) In: a #数组a的相应元素发生了改变 Out: array([ 0, -1, 2, 3, 4, 5]) 2. 切片 尽管数组 b 是同原数组切片产生的视图,视图与原数组共享数据,但它们仍是独立的对象。各自有自己的维度、形状、大小等属性。 --- slide16.xml --- 3.1.3 Numpy数组基本操作 实例演示 数组的运算包括数组与数的运算,以及数组与数组的运算。 数组与数进行运算时,数组的每一个元素会与数进行运算,运算结果会保存到一个新数组,原数组保持不变。 数组也可以与数进行比较运算,结果是一个布尔数组。 数组与数组运算要求两数组形状相同,对应位置上的元素之间会执行相应的运算。 In: a = np.arange(6) b = a + 3 a Out: array([0, 1, 2, 3, 4, 5]) In: b Out: array([3, 4, 5, 6, 7, 8]) 3. 运算 In: c = a > 3 c Out: array([False, False, False, False, True, True]) In: a = np.arange(1,4) b = np.arange(4,7) a * b Out: array([ 4, 10, 18]) In: a < b Out: array([ True, True, True]) --- slide17.xml --- 3.1.4 Numpy函数 实例演示 一种是numpy模块的函数: np.函数名() 另一种是numpy数组的成员函数: 数组对象.函数名() 有一些函数(如reshape())既是numpy模块的函数,又是数组的成员函数,可以通过两种方式使用。 In: a = np.arange(8) b = np.reshape(a, (2,4)) #调用numpy模块的函数,传入参数a c = a.reshape(2,4)#调用数组a的成员函数,无须传参数 使用形式 --- slide18.xml --- 3.1.4 Numpy函数 实例演示 用于对数组a进行原地排序,即直接修改数组本身,使其元素按照升序排列。 In: a = np.random.randint(1,50, size=(6)) a Out: array([40, 7, 8, 48, 13, 49]) In: a.sort() In: a Out: array([ 7, 8, 13, 40, 48, 49]) sort()函数 Numpy 数组没有降序排序的功能。如要实现降序排序,可以按照如下的方法进行 In: a = np.random.randint(1,50, size=(6)) a.sort() a = a[::-1] a Out: array([48, 45, 45, 34, 14, 7]) --- slide19.xml --- 3.1.4 Numpy函数 实例演示 如果要对二维及以上的数组排序,需要用axis参数指定排序的轴。 In: a = np.random.randint(1,50, size=(12)).reshape(3,4) a.sort(axis=1) #按轴1排序,在水平方向上排序 a Out: array([[17, 21, 23, 29], [ 9, 17, 21, 28], [ 6, 18, 37, 44]]) In: a = np.random.randint(1,50, size=(12)).reshape(3,4) a.sort(axis=0) #按轴0排序,在竖直方向上排序 a Out: array([[ 1, 18, 31, 2], [18, 23, 32, 13], [32, 32, 47, 39]]) sort()函数 --- slide20.xml --- 3.1.4 Numpy函数 实例演示 Numpy用于计算的函数有两类。 逐元素操作的函数,对数组中的每个元素分别计算,返回一个新数组。这类函数不会改变数组的整体结构,而是对其中每个元素单独进行计算,如sqrt、sin、cos等。 In: a = np.array((4,9,16,25)).reshape(2,2) np.sqrt(a) # 对数组的每一个元素求平方根 Out: array([[2., 3.], [4., 5.]]) 用于计算的函数 --- slide21.xml --- 3.1.4 Numpy函数 实例演示 Numpy用于计算的函数有两类。 聚合操作,用于将数组中的所有元素进行计算,返回一个标量或指定轴上的累计结果。它可以对整个数组或特定轴进行操作,改变数组的维度,如sum、max等。 In: a = np.array((4,9,16,25)).reshape(2,2) np.sqrt(a) Out: array([[2., 3.], [4., 5.]]) In: a = np.random.randint(1,10, size=(3,4)) a Out: array([[4, 4, 5, 2], [5, 4, 2, 7], [8, 8, 4, 9]]) In: a.sum() #未指定参数,对所有元素求和,结果为一个数 Out: 62 In: a.sum(axis = 0) #指定轴0,按列求和,结果为一个一维数组 Out: array([17, 16, 11, 18]) In: a.sum(axis = 1) #指定轴1,按行求和,结果为一个一维数组 Out: array([15, 18, 29]) 用于计算的函数 --- slide22.xml --- 3.1.4 Numpy函数 实例演示 dot()函数用于计算两个数组的点积。 对于一维数组,它返回向量的点积;对于二维数组(如矩阵),它返回矩阵乘积。 二维数组与一维数组进行点积是二维数组的每一行与一维数组进行点积,其结果是一维数组。 In: a = np.array([1, 2, 3]) b = np.array([4, 5, 6]) result = np.dot(a, b) result Out: 32 dot()函数 In: a = np.arange(6).reshape(3,2) b = np.arange(3,5) result = np.dot(a, b) result Out[20]: array([ 4, 18, 32]) --- slide23.xml --- 3.1.4 Numpy函数 实例演示 二维数组与二维数组进行点积,要求第一个数组的列数等于第二个数组的行数。 In: a = np.arange(6).reshape(3,2) b = np.arange(3,9).reshape(2,3) result = np.dot(a, b) result Out: array( [[ 6 7 8] [24 29 34] [42 51 60]] ) dot()函数 --- slide24.xml --- 3.1.4 Numpy函数 实例演示 where() 函数用于根据条件筛选或生成数组,有两种用法: 条件筛选:numpy.where(condition) 参数:条件,常用比较运算表示 功能:返回满足条件的元素的索引,常用于找出数组中符合条件的位置。 In: a = np.array([12,21,32,14,36]) b = np.where( a > 20 ) Out: (array([1, 2, 4], dtype=int64),) #返回满足条件的索引 where() 函数 --- slide25.xml --- 3.1.4 Numpy函数 实例演示 where() 函数用于根据条件筛选或生成数组,有两种用法: 条件赋值:numpy.where(condition, x, y) 参数:条件,两个数组 功能:根据给定的条件返回数组。当条件为真时返回x对应的值,否则返回y对应的值,要求x和y与原数组的形状相同。 In: a = np.array([12,21,32,14,36]) b = a * 2 c = a // 2 d = np.where(a > 20, b, c) #第2和第3个参数都是数组 Out: array([ 6, 42, 64, 7, 72]) # 数组 a 第一个元素不满足 >20 的条件,故 d 的第一个元素是 c 的第一个元素,其余的依此类推。 where() 函数 --- slide26.xml --- 3.2 Numpy数据类型 3.2.1 Numpy数据类型概述 3.2.2 Numpy数据类型转换 --- slide27.xml --- 3.2.1 Numpy数据类型概述 实例演示 常用的数据类型有: 整数类型 (int8, int16, int32, int64) 无符号整数类型 (uint8, uint16, uint32, uint64) 浮点数类型 (float16, float32, float64) 复数类型 (complex64) 如果创建数组时不指定数据类型,将由系统自动确定数据类型。 也可以通过参数dtype指定数据类型。 In: a = np.arange(6) a.dtype Out: dtype('int32') In: b = np.random.rand(3) b.dtype Out: dtype('float64') In: a = np.arange(6, dtype=np.int16) a.dtype Out: dtype('int16') In: b = np.array([1.1,2.3, 3.4], dtype=np.float32) b.dtype Out: dtype('float32') --- slide28.xml --- 3.2.2 Numpy数据类型转换 实例演示 astype()方法用于将数组的元素类型转换为指定的类型,并返回一个新的数组,而不改变原数组。astype()方法可以将数组从一种数据类型转换为另一种。 当从高精度类型转换为低精度类型时(如float64转int32),可能会丢失数据的精度,需要慎重对待。 In: a = np.arange(6) a.dtype Out: dtype('int32') In: b = a.astype( np.int16 ) #改变a的数据类型,并创建新数组 b.dtype Out: dtype('int16') In: a = np.random.rand(4) a.dtype Out: dtype('float64') In: b = a.astype(np.float32) #改变a的数据类型,并创建新数组 b.dtype Out: dtype('float32') --- slide29.xml --- 3.3 Numpy数组广播机制 使两个数组的维数相同,且各维度的长度相同 用于处理不同形状数组之间运算 机理 功能 --- slide30.xml --- 3.3 Numpy数组广播机制 如果两个数组的维数不同,在维数较小的数组前面补1,直到两个数组的维数相同。 规则一 从左到右逐维检查两个数组的长度,如果长度不同且有一个长度为1,则扩充长度为1的维度,直到两个数组相等;如果长度不同且都不为1,则不能扩充。 规则二 --- slide31.xml --- 实例 演示 In: a = np.arange(6).reshape(3,2) a Out: array([[0, 1], [2, 3], [4, 5]]) In: b = np.arange(1, 3) b Out: array([1, 2]) 【例3-1】形状为(3,2)的数组a与形状为(2,)的数组b的运算a + b。 a的形状为(3,2),b的形状为(2,) 首先按照规则一,把数组b的形状扩充为(1,2)。 然后按照规则二,将它扩充为3,数组b的形状变为(3,2)。 至此,两个数组维数相同,形状相同,可以进行运算。 In: a + b Out: array([[1, 3], [3, 5], [5, 7]]) --- slide32.xml --- 实例 演示 In: a=np.arange(6).reshape(2,3) a Out: array([[0, 1, 2], [3, 4, 5]]) In: b = np.arange(1,3).reshape(2,1) b Out: array([[1], [2]]) 【例3-2】形状为(2,3)的数组a与形状为(2,1)的数组b的运算a + b。 数组a和b都是二维数组,维数相同,无须扩充。两个数组的第一维长度都是2,无须扩充。数组a的第二维长度为3,数组b的第二维长度为1,按照规则二,将数组b的第二维长度扩充为3,数组的形状变为(2, 3)。至此,两个数组维数相同,形状相同,可以进行运算。 In: a + b Out: array([[1, 2, 3], [5, 6, 7]]) --- slide33.xml --- 实例 演示 In: a=np.arange(6).reshape(2,3) a Out: array([[0, 1, 2], [3, 4, 5]]) In: b = np.arange(1,4).reshape(3,1) b Out: array([[1], [2], [3]]) 【例3-3】形状为(2,3)的数组a与形状为(3,1)的数组b不满足广播规则。 虽然数组a和b都是二维数组,但是a的第一维长度为2,b的第一维长度为3,不满足广播规则,不能扩充。 --- slide34.xml --- 3.4 特殊数值处理 实例演示 概念:特殊数值是指在特定计算过程中可能出现的特殊值 产生 原因:这些特殊数值通常在数学运算中产生,用来表示无法被常规数值定义的结果。 NaN表示无效或未定义的数值,例如0除以0或对负数取平方根时可能出现。 Inf表示正无穷或负无穷,通常在除以零的运算或超过计算机所能表示的数值范围时出现。正无穷(+inf)通常由正数除以零产生,负无穷(-inf)由负数除以零产生。 在数组计算中,如果不加处理,特殊数值会传播到计算结果中。 In: b = np.array([1, -1, 0]) b / 0 Out: array([ inf, -inf, nan]) In: a = np.array([1, 2, np.nan, 4]) np.sum(a) Out: nan --- slide35.xml --- 3.4 特殊数值处理 实例演示 NumPy提供了很多处理这些特殊数值的函数,在数据清洗或数值稳定性分析时处理特殊数值。 np.isnan()逐个判断数组元素是否为 NaN; np.isinf()逐个判断数组元素是否为无穷大。 In: a = np.array([1, 2, np.nan, np.inf, -np.inf]) np.isnan(a) Out: array([False, False, True, False, False]) In: np.isinf(a) Out: array([False, False, False, True, True]) --- slide36.xml --- 3.4 特殊数值处理 实例演示 (1)过滤特殊值 可以使用布尔索引来过滤掉数组中的 NaN 和 inf,只保留有效的数值。 arr = np.array([1, np.nan, np.inf, -np.inf, 2]) #使用布尔索引从原数组 arr 中筛选出有效元素 valid_arr = arr[~(np.isnan(arr) | np.isinf(arr))]#~逻辑反运算,| 逻辑或运算 print(valid_arr) --- slide37.xml --- 3.4 特殊数值处理 实例演示 (2)替换特殊值 替换为特定值:可以使用 np.nan_to_num() 函数将 NaN 替换为 0,将正无穷替换为一个很大的数,将负无穷替换为一个很小的数。 arr = np.array([1, np.nan, np.inf, -np.inf, 2]) replaced_arr = np.nan_to_num(arr, nan=0, posinf=1e10, neginf=-1e10) print(replaced_arr) --- slide38.xml --- 3.4 特殊数值处理 实例演示 (3)替换为统计值 替换为统计值:可以将 NaN 替换为数组的均值、中位数等统计值。 arr = np.array([1, np.nan, 3, 4, 5]) mean_value = np.nanmean(arr) arr[np.isnan(arr)] = mean_value print(arr) --- slide39.xml --- 3.4 特殊数值处理 实例演示 (4)计算时忽略特殊值 NumPy 提供了一些专门处理 NaN 的函数,如 np.nansum()、np.nanmean() 等,这些函数在计算时会忽略 NaN。 arr = np.array([1, np.nan, 3, 4, 5]) sum_without_nan = np.nansum(arr) print(sum_without_nan)