分享

Python | Numpy 图文详解(向量、切片索引、广播)

 excel05 2023-06-25 发布于福建

NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。
NumPy 是一个运行速度非常快的数学库,主要用于数组计算,包含:

  • 一个强大的N维数组对象 ndarray

  • 广播功能函数

  • 整合 C/C++/Fortran 代码的工具

  • 线性代数、傅里叶变换、随机数生成等功能

常用向量

张量(Tensor):Tensor = multi-dimensional array of numbers 张量是一个多维数组,它是标量,向量,矩阵的高维扩展 ,是一个数据容器,张量是矩阵向任意维度的推广

注意,张量的维度(dimension)通常叫作轴(axis), 张量轴的个数也叫作阶(rank)]

标量(scalar):只有一个数字的张量叫标量(也叫标量张量、零维张量、0D 张量)

x = np.array(12)
print(x.ndim) 可以用 ndim 属性来查看一个 Numpy 张量的轴的个数。标量张量有 0 个轴( ndim == 0 )。

向量(vector):数字组成的数组叫作向量(vector)或一维张量(1D 张量)。一维张量只有一个轴。下面是一个 Numpy 向量

np.array([12, 3, 6, 14, 7])
这个向量有 5 个元素,所以被称为 5D 向量。不要把 5D 向量和 5D 张量弄混! 5D 向量只有一个轴,沿着轴有 5 个维度,而 5D 张量有 5 个轴(沿着每个轴可能有任意个维度)

矩阵(matrix):是一个按照长方阵列排列的复数或实数集合,矩阵是二维张量(2D 张量)

np.array([[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]])
向量组成的数组叫作矩阵(matrix)或二维张量(2D 张量)。矩阵有 2 个轴(通常叫作行和列)。你可以将矩阵直观地理解为数字组成的矩形网格。下面是一个 Numpy 矩阵。

3D 张量与n 维张量
将多个矩阵组合成一个新的数组,可以得到一个 3D 张量,你可以将其直观地理解为数字组成的立方体。下面是一个 Numpy 的 3D 张量。

np.array([[[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]], [[5, 78, 2, 34, 0], [6, 79, 3, 35, 1], [7, 80, 4, 36, 2]]])

将多个 3D 张量组合成一个数组,可以创建一个 4D 张量,以此类推。深度学习处理的一般是 0D 到 4D 的张量,但处理视频数据时可能会遇到 5D 张量。

文章图片1
文章图片2

张量属性

张量是由以下三个关键属性来定义的。

  • 轴的个数(阶):例如,3D 张量有 3 个轴,矩阵有 2 个轴。这在 Numpy 等 Python 库中也叫张量的 ndim 。

  • 形状(shape):这是一个整数元组,表示张量沿每个轴的维度大小(元素个数)。例如,前面矩阵示例的形状为 (3, 5) ,3D 张量示例的形状为 (3, 3, 5) 。向量的形状只包含一个元素,比如 (5,) ,而标量的形状为空,即 () 。(张量的形状)

  • 数据类型(dtype):这是张量中所包含数据的类型,例如,张量的类型可以是 float32 、 uint8 、 float64 等。在极少数情况下,你可能会遇到字符( char )张量。注意:Numpy(以及大多数其他库)中不存在字符串张量,因为张量存储在预先分配的连续内存段中,而字符串的长度是可变的,无法用这种方式存储。

data: Tensor的值;
dtype: Tensor的数据类型;
shape: Tensor的形状;
device: Tensor所在的设备(CPU/GPU);
requires_grad: 是否需要梯度;
grad: Tensor的梯度;
grad_fn: 创建Tensor的函数;
is_leaf: 是否是叶子节点

文章图片3

数据张量

向量数据:2D 张量,形状为 (samples, features)

