Numpy Array对象

  • ndarray是不可变对象

1. ndarray类的基本属性

  • shape:元组
  • size:数组中元素的总数
  • ndim:维度的数量
  • nbytes:存储数据的字节数
  • dtype:数组中元素的数据类型

2. 数据类型

dtype变体说明
intintn(n = 8, 16, 32, ···)
uintuintn(n = 8, 16, 32, ···)无符号整型
boolBool
floatfloatn浮点类型
complexcomplexN复数浮点型
  • 还支持其他非数字类型:字符串、对象等
  • 默认的数据类型是float
  • ndarray对象创建后的dtype不能更改,除非复制数组进行类型转换:
    data = np.array([1, 2, 3],dtype = np.float)
    # 类型转换
    data = np.array(data, dtype = np.int)
    
    • 也可以使用astype方法:
    data.astype(np.int)
    
  • 在进行运算时,数据类型可能会发生转变。
    • 将浮点类型与复数浮点类型——>复数类型
实部与虚部
  • 不管的type的类型是什么,ndarray实例都具有real和imag属性

创建数组

函数名数组类型
np.arry使用类数组对象创建
np.zeros
np.ones
np.fill创建指定数值填充的数组
np.diag
np.arange指定开始值,结束值以及增量值(不包含结束值)
np.linspace指定开始值,结束值,元素数量(包含结束值)
np.logspace等比数列,指定开始值和结束值是可选参数base的幂次(默认为10)
np.meshgrid从一维坐标向量生成坐标矩阵(或高维坐标数组)
np.fromfunction
np.fromfile
np.loadtxt从文本文件读取数据以创建数组
np.genfromtxt相比于loadtxt,还支持处理缺失值
np.random.rand0,1之间分布的随机数

meshgrid

将一维坐标向量转换为多维网格坐标矩阵,便于在多维空间中计算和可视化。

网格生成机制
给定一维向量 x(长度 m)和 y(长度 n),meshgrid 输出两个矩阵:

  • X:每行复制 x,共 n 行(y 的长度),形状为 (n, m)
  • Y:每列复制 y,共 m 列(x 的长度),形状为 (n, m)
    示例
import numpy as np
x = [1, 2, 3]; y = [4, 5]
X, Y = np.meshgrid(x, y)

输出:

X = [[1, 2, 3], 
     [1, 2, 3]]  # 每行重复 x  
Y = [[4, 4, 4], 
     [5, 5, 5]]  # 每列重复 y

几何意义:网格点坐标为 (X[i,j], Y[i,j]),覆盖所有组合(如 (1,4), (2,4), (3,4), (1,5)...)。

参数解析

1. 索引模式 indexing
  • 'xy'(默认,笛卡尔坐标):输出形状为 (n, m),适合绘图(X 为横轴,Y 为纵轴)。
  • 'ij'(矩阵索引):输出形状为 (m, n),适合矩阵运算(如线性代数)。
    示例
X_xy, Y_xy = np.meshgrid(x, y)          # 形状 (2,3)
X_ij, Y_ij = np.meshgrid(x, y, indexing='ij')  # 形状 (3,2)
2. 高维扩展

支持三维网格生成:

x = [1, 2]; y = [3, 4]; z = [5, 6]
X, Y, Z = np.meshgrid(x, y, z)  # 输出形状 (2,2,2)

典型应用场景

1. 绘制等高线图(Contour Plot)

import matplotlib.pyplot as plt
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X**2 + Y**2)  # 计算网格点函数值
plt.contourf(X, Y, Z, cmap='viridis')
plt.colorbar()
plt.show()

关键X, Y 定义网格坐标,Z 为每个点的计算值。

使用其他数组的属性创建相同属性的数组

np.ones_likenp.zeros_likenp.full_likenp.empty_like

创建矩阵数组

  • np.identity:生成单位矩阵
  • np.eye:生成对角线为1的矩阵,偏移量为可选参数
    np.eye(5,k = 1)
    np.eye(5,k = -1)
    
  • np.diag:创建对角线为任意一维数组的函数,也可指定偏移量(关键字k)

索引和切片

视图

  • 使用切片操作从数组中提取的子数组是同一底层数组数据的视图。也就是说,它们引用的是原始数组在内存中的同一份数据,但是具有不同的stides设置。视图中的元素被赋予新值后,原始数组中的值也会随之更新。
  • 当需要的是数组的副本而不是视图时,应该使用ndarray的copy方法显式地复制视图
    C = B[1:10 1:10].copy()
    

