【Python|Numpy】Numpy库核心内容

371 阅读22分钟

本文参考网络上的博文、视频以及Numpy官方文档用自己的理解进行书面化的知识总结,旨在帮助系统认识和使用Numpy库,库中函数很多,在了解常用的基本内容后,建议勤加练习,通过经常使用来熟悉库中的各种函数。

Numpy中文官方文档:www.numpy.org.cn/

练习网站:Numpy入门练习101题

文章内容

  • 基本知识
  • 常用方法和数据类型
  • 随机数生成
  • 广播机制

为什么使用Numpy

NumPy是Python中科学计算的基础包。它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种API,有包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换、基本线性代数,基本统计运算和随机模拟等等.

  1. Numpy底层使用 C 语言数组 来实现,针对许多运算进行了优化,运行效率相对Python中的数据结构效率要高得多

    例:SIMD(Single Instruction Multiple Data):一条命令处理多个数据(CPU优化)。这样就不用再反复去读指令,提高效率。

  2. 支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库,简单便捷
  3. 为很多其它包提供科学数值运算的基础

    如:PyTorch、Scipy

数组创建/维度

ndarray对象:Numpy数组的数据类型,用于存放同类型元素的多维数组,其中每个元素在内存中都有相同存储大小的区域

数组形状:ndarray对象的shape属性,元组,用于表示数组嵌套了几层(维度),每层元素有几个。

shape元组中元素有几个就代表嵌套了几层,每个元素值为该层元素个数,从左到右对应数组的从外到内

  • [1,2][1,2]:维度为 1,shape为 (1,)
  • [[1,2],[3,4]][[1,2], [3,4]]:维度为2,shape为 (2, 2)
  • [[[1,2,3],[2,3,4]]][[[1,2,3], [2,3,4]]]:维度为3,shape为 (1, 2, 3)

数组创建方法

① np.array(object):传入一个数组或嵌套的数列,创建Numpy数组(ndarray)

  • object:序列对象,例如listtuple
  • copy:对象是否需要复制
  • ndmin:指定生成数组的最小维度

方法名称(必要参数):介绍

  • 参数介绍
import numpy as np

arr1 = np.array([1, 2, 3, 4, 5])
# arr = np.array((1, 2, 3, 4))
print(arr1, type(arr1), arr1.shape)   # [1 2 3 4 5] <class 'numpy.ndarray'> (5,)

arr2 = np.array(range(5), ndmin=2)
print(arr2, arr2.shape)   # [[0 1 2 3 4]] (1, 5)

② np.arange(stop):类似range(),指定范围和步长()生成一个 ndarray 数组

  • start:起始值,默认为0
  • stop:终止值
  • step:步长,默认为1

左闭右开:[start, stop)

import numpy as np    # 导包

arr1 = np.arange(5)
print(arr1, arr1.shape)   # [0 1 2 3 4] (5,)

arr2 = np.arange(1, 12, 2)
print(arr2, arr2.shape)   # [ 1  3  5  7  9 11] (6,)

arr3 = np.arange(1, 12, 1.5)
print(arr3, arr3.shape)  # [ 1.   2.5  4.   5.5  7.   8.5 10.  11.5] (8,)

③ np.linspace(start, stop):创建一维数组,生成指定范围内一定数量的等步长数据(等差数列)

  • start:起始值
  • stop:终止值
  • num:默认为50,生成等步长的样本数量
  • endpoint:默认值为True,包含指定步长中的最后一个元素([start, stop]),反之不包含

linspace:linear(线性的,等差的) space

import numpy as np    # 导包

arr1 = np.linspace(1, 10, num=5)
print(arr1, arr1.shape)   # [ 1.    3.25  5.5   7.75 10.  ] (5,)

④ np.logspace(start, stop):创建一个于等比数列

  • start:起始值:basestart^{start}
  • stop:终止值:basestop^{stop}
  • num:默认为50,生成数据元素个数
  • endpoint:默认为True,包含指定范围中最后一个元素
  • base:对数log的底数

例:np.logspace(1, 2, num=2, base=10):在 [10, 100] 范围内生成一个包含两个元素的等比数列作为数组内容

linspace() 和 logspace() 在绘图时用的比较多

import numpy as np    # 导包

arr1 = np.logspace(1, 2, num=2, endpoint=True, base=10)
print(arr1, arr1.shape)   # [ 10. 100.] (2,)

修改数组形状

① np.reshape(arr, newshape):不改变数据的条件下修改形状

  • arr:要修改形状的数组
  • newshape:整数或者整数数组,新的形状应当兼容原有形状。

在更改数组形状时,按维度从小到大进行数据填充,直到符合设置的形状。

例:某数组形状为 (2, 1, 4),从左到右依次为 维度/轴0(2),维度/轴1(1),维度/轴2(4)

例:[1, 2, 3, 4] -> [[1, 2], [3, 4]],则先得到 [1, 2],[3, 4],再得到 [[1, 2], [3, 4]]

传入的newshape是一个包含两个元素的元组,当其中一个值为-1时,表示自适应

import numpy as np    # 导包

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

arr2 = np.arange(10).reshape((1,2,5))
print(arr2, arr2.shape)
# [[[0 1 2 3 4]
#   [5 6 7 8 9]]] (1, 2, 5)