这是最常见的数据。对于这种数据集,每个数据点都被编码为一个向量,因此一个数据批量就被编码为 2D 张量(即向量组成的数组),其中第一个轴是样本轴,第二个轴是特征轴。
例子:

  • 人口统计数据集,其中包括每个人的年龄、邮编和收入。每个人可以表示为包含 3 个值的向量,而整个数据集包含 100 000 个人,因此可以存储在形状为 (100000, 3) 的 2D张量中。

  • 文本文档数据集,我们将每个文档表示为每个单词在其中出现的次数(字典中包含20 000 个常见单词)。每个文档可以被编码为包含 20 000 个值的向量(每个值对应于字典中每个单词的出现次数),整个数据集包含 500 个文档,因此可以存储在形状为(500, 20000) 的张量中。

时间序列数据或序列数据:3D 张量,形状为 (samples, timesteps, features)

当时间(或序列顺序)对于数据很重要时,应该将数据存储在带有时间轴的 3D 张量中。每个样本可以被编码为一个向量序列(即 2D 张量),因此一个数据批量就被编码为一个 3D 张量(见下图)

文章图片4


根据惯例,时间轴始终是第 2 个轴(索引为 1 的轴)。

我们来看几个例子。

  • 股票价格数据集。每一分钟,我们将股票的当前价格、前一分钟的最高价格和前一分钟的最低价格保存下来。因此每分钟被编码为一个 3D 向量,整个交易日被编码为一个形状为 (390, 3) 的 2D 张量(一个交易日有 390 分钟),而 250 天的数据则可以保存在一个形状为 (250, 390, 3) 的 3D 张量中。这里每个样本是一天的股票数据。

  • 推文数据集。我们将每条推文编码为 280 个字符组成的序列,而每个字符又来自于 128个字符组成的字母表。在这种情况下,每个字符可以被编码为大小为 128 的二进制向量(只有在该字符对应的索引位置取值为 1,其他元素都为 0)。那么每条推文可以被编码为一个形状为 (280, 128) 的 2D 张量,而包含 100 万条推文的数据集则可以存储在一个形状为 (1000000, 280, 128) 的张量中。

图像:3D 张量 形状为 (height,width,channels)

图像通常具有三个维度:高度、宽度和通道。通常通道为3=R、G、B

图像:4D张量,形状为 (samples, height, width, channels) 或 (samples, channels,height, width) 。

图像通常具有三个维度:高度、宽度和颜色深度。虽然灰度图像(比如 MNIST 数字图像)只有一个颜色通道,因此可以保存在 2D 张量中,但按照惯例,图像张量始终都是 3D 张量,灰度图像的彩色通道只有一维。因此,如果图像大小为 256×256,那么 128 张灰度图像组成的批量可以保存在一个形状为 (128, 256, 256, 1) 的张量中,而 128 张彩色图像组成的批量则可以保存在一个形状为 (128, 256, 256, 3) 的张量中。
图像张量的形状有两种约定:通道在后(channels-last)的约定(在 TensorFlow 中使用)和通道在前(channels-first)的约定(在 Theano 中使用)。

Google 的 TensorFlow 机器学习框架将颜色深度轴放在最后: (samples, height, width, color_depth) 。与此相反,Theano将图像深度轴放在批量轴之后: (samples, color_depth, height, width) 。如果采用 Theano 约定,前面的两个例子将变成 (128, 1, 256, 256) 和 (128, 3, 256, 256) 。Keras 框架同时支持这两种格式。

如下图所示是一张普通的水果图片,按照RGB三原色表示,其可以拆分为红色、绿色和蓝色的三张灰度图片,如果将这种表示方法用张量的形式写出来,就是图中最下方的那张表格

文章图片6
文章图片7


图中只显示了前5行、320列的数据,每个方格代表一个像素点,其中的数据[1.0, 1.0, 1.0]即为颜色。假设用[1.0, 0, 0]表示红色,[0, 1.0, 0]表示绿色,[0, 0, 1.0]表示蓝色,那么如图所示,前面5行的数据则全是白色

用四阶张量表示一个包含多张图片的数据集,其中的四个维度分别是:图片在数据集中的编号,图片高度、宽度,以及色彩数据。

视频:5D张量,形状为 (samples, frames, height, width, channels) 或 (samples,frames, channels, height, width)

视频数据是现实生活中需要用到 5D 张量的少数数据类型之一。视频可以看作一系列帧,每一帧都是一张彩色图像。由于每一帧都可以保存在一个形状为 (height, width, color_depth) 的 3D 张量中,因此一系列帧可以保存在一个形状为 (frames, height, width,color_depth) 的 4D 张量中,而不同视频组成的批量则可以保存在一个 5D 张量中,其形状为(samples, frames, height, width, color_depth) 。

举个例子,一个以每秒 4 帧采样的 60 秒 YouTube 视频片段,视频尺寸为 144×256,这个视频共有 240 帧。4 个这样的视频片段组成的批量将保存在形状为 (4, 240, 144, 256, 3)的张量中。总共有 106 168 320 个值!如果张量的数据类型( dtype )是 float32 ,每个值都是32 位,那么这个张量共有 405MB。好大!你在现实生活中遇到的视频要小得多,因为它们不以float32 格式存储,而且通常被大大压缩,比如 MPEG 格式。

广播

广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行。
如果两个数组 a 和 b 形状相同,即满足 a.shape == b.shape,那么 a*b 的结果就是 a 与 b 数组对应位相乘。这要求维数相同,且各维度的长度相同。

import numpy as np'''如果两个数组 a 和 b 形状相同,即满足 a.shape == b.shape,那么 a*b 的结果就是 a 与 b 数组对应位相乘。这要求维数相同,且各维度的长度相同。'''a = np.array([1, 2, 3, 4])b = np.array([10, 20, 30, 40])c = a * bprint(c)  # [ 10  40  90 160]'''当运算中的 2 个数组的形状不同时,numpy 将自动触发广播机制。如:'''a = np.array([[0, 0, 0],              [10, 10, 10],              [20, 20, 20],              [30, 30, 30]])b = np.array([0, 1, 2])print(a + b)print('\n')'''4x3 的二维数组与长为 3 的一维数组相加,等效于把数组 b 在二维上重复 4 次再运算:'''a = np.array([[0, 0, 0],              [10, 10, 10],              [20, 20, 20],              [30, 30, 30]])b = np.array([0, 1, 2])bb = np.tile(b, (4, 1))  # 重复 b 的各个维度, 假设reps的维度为d,那么新数组的维度为max(d,A.ndim)print(bb)print(a + bb)
文章图片11

如果两个 Tensor 的形状的长度不一致,会在较小长度的形状矩阵前部添加 1,直到两个 Tensor 的形状长度相等。
保证两个 Tensor 形状相等之后,每个维度上的结果维度就是当前维度上的较大值。

import numpy as np '''如果两个 Tensor 的形状的长度不一致,会在较小长度的形状矩阵前部添加 1,直到两个 Tensor 的形状长度相等。保证两个 Tensor 形状相等之后,每个维度上的结果维度就是当前维度上的较大值。'''x = np.ones([2, 1, 4])y = np.ones((3, 1))print('x => ', x)print('y => ', y)print('x+y => ', x + y)
文章图片12

广播的规则:

  • 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。

  • 输出数组的形状是输入数组形状的各个维度上的最大值。

  • 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。

  • 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。

简单理解:对两个数组,分别比较他们的每一个维度(若其中一个数组没有当前维度则忽略),满足:

  • 数组拥有相同形状。

  • 当前维度的值相等。

  • 当前维度的值有一个是 1

NumPy 应用

NumPy 通常与 SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用, 这种组合广泛用于替代 MatLab,是一个强大的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。
SciPy 是一个开源的 Python 算法库和数学工具包。
SciPy 包含的模块有最优化、线性代数、积分、插值、特殊函数、快速傅里叶变换、信号处理和图像处理、常微分方程求解和其他科学与工程中常用的计算。
Matplotlib 是 Python 编程语言及其数值数学扩展包 NumPy 的可视化操作界面。它为利用通用的图形用户界面工具包,如 Tkinter, wxPython, Qt 或 GTK+ 向应用程序嵌入式绘图提供了应用程序接口(API)。

切片和索引

arr[x][y] = arr[x,y] 两种表达方式

import numpy as nparr = np.arange(21)  # arange() 函数创建 ndarray 对象# arr = arr.reshape(3, 7)arr.shape = (3, 7)'''[[ 0  1  2  3  4  5  6] [ 7  8  9 10 11 12 13] [14 15 16 17 18 19 20]]'''print(arr)print('\n')'''arr[x][y] = arr[x,y]  两种表达方式冒号 : 的解释:如果只放置一个参数,如 [2],将返回与该索引相对应的单个元素。如 [2:],表示从该索引开始以后的所有项都将被提取。如果使用了两个参数,如 [2:7],那么则提取两个索引(不包括停止索引)之间的项。'''print('arr[1:2] =>', arr[1:2])  # 【1~2] 行,右侧不包含,列全部显示 => [[ 7  8  9 10 11 12 13]]print('arr[1:] =>', arr[1:])  # 1 行开始,剩下的全部显示,列全部显示 => [[ 7  8  9 10 11 12 13] [14 15 16 17 18 19 20]]print('arr[:2] =>', arr[:2])  # 取前面两行数据 [[ 0  1  2  3  4  5  6] [ 7  8  9 10 11 12 13]]print('arr[2][1:6:2] =>', arr[2][1:6:2])  # start:stop:step => 第2行,1~6列,步长2(默认为1)   [15 17 19]print('arr[2, 1:6:2] =>', arr[2, 1:6:2])  # start:stop:step => 第2行,1~6列,步长2(默认为1)   [15 17 19]print('arr[:2, 1:6:2] =>', arr[:2, 1:6:2])  # start:stop:step => 前2行,1~6列,步长2(默认为1) [[ 1  3  5] [ 8 10 12]]print('\n')'''切片还可以包括省略号 … ,来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的 ndarray。'''print('arr[1] => ', arr[1])  # 1行,所有列数据 [ 7  8  9 10 11 12 13]print('arr[1, ...] => ', arr[1, ...])  # 1行,所有列数据 [ 7  8  9 10 11 12 13]print('arr[..., 3] => ', arr[..., 3])  # 所有行,第3列数据 [ 3 10 17]print('arr[1] => ', arr[..., 2:])  # 所有行 第3列及剩下的所有元素print('\n')

高级索引

NumPy 中的高级索引指的是使用整数数组、布尔数组或者其他序列来访问数组的元素。相比于基本索引,高级索引可以访问到数组中的任意元素,并且可以用来对数组进行复杂的操作和修改。

文章图片13
import numpy as nparr = np.arange(21) # arange() 函数创建 ndarray 对象# arr = arr.reshape(3, 7)arr.shape = (3, 7)'''[[ 0 1 2 3 4 5 6] [ 7 8 9 10 11 12 13] [14 15 16 17 18 19 20]]'''print(arr)print('\n')'''高级索引'''# 整数数组索引是指使用一个数组来访问另一个数组的元素。这个数组中的每个元素都是目标数组中某个维度上的索引值。print('arr[[0, 1, 2], [2, 1, 3]] => ', arr[[0, 1, 2], [2, 1, 3]]) # [0,2]、【1,1】、[2,3] => [ 2 8 17]rows = np.array([[0, 1], [2, 1], [1, 0]])cols = np.array([[2, 1], [3, 2], [0, 2]])'''0,2 1,12,3 1,21,0 0,2'''print('arr[rows, cols] => ', arr[rows, cols]) # [[ 2 8] [17 9] [7 2]]print('\n')'''可以借助切片 : 或 … 与索引数组组合。'''print('arr[1:3, 1:4] => ', arr[1:3, 1:4]) # [[ 8 9 10] [15 16 17]]print('arr[1:3, [1, 4]] => ', arr[1:3, [1, 4]]) # [[ 8 11] [15 18]]'''[[ 0 1 2 3 4 5 6] [ 7 8 9 10 11 12 13] [14 15 16 17 18 19 20]]'''print('arr[..., 1:] => ', arr[..., 1:]) # [[ 1 2 3 4 5 6] [ 8 9 10 11 12 13] [15 16 17 18 19 20]]
文章图片14
文章图片15
文章图片16

布尔索引

我们可以通过一个布尔数组来索引目标数组。
布尔索引通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。

import numpy as nparr = np.arange(21)  # arange() 函数创建 ndarray 对象# arr = arr.reshape(3, 7)arr.shape = (3, 7)'''[[ 0  1  2  3  4  5  6] [ 7  8  9 10 11 12 13] [14 15 16 17 18 19 20]]'''print(arr)print('\n')'''获取大于 5 的元素'''print('arr[arr > 5]', arr[arr > 5])  # [ 6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]'''~(取补运算符)来过滤NaN。'''arr = np.array([np.nan, 1, 2, np.nan, 3, 4, 5])print('arr[~np.isnan(a)]', arr[~np.isnan(arr)])  # [1. 2. 3. 4. 5.]'''从数组中过滤掉非复数元素。'''arr = np.array([1, 2 + 6j, 5, 3.5 + 5j])print('arr[np.iscomplex(arr)]', arr[np.iscomplex(arr)])  # [2. +6.j 3.5+5.j]

花式索引

花式索引指的是利用整数数组进行索引。
花式索引根据索引数组的值作为目标数组的某个轴的下标来取值。
对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素,如果目标是二维数组,那么就是对应下标的行。
花式索引跟切片不一样,它总是将数据复制到新数组中。

一维数组

一维数组只有一个轴 axis = 0,所以一维数组就在 axis = 0 这个轴上取值:

import numpy as npx = np.arange(9) # [0 1 2 3 4 5 6 7 8]print(x)# 一维数组读取指定下标对应的元素print('-------读取下标对应的元素-------')x2 = x[[0, 6]] # 使用花式索引print(x2) # [0 6]print(x2[0]) # 0print(x2[1]) # 6

二维数组

import numpy as nparr = np.arange(21)  # arange() 函数创建 ndarray 对象# arr = arr.reshape(3, 7)arr.shape = (3, 7)'''[[ 0  1  2  3  4  5  6] [ 7  8  9 10 11 12 13] [14 15 16 17 18 19 20]]'''print(arr)print('\n')print('arr[1, [0, 2]] => ', arr[1, [0, 2]])  # [7 9]print('arr[[0, 2], 1] => ', arr[[0, 2], 1])  # [ 1 15]print('arr[[0, 2]] => ', arr[[0, 2]])  # [[ 0  1  2  3  4  5  6] [14 15 16 17 18 19 20]]# 传入顺序索引数组print('arr[[2,0,1]] => ', arr[[2, 0, 1]])  # [[14 15 16 17 18 19 20] [ 0  1  2  3  4  5  6] [ 7  8  9 10 11 12 13]]# 传入倒序索引数组print('arr[[-2,-0,-1]] => ', arr[[-2, -0, -1]])  # [[ 7  8  9 10 11 12 13] [ 0  1  2  3  4  5  6] [14 15 16 17 18 19 20]]# 传入多个索引数组(要使用 np.ix_)'''np.ix_ 函数就是输入两个数组,产生笛卡尔积的映射关系。笛卡尔乘积是指在数学中,两个集合 X 和 Y 的笛卡尔积(Cartesian product),又称直积,表示为 X×Y,第一个对象是X的成员而第二个对象是 Y 的所有可能有序对的其中一个成员。例如 A={a,b}, B={0,1,2},则:A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}[1, 0, 2, 1], [0, 3, 1, 2] => (1,0),(1,3),(1,1),(1,2),(0,0),(0,3),(0,1),(0,2)....'''print('arr[np.ix_([1,5,7,2],[0,3,1,2])] => ', arr[np.ix_([1, 0, 2, 1], [0, 3, 1, 2])])  # [[ 7 10  8  9] [ 0  3  1  2] [14 17 15 16] [7 10  8  9]]

相关链接

NumPy 官网 http://www./
NumPy 源代码:
https://github.com/numpy/numpy
SciPy 官网:https://www./
SciPy 源代码:
https://github.com/scipy/scipy
Matplotlib 教程:Matplotlib 教程
Matplotlib 官网:https:///
Matplotlib 源代码:
https://github.com/matplotlib/matplotlib

结语

喜欢人工智能的小伙伴记得关注点赞哦~

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多