花式索引fancy indexing

  • 可以使用另外一个Numpy数组、Python列表、整数序列进行索引
A = linespace(0,1,11)
A[np.array([0,2,4])] # 取出index为0,2,4的元素

布尔索引

  • 索引操作可以很好地和布尔操作结合起来
A[A > 0.5]
  • 花式索引和布尔索引得到的不是视图,而是新的独立数组
  • 可以使用花式索引和布尔索引来改变所选元素,对其赋值会改变原数组元素的值

调整形状和大小

函数/方法说明
np.reshape和np.ndarray.reshape
np.ndarray.flatten创建副本,并折叠为一维数组
np.ravel和np.ndarray.ravel创建视图,并折叠为一维数组
np.squeeze删除长度为1的维度
np.expand_dims和np.newaxis增加长度为1的维度
np.transpose,np.ndarray.transpose和np.ndarray.T对数组进行转置,即对相应的轴进行反转
np.hstack水平叠加,沿着轴1
np.vstack垂直叠加,沿着轴0
np.dstack深度叠加,沿着轴2
np.concatenate沿着给定轴堆叠
np.resize根据给定的大小创建原始数组的新副本
np.append在数组中添加新元素(会创建新副本)
np.insert
np.delete

使用索引表达式和 np.newaxis 关键字添加新的空轴

data = np.arange(0,5)
column = data[:, np.newaxis]
np.expand_axis(data, axis=1)

row = data[np.newaxis, :]
np.expand_axis[data, axis=0]

数组的拼接和堆叠

  • 堆叠函数
data = np.arange(5)
np.vstack(data, data, data)

np.stack()堆叠

一、函数原型与参数
numpy.stack(arrays, axis=0)
  • arrays:需堆叠的数组序列(列表或元组),所有数组形状必须相同
  • axis:指定新轴插入位置(默认 axis=0)。取值范围为 [-ndim-1, ndim],其中 ndim 是输入数组的维度。
输出形状规律
输入形状axis输出形状堆叠方式
(3,)0(2, 3)整体堆叠(批量维度)
(3,)1(3, 2)按元素位置堆叠
(2, 2)0(2, 2, 2)数组整体作为新维度的切片
(2, 2)2(2, 2, 2)同位置元素组成新维度

各拼接函数的比较

函数维度变化堆叠方式示例输入→输出
np.stack()+1 维沿新轴堆叠(2,2)(2,2,2)(新增轴)
np.concatenate()不变沿现有轴拼接(2,2) + (2,2)(4,2)
np.vstack()不变垂直堆叠(行方向)(2,3) + (1,3)(3,3)
np.hstack()不变水平堆叠(列方向)(2,2) + (2,3)(2,5)

核心区别stack 新增维度,而其他函数仅扩展现有维度。

向量化表达式

  • 用数组保存数据的目的是希望使用简洁的向量化表达式(vectorized expression)来处理数据,这些表达式能够对数组中的所有元素进行批处理操作。有效地使用向量化表达式能够去除很多显式的for循环
  • 二元操作要求表达式中的所有数组都具有兼容大小。
    • 兼容大小的含义:表达式中的变量要么是标量,要么是相同形状的数组

形状不同的数组广播的条件

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

Numpy的广播规则:

较小的数组可以通过广播匹配较大的数组。

  • 维度不相等,维度较少的数组从左开始填充长度为1的新维度,直到维度一样。