arr3 = np.arange(9).reshape((3,-1))
print(arr3, arr3.shape)
#   [[0 1 2]
#    [3 4 5]
#    [6 7 8]] (3, 3)

arr4 = np.arange(9).reshape((-1,9))
print(arr4, arr4.shape)    # [[0 1 2 3 4 5 6 7 8]] (1, 9)

② arr.flatten():合并所有维度 (展平)

  • arr:数组

flatten:(使)变平

arr1 = np.arange(12).reshape((-1,4))
print(arr1, arr1.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

arr2 = arr1.flatten()
print(arr2, arr2.shape)  # [ 0  1  2  3  4  5  6  7  8  9 10 11] (12,)

③ np.ravel(arr):合并所有维度 (展平)

  • arr:数组

ravel:展平,(把缠绕的物体)拉平

arr1 = np.arange(12).reshape((1, -1, 4))
print(arr1, arr1.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

arr2 = arr1.ravel()
print(arr2, arr2.shape)    # [ 0  1  2  3  4  5  6  7  8  9 10 11] (12,)

arr3 = np.ravel(arr1)
print(arr3, arr3.shape)    # [ 0  1  2  3  4  5  6  7  8  9 10 11] (12,)

flatten()ravel()的区别:flatten()函数返回的是数组的副本 (copy),而ravel()函数返回的是原数组的视图 (view);flatten()函数会在内存中复制一份数组,而ravel()函数不会.

  • 数组的视图 (view):一个数组的不同表示方式,即通过不同的方式访问同一块数据的不同方式。类似于引用,操作的是同一块数据。
  • 建议用flatten()flatten()更安全,但ravel()速度更快

④ np.squeeze(arr):去掉冗余维度

  • arr:数组

squeeze:压缩,挤压

arr1 = np.arange(12).reshape((1, -1, 4))
print(arr1, arr1.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

arr2 = np.squeeze(arr1)
print(arr2, arr2.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

arr3 = arr1.squeeze()
print(arr3, arr3.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

⑤ np.swapaxes(arr, ax1, ax2):交换数组的两个维度(轴),返回一个新数组

  • arr:处理数组
  • ax1:维度1
  • ax2:维度2

swapaxes:swap:交换,axes:数轴,维度.

arr1 = np.arange(8).reshape((1, 2, 4))
print(arr1, arr1.shape)
# [[[0 1 2 3]
#   [4 5 6 7]]] (1, 2, 4)

arr2 = np.swapaxes(arr1, 0, 1)
print(arr2, arr2.shape)
# [[[0 1 2 3]]
#  [[4 5 6 7]]] (2, 1, 4)

⑥ np.transpose(arr, newshape):交换数组的维度

  • arr:处理数组
  • newshape:元组,以数组arr的shape元组中的维度索引来代表某一维度,交换维度,默认为原来顺序的逆序

transpose:转置

可简写为arr.T,等价于arr.transpose(),当为二维数组时,相当于矩阵的转置

arr1 = np.arange(8).reshape((1, 2, 4))
print(arr1, arr1.shape)
# [[[0 1 2 3]
#   [4 5 6 7]]] (1, 2, 4)

arr2 = np.transpose(arr1, (1, 0, 2))
print(arr2, arr2.shape)
# [[[0 1 2 3]]
#  [[4 5 6 7]]] (2, 1, 4)

arr3 = arr1.transpose()
print(arr3, arr3.shape)

arr4 = arr1.T    # 简写, 相当于transpose(arr)
print(arr4, arr4.shape)
# [[[0]
#   [4]]
#  [[1]
#   [5]]
#  [[2]
#   [6]]
#  [[3]
#   [7]]] (4, 2, 1)  arr3, arr4

矩阵初始化

矩阵其实就是一个维度为2的数组,这里介绍的方法数组创建也适用,自定义形状即可

① np.zeros(shape):创建指定大小的数组,数组元素以 0 来填充

  • shape:创建的数组形状
arr1 = np.zeros((2,2))
print(arr1, arr1.shape)
# [[0. 0.]
#  [0. 0.]] (2, 2)

② np.ones(shape):创建指定形状的数组,数组元素以 1 来填充

  • shape:创建的数组形状
arr1 = np.ones((2,2))
print(arr1, arr1.shape)
# [[1. 1.]
#  [1. 1.]] (2, 2)

③ np.full(shape, fill_value):创建指定形状数组,数组元素以fill_value来填充

  • shape:创建的数组形状
  • fill_value:填充元素值
arr1 = np.full((2, 2), 10)
print(arr1, arr1.shape)
# [[10 10]
#  [10 10]] (2, 2)

arr2 = np.full((2, 2), [1, 2])
print(arr2, arr2.shape)
# [[1 2]
#  [1 2]] (2, 2)

arr3 = np.full((2, 2), [[1, 2], [1, 2]])
print(arr3, arr3.shape)
# [[1 2]
#  [1 2]] (2, 2)

需要注意的是,如果fill_value也是一个数组,那么会把其中所有的元素取出来按维度从小到大进行填充,而不是把fill_value看作一个元素进行填充。

例如上面的测试 arr2,若将fill_value看作一个元素,那么结果应该是 [[[1,2],[1,2]][[1,2],[1,2]]]\begin{bmatrix} [[1,2],[1,2]] \\ [[1,2],[1,2]]\end{bmatrix},显然这是错误的.

④ np.zeros_like(arr)/np.ones_like(arr):创建和arr形状相同的矩阵,但元素全为0/1

  • arr:给定要创建相同形状的数组
lst = [1, 2, 3]
arr1 = np.zeros_like(lst)
print(arr1, arr1.shape)    # [0 0 0] (3,)

arr2 = np.ones_like(arr1)
print(arr2, arr2.shape)    # [1 1 1] (3,)

⑤ np.identity(n):创建一个 nn 阶单位矩阵

  • n:单位矩阵都是方阵,形状为 nnn*n

单位矩阵:identity matrix

arr1 = np.identity(3)
print(arr1, arr1.shape)
#   [[1. 0. 0.]
#    [0. 1. 0.]
#    [0. 0. 1.]] (3, 3)

⑥:np.eye(n, m):返回一个矩阵,对角线元素为 1,其他位置为零

  • n:矩阵为 nn
  • m:矩阵为 mm
arr1 = np.eye(3, 4)
print(arr1, arr1.shape)
# [[1. 0. 0. 0.]
#  [0. 1. 0. 0.]
#  [0. 0. 1. 0.]] (3, 4)

⑦ np.triu(matrix):选取矩阵matrix中的上三角部分,返回该矩阵对应的上三角型矩阵

  • matrix:传入的矩阵
  • k:对角线偏移量,默认为0,正数为向上偏移,负数为向下偏移

上三角型矩阵:upper triangular matrix

triu:tri, 表示triangular三角形; u,表示upper

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

arr2 = np.triu(arr1)
print(arr2, arr2.shape)
# [[0 1 2]
#  [0 4 5]
#  [0 0 8]] (3, 3)

arr3 = np.triu(arr1, k=-1)
print(arr3, arr3.shape)
# [[0 1 2]
#  [3 4 5]
#  [0 7 8]] (3, 3)

⑧ np.tril(matrix):选取矩阵matrix中的下三角部分,返回该矩阵对应的下三角型矩阵

  • matrix:传入的矩阵
  • k:对角线偏移量(diagonal offset),默认为0

下三角型矩阵:lower triangular matrix

tril:tri, 表示triangular三角形; u,表示lower

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

arr2 = np.tril(arr1)
print(arr2, arr2.shape)
# [[0 0 0]
#  [3 4 0]
#  [6 7 8]] (3, 3)

arr3 = np.tril(arr1, k=-1)
print(arr3, arr3.shape)
# [[0 0 0]
#  [3 0 0]
#  [6 7 0]] (3, 3)

函数总结

  1. np.array():创建数组
  2. np.arange():创建指定间距的数组
  3. np.linspace()np.logspace():指定范围内插入n个值 (等差/等比)
  4. np.zeros()np.ones()np.full():填充指定形状的数组
  5. np.eye()np.identity():对角线为1的矩阵,前者为矩形,后者为方阵/单位矩阵
  6. np.flatten()np.ravel()np.squeeze():矩阵维度的简化 (展平/删除冗余)
  7. np.swapaxes()np.transpose():改变矩阵维度的顺序/维度交换

随机数生成

基本随机数

① np.random.rand(d0d_0, d1d_1, \cdots, dnd_n):产生[0, 1)范围的浮点随机数

  • d0, d1, ... , dn:第n个维度的长度,默认d0 = 1
arr1 = np.random.rand()    # 产生一个随机数
print(arr1)    # 0.38355872110707856

arr2 = np.random.rand(3)    # 产生1*3的随机数
print(arr2, arr2.shape)    # [0.39750525 0.15387126 0.30989765] (3,)

arr3 = np.random.rand(2, 3, 2)    # 产生2*3*2的随机数
print(arr3, arr3.shape)
# [[[0.43578811 0.9232773 ]
#   [0.53367378 0.25064657]
#   [0.13740034 0.9222307 ]]
#  [[0.38711867 0.0190128 ]
#   [0.80526692 0.79917576]
#   [0.51573047 0.54251812]]] (2, 3, 2)

② np.random.random_sample(size):产生[0, 1)范围内的浮点随机数

  • size:生成的数组形状

效果上np.random.random_sample()np.random.rand()相同,仅有参数不同

arr1 = np.random.random_sample()    # 产生一个随机数
print(arr1)    # 0.6361677466207701

arr2 = np.random.random_sample((3,))    # 产生1*3的随机数
print(arr2, arr2.shape)    # [0.12840137 0.33108969 0.76329813] (3,)

arr3 = np.random.random_sample((2, 3, 2))    # 产生2*3*2的随机数
print(arr3, arr3.shape)
# [[[0.05188732 0.97794635]
#   [0.02034027 0.74506015]
#   [0.47518308 0.59382483]]
#  [[0.49501536 0.69473901]
#   [0.07302364 0.72649196]
#   [0.95626467 0.74772601]]] (2, 3, 2)

③ np.random.randint(low, high, size):产生[low, high)范围内的整型随机数

  • low, high:随机数范围,low默认为0
  • size:生成数组的形状,默认为None,也就是产生一个数
arr1 = np.random.randint(10)   # 产生一个随机数
print(arr1)  # 9

arr2 = np.random.randint(1, 10, (1, 3))    # 产生1*3的随机数
print(arr2, arr2.shape)    # [[3 7 7]] (1, 3)

arr3 = np.random.randint(1, 10, (2, 3, 2))    # 产生2*3*2的随机数
print(arr3, arr3.shape)
# [[[8 6]
#   [3 3]
#   [6 1]]
#  [[8 7]
#   [5 7]
#   [2 9]]] (2, 3, 2)

常用分布随机数生成

① np.random.randn(d0d_0, d1d_1, ..., dnd_n):产生标准正态分布的随机数

  • d0, d1, ..., dn:dn表示第n个维度的长度,默认d0=1d_0=1
arr1 = np.random.randn()   # 产生一个随机数
print(arr1)  # 1.5758304178304428

arr2 = np.random.randn(3)    # 产生1*3的随机数
print(arr2, arr2.shape)    # [-1.18105847 -0.42935446  0.30772763] (3,)

arr3 = np.random.randn(2, 3, 2)    # 产生2*3*2的随机数
print(arr3, arr3.shape)
# [[[ 0.35657307  0.21227592]
#   [ 0.76251832  0.05266431]
#   [-0.22340997  1.05344811]]
#  [[-0.84855443 -1.77534048]
#   [ 1.35671891  1.63834328]
#   [ 0.29055626  0.6890386 ]]] (2, 3, 2)

② np.random.normal(loc, scale, size):产生正态分布随机数

  • loc, scale:前者对应期望 μ\mu ,后者对应标准差 σ\sigma,默认参数为标准正态分布
  • size:生成数组的形状
arr1 = np.random.normal()
print(arr1)    # -0.6566102139999488

arr2 = np.random.normal(size=(1,2))
print(arr2, arr2.shape)    # [[ 0.25710176 -1.10576116]] (1, 2)

arr3 = np.random.normal(1, 2, (2,2))
print(arr3, arr3.shape)
# [[ 1.41109961  1.76193233]
#  [-0.79156642  2.40200455]] (2, 2)

③ np.random.uniform(low, high, size):产生均匀分布随机数

  • low, high:确定随机数范围,[low, high)
  • size:生成数组的形状
arr1 = np.random.uniform()
print(arr1)    # 0.8139235157803811

arr2 = np.random.uniform(size=(1,2))
print(arr2, arr2.shape)    # [[0.91631668 0.69787196]] (1, 2)

arr3 = np.random.uniform(1, 2, (2,2))
print(arr3, arr3.shape)
# [[1.93483797 1.29597413]
#  [1.23901182 1.48961812]] (2, 2)

其它还有二项分布(np.random.binomial())、泊松分布(np.random.poisson())、卡方分布(np.random.chisquare())...需要使用的时候再查即可

数组选取/运算

前面提到过,矩阵就是一个二维的数组,矩阵拼接等方法同理

数组拼接

① np.hstack(arrs):水平方向拼接数组,返回一个新数组

  • arrs:(arr1, arr2...)

hstack:horizontal (水平的) stack (堆叠,叠加)

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

arr4 = np.hstack((arr1, arr2, arr3))
print(arr4, arr4.shape)  # [1 2 3 4 5 6 7 8 9] (9,)

② np.vstack(arrs):垂直方向拼接矩阵(数组),返回一个新矩阵(数组)

  • arrs:(arr1, arr2...)

vstack:vertical(垂直的) stack(堆叠,叠加)

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

arr4 = np.vstack((arr1, arr2, arr3))
print(arr4, arr4.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

③ np.concatenate(arrs, axis):

  • arrs:(arr1, arr2...)
  • axis:指定数组拼接的轴,默认为0 (水平方向拼接)

concatenate:拼接,连接

axis:轴/维度,一般用于指定轴,axes是axis的复数形式,一般用于指定多个轴/维度

在使用vstack()函数的时候,多个一维数组可以拼接为二维数组,但不能在concatenate()函数中这样使用,在concatenate()函数中只能允许二维数组拼接为二维数组。因为concatenate()函数中要指定按某一轴进行拼接,若待拼接数组没有这一轴(一维数组只有轴0, 没有轴1),则会报错,无法进行拼接。

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

arr4 = np.concatenate((arr1, arr2, arr3))
print(arr4, arr4.shape)  # [1 2 3 4 5 6 7 8 9] (9,)

arr5 = np.concatenate((arr1, arr2, arr3), axis=0)
print(arr5, arr5.shape)  # [1 2 3 4 5 6 7 8 9] (9,)

arr1 = arr1.reshape((1, 3))
arr2 = arr2.reshape((1, 3))
arr3 = arr3.reshape((1, 3))
print(arr1.shape)  # (1, 3)

arr6 = np.concatenate((arr1, arr2, arr3), axis=1)
print(arr6, arr6.shape)  # [[1 2 3 4 5 6 7 8 9]] (1, 9)

arr7 = np.concatenate((arr1, arr2, arr3), axis=0)
print(arr7, arr7.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

对于数组拼接,应保证待拼接的数组的形状相同,否则报错.

数组选取

① 类似于切片操作,不过这里是多维度的切片

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

print(arr1[:2, :2])
# [[0 1]
#  [3 4]]

print(arr1[:2, 0])    # [0 3]

print(arr1[:, [0, 2]])
# [[0 2]
#  [3 5]
#  [6 8]]

exp1 = [0, 2]

print(arr1[exp1])
# [[0 1 2]
#  [6 7 8]]
  • [:2, :2]:选取前2行中每行的前2个元素
  • [:2, 0]:选取前2行中每行的第0个元素
  • [:, [0, 2]]:选取所有行中每行的第0个元素和第2个元素

② np.diag(matrix):将行向量转为对角矩阵 / 将矩阵中的对角元素提取出来转为行向量

  • matrix:处理矩阵,维数小于等于2
  • k:默认为0,对角线偏移量

diag:diagonal (对角线)

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

arr2 = np.diag(arr1)
print(arr2, arr2.shape)    # [0 4 8] (3,)

arr3 = np.diag(np.arange(3).reshape(-1))
print(arr3, arr3.shape)
# [[0 0 0]
#  [0 1 0]
#  [0 0 2]] (3, 3)

③ np.where(condition, arr, value):用法1,保留符合条件的值,不符合条件的值用value替换;用法2,根据条件选取特定的值,返回符合条件的索引

  • condition:条件
  • arr:处理数组
  • value:替换值

条件中的判断条件用参数arr表示arr中的每个元素,注意应与数组名称相同

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

# 用法 1
arr2 = np.where(arr1>4, arr1, 0)
print(arr2)
# [[0 0 0]
#  [0 0 5]
#  [6 7 8]]

exp1 = arr1 > 4
arr3 = np.where(exp1, arr1, 0)
print(arr3)
# [[0 0 0]
#  [0 0 5]
#  [6 7 8]]

# 用法 2
exp2 = np.where(arr1 > 4)
print(exp2)    # (array([1, 2, 2, 2], dtype=int64), array([2, 0, 1, 2], dtype=int64))
print(arr1[exp2])    # [5 6 7 8]

# []本身也支持条件选取
print(arr1[arr1 > 4])    # [5 6 7 8]

④ np.argwhere(condition):根据条件返回符合条件的数值的位置

  • condition:条件

np.where()不同的是,前者返回的是每一个元素行和列拼接的一个序列,np.argwhere()返回的是一个list,里面的每个元素就是每个符合条件的数据的位置

arr1 = np.arange(9).reshape((3, -1))
print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

arr2 = np.argwhere(arr1 > 4)
print(arr2)
# [[1 2]
#  [2 0]
#  [2 1]
#  [2 2]]

exp = arr1 > 4
print(np.argwhere(exp))
# [[1 2]
#  [2 0]
#  [2 1]
#  [2 2]]

⑤ np.isnan(value)/ np.isinf(value):判断是否是 空值/无穷大值

arr1 = np.array([-1, 0, 1])
logarr1 = np.log(arr1)
print(logarr1, logarr1.shape)    # [ nan -inf   0.] (3,)

print(np.isinf(logarr1))    # [False  True False]
print(np.isnan(logarr1))    # [ True False False]

# 错误示范: np.where(logarr1 == np.inf) ×
indexes_inf = np.where(np.isinf(logarr1))
indexes_nan = np.where(np.isnan(logarr1))

print(indexes_inf)    # (array([1], dtype=int64),)
print(indexes_nan)    # (array([0], dtype=int64),)

np.log(arr):对arr中每个元素做底数为 e 的取对数操作

不能用 "==" 来判断是否为 空值或极大值

np.inf()np.isnan()返回一个bool列表,符合条件的元素位置为True,反之为False

数值运算

① Numpy中常用的常量

  • np.nan:空值
  • np.inf:无穷大值
  • np.e:常数 e
  • np.pi:常数 π\pi
  • np.newaxis:空数轴
# 常用常量
print(np.nan, type(np.nan))    # nan <class 'float'>
print(np.pi, type(np.pi))    # 3.141592653589793 <class 'float'>
print(np.inf, type(np.inf))    # inf <class 'float'>
print(np.e, type(np.e))    # 2.718281828459045 <class 'float'>
print(np.newaxis, type(np.newaxis))    # None <class 'NoneType'>

# 对于空数轴 newaxis
arr1 = np.arange(3)
print(arr1, arr1.shape)    # [0 1 2] (3,)

arr2 = arr1[np.newaxis, np.newaxis, :]
print(arr2, arr2.shape)    # [[[0 1 2]]] (1, 1, 3)

# arr2 和 arr3 等价
arr3 = arr1.reshape(1, 1, -1)
print(arr3.shape)    # (1, 1, 3)

② Numpy数组的加减乘除:使用符号 +,-,*,/ 即可

  • 数组中与数字进行加减乘除操作,相当于数组中所有元素对该数进行运算操作 (按位运算).
  • 数组与数组进行加减乘除操作,相当于对应位置的元素进行运算操作

这里的按位运算是指按数组中每一位的数据进行运算,和位运算没关系

arr1 = np.arange(9).reshape((3, -1))
arr2 = np.arange(9, 18).reshape((3, -1))

print(arr1, arr1.shape)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]] (3, 3)

print(arr2, arr2.shape)
# [[ 9 10 11]
#  [12 13 14]
#  [15 16 17]] (3, 3)

# 矩阵乘以常数
arr3 = arr1 + 1
print(arr3, arr3.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

arr4 = arr1 - 1
print(arr4, arr4.shape)
# [[-1  0  1]
#  [ 2  3  4]
#  [ 5  6  7]] (3, 3)

arr5 = arr1 * 2
print(arr5, arr5.shape)
# [[ 0  2  4]
#  [ 6  8 10]
#  [12 14 16]] (3, 3)

arr6 = arr1 / 10
print(arr6, arr6.shape)
# [[0.  0.1 0.2]
#  [0.3 0.4 0.5]
#  [0.6 0.7 0.8]] (3, 3)

# 矩阵之间的四则运算
arr7 = arr1 + arr2
print(arr7, arr7.shape)
# [[ 9 11 13]
#  [15 17 19]
#  [21 23 25]] (3, 3)

arr8 = arr1 - arr2
print(arr8, arr8.shape)
# [[-9 -9 -9]
#  [-9 -9 -9]
#  [-9 -9 -9]] (3, 3)

arr9 = arr1 * arr2
print(arr9, arr9.shape)
# [[  0  10  22]
#  [ 36  52  70]
#  [ 90 112 136]] (3, 3)

arr10 = arr1 / arr2
print(arr10, arr10.shape)
# [[0.         0.1        0.18181818]
#  [0.25       0.30769231 0.35714286]
#  [0.4        0.4375     0.47058824]] (3, 3)

③ 常用按位运算方法

  • np.log(arr):按位以 ee 为底数取对数
  • np.exp(arr):按位取 exe^x
  • np.sin(arr) / np.cos(arr) / np.tan(arr):按位去对应函数值

    反正弦/余弦...函数前面加arc即可

  • np.power(arr, n):按位取 xnx^n
arr1 = np.arange(1, 10).reshape((3, -1))
print(arr1, arr1.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

print(np.log(arr1))
# [[0.         0.69314718 1.09861229]
#  [1.38629436 1.60943791 1.79175947]
#  [1.94591015 2.07944154 2.19722458]]

print(np.exp(arr1))
# [[2.71828183e+00 7.38905610e+00 2.00855369e+01]
#  [5.45981500e+01 1.48413159e+02 4.03428793e+02]
#  [1.09663316e+03 2.98095799e+03 8.10308393e+03]]

print(np.sin(arr1))
# [[ 0.84147098  0.90929743  0.14112001]
#  [-0.7568025  -0.95892427 -0.2794155 ]
#  [ 0.6569866   0.98935825  0.41211849]]

print(np.cos(arr1))
# [[ 0.54030231 -0.41614684 -0.9899925 ]
#  [-0.65364362  0.28366219  0.96017029]
#  [ 0.75390225 -0.14550003 -0.91113026]]

print(np.tan(arr1))
# [[ 1.55740772 -2.18503986 -0.14254654]
#  [ 1.15782128 -3.38051501 -0.29100619]
#  [ 0.87144798 -6.79971146 -0.45231566]]

print(np.power(arr1, 2))
# [[ 1  4  9]
#  [16 25 36]
#  [49 64 81]]

④ np.sum(arr):求和

  • axis:指定按某一轴求和;默认为数组中全部元素求和
arr1 = np.arange(1, 10).reshape((3, -1))
print(arr1, arr1.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

print(np.sum(arr1))    # 45

print(np.sum(arr1, axis=0))    # 按列求和  [12 15 18]

print(np.sum(arr1, axis=1))    # 按行求和  [ 6 15 24]

⑤ np.cumsum(arr):返回一个新的数组,其中每个元素是原始数组中该位置及之前所有元素的和

  • axis:按指定轴进行累积;默认会从左到右从上到下对每个元素求累积并放到一个一维数组中.
arr1 = np.arange(1, 10).reshape((3, -1))
print(arr1, arr1.shape)
# [[1 2 3]
#  [4 5 6]
#  [7 8 9]] (3, 3)

print(np.cumsum(arr1))    # [ 1  3  6 10 15 21 28 36 45]

print(np.cumsum(arr1, axis=0))
# [[ 1  2  3]
#  [ 5  7  9]
#  [12 15 18]]

print(np.cumsum(arr1, axis=1))
# [[ 1  3  6]
#  [ 4  9 15]
#  [ 7 15 24]]

cumsum:cumulative sum 累积和

⑥ np.dot(vec1, vec2):求两向量的内积 (点乘)

arr1 = np.array([1, 2])
arr2 = np.array([1, 2])

print(np.dot(arr1.T, arr2))    # 5
print(np.dot(arr1, arr2))    # 自动求转置后相乘 5

[12][12]=11+22=5\begin{bmatrix}1 & 2\end{bmatrix}\begin{bmatrix}1 \\ 2\end{bmatrix}=1*1+2*2=5

对于二维数组,它相当于矩阵乘法,对于一维数组,则是向量的内积,对于n维数组,它是a的最后一个轴向和b的倒数第二个轴向的乘积和。

⑦ np.outer(vec1, vec2):求两向量的外积 (叉乘)

arr1 = np.array([1, 2])
arr2 = np.array([1, 2])

print(np.outer(arr1, arr2.T))
print(np.outer(arr1, arr2))    # 自动求转置后相乘
# [[1 2]
#  [2 4]]

[12][12]=[1224]\begin{bmatrix}1 \\ 2\end{bmatrix}\begin{bmatrix}1 & 2\end{bmatrix}=\begin{bmatrix} 1 & 2 \\ 2 & 4 \end{bmatrix}

⑧ np.multiply(arr1, arr2):两矩阵对应位置元素的乘积,相当于 *

维度相等时才能使用,当数组或者矩阵的维度不相同时,会根据一定的广播规则 (下面会说明) 将维数扩充到一致的形式

arr1 = np.arange(1, 5).reshape((2, -1))
arr2 = np.arange(5, 9).reshape((2, -1))

print(arr1)
# [[1 2]
#  [3 4]]

print(arr2)
# [[5 6]
#  [7 8]]

print(np.multiply(arr1, arr2))
# [[ 5 12]
#  [21 32]]

⑨ np.matmul(arr1, arr2):矩阵乘法

arr1 = np.arange(1, 5).reshape((2, -1))
arr2 = np.arange(5, 9).reshape((2, -1))

print(arr1)
# [[1 2]
#  [3 4]]

print(arr2)
# [[5 6]
#  [7 8]]

print(np.matmul(arr1, arr2))
print(arr1 @ arr2)
# [[19 22]
#  [43 50]]

np.matmul()等价于符号 @

matmul:matrix multiply

广播机制 (Broadcasting)

广播(Broadcast) 是 numpy 对于不同形状的数组进行数值计算的方式。

a = np.array([1, 2, 3])
b = 2
print(a * b)    # [2 4 6]

计算过程:[123]2=[123][222]=[246]\begin{bmatrix}1 & 2 & 3\end{bmatrix}*2=\begin{bmatrix}1 & 2 & 3\end{bmatrix}*\begin{bmatrix}2 & 2 & 2\end{bmatrix}=\begin{bmatrix}2 & 4 & 6\end{bmatrix}

  • shape1: (3,)(3,)
  • shape2:(1,)(1,)
  • 广播:(1,)(3,)(1,) \rightarrow (3,)

ps:这里把2写成shape为(1,)(1,)是维度变化后的情况,因为广播的时候会把维度小的数组维度变换成和另一个数组一致的情况,然后再将数据进行复制变换成相同形状,下面会详细介绍.

arr1 = np.array([[0, 0, 0],
                 [10, 10, 10],
                 [20, 20, 20],
                 [30, 30, 30]])
arr2 = np.array([1, 2, 3])

arr = arr1 + arr2
print(arr, arr.shape)
# [[ 1  2  3]
#  [11 12 13]
#  [21 22 23]
#  [31 32 33]] (4, 3)

计算过程:

[000101010202020303030]+[123]=[000101010202020303030]+[123123123123]=[123111213212223313233]\begin{bmatrix}0 & 0 & 0 \\ 10 & 10 & 10 \\ 20 & 20 & 20 \\ 30 & 30 & 30\end{bmatrix}+\begin{bmatrix}1 & 2 & 3\end{bmatrix}=\begin{bmatrix}0 & 0 & 0 \\ 10 & 10 & 10 \\ 20 & 20 & 20 \\ 30 & 30 & 30\end{bmatrix}+\begin{bmatrix}1 & 2 & 3 \\ 1 & 2 & 3 \\ 1 & 2 & 3 \\ 1 & 2 & 3\end{bmatrix}=\begin{bmatrix}1 & 2 & 3 \\ 11 & 12 & 13 \\ 21 & 22 & 23 \\ 31 & 32 & 33\end{bmatrix}

  • shape1:(4,3)(4, 3)
  • shape2:(1,3)(1, 3)
  • 广播:(1,3)(4,3)(1, 3) \rightarrow (4, 3)

Broadcast(广播):In order to broadcast, the size of the trailing axes for both arrays in an operation must either be the same size or one of them must be one. (官网)

对于两个数组进行运算,对两个数组的维度从右往左进行比较,对于两数组的每个维度,必须 相等 或 满足其中一个为1

  • 如果不存在则默认为 1

例如上方的案例,shape1:(4,3)(4, 3),shape2:(1,3)(1, 3)

  1. 从右向左比较维度,第一个维度都为3,相同
  2. 第二个维度一个为4,另一个为1
  3. 将第二个数组元素进行复制,变换至第二个维度为4

[00010100]23+[1234]14\begin{bmatrix} 0 & 0 & 0 \\ 10 & 10 & 0 \end{bmatrix}_{2*3} + \begin{bmatrix} 1 & 2 & 3 & 4\end{bmatrix}_{1*4}

  • 这个案例就无法使用广播机制进行运算,因为从右往左两数组的第一个维度不相等也不满足其中一个为1

如果我们想对形状不同的数组应用广播机制进行计算,该如何进行呢?

arr1 = np.array([0, 10, 20, 30])  # (4,)
arr2 = np.array([1 , 2, 3])    # (3, )

print(arr1[:, np.newaxis] + arr2)    # (4, 1) + (1, 3)
# [[ 1  2  3]
#  [11 12 13]
#  [21 22 23]
#  [31 32 33]]

可以看到原来arr1arr2的形状是无法用广播机制的,但是经过变换,我们将arr1变换成了(4,1)(4, 1),两个维度都满足其中一个数组为1,这样就能正常运用广播机制进行计算了

计算过程:

  1. [0102030]>[0102030]\begin{bmatrix} 0 & 10 & 20 & 30 \end{bmatrix} -> \begin{bmatrix} 0 \\ 10 \\ 20 \\ 30 \end{bmatrix}

  2. [0102030]+[123]=[000101010202020303030]+[123123123123]=[123111213212223313233]\begin{bmatrix} 0 \\ 10 \\ 20 \\ 30 \end{bmatrix}+\begin{bmatrix} 1 & 2 & 3 \end{bmatrix} = \begin{bmatrix} 0 & 0 & 0 \\ 10 & 10 & 10 \\ 20 & 20 & 20 \\ 30 & 30 & 30 \end{bmatrix}+\begin{bmatrix} 1 & 2 & 3 \\ 1 & 2 & 3 \\ 1 & 2 & 3 \\ 1 & 2 & 3 \end{bmatrix} = \begin{bmatrix} 1 & 2 & 3 \\ 11 & 12 & 13 \\ 21 & 22 & 23 \\ 31 & 32 & 33 \end{bmatrix}

其它案例:

arr1 = np.arange(16).reshape((4, 4))
arr2 = np.arange(8).reshape((2,4))

print(arr1, arr1.shape)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]] (3, 4)

print(arr2, arr2.shape)
# [[0 1 2 3]
#  [4 5 6 7]] (2, 4)

print(arr1 + arr2)   # 无法广播

从右向左第二个维度没有1,且不相同

函数总结

拼接和选取

  1. np.hstack(), np.vstack(), np.concatenate():数组的拼接
  2. [][] 切片操作 (slicing),类似Python中列表的切片
  3. np.diag():选取对角线上的内容或创建对角矩阵
  4. np.where()np.argwhere():前者可以设置替换值,也可以返回符合条件的元素索引,后者返回符合条件元素索引list

数值运算

  1. 预定义的常数:np.inf/np.e/np.pi/np.nan
  2. 预定义的特殊值:np.nan/np.inf/np.newaxis
  3. 数组与数组对应元素之间的运算/数组与数字的按位运算:+,-,*,/
  4. 特殊函数按位运算:np.exp()/np.log()/np/sin()/np.power()...
  5. 数组内的数字运算:np.sum()/np.cumsum()...
  6. 向量内积和外积:np.dot()/np.outer()
  7. 矩阵乘法:np.matmul()/@
  8. 广播机制 (Broadcast)

数值类型

数据类型

(表格来自官方文档)

Numpy 的类型C 的类型描述
np.int8int8_t字节(-128到127)
np.int16int16_t整数(-32768至32767)
np.int32int32_t整数(-2147483648至2147483647)
np.int64int64_t整数(-9223372036854775808至9223372036854775807)
np.uint8uint8_t无符号整数(0到255)
np.uint16uint16_t无符号整数(0到65535)
np.uint32uint32_t无符号整数(0到4294967295)
np.uint64uint64_t无符号整数(0到18446744073709551615)
np.intpintptr_t用于索引的整数,通常与索引相同 ssize_t
np.uintpuintptr_t整数大到足以容纳指针
np.float32float
np.float64 / np.float_double请注意,这与内置python float的精度相匹配。
np.complex64float complex复数,由两个32位浮点数(实数和虚数组件)表示
np.complex128 / np.complex_double complex请注意,这与内置python 复合体的精度相匹配。

很常用的 intfloat 掌握,其它等用到的时候再查即可

数据类型相关操作

① 可以使用.dtype属性来查看数据类型,每个numpy数组中所有元素的数据类型都是统一的

arr1 = np.arange(4).reshape((2, 2))
print(arr1, arr1.dtype)
# [[0 1]
#  [2 3]] int32

arr2 = arr1 - 1.1
print(arr2, arr2.dtype)
# [[-1.1 -0.1]
#  [ 0.9  1.9]] float64

arr3 = arr1 + (1+1j)
print(arr3, arr3.dtype)
# [[1.+1.j 2.+1.j]
#  [3.+1.j 4.+1.j]] complex128

自动推导:在运算过程中,数组元素的数据类型会自动推导得到相应数据类型

② np.astype(type):强制转换数组的元素数据类型

  • type:numpy数据类型
arr1 = np.arange(4).reshape((2, 2))
print(arr1, arr1.dtype)
# [[0 1]
#  [2 3]] int32

arr2 = arr1.astype(np.float64)
print(arr2, arr2.dtype)    # float64
# [[0. 1.]
#  [2. 3.]] float64

arr3 = arr1.astype(int)
print(arr3.dtype)    # int32

arr4 = arr1.astype(np.bool_)
print(arr4, arr4.dtype)
# [[False  True]
#  [ True  True]] bool

Complex Warning:这种强制的转换可能会报这样的错误,提示可能丢失了一些数据信息

Reference

  1. b站讲解视频视频:Numpy库核心内容
  2. 知乎文章:Numpy中常用随机函数的总结
  3. 菜鸟教程:NumPy 教程
  4. Numpy中文官方文档:NumPy中文官方文档