01. NumPy介绍与安装

1.1 NumPy介绍

1.1.1 什么是NumPy

  • NumPy是用Python进行科学计算的基础软件包,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种API,有包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等等。
  • NumPy与Pandas、Matplotlib一起被誉为Python数据分析三剑客。
  • 除了明显的科学用途外,NumPy还可以用作通用数据的高效多维容器,可以定义任意数据类型,这使得NumPy能够无缝快速地与各种数据库集成。
  • 总的来说,NumPy作为科学计算的基础软件包,它包括:

    • 功能强大的N维数组对象。
    • 精密广播功能函数。
    • 集成C/C+Fortran代码的工具。
    • 强大的线性代数、傅立叶变换和随机数功能。

      1.1.2 NumPy中的两大核心利器

  • N维数组:NumPy中最重要的一个特点是其N维数组对象ndarray

    • ndarray是用于存放同类型元素的多维数组,其中的每个元素在内存中都有相同存储大小的区域。
  • 索引和切片:ndarray对象的内容可以通过索引或切片来访问和修改。

    1.1.3 NumPy数组与Python原生数组的区别

  • Python中最常用的数据容器是列表list,但实际上原生Python也是有数组Array的。

  • NumPy数组和原生Python Array之间有几个重要的区别:

    • NumPy数组在创建时具有固定的大小,与Python的原生数组对象(可以动态增长)不同。更改ndarray的大小将创建一个新数组并删除原来的数组。
    • NumPy数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。
      • 例外情况:Python的原生数组里包含了NumPy的对象的时候,这种情况下就允许不同大小元素的数组。
    • NumPy数组有助于对大量数据进行高级数学和其他类型的操作。通常,这些操作的执行效率更高,比使用Python原生数组的代码更少。
    • 越来越多的基于Python的科学和数学软件包使用NumPy数组;虽然这些工具通常都支持Python的原生数组作为参数,但它们在处理之前会还是会将输入的数组转换为NumPy的数组,而且也通常输出为NumPy数组。换句话说,为了高效地使用当今科学/数学基于Python的工具(大部分的科学计算工具),你只知道如何使用Python的原生数组类型是不够的,还需要知道如何使用NumPy数组。

      1.1.4 NumPy的两个相关文档

  • 官方英文文档:https://numpy.org/

  • 官方中文文档:https://numpy.org.cn/

    1.2 NumPy安装与导入

  • Anaconda中默认带有NumPy库,因此Anaconda环境无需再次手动下载NumPy,直接导入即可。

  • 普通Python环境中可以通过pip命令来安装NumPy。

    1. pip install numpy
  • NumPy在使用前需要导入,并且按照习惯还会取别名为np。

    1. import numpy as np
  • NumPy初体验:

    1. >>> import numpy as np
    2. >>> np.eye(4) # 生成一个4行4列的单位矩阵。
    3. array([[1., 0., 0., 0.],
    4. [0., 1., 0., 0.],
    5. [0., 0., 1., 0.],
    6. [0., 0., 0., 1.]])

    02. 数组的基本操作

    2.1 普通数组的构建

    2.1.1 array()将列表构建成数组

  • array(lst)函数会根据列表对象lst的维度,构建相同维度的数组。

  • 一维数组的构建:

    1. >>> nums = [19, 27, 33, 45, 67]
    2. >>> arr = np.array(nums)
    3. >>> arr
    4. array([19, 27, 33, 45, 67])
  • 二维数组的构建:

    1. >>> stu_scores = [
    2. ... [78, 89, 76],
    3. ... [98, 78, 38],
    4. ... [89, 78, 87]
    5. ... ]
    6. >>> arr = np.array(stu_scores)
    7. >>> arr
    8. array([[78, 89, 76],
    9. [98, 78, 38],
    10. [89, 78, 87]])
  • 补充说明:在Jupyter Notebook中,使用np.array?这样的格式可以查看API的帮助信息。

    2.1.2 linspace()生成指定规模的等差数列

  • linspace()的函数格式:

    1. np.linspace(
    2. start, # 等差数列的起始值
    3. stop, # 等差数列的结束值
    4. num=50, # 将等差数列均匀的分成num分(默认情况下是50分)
    5. endpoint=True, # 是否包含结束值(默认情况下是包含的)
    6. retstep=False, # 是否显示步长(默认情况下是不显示的)
    7. dtype=None,
    8. axis=0,
    9. )
  • 默认情况下,等差数列会分成50份元素。

    1. >>> arr = np.linspace(1, 10)
    2. >>> arr
    3. array([ 1. , 1.18367347, 1.36734694, 1.55102041, 1.73469388,
    4. 1.91836735, 2.10204082, 2.28571429, 2.46938776, 2.65306122,
    5. 2.83673469, 3.02040816, 3.20408163, 3.3877551 , 3.57142857,
    6. 3.75510204, 3.93877551, 4.12244898, 4.30612245, 4.48979592,
    7. 4.67346939, 4.85714286, 5.04081633, 5.2244898 , 5.40816327,
    8. 5.59183673, 5.7755102 , 5.95918367, 6.14285714, 6.32653061,
    9. 6.51020408, 6.69387755, 6.87755102, 7.06122449, 7.24489796,
    10. 7.42857143, 7.6122449 , 7.79591837, 7.97959184, 8.16326531,
    11. 8.34693878, 8.53061224, 8.71428571, 8.89795918, 9.08163265,
    12. 9.26530612, 9.44897959, 9.63265306, 9.81632653, 10. ])
  • 可以通过给参数num赋值来指定元素的个数,如指定元素为10个。

    1. >>> arr = np.linspace(1, 10, num=10)
    2. >>> arr
    3. array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])

    2.1.3 arange()生成指定起始值的等差数列

  • arange()的函数格式:arange([start,] stop[, step,], dtype=None, *, like=None)

    • start:起始值,若不设置则默认起始值为0。
    • stop:结束值,默认结果不包含结束值。
    • step:步长,若不设置则默认为步长为1。
    • arange()在使用上与Python中的range()基本一致。
  • 默认起始值为0,步长为1,不包含结束值。 ```python

    arr1 = np.arange(10) arr1 array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr2 = np.arange(1, 10, 3) # 指定起始值为1,结束值10,步长为3。 arr2 array([1, 4, 7]) ```

  • 同样的,也可以生成倒序数组。

    1. >>> arr3 = np.arange(10, 0, -1)
    2. >>> arr3
    3. array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])

    2.1.4 random.choice()从序列中随机获取n个数据

  • 函数格式:arr = np.random.choice(序列, size=规模)

    • 序列:要在哪个序列中随机获取数据。
    • size:
      • 当赋予数值数据时,如10,那么会从序列中随机获取10个数据,然后组成一个一维数组。
      • 当赋予元组数据时,如(3,4),那么会从序列中随机获取3 × 4 = 12个数据,任何封装成一个三行四列的二维数组。
  • 示例1:从[1, 100)中随机获取10个数据,生成一个一维数组。

    1. >>> arr = np.random.choice(range(1, 100), size=10)
    2. >>> arr
    3. array([98, 44, 15, 6, 31, 80, 80, 38, 41, 37])
  • 示例2:从[1, 100)中随机获取12个数据,生成一个三行四列的二维数组。

    1. >>> arr = np.random.choice(range(1, 100), size=(3, 4))
    2. >>> arr
    3. array([[18, 23, 17, 68],
    4. [23, 85, 12, 62],
    5. [80, 49, 78, 77]])

    2.1.5 random.random()从随机生成n个数据

  • 函数格式:arr = np.random.random(size)

  • 作用:生成指定size的随机数,随机范围为:01. NumPy基本操作 - 图1
  • 示例1:从01. NumPy基本操作 - 图2之间生成30个随机数,构成一维数组。

    1. >>> arr = np.random.random(30)
    2. >>> arr
    3. array([0.43854591, 0.97862806, 0.3445239 , 0.62005662, 0.94804321,
    4. 0.20079613, 0.0653225 , 0.35868228, 0.53748766, 0.66208933,
    5. 0.97360658, 0.06208119, 0.25152181, 0.00167097, 0.08546321,
    6. 0.32184125, 0.3599573 , 0.65355639, 0.66726954, 0.0780334 ,
    7. 0.83239922, 0.8198722 , 0.64646726, 0.13122817, 0.86871274,
    8. 0.85647658, 0.74319636, 0.80558596, 0.65611706, 0.03512057])
  • 示例2:从01. NumPy基本操作 - 图3之间生成30个随机数,组成一个3行10列的二维数组。

    1. >>> arr = np.random.random((3, 10))
    2. >>> arr
    3. array([[0.19307978, 0.86940541, 0.51298214, 0.98195816, 0.1861913 ,
    4. 0.35024992, 0.29003271, 0.16901297, 0.97721579, 0.54665195],
    5. [0.25377677, 0.8947084 , 0.58767174, 0.6974886 , 0.28304737,
    6. 0.15243733, 0.07271893, 0.44774389, 0.72439264, 0.11908445],
    7. [0.25218519, 0.65684472, 0.6312397 , 0.62459449, 0.3507436 ,
    8. 0.3711958 , 0.37446269, 0.91185288, 0.88845136, 0.26163761]])

    2.1.6 random.randint()从随机生成n个数据

  • 函数格式:arr = np.random.randint(low, high, size)

    • low:生成函数的最小值。
    • high:生成函数的最大值。与Python的random模块中的randint()函数不同,NumPy的randint()不包含最大值,即生成范围是01. NumPy基本操作 - 图4
    • size:与2.1.4相同。
  • 示例1:从50到100中生成30个数据,组成一维数组。

    1. >>> arr = np.random.randint(50, 100, size=30)
    2. >>> arr
    3. array([99, 52, 89, 76, 53, 70, 58, 74, 90, 97, 64, 65, 84, 83, 50, 80, 98,
    4. 94, 67, 52, 82, 54, 67, 79, 82, 97, 51, 85, 98, 50])
  • 示例2:从50到100中生成30个数据,组成一个3行10列的二维数组。

    1. >>> arr = np.random.randint(50, 100, size=(3, 10))
    2. >>> arr
    3. array([[71, 70, 60, 52, 93, 93, 62, 95, 95, 80],
    4. [70, 99, 80, 83, 62, 87, 86, 59, 64, 75],
    5. [78, 57, 55, 96, 98, 96, 84, 66, 52, 77]])

    2.1.7 random.randn()生成满足正态分布的数据

  • random.randn(num)会生成num个满足正态分布的数据。

    • 注意:只有当num足够大的时候才有意义。
  • 示例:生成1w个满足正态分布的数据。

    1. >>> arr = np.random.randn(10000)
    2. >>> arr
    3. array([ 0.93244973, 0.86613527, -0.62138941, ..., -1.23484073,
    4. -0.94799863, 0.54025911])
    5. >>> arr.size # 获取数据的元素个数,参考2.3.5。
    6. 10000

    2.1.8 元素类型

  • NumPy数组会自动根据数组构建时元素的类型,对数组元素类型进行赋值。 ```python

    arr1 = np.array([1, 2, 3, 4]) arr1.dtype dtype(‘int32’)

arr2 = np.array([1.1, 2.2, 3.3, 4.4]) arr2.dtype dtype(‘float64’) ```

  • 并且由于NumPy数组中存储的都是同一类型的元素,因此小类型元素会自动转换成大类型进行存储,数组的类型也是由大类型决定的。

    • 以下例子中,可以发现整型数据1、3变成浮点型数据1.3.进行存储。
      1. >>> arr3 = np.array([1, 2.2, 3, 4.5])
      2. >>> arr3
      3. array([1. , 2.2, 3. , 4.5])
      4. >>> arr3.dtype
      5. dtype('float64')
  • 当然,在创建数组时,可以显式的指定数组的类型。 ```python

    arr4 = np.array([1, 2, 3, 4, 5], dtype=np.float64) # 用整型列表构建浮点型数组 arr4 array([1., 2., 3., 4., 5.]) arr4.dtype dtype(‘float64’)

arr5 = np.array([1.1, 1.2, 1.3, 1.4, 1.5, 1.6], dtype=np.int32) # 用浮点型列表构建整型数组(截断) arr5 array([1, 1, 1, 1, 1, 1]) arr5.dtype dtype(‘int32’) ```