算术运算

  • 如果对不兼容大小或形状的数组进行算数运算,则会引发ValueError异常
  • 对一两个数组进行算术运算的结果是得到一个新的、在内存中独立存在的数组。
    • 使用原位运算(+=,-=,*=,/=,//=,**=)可以减少内存占用并提高性能。

逐个元素进行操作的向量化函数

alt text alt text

聚合函数

  • 数组作为输入,返回一个标量 alt text

布尔数组和条件表达式

  • 广播机制同样适用于比较运算符
  • 为了把比较运算的结果应用到if语句中,需要使用np.all和np.any两个聚合函数
  • 布尔数组的好处——避免if语句:当布尔值数组在算术表达式中与标量或另一种数值类型的 NumPy 数组一起出现时,布尔值数组会被转换为数值类型的数组,False 和 True 分别被转换为0和1。
    A = np.array([-1,-1,0,1,1])
    1*(A > 0)
    
  • 这对于条件计算非常有用,例如定义分段函数。如果想要定义描述具有给定高度、宽度和位置的脉冲分段函数,可将高度(标量变量)乘上脉冲的两个布尔值数组来实现:
def pulse(x, position, height, width):
    return height * (x >= position) * (x <= (position + width))

alt text

集合运算

alt text

alt text

数组运算

alt text

矩阵和向量运算

函数说明
np.dot
np.inner向量内积(标量积)
np.cross向量叉积(注意区分outer)
np.tensordot
np.outer对表示向量的两个数组进行外积(向量的张量积)
np.kron
np.einsum

矩阵乘法

  • np.dot或者@符号
  • 一维数组会视情况解释为shape(1,3)或者shape(3,)

外积

若 (\mathbf{u} \in \mathbb{R}^m),(\mathbf{v} \in \mathbb{R}^n),则外积 (\mathbf{u} \otimes \mathbf{v}) 是一个矩阵 (A_{m \times n}),其中 (A_{ij} = u_i v_j)。
示例:(\mathbf{u} = [1, 2]^\top),(\mathbf{v} = [3, 4, 5]^\top),则: [ \mathbf{u} \otimes \mathbf{v} = \begin{bmatrix} 3 & 4 & 5 \ 6 & 8 & 10 \end{bmatrix} ] 此运算常用于矩阵分解(如SVD)和协方差计算

Kronecker积

又称克罗内克积或直积,它将两个任意大小的矩阵组合成一个更大的分块矩阵。

1. 基本定义

给定矩阵 ( A \in \mathbb{R}^{m \times n} ) 和 ( B \in \mathbb{R}^{p \times q} ),它们的Kronecker积 ( A \otimes B ) 是一个分块矩阵:
[ A \otimes B = \begin{bmatrix} a_{11}B & a_{12}B & \cdots & a_{1n}B \ a_{21}B & a_{22}B & \cdots & a_{2n}B \ \vdots & \vdots & \ddots & \vdots \ a_{m1}B & a_{m2}B & \cdots & a_{mn}B \end{bmatrix}{(mp) \times (nq)} ]
其中每个子块 ( a
{ij}B ) 是标量 ( a_{ij} ) 与矩阵 ( B ) 的乘积。结果矩阵的维度为 ( (mp) \times (nq) ) 。

2. 计算示例

设 ( A = \begin{bmatrix} 1 & 2 \ 3 & 4 \end{bmatrix} ),( B = \begin{bmatrix} 0 & 5 \ 6 & 7 \end{bmatrix} ),则:
[ A \otimes B = \begin{bmatrix} 1 \cdot B & 2 \cdot B \ 3 \cdot B & 4 \cdot B \end{bmatrix} = \begin{bmatrix} 0 & 5 & 0 & 10 \ 6 & 7 & 12 & 14 \ 0 & 15 & 0 & 20 \ 18 & 21 & 24 & 28 \end{bmatrix} ]
此例中,( A ) 的每个元素被 ( B ) 的完整副本替换并缩放 。

二、核心性质

Kronecker积满足以下代数性质:
| 性质 | 数学表达 | 意义 | |------------------|-----------------------------------------------------------------------------|--------------------------------------------------------------------------| | 分配律 | ( A \otimes (B + C) = A \otimes B + A \otimes C ) | 对矩阵加法的兼容性 | | 结合律 | ( (A \otimes B) \otimes C = A \otimes (B \otimes C) ) | 支持链式运算 | | 混合乘积 | ( (A \otimes B)(C \otimes D) = (AC) \otimes (BD) ) | 与矩阵乘法的协同性(需维度匹配) | | 转置 | ( (A \otimes B)^T = A^T \otimes B^T ) | 转置操作的分配性 | | 逆矩阵 | ( (A \otimes B)^{-1} = A^{-1} \otimes B^{-1} )(若 ( A, B ) 可逆) | 逆运算的独立性 | | 特征值 | 若 ( \lambda_i ) 是 ( A ) 的特征值,( \mu_j ) 是 ( B ) 的特征值,则 ( \lambda_i \mu_j ) 是 ( A \otimes B ) 的特征值 | 特征值可分解性 | | 非交换性 | ( A \otimes B \neq B \otimes A )(一般情况) | 运算顺序影响结果 |

三、应用场景
1. 科学计算与工程领域
  • 量子计算:描述多量子比特系统的复合状态(如 ( \psi_{\text{total}} = \psi_1 \otimes \psi_2 ))。
  • 信号处理:构造多维滤波器,处理多通道信号(如图像、音频)。
  • 图论:生成复杂网络的邻接矩阵(如图的笛卡尔积 ( G_1 \times G_2 ) 的邻接矩阵为 ( A_{G_1} \otimes A_{G_2} ))。
2. 数据分析与机器学习
  • 协方差建模:构建高维数据的结构化协方差矩阵(如面板数据 ( \Sigma = \Sigma_{\text{时间}} \otimes \Sigma_{\text{变量}} ))。
  • 神经网络加速:通过分块计算简化参数矩阵乘法,提升训练效率(如TensorFlow/PyTorch的 kron 函数)。
  • 图像处理:实现像素级操作(如2倍放大:用 ( \begin{bmatrix} 1 & 1 \ 1 & 1 \end{bmatrix} ) 与图像矩阵做Kronecker积)。
3. 数值优化
  • 大规模线性系统:将高维问题分解为低维子问题(如方程 ( AXB = C ) 转化为 ( (B^T \otimes A) \text{vec}(X) = \text{vec}(C) ))。
  • 稀疏矩阵表示:高效存储和计算分块稀疏矩阵 。
1. 基础计算工具
  • NumPynp.kron(A, B) 直接计算,适合中小矩阵 。
  • PyTorch/TensorFlowtorch.kron()tf.linalg.kronecker,支持GPU加速 。
2. 高性能优化策略
场景优化方法目的
大矩阵计算分块算法(Blocking)减少内存占用
并行处理多线程/分布式计算(如Spark)加速大规模运算
稀疏矩阵仅存储非零块节省存储空间
JIT编译使用Numba优化循环提升纯Python代码效率

代码示例(NumPy)

import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[0, 5], [6, 7]])
C = np.kron(A, B)  # 计算Kronecker积

