NumPy库简介及高效使用指南

NumPy库简介及高效使用指南

一、为什么要用NumPy库?

Python原生的数据结构和语法虽然也能实现许多功能,但效率较低。例如,当我们要求100万个数的倒数时,使用Python原生的for循环实现耗时较长。我们通过%timeit测试得出,使用Python原生for循环实现平均每次运行时间为145毫秒;而使用NumPy库的向量化操作实现相同功能,平均每次运行时间仅为5.8毫秒左右,是前者的大约25倍。

NumPy库之所以高效,主要有以下几个原因:

  • 底层使用C语言编写,C语言是编译型语言,执行速度快。
  • NumPy数组是连续单一类型存储,这使得NumPy数组在内存中是连续存储的,而Python列表是分散存储的,因此NumPy数组的运行更加高效。
  • Python语言执行时有线程锁,无法实现真正的多线程并行运算;而C语言不受此限制,因此在并行计算方面,NumPy库更胜一筹。

二、NumPy数组的创建

(一)从现有数据创建

  1. 从列表创建

    可以直接将Python列表传入np.array()函数来创建NumPy数组,并且可以在创建时指定数据类型,如np.array([1, 2, 3], dtype=float)

  2. 创建特殊数组

    • np.zeros(5):创建长度为5,数值都为0的数组。
    • np.ones((2, 4), dtype=float):创建2行4列,数值都为1的浮点型数组。
    • np.full((3, 5), 8.8):创建3行5列,数值都为8.8的数组。
    • np.eye(3):创建3×3的单位矩阵。
    • np.arange(1, 15, 2):创建从1开始到15(不包括15),步长为2的线性序列数组。
    • np.linspace(0, 1, 4):创建0到1之间平均分为3份的等差数列,共4个数值。
    • np.logspace(0, 9, 10):创建10个数值构成的等比数列,数值范围从10的0次方到10的9次方。

(二)创建随机数组

  • np.random.random((3, 3)):创建3×3的数组,数值为0到1之间的均匀分布随机数。
  • np.random.normal(0, 1, (3, 3)):创建3×3的数组,数值为均值为0,标准差为1的正态分布随机数。
  • np.random.randint(0, 10, (3, 3)):创建3×3的数组,数值为0到10(不包括10)之间的随机整数。
  • np.random.permutation([1, 4, 9, 12, 7]):对数组进行随机重排,不改变原数组。
  • np.random.shuffle(x):对数组元素进行随机重排,改变原数组。
  • np.random.choice(np.arange(10, 24), size=(4, 3), p=[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4]):从数组中进行随机采样,p参数为采样概率。

三、NumPy数组的属性与操作

(一)属性

  • 形状:x.shape,如(3, 4)表示3行4列。
  • 维度:x.ndim,如2表示二维数组。
  • 大小:x.size,表示元素个数。
  • 数据类型:x.dtype,如float64

(二)索引与切片

  • 对于一维数组,索引与Python列表类似,可以使用正向索引和负向索引。
  • 对于多维数组,例如x[0, 0]表示访问二维数组第0行第0列元素。
  • 切片操作与列表类似,但需要分别对行和列设置,如x[:2, :3]表示取前两行前三列。
  • 切片得到的是视图,若要得到副本需使用copy()方法。

(三)变形

  • 使用reshape()函数改变数组形状,如x.reshape(3, 4)将一维数组变为3行4列二维数组,但前后元素个数需相等。
  • 将一维数组变为行向量或列向量可使用x.reshape(1, -1)x.reshape(-1, 1),也可使用x[np.newaxis, :]x[:, np.newaxis]
  • 将多维数组变为一维数组可使用flatten()ravel()reshape(-1),其中flatten()返回副本,ravel()返回视图。

(四)拼接与分裂

  • 水平拼接:np.hstack([x1, x2])np.c_[x1, x2]
  • 垂直拼接:np.vstack([x1, x2])np.r_[x1, x2]
  • 分裂:np.split(x, [2, 7])将一维数组在第2个元素和第7个元素前分割,np.hsplit(x, [2, 4])np.vsplit(x, [2, 4])分别对二维数组进行水平和垂直分割。

四、NumPy数组的运算

(一)向量化运算

  • 数组与数字的加减乘除等运算符作用于每个元素,如x + 5
  • 数组间的加减乘除等运算符作用于对应位置元素,如x1 + x2
  • 还支持取反、乘方、求整数商、求余数、求绝对值、三角函数、反三角函数、指数运算、对数运算等向量化操作。

(二)矩阵运算

  • 转置:x.T
  • 矩阵乘法:x.dot(y)np.dot(x, y)

(三)广播运算

  • 当两个数组形状不匹配时,较小数组会沿着维度为1的方向扩展,使两者形状相同后进行运算。如x + 5,5会被扩展成与x形状相同的数组,每个元素都加5。

(四)比较运算与掩码

  • 比较运算结果为布尔数组,如x > 5
  • 可对布尔数组进行求和、判断是否全为True/False、判断是否存在True/False等操作。
  • 可将布尔数组作为掩码筛选出符合条件的元素,如x[x > 5]

(五)花哨索引

  • 对于一维数组,可通过索引数组取出多个指定位置元素,如x[[1, 5, 8]]
  • 对于多维数组,可通过行索引和列索引数组组合取出多个指定位置元素,如x[[0, 1, 2], [1, 3, 0]]

五、NumPy通用函数

  • 排序:np.sort(x)临时排序,x.sort()永久排序,np.argsort(x)获取排序后的索引。
  • 最大最小值:np.max(x)np.min(x)np.argmax(x)np.argmin(x)获取最大最小值索引。
  • 求和求积:x.sum()np.sum(x)x.prod()np.prod(x),可按行axis=1或按列axis=0求和求积。
  • 统计函数:np.median(x)求中位数,np.mean(x)求均值,np.var(x)求方差,np.std(x)求标准差。

习题

1. 为什么NumPy库(n派库)比Python原生的for循环更高效?

答案:NumPy库比Python原生的for循环更高效的原因在于其底层是用C语言编写的,C语言是一种编译型语言,执行速度快;NumPy数组在内存中是连续单一类型存储,避免了类型检查和函数查找的时间开销;此外,Python有线程锁,无法实现真正的多线程并行运算,而C语言不受此限制,因此NumPy在并行计算方面更胜一筹。

2. NumPy数组的创建有哪两种方式?

答案:NumPy数组的创建有两种方式:一种是从Python列表开始创建,使用np.array()方法;另一种是从无到有创建特定的数组,如全零数组np.zeros()、全一数组np.ones()、自定义值填充数组np.full()、单位矩阵np.eye()、线性序列数组np.arange()、等差数列np.linspace()、等比数列np.logspace()、随机数组np.random.random()、正态分布随机数组np.random.normal()等。

3. NumPy数组的属性有哪些?

答案:NumPy数组的属性包括形状shape、维度ndim、大小size、数据类型dtype

4. 如何对NumPy数组进行索引和切片?

答案:NumPy数组的索引和切片与Python列表类似,但需要分别对行和列进行操作。例如,x[0, 0]表示访问二维数组的第一行第一列元素。切片操作如x[:2, :3]表示取前两行前三列。需要注意的是,切片返回的是视图而非副本,若要获得副本,需使用x[:2, :3].copy()

5. 什么是NumPy数组的广播机制?

答案:NumPy数组的广播机制是指当两个数组在形状的维度上不匹配时,较小的数组会在维度为一的维度上进行扩展,使其形状与较大的数组匹配,从而可以进行逐元素运算。例如,一个形状为(3, 1)的数组和一个形状为(1, 3)的数组相加时,会扩展成(3, 3)进行运算。

6. NumPy数组的四大运算包括哪些?

答案:NumPy数组的四大运算包括向量化的运算、矩阵化的运算、广播运算和比较运算与掩码。向量化的运算指对数组中所有元素进行逐元素运算;矩阵化的运算包括矩阵转置和矩阵乘法;广播运算是指形状不同的数组通过扩展进行逐元素运算;比较运算与掩码是指通过比较运算生成布尔数组,并用布尔数组作为掩码进行数据筛选。

7. 如何对NumPy数组进行排序和统计?

答案:可以使用np.sort()对数组进行排序,argsort()获取排序后的索引。统计操作包括求最大值np.max()、最小值np.min()、求和np.sum()、求积np.prod()、求中位数np.median()、求均值np.mean()、求方差np.var()、求标准差np.std()等。

8. 什么是NumPy数组的花哨索引?

答案:花哨索引是指通过一个或多个索引数组来选择数组中的元素。例如,x[[0, 2, 4]]会选择一维数组中的第0、2、4个元素。对于多维数组,可以同时使用行索引和列索引来选择元素,如x[[0, 1], [1, 3]]会选择(0,1)和(1,3)位置的元素。

9. NumPy数组的拼接和分裂有哪些方法?

答案:拼接方法包括水平拼接np.hstack()np.c_[],垂直拼接np.vstack()np.r_[]。分裂方法包括np.split()np.hsplit()(水平分裂)、np.vsplit()(垂直分裂)。这些方法可以根据指定的索引或位置对数组进行分割或组合。


markdown

习题1:创建随机数组并获取属性

题目: 创建一个4x6的二维数组,元素由0至1之间的均匀分布随机数构成,并输出该数组的形状、大小、维度以及数据类型。

答案: 使用np.random.random函数创建4x6的数组,其shape为(4, 6),size为24,维度为2,数据类型为float64。

习题2:数组形状变换

题目: 将习题1中的二维数组分别转化为3x8的数组和平摊为一维数组。

答案: 使用reshape函数,3x8的数组可以通过reshape(3, 8)获得,一维数组可以通过reshape(-1)获得。

习题3:数组拼接

题目: 创建两个4x4的二维数组,元素为整数随机取自区间0到10。将这两个数组分别进行水平拼接和垂直拼接。

答案: 使用np.hstack进行水平拼接,使用np.vstack进行垂直拼接。也可以使用np.concatenate函数,指定axis参数为1(水平)或0(垂直)。

习题4:创建特殊结构数组

题目: 创建一个16x16的数组,其中数组的外围均为1,内部均为0。

答案: 首先创建一个全零的16x16数组,然后通过切片赋值的方式将第一行、第一列、最后一行和最后一列设置为1。

习题5:统计分析与数据处理

题目: 创建一个由10,000个元素构成的一维数组,元素随机取值0至100。求该数组元素的均值、方差和标准差,并对其进行标准化处理。

答案: 使用np.meannp.varnp.std函数分别计算均值、方差和标准差。标准化处理公式为(x - np.mean(x)) / np.std(x)

习题6:成绩筛选与排序

题目: 创建一个由30个元素构成的一维数组,元素随机取自80至100。筛选出大于等于95分的成绩,小于等于85分的成绩,以及大于85分且小于95分的成绩,并对所有成绩进行排序。

答案: 使用布尔索引筛选成绩,如x[x >= 95]表示大于等于95分的成绩,x[x <= 85]表示小于等于85分的成绩,x[(x > 85) & (x < 95)]表示大于85分且小于95分的成绩。使用np.sort函数对成绩进行排序。

习题7:矩阵运算性能比较

题目: 构建两个3x3的二维列表A和B,分别实现逐元素乘法运算和矩阵乘法,并比较两种运算的性能。

答案: 使用Python的for循环手工实现逐元素乘法运算,并记录时间。使用np.multiply实现逐元素乘法,使用np.dot实现矩阵乘法,并比较两者的时间开销。通常情况下,NumPy提供的矩阵运算速度远快于手工实现。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六月五日

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值