2.2 特殊数组的构建

2.2.1 eye()单位矩阵

  • 所谓单位矩阵,就是主对角线上的所有元素全为1,其余元素全为0的矩阵。
  • eye(N)可以生成N行N列的单位矩阵。

    1. >>> identity_matrix = np.eye(4)
    2. >>> identity_matrix
    3. array([[1., 0., 0., 0.],
    4. [0., 1., 0., 0.],
    5. [0., 0., 1., 0.],
    6. [0., 0., 0., 1.]])
  • eye(N, M)可以生成N行M列的单位矩阵。(因为单位矩阵从定义上来将是一个方正,N应该等于M,所以这种方式一般不用)

    1. >>> identity_matrix = np.eye(3, 6)
    2. >>> identity_matrix
    3. array([[1., 0., 0., 0., 0., 0.],
    4. [0., 1., 0., 0., 0., 0.],
    5. [0., 0., 1., 0., 0., 0.]])

    2.2.2 zeros()零矩阵

  • 所谓零矩阵,就是所有元素全为0的矩阵。

  • zeros((m, n))可以生成一个m行n列的零矩阵。

    1. >>> zero_matrix = np.zeros((3, 6))
    2. >>> zero_matrix
    3. array([[0., 0., 0., 0., 0., 0.],
    4. [0., 0., 0., 0., 0., 0.],
    5. [0., 0., 0., 0., 0., 0.]])
  • 生成的数组中的数据默认是浮点型的,但是可以手动指定生成整型的数据。

    1. >>> zero_matrix = np.zeros((3, 6), dtype=np.int32)
    2. >>> zero_matrix
    3. array([[0, 0, 0, 0, 0, 0],
    4. [0, 0, 0, 0, 0, 0],
    5. [0, 0, 0, 0, 0, 0]])

    2.2.3 ones()元素全为1的矩阵

  • ones((m, n))可以生成一个m行n列的元素全为1的矩阵。

    1. >>> one_matrix = np.ones((3, 4))
    2. >>> one_matrix
    3. array([[1., 1., 1., 1.],
    4. [1., 1., 1., 1.],
    5. [1., 1., 1., 1.]])
  • 生成的数组中的数据默认是浮点型的,但是可以手动指定生成整型的数据。

    1. >>> one_matrix = np.ones((3, 4), dtype=np.int32)
    2. >>> one_matrix
    3. array([[1, 1, 1, 1],
    4. [1, 1, 1, 1],
    5. [1, 1, 1, 1]])

    2.2.4 zeros_like形状相同的零矩阵

  • zeros_like(arr1)会返回一个形状与arr1相同的数组,但这个数组中所有元素全为0。

    1. >>> arr1 = np.array([
    2. ... [1, 2, 3, 4],
    3. ... [5, 6, 7, 8],
    4. ... [9, 10, 11, 12]
    5. ... ])
    6. >>> arr1.shape
    7. (3, 4)
    8. >>> np.zeros_like(arr1)
    9. array([[0, 0, 0, 0],
    10. [0, 0, 0, 0],
    11. [0, 0, 0, 0]])

    2.2.5 empty()随机元素矩阵

  • empty((m, n))可以生成一个m行n列的矩阵,矩阵中的元素随机生成。

    1. >>> random_matrix = np.empty((3, 5))
    2. >>> random_matrix
    3. array([[6.23042070e-307, 3.56043053e-307, 1.60219306e-306,
    4. 7.56571288e-307, 1.89146896e-307],
    5. [1.37961302e-306, 1.05699242e-307, 8.01097889e-307,
    6. 1.78020169e-306, 7.56601165e-307],
    7. [1.02359984e-306, 2.04719290e-306, 1.00132653e-307,
    8. 8.01097889e-307, 1.02353330e-306]])

    2.3 数组中常用的属性与属性方法

    2.3.1 实验数据准备

  • 从50到100中生成20个数据,组成一个4行5列的二维数组。

    1. >>> arr = np.random.randint(50, 100, size=(4, 5))
    2. >>> arr
    3. array([[85, 70, 72, 95, 78],
    4. [90, 77, 66, 56, 55],
    5. [65, 90, 50, 54, 88],
    6. [51, 90, 88, 82, 69]])

    2.3.2 ndim数组维度

  • 维度轴(rank)的概念:

    • NumPy的主要对象是同构多维数组。它是一个元素表(通常是数字),所有类型都相同,由非负整数元组索引。在NumPy维度中称为轴。
    • 例如,3D空间中的点的坐标[1, 2, 1]具有一个轴,该轴有3个元素,所以我们说它的长度为3。
    • 而在下图所示的例子中,数组有2个轴。第一轴(纵轴)的长度为2,第二轴(横轴)的长度为3。

      1. [[ 1., 0., 0.],
      2. [ 0., 1., 2.]]
    • 由此可推:一维数组具有一个轴(x轴),二维数组具有两个轴(x轴、y轴),三维数组具有三个轴(x轴、y轴、z轴),……,N维数组具有N个轴。

    • 维度的数量被称为rank,即N维数组的rank为N。
  • 数组对象.ndim可以获取数组的维度,即数组的轴的个数。

    1. >>> arr.ndim
    2. 2

    2.3.3 shape数组形状

  • 数组对象.shape可以获取数组的形状。

  • 一般对二维数组而言,shape属性的值为(x, y),代表着x行y列。

    1. >>> arr.shape
    2. (4, 5)
  • shape元组的长度取决于ndin,由此二维数组的shape长度固定为2。

    2.3.4 reshape()修改数组形状

  • 数组对象.reshape(x, y)可以将数组的形状更改成x行y列。

    1. >>> new_shape_arr = arr.reshape(2, 10)
    2. >>> new_shape_arr
    3. array([[74, 93, 63, 79, 51, 83, 88, 88, 64, 86],
    4. [86, 63, 87, 93, 77, 79, 71, 61, 60, 83]])
    5. >>> new_shape_arr.shape
    6. (2, 10)
  • 注意:这个操作只会按照固定的shape生成一个新的array,并不会影响原array。

    1. >>> arr.shape
    2. (4, 5)
  • 有时候可能只知道要生成几行,不知道要生成几列;或者只知道要生成几列,不知道要生成几行。那么此时可以只写知道的数据,不知道的用-1代替。 ```python

    将arr数组转换成4行n列。

    arr.reshape(4, -1) array([[85, 70, 72, 95, 78],

    1. [90, 77, 66, 56, 55],
    2. [65, 90, 50, 54, 88],
    3. [51, 90, 88, 82, 69]])

将arr数组转换成n行4列。

arr.reshape(-1, 4) array([[85, 70, 72, 95], [78, 90, 77, 66], [56, 55, 65, 90], [50, 54, 88, 51], [90, 88, 82, 69]]) ```

2.3.5 size数组元素个数

  • 数组对象.size可以获取数组中所有元素的总个数。
  • size属性的值等于shape的元素的乘积,即若arr.shape = (x, y),则arr.size = x * y。

    1. In [6]: arr.size
    2. Out[6]: 20

    2.3.6 dtype数组元素类型

  • NumPy数组中存放的都是同类型的元素,因此用数组对象.dtype就可以查看数组中所有元素的类型。

    1. >>> arr.dtype
    2. dtype('int32')
  • dtype的结果可能是标准的Python类型,也可能是NumPy自己提供的类型,如:numpy.int32numpy.int16numpy.float64

    2.3.7 itemsize元素大小

  • 数组对象.itemsize用于获取数组中每个元素的字节大小。

    1. >>> arr.itemsize
    2. 4
  • 实际上这个属性查看的是ndarray.dtype.itemsize

  • 例如,元素为float64类型的数组的itemsize为8(=64/8),而complex32类型的数组的itemsize为4(=32/8)。

    2.4 打印数组

    2.4.1 数组的打印规则

  • 当打印数组时,NumPy会以类似于嵌套列表的方式显示它,但具有以下布局:

    • 最后一个轴从左到右打印,
    • 倒数第二个从上到下打印,
    • 其余部分也从上到下打印,每个切片用空行分隔。
  • 以一维数组、二维数组、三维数组为例:

    • 一维数组:打印为行。

      1. >>> arr1 = np.arange(6)
      2. >>> arr1
      3. array([0, 1, 2, 3, 4, 5])
    • 二维数组:打印为矩阵。

      1. >>> arr2 = np.arange(12).reshape(4, 3)
      2. >>> arr2
      3. array([[ 0, 1, 2],
      4. [ 3, 4, 5],
      5. [ 6, 7, 8],
      6. [ 9, 10, 11]])
    • 三维数组:打印为矩阵组表。

      1. >>> arr3 = np.arange(24).reshape(2, 3, 4)
      2. >>> arr3
      3. array([[[ 0, 1, 2, 3],
      4. [ 4, 5, 6, 7],
      5. [ 8, 9, 10, 11]],
      6. [[12, 13, 14, 15],
      7. [16, 17, 18, 19],
      8. [20, 21, 22, 23]]])

      2.4.2 打印大数组

  • 当数组规模太大时,会无法打印所有数据,NumPy会自动跳过数据的中心部分并仅打印角点:

    1. >>> np.arange(100000).reshape(1000, -1)
    2. array([[ 0, 1, 2, ..., 97, 98, 99],
    3. [ 100, 101, 102, ..., 197, 198, 199],
    4. [ 200, 201, 202, ..., 297, 298, 299],
    5. ...,
    6. [99700, 99701, 99702, ..., 99797, 99798, 99799],
    7. [99800, 99801, 99802, ..., 99897, 99898, 99899],
    8. [99900, 99901, 99902, ..., 99997, 99998, 99999]])
  • 若有时候真的需要NumPy打印整个数组,则需要将NumPy的set_printoptions中threshold的值设置的足够大。 ```python import sys import numpy as np

np.set_printoptions(threshold=sys.maxsize)

arr = np.arange(100000).reshape(1000, -1) print(arr)

  1. <a name="JvkWw"></a>
  2. ## 03. 数组的索引、切片与迭代(未完成 04.Numpy常用API,数据转换 13:37)
  3. <a name="dPC9e"></a>
  4. ### 3.1 数组的索引
  5. <a name="csSnS"></a>
  6. #### 1.3.1 实验数据准备
  7. - 从50到100中生成20个数据,组成一个4行5列的二维数组。
  8. ```python
  9. In [3]: arr = np.random.randint(50, 100, size=(4, 5))
  10. ...: arr
  11. Out[3]:
  12. array([[96, 68, 82, 73, 99],
  13. [97, 59, 74, 95, 67],
  14. [73, 61, 64, 66, 57],
  15. [62, 58, 65, 50, 71]])