用kronecker积计算外积

  • 要获得与np.outer相同的结果,输入的一维数组必须在np.kron的第一、第二个参数扩展为(N,1),(1,N)

爱因斯坦求和约定


二维数组

identity = np.array([[1, 0], [0, 1]])
print(identity)

[out]:[[1 0]
    [0 1]]

二维数组的索引

print(identity[0, 1] is identity[0][1], identity[0, 1] == identity[0][1])

[out]:False True
  • 前一种方法是从二维数组中取元素,后一种是先生成一个复制了的一维数组,再取其中的元素,因此 is 判断给出 False ,但它们相等。
square = np.arange(100)
square.shape = (10, 10)
print(square)
  • 首先生成了一个长度为 100 的一维数组,随后在保持数据不变的前提下,把它的形状改成了 (10, 10) ,即把它解读成二维方阵。可以理解为数组对象的shape属性是一个tuple,更改了这个属性。
  • 这个操作也可以调用 reshape 函数实现:
np.arange(100).reshape((10, 10))

索引取一整行或者列

square[:, 0] # 取出第一列
square[::3, ::5] # 从第一行,列开始每隔3行每隔5列取出元素(Index为3,5倍数的元素,0也匹配)
square[2::3, 3:5] # 从第3(2+1)行开始,每3行取一次
  • :::的用法与python的list类似,起始(default值0),终止(default值=维数+1,左闭右开),间隔(default=1)
  • 当间隔为负数时,例如array[::-1],起始为数组末尾,终止为数组开始。 这很python!!! 符合人类语言。

任意索引(fancy index)

  • 分别传两个相等形状的索引数组,按索引数组形状排列对应的输出
print(m6[[1, 2, 3], [3, 4, 5]]) # 给出的新数组的形状由[1,2,3]的形状决定
[out]: [13 24 35]

print(m6[[[1], [2]], [[3], [4]]]) # 输出形状是[[],[]]
[out]:[[13]
       [24]]

数组运算

  • 可以对numpy数组做各种操作,+-/……以及+=,-=,=,/=等运算符

降维运算

  • np.mean()函数用于求数组的均值
  • np.std()函数用于求数组的标准差
  • np.var()求方差
  • np.max()函数用于求数组的最大值
  • np.min()函数用于求数组的最小值
  • np.median()函数用于求数组的中位数
  • np.sum()函数用于求数组的和
  • np.prod()函数用于求数组的积

如果只针对某一个维度,使用axis参数指定

np.sum(square, axis=0), np.mean(square, axis=1)
  • axis=0表示对每一列求和
  • axis=1表示对每一行求均值
  • 对于多维数组,axis参数也可以使用tuple,指定多个维度

矩阵的拓展(广播机制)