1.3.2 索引单行、单列、单个元素

  • 数组对象[num_of_index]可以获取一行数据。 ```python In [4]: arr[0] Out[4]: array([96, 68, 82, 73, 99])

In [5]: arr[-2] Out[5]: array([73, 61, 64, 66, 57])

  1. - `数组对象[:, n]`可以获取第n列的数组。
  2. - 它的本质是先定位所有行的数据,因此第一个切片参数是`[:]`
  3. - 定位完所有行数据后,再从这些行数据中提取出第n列的数据,因此第二个切片参数是`n`
  4. - 将二者合并在一起就是`[:, n]`
  5. ```python
  6. In [6]: arr[:, 2]
  7. Out[6]: array([82, 74, 64, 65])
  • 数组对象[n, m]可以获取第n行第m列的数据。

    • 它的本质是先定位第n的数据,因此第一个切片参数是n
    • 定位完第n行数据后,再从第n行数据中提取出第m列的数据,因此第二个切片参数是m
    • 将二者合并在一起就是[n, m]
      1. In [7]: arr[2, 3]
      2. Out[7]: 66

      1.3.3 索引多行、多列、多个元素

  • 数组对象[[a, b, c, ……]]可以获取第a行、第b行、第c行、第……行的数据。(即多行数据)

    1. In [8]: arr[[0, 2, 3]]
    2. Out[8]:
    3. array([[96, 68, 82, 73, 99],
    4. [73, 61, 64, 66, 57],
    5. [62, 58, 65, 50, 71]])
  • 数组对象[:, [a, b, c, ……]]可以获取第a列、第b列、第c列、第……列的数据。(即多列数据)

    1. In [9]: arr[:, [0, 1, 3]]
    2. Out[9]:
    3. array([[96, 68, 73],
    4. [97, 59, 95],
    5. [73, 61, 66],
    6. [62, 58, 50]])
  • 数组对象[n:m, z]可以获取n~m行中第z列的数组。

    1. In [10]: arr[1:3, 3]
    2. Out[10]: array([95, 66])

    1.3.4 布尔索引

  • 数组除了基础索引外,还支持布尔序列取值(布尔序列即由多个布尔类型的元素构成的列表)。

  • 布尔索引的取值规则:True索引位上的元素保留,False索引位上的元素舍去。
  • 索引行:需要保证布尔序列中元素的个数与数组中行的个数一样多,如索引第1行和第4行的数据。

    1. In [11]: row_bools = [True, False, False, True]
    2. ...: arr[row_bools]
    3. Out[11]:
    4. array([[96, 68, 82, 73, 99],
    5. [62, 58, 65, 50, 71]])
  • 索引列:需要保证布尔序列中元素的个数与数组中列的个数一样多,如索引第2行、第4行和第5行的数据。

    1. In [12]: column_bools = [False, True, False, True, True]
    2. ...: arr[:, column_bools]
    3. Out[12]:
    4. array([[68, 73, 99],
    5. [59, 95, 67],
    6. [61, 66, 57],
    7. [58, 50, 71]])

    3.2 数组的切片

  • 数组的切片与列表的切片几乎一致,只不过数组有维度的概念,因此可以进行不同方向上的切片。

    1.4.1 实验数据准备

  • 从50到100中生成20个数据,组成一个4行5列的二维数组。

    1. In [2]: arr = np.random.randint(50, 100, size=(4, 5))
    2. ...: arr
    3. Out[2]:
    4. array([[62, 98, 97, 62, 61],
    5. [78, 59, 80, 62, 74],
    6. [62, 67, 70, 84, 56],
    7. [83, 50, 98, 76, 98]])

    1.4.2 行切片

  • 行切片格式:数组对象[start:stop:step]

  • 切取第1、2行的数据

    1. In [3]: arr[1:3]
    2. Out[3]:
    3. array([[78, 59, 80, 62, 74],
    4. [62, 67, 70, 84, 56]])
  • 以行,以步长为2切取整个数组。

    1. In [4]: arr[::2]
    2. Out[4]:
    3. array([[62, 98, 97, 62, 61],
    4. [62, 67, 70, 84, 56]])
  • 负向行切片。

    1. In [5]: arr[::-1]
    2. Out[5]:
    3. array([[83, 50, 98, 76, 98],
    4. [62, 67, 70, 84, 56],
    5. [78, 59, 80, 62, 74],
    6. [62, 98, 97, 62, 61]])

    1.4.3 列切片

  • 列切片格式:数组对象[:, start:stop:step]

  • 切取前三列:

    1. In [6]: arr[:, :3]
    2. Out[6]:
    3. array([[62, 98, 97],
    4. [78, 59, 80],
    5. [62, 67, 70],
    6. [83, 50, 98]])
  • 切取第1、2列:

    1. In [7]: arr[:, 1:3]
    2. Out[7]:
    3. array([[98, 97],
    4. [59, 80],
    5. [67, 70],
    6. [50, 98]])
  • 负向列切片。

    1. In [8]: arr[:, ::-1]
    2. Out[8]:
    3. array([[61, 62, 97, 98, 62],
    4. [74, 62, 80, 59, 78],
    5. [56, 84, 70, 67, 62],
    6. [98, 76, 98, 50, 83]])

    1.4.4 行列结合切片

  • 行列结合切片格式:数组对象[行切片, 列切片]

  • 切取前三行的前三列:

    1. In [9]: arr[:3, :3]
    2. Out[9]:
    3. array([[62, 98, 97],
    4. [78, 59, 80],
    5. [62, 67, 70]])
  • 切取后三行的前三列:

    1. In [10]: arr[-3:, :3]
    2. Out[10]:
    3. array([[78, 59, 80],
    4. [62, 67, 70],
    5. [83, 50, 98]])
  • 切取后三行的后三列:

    1. In [11]: arr[-3:, -3:]
    2. Out[11]:
    3. array([[80, 62, 74],
    4. [70, 84, 56],
    5. [98, 76, 98]])

    3.2 数组的迭代

    04. 数组的运算

    4.1 数组矢量化

    4.1.1 矢量化运算概述

  • 表达式替换显示循环的方式称之为矢量化运算。

  • 对于列表而言,要对其中所有元素进行同样的操作,需要先遍历每一个元素,再对每一个元素进行相同的操作。
  • 如,要将列表中所有元素+1,可以使用以下两种方法: ```python

    方式一:直接遍历

    list1 = [12, 42, 43, 21, 34, 98] for i in range(len(list1)): list1[i] = list1[i] + 1 print(list1) # [13, 43, 44, 22, 35, 99]

方式二:列表推导式

list1 = [12, 42, 43, 21, 34, 98] new_list = [i + 1 for i in list1] print(new_list) # [13, 43, 44, 22, 35, 99]

  1. - 对于数组而言,这些算术运算符会应用到元素级别(广播操作)。
  2. - 即数组可以直接对数据对象进行计算,但实际上数组会自动将计算作用与数组中的元素。
  3. - 如,要将数组中所有元素+1,可以直接对数组对象进行+1
  4. ```python
  5. arr = np.array(list1)
  6. print(arr) # [12 42 43 21 34 98]
  7. print(arr + 1) # [13 43 44 22 35 99]

4.1.2 矢量化运算的示例

  • 数据准备:

    1. arr1 = np.array([8, 23, 49, 3, 22])
    2. arr2 = np.array([4, 35, 18, 41, 34])
  • 加法: ```python

    普通矢量化运算

    arr1 + 6 array([14, 29, 55, 9, 28])

两个数组按位运算

arr1 + arr2 array([12, 58, 67, 44, 56]) ```

  • 减法: ```python

    普通矢量化运算

    arr1 - 10 array([-2, 13, 39, -7, 12])

两个数组按位运算

arr1 - arr2 array([ 4, -12, 31, -38, -12]) ```

  • 乘法: ```python

    普通矢量化运算

    arr1 * 3 array([ 24, 69, 147, 9, 66])

两个数组按位运算

arr1 * arr2 array([ 32, 805, 882, 123, 748]) ```

  • 除法: ```python

    普通矢量化运算

    arr1 / 2 array([ 4. , 11.5, 24.5, 1.5, 11. ])

两个数组按位运算

arr1 / arr2 array([2. , 0.65714286, 2.72222222, 0.07317073, 0.64705882]) ```

  • 次幂: ```python

    普通矢量化运算

    arr1 ** 2 array([ 64, 529, 2401, 9, 484], dtype=int32)

两个数组按位运算

arr1 ** arr2 array([ 4096, -2022515065, 1729633, 2069870691, 0], dtype=int32) ```

  • 取模: ```python

    普通矢量化运算

    arr1 % 2 array([0, 1, 1, 1, 0], dtype=int32)

两个数组按位运算

arr1 % arr2 array([ 0, 23, 13, 3, 22], dtype=int32) ```

4.1.3 矩阵的乘法(数学)

  • 矩阵相乘最重要的方法是矩阵的乘机,它只有在第一个矩阵的列数和第二个矩阵的行数相同时才有意义。
  • 01. NumPy基本操作 - 图501. NumPy基本操作 - 图6的矩阵,01. NumPy基本操作 - 图701. NumPy基本操作 - 图8的矩阵,那么称01. NumPy基本操作 - 图9的矩阵01. NumPy基本操作 - 图10为矩阵01. NumPy基本操作 - 图1101. NumPy基本操作 - 图12的乘积,记作01. NumPy基本操作 - 图13

01. NumPy基本操作 - 图14

  • 其中,矩阵01. NumPy基本操作 - 图15中的第01. NumPy基本操作 - 图16行第01. NumPy基本操作 - 图17列元素可以表示为:

01. NumPy基本操作 - 图18

  • 注意:对于矩阵01. NumPy基本操作 - 图19和矩阵01. NumPy基本操作 - 图20而言,01. NumPy基本操作 - 图21

    4.1.4 矩阵的乘法(NumPy)

  • 4.1.2中介绍的arr1 * arr2是矩阵元素的矢量化运算。

  • 而矩阵的乘法需要使用arr1 @ arr2或者arr1.dot(arr2)的方式实现。 ```python

    数据准备。

    arr1 = np.array([[2, 3, 4], [5, 6, 7]]) arr2 = np.array([[3, 4], [5, 6], [7, 8]])

两个数组的形状不同,无法进行广播操作。

arr1 * arr2 Traceback (most recent call last): File ““, line 1, in ValueError: operands could not be broadcast together with shapes (2,3) (3,2)

此时要相乘,就只能进行矩阵的乘法运算。

arr1 @ arr2 array([[ 49, 58], [ 94, 112]]) array([[ 49, 58], [ 94, 112]]) ```

4.1.5 带有比较运算的矢量化运算

  • 数据准备:

    1. arr1 = np.array([8, 23, 49, 3, 22])
    2. arr2 = np.array([4, 35, 18, 41, 34])
  • 数组的比较运算是针对于数组中每个元素进行的,结果是一个布尔类型的数组。

  • 如,判断数组的每个元素是否大于20,若大于20的,则该索引位为True,否则为False。

    1. >>> arr1 > 20
    2. array([False, True, True, False, True])
  • 结合3.1.4 布尔索引中的知识,可以用逻辑运算筛选出符合条件的数据。

  • 如,筛选出arr1中所有大于20的数据。 ```python

    分开写法:

    flag_bool = arr1 > 20 arr1[flag_bool] array([23, 49, 22])

结合写法:

arr1[arr1 > 20] array([23, 49, 22]) ```

4.1.6 带有逻辑运算的矢量化运算

  • 逻辑运算符:
    • 与运算:用&连接。
    • 或运算:用|连接。
    • 非运算:在条件前用~标注。
    • 多条件时,每个条件要用小括号包裹。
  • 示例1:筛选出arr1中大于20小于30的数据。

    1. >>> arr1 = np.array([8, 23, 49, 3, 22])
    2. >>> arr1[(arr1 > 20) & (arr1 < 30)]
    3. array([23, 22])
  • 示例2:筛选出arr中不大于40的数据。 ```python

    arr1 = np.array([8, 23, 49, 3, 22])

方式一:取反

arr1[~(arr1 > 40)] array([ 8, 23, 3, 22])

方式二:小于等于

arr1[arr1 <= 40] array([ 8, 23, 3, 22]) ```

4.2 聚合函数

4.2.1 聚合函数概述与实验数据

  • 聚合函数是指数学统计中的那些函数。
  • 常见的聚合函数如:最大值、最小值、平均值、标准差、中间值、求和等。
  • 实验数据:从20到100中生成20个数据,组成一个4行5列的二维数组。

    1. >>> arr = np.random.randint(20, 100, size=(4, 5))
    2. >>> arr
    3. array([[39, 96, 79, 61, 38],
    4. [21, 82, 78, 21, 32],
    5. [57, 99, 96, 67, 47],
    6. [64, 36, 48, 53, 31]])

    4.2.2 max()最大/min()最小值

  • NumPy中的min(arr)函数可以求一个数组arr中的最小值。

    1. >>> np.min(arr)
    2. 21
  • NumPy中的max(arr)函数可以求一个数组arr中的最大值。

    1. >>> np.max(arr)
    2. 99

    4.2.3 mean()平均值

  • NumPy中的mean(arr)函数可以求一个数组arr中所有元素的平均值。

    1. >>> np.mean(arr)
    2. 57.25

    4.2.4 标准差与方差的概念

  • 标准差的概念:将样本数据中每个元素的值减去样本的平均值的差的平方和的平均值。

  • 标准差的本质:计算数据样本中每个数据到平均数据的距离。
  • 基本计算方式:如现有一个数据:[23, 67, 15, 92]
    • 第一步:求其平均数:49.25
    • 第二步:求每个元素减去平均数的差的平方之和:

01. NumPy基本操作 - 图22

  • 第三步:求平方和的平均值(即平方之和除以元素个数):1001.1875
  • 第四步:将平方和的平均值开根号:31.641547054466223
    • 总结:假设样本数据x1、x2、x3、……、xn,a为这组数据的平均数。那么样本数据到a的“平均距离”为:

01. NumPy基本操作 - 图23

  • 两个公式:

01. NumPy基本操作 - 图24

  • 标准差的应用场景:标准差和方差都是衡量一组数据离散程度的统计量。在实际运算中,标准差和方差越小,表示离散程度越小(波动越小),代表着数据越稳定。
  • 典型应用:正态分布。

01. NumPy基本操作 - 图25

4.2.5 std()标准差

  • NumPy中的std(arr)函数可以求一个数组arr的标准差。

    1. >>> np.std(np.array([23, 67, 15, 92]))
    2. 31.641547054466223

    4.2.6 sum()求和

  • NumPy中的sum()函数可以求一个数组arr中所有元素之和。

    1. >>> np.sum(arr)
    2. 1145

    4.2.7 中位数的概念

  • 奇数个数据求中位数:先对数据进行从小到大排序,然后取最中间那一个数据的值。

    • 如数据:[86, 21, 43, 76, 31, 53, 56]
    • 排序得:[21, 31, 43, 53, 56, 76, 86]
    • 中间值为:53.0
  • 偶数个数据求中位数:先对数据进行从小到大排序,然后取最中间那两个数据的平均值。

    • 如数据:[86, 43, 53, 31, 76, 56]
    • 排序得:[31, 43, 53, 56, 76, 86]
    • 中间值为:01. NumPy基本操作 - 图26

      4.2.8 median()中位数

  • NumPy中的median(arr)函数可以求一个数组arr的中位数。 ```python

    奇数个数据求中位数

    np.median(np.array([86, 21, 43, 76, 31, 53, 56])) 53.0

偶数个数据求中位数

np.median(np.array([21, 43, 53, 56, 76, 86])) 54.5 ```

4.2.9 axis计算维度

  • axis是一些聚合函数中的属性,如max()、min()、mean()、sum()等函数都有axis属性。
  • axis属性是对数据进行指定维度上的操作:
    • axis=0:表示以列为单位进行聚合函数计算。
    • axis=1:表示以行为单位进行聚合函数计算。
  • 示例1:求取arr中每一行的最大值以及每一列的最小值。 ```python

    每一行的最大值

    np.max(arr, axis=1) array([96, 82, 99, 64])

每一列的最小值

np.min(arr, axis=0) array([21, 36, 48, 21, 31]) ```

  • 示例2:对arr按行求和按列求平均。 ```python

    按行求和

    np.sum(arr, axis=1) array([313, 234, 366, 232])

按列求平均

np.mean(arr, axis=0) array([45.25, 78.25, 75.25, 50.5 , 37. ]) ```

4.3 通函数

4.3.1 通函数的概念

  • NumPy提供熟悉的数学函数,例如sincosexp等,这些函数在NumPy中被称为通函数。
  • 在NumPy中,这些函数会在数组上按照元素进行运算,然后产生一个数组或一个集中结果作为输出。

    4.3.2 sin()正弦函数

  • sin(x)可以返回x的正弦值。

    1. >>> np.sin(1)
    2. 0.8414709848078965
  • sin(数组)则可以返回一组数据的正弦值。

    1. >>> arr1 = np.arange(1, 10)
    2. >>> arr2 = np.sin(arr1)
    3. >>> arr2
    4. array([ 0.84147098, 0.90929743, 0.14112001, -0.7568025 , -0.95892427,
    5. -0.2794155 , 0.6569866 , 0.98935825, 0.41211849])

    4.3.3 cos()余弦函数

  • cos(x)可以返回x的余弦值。

    1. >>> np.cos(1)
    2. 0.5403023058681398
  • cos(数组)则可以返回一组数据的余弦值。

    1. >>> arr1 = np.arange(1, 10)
    2. >>> arr2 = np.cos(arr1)
    3. >>> arr2
    4. array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362, 0.28366219,
    5. 0.96017029, 0.75390225, -0.14550003, -0.91113026])

    4.3.4 all()判断数据是否全为真

  • NumPy中的all(arr)函数用于判断数组arr中的元素是否全为真。若全为真则返回True,有一个为假则返回False。

    1. >>> arr1 = np.array([True, True, True, True])
    2. >>> np.all(arr1)
    3. True
    4. >>> arr2 = np.array([True, False, True, True])
    5. >>> np.all(arr2)
    6. False
  • 同时,all()函数还满足非布尔类型的布尔判断。

    • 非布尔类型的布尔判断规则:0与空为False,其余为True。
      1. >>> arr3 = np.array([12, 0, 34, 56])
      2. >>> np.all(arr3)
      3. False
  • 注意:除了all(arr)之外,arr.all()也可以实现同样的效果。

    1. >>> arr3.all()
    2. False

    4.3.5 any()判断数组中是否有真元素

  • NumPy中的all(arr)函数用于判断数组arr中是否有真元素。即数组中有一个元素为真即范围True,全为假才返回False。

    1. >>> arr1 = np.array([0, 0, 0, 0])
    2. >>> arr1.any()
    3. False
    4. >>> arr2 = np.array([1, 1, 0, 1])
    5. >>> np.any(arr2)
    6. True

    4.3.6 通函数的应用——画图

  • 4.3.2 sin()正弦函数中介绍了sin(数组)则可以返回一组数据的正弦值,那么就可以结合matplotlib库画出一组三角函数图。 ```python import numpy as np import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100) y = np.sin(x)

plt.plot(x, y) plt.show()

  1. - 可以得到图像:
  2. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2692415/1670126477634-da6768a1-f84a-4ca9-8f90-e454c279767f.png#averageHue=%23fafafa&clientId=ufae3ee66-ffeb-4&from=paste&height=244&id=iUm8I&originHeight=244&originWidth=380&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13038&status=done&style=none&taskId=ue54f16d6-b015-46ee-8e34-c13d971c1a8&title=&width=380)
  3. - 注意:绘图部分的知识会重点在03. Matplotlib中讲解,这里只需要知道`sin()`函数的作用即可。
  4. <a name="PV7hI"></a>
  5. ## 05. 数据修改(目前还只是前置课的知识)
  6. - 数组是一个可变的序列,可通过索引修改元素。
  7. - 使用等号修改赋值时,会将等号左边索引到的所有元素都改成等号右边的值。
  8. - 数据准备:从50100中生成20个数据,组成一个45列的二维数组。
  9. ```python
  10. In [2]: arr = np.random.randint(50, 100, size=(4, 5))
  11. ...: arr
  12. Out[2]:
  13. array([[60, 71, 65, 52, 56],
  14. [66, 79, 62, 54, 99],
  15. [57, 53, 84, 96, 59],
  16. [91, 78, 54, 80, 87]])
  • 将第0行中的所有数据都改为99。

    1. In [3]: arr[0] = 99
    2. ...: arr
    3. Out[3]:
    4. array([[99, 99, 99, 99, 99],
    5. [66, 79, 62, 54, 99],
    6. [57, 53, 84, 96, 59],
    7. [91, 78, 54, 80, 87]])
  • 将第3列中的所有数据都改为88。

    1. In [4]: arr[:, 3] = 88
    2. ...: arr
    3. Out[4]:
    4. array([[99, 99, 99, 88, 99],
    5. [66, 79, 62, 88, 99],
    6. [57, 53, 84, 88, 59],
    7. [91, 78, 54, 88, 87]])
  • 将第3行第2列的数据改为77。

    1. In [5]: arr[3, 2] = 77
    2. ...: arr
    3. Out[5]:
    4. array([[99, 99, 99, 88, 99],
    5. [66, 79, 62, 88, 99],
    6. [57, 53, 84, 88, 59],
    7. [91, 78, 77, 88, 87]])
  • 将数组中所有元素改为66。

    1. In [6]: arr[:] = 66
    2. ...: arr
    3. Out[6]:
    4. array([[66, 66, 66, 66, 66],
    5. [66, 66, 66, 66, 66],
    6. [66, 66, 66, 66, 66],
    7. [66, 66, 66, 66, 66]])

    06. 数据的处理(目前还只是前置课的知识)

    6.1 数据去重

  • NumPy中的unique()函数可以取出数组中重复出现的数据,并且在去重完成后会从小到大排序。

    1. In [2]: arr = np.array([1, 3, 1, 5, 4, 7, 8, 1, 7, 7])
    2. ...: np.unique(arr)
    3. Out[2]: array([1, 3, 4, 5, 7, 8])

    6.2 空值(缺失值)处理

    6.2.1 空值的基本认识

  • 在NumPy中,数据的空值也被称为缺失值。

  • Python中的空值是None,但NumPy中的空值是np.nan,是一个浮点类型的数据。

    1. In [2]: type(np.nan)
    2. Out[2]: float
  • 由于NumPy中的空值是float类型的数据,因此它可以与其他数据做运算,但结果依旧为空值。 ```python

    创建一个带有空值的数组

    In [3]: arr = np.array([19, 27, 33, np.nan, 97, 65]) …: arr Out[3]: array([19., 27., 33., nan, 97., 65.])

让数据进行矢量化运算,发现非空的数据都成功完成+1,但空值依旧为空值。

In [4]: arr + 1 Out[4]: array([20., 28., 34., nan, 98., 66.])

  1. <a name="nI6zb"></a>
  2. #### 6.2.2 空值的定位
  3. - 在对空值进行特定的处理之前,首先第一步一定是定位空值。
  4. - NumPy中的isnan()函数会一次对数组中的元素进行判断,结果是一个布尔类型的数组。
  5. - 其中非空元素对应Flase,np.nan对应True。
  6. ```python
  7. In [5]: arr
  8. Out[5]: array([19., 27., 33., nan, 97., 65.])
  9. In [6]: np.isnan(arr)
  10. Out[6]: array([False, False, False, True, False, False])
  • 基于isnan()函数,NumPy还可以实现判断数组中是否存在空值,对此NumPy中有两个函数可以做到这一点:
    • np.all():基于isnan()函数的结果进行判断,只有当序列中结果全为True时,结果才为True。(故用于判断是否是全空值数组) ```python In [7]: arr Out[7]: array([19., 27., 33., nan, 97., 65.])

In [8]: np.all(np.isnan(arr)) Out[8]: False

  1. - np.any():基于isnan()函数的结果进行判断,只要当序列中有一个True时,结果就为True。(故用于判断数组中是否包含空值)
  2. ```python
  3. In [9: arr
  4. Out[9: array([19., 27., 33., nan, 97., 65.])
  5. In [10]: np.any(np.isnan(arr))
  6. Out[10]: True

6.2.3 空值的处理

  • 空值的处理方式有移除和填充两种。
  • 填充:首先根据isnan()函数的布尔数组结果索引空值,然后将索引到的空值改为指定的数据。 ```python In [11]: arr Out[11]: array([19., 27., 33., nan, 97., 65.])

In [12]: np.isnan(arr) Out[12]: array([False, False, False, True, False, False])

In [13]: arr[np.isnan(arr)] = 0 # 索引空值,并将空值修改为0。

In [14]: arr Out[14]: array([19., 27., 33., 0., 97., 65.])

  1. - 移除:即取isnan()函数的布尔数组结果中False对应的值。
  2. ```python
  3. In [17]: arr = np.array([19, 27, 33, np.nan, 97, 65])
  4. In [18]: arr = arr[~np.isnan(arr)]
  5. In [19]: arr # 从结果上来看,np.nan被移除了。
  6. Out[19]: array([19., 27., 33., 97., 65.])