n[None, :] + n[:, None]
  • 使用n[None, :]得到:[out]:array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])获得一个二维数组,n[:, None]同理
  • 对于两个形状不同的数组,numpy使用广播机制得到:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
    [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
    [ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
    [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
    [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    [ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
    [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
    [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]])

矩阵运算

元素乘法与矩阵乘法区别

a = b = np.ones((2,2))
print(a * b) # 对应位置元素相乘,输出[[1 1][1 1]]
print(a @ b) # 矩阵乘法,输出[[2 2][2 2]]

张量

  • 矩阵可以表示为两个下标的矩阵元:二阶张量aija_{ij}
    • 三个下标:三阶张量bijkb_{ijk}

eps()

  • 反对称张量: eps(n):返回一个n阶反对称张量对应的高阶矩阵(自定义模块asym.py中)

张量乘法

  • np.tensordot(),可标注哪些下标进行缩并(内积操作) Σaijbjk=cik \Sigma a_{ij}b_{jk} = c_{ik}
a = np.arange(60).reshape((3,4,5))
b = np.arange(24).reshape((4,3,2))
np.tensordot(a,b,axes = ([0,1],[1,0])) # 合并两个方向,输出为一个二阶张量(6-2*2=2)

爱因斯坦约定

  • eeinsum:
    a = np.arange(25).reshape(5,5)
    b = np.arange(5)
    np.einsum('ii',a) # 取迹
    np.einsum('ij->i', a) # 对j方向求和
    np.einsum('ij,i', a,b) # 矩阵乘法
    
    # 之前的tensordot等价于如下:
    np.einsum('ijk,jil',a,b)
    np.einsum('ijk,jil->kl',a,b)
    
  • 灵活缩并
    a = np.ones(64).reshape(2,4,8)
    np.einsum('ijk,ilm,njm,nlk',a,a,a,a)
    #=>4096.0
    

例:Pauli矩阵

  • python中定义的虚数单位j
  • numpy中的矩阵乘法运算符@
  • 量子力学中自旋为1/2态空间下的角动量算符表示。 σ1=(0110) σ2=(0ii0)σ3=(1001) \sigma_1 = \begin{pmatrix} 0 & 1 \ 1 & 0 \end{pmatrix} \space \sigma_2 = \begin{pmatrix} 0 & -i \ i & 0 \end{pmatrix} \sigma_3 = \begin{pmatrix} 1 & 0 \ 0 & -1 \end{pmatrix}
    • 这三个矩阵一起,可以看作一个3*2*2的三阶张量

Pauli矩阵的对易关系

σ1σ2=iσ3=σ2σ1 \sigma_1\sigma_2 = i\sigma_3 = - \sigma_2\sigma_1

pauli = []
pauli.append(np.array([0,1,1,0]).reshape(2,2))
pauli.append(np.array([0,-1j,1j,0]).reshape(2,2))
pauli.append(np.array([1,0,0,-1]).reshape(2,2))

特征值和迹

  • NumPy 的 linalg (意为 linear algebra)子模块
    np.linalg.eigvals(square) # 计算方阵的特征值
    np.trace(square) # 计算方阵的迹
    np.linalg.det() # 计算矩阵行列式
    

alt text

  • 全反对称张量的含义:
    • 全:3*3*3,4*4*4
    • 反对称
pauli = np.array(pauli)
(np.einsum('ixy,jxy -> ijxz', pauli,pauli)
- np.einsum('ixy,jxy -> jixz', pauli,pauli)) ==
2j * np.einsum('ijk, kxy', eps(3), pauli) # 或者 np.tensordot(eps(3), pauli, axes = ([2], [0]))

更简单的形式

  • @符号,重载
    • 对于矩阵,是矩阵乘法,自动缩并
    • 对于张量 almij@blmjk=clmik a_{lmij}@b_{lmjk} = c_{lmik}
# pauli.shape = (3,2,2)
# 对pauli进行拓展:pauli[:,None].shape= (3,1,2,2),pauli[Noen,:].shape = (1,3,2,2)
# 因为之后的两个2*2没有变,所以省略了(pauli[:, None, :, :])
pauli[:, None] - pauli[None, :] # 通过扩展和广播机制,形状变为(3,3,2,2)

# 验证:
np.all(commute(pauli[:, None], pauli[None, :]) == 
        2j * np.tensordot(eps(3), pauli, axes = ([2], [0]))) 

alt text


NumPy 中的 np.stack() 函数用于沿新轴堆叠多个同形状数组,生成一个维度更高的新数组。以下是其核心用法详解: