[python][科学计算][numpy]使用指南(汇总)

最后一次更新日期: 2019/4/13

NumPy 是一个 Python 包。 它代表 “Numeric Python”。 它是一个由多维数组对象(ndarray)和用于处理数组的例程集合组成的库。
使用NumPy,开发人员可以执行以下操作:

  • 数组的算数和逻辑运算。
  • 傅立叶变换和用于图形操作的例程。
  • 与线性代数有关的操作。 NumPy 拥有线性代数和随机数生成的内置函数。

使用前先导入模块:
import numpy as np

此篇为汇总,点击下方链接可前往各小节
使用指南1 - 属性 (数据类型,形状,维数,元素数,元素大小,字节数,顺序)
使用指南2 - 创建 (从已有数据创建,快速填充)
使用指南3 - 运算 (数组/集合/位/字符串/统计/线性代数运算,常量,广播)
使用指南4 - 查找 (索引,遍历,抽样,视图)
使用指南5 - 变更 (更新,扩增,删除)
使用指南6 - 重构 (类型转换,重塑,排序,去重,拆分)
使用指南7 - 读写 (保存,读取)
使用指南8 - 其他 (矩阵类型,张量运算,傅里叶变换,图像处理)

一. 属性

1. 数据类型

ndarray.dtype

numpy常用数据类型
dtype type 类型名 说明
np.dtype('bool') np.bool_ 布尔类型 True or False
np.dtype('int8' | 'i1' | 'b') np.int8 8位整数(字节) -2^7 to 2^7-1
np.dtype('int16' | 'i2') np.int16 16位整数 -2^15 to 2^15-1
np.dtype('int32' | 'i4' | 'int' | 'i') np.int32 32位整数 -2^31 to 2^31-1
np.dtype('int64' | 'i8') np.int64 64位整数 -2^63 to 2^63-1
np.dtype('uint8' | 'u1' | 'B') np.uint8 8位无符号整数 0 to 2^8-1
np.dtype('uint16' | 'u2') np.uint16 16位无符号整数 0 to 2^16-1
np.dtype('uint32' | 'u4' | 'uint') np.uint32 32位无符号整数 0 to 2^32-1
np.dtype('uint64' | 'u8') np.uint64 64位无符号整数 0 to 2^64-1
np.dtype('float16' | 'f2') np.float16 半精度浮点数 1符号位+5指数位+10尾数位
np.dtype('float32' | 'f4' | 'f') np.float32 单精度浮点数 1符号位+8指数位+23尾数位
np.dtype('float64' | 'f8' | 'float' | 'd') np.float64 双精度浮点数 1符号位+11指数位+52尾数位
np.dtype('complex64' | 'c8') np.complex64 64位复数 双32位浮点数(实部+虚部)
np.dtype('complex128' | 'complex' | 'c16') np.complex128 128位复数 双64位浮点数(实部+虚部)
np.dtype('object' | 'O') object 对象 可用于存储引用类型
np.dtype('string_' | 'S' | 'S1' | 'S2' ...) np.bytes_ 定长字符串 需要声明长度
np.dtype('unicode' | 'U' | 'U1' | 'U2' ...) np.str_ 定长Unicode字符串 需要声明长度
np.dtype('datetime64' | 'M') np.datetime64 日期时间 可指定日期单位

注:同一种dtype可以通过多种字符串标识去声明的,见 | 分割的多项。
python的基本数据类型可被numpy识别,转换为对应类别下的默认类型,int对应np.int32float对应np.float64complex对应np.complex128

dtype与type的转换

获取dtype的type:dtype.type
通过type创建dtype:dtype(type)

dtype与type可以直接进行比较

np.dtype('int')==np.int32

dtype的字节顺序

np.dtype('<i8')

字节顺序是通过对数据类型预先设定"<"或">"来决定的。"<"意味着小端法(最小值存储在最小的地址,即低位组放在最前面)。">"意味着大端法(最重要的字节存储在最小的地址,即高位组放在最前面)。一般情况下采用默认设置即可。

2. 元素个数

ndarray.size

3. 元素的子节大小

ndarray.itemsize

由数据类型决定,每8位为1字节。

4. 总字节数

ndarray.nbytes

由size和itemsize计算得到。

5. 维数

ndarray.ndim

ndarray的每一个维度被描述为轴(axis),维数即轴的个数,轴的编号从0开始。

6. 形状

ndarray.shape

tuple类型,每一位对应到每个轴,例如0轴方向长为10个元素,1轴方向长为5个元素的数组形状为(10,5)

7. 内存布局

numpy有两种数据存储的方式,一种是C-order,即行序优先,另一种是Fortran-order,即列序优先,未显式指定时默认C-order。由于内部的优化机制,多数情况下两者在使用时没有明显区别,一般不需要更改默认设置。
但在使用诸如迭代器之类的方法时,可通过order参数指定元素访问顺序,其中'C'指代C-order,'F'指代F-order。

二. 创建

1. 从已有数据创建

(1). 从list创建
#一维数组
a1=np.array([1,2])
#二维数组
a2=np.array([[1.,2.],[3.,4.]])
#三维数组
a3=np.array([[[1,2],[3,4]],[[5,6],[7,8]]],dtype='float')
#以此类推

从嵌套的序列结构创建多维数组时,最外面的一层对应数组第一个轴,以此类推。
第二个参数dtype可以显式声明数组的数据类型,可传入dtypetype(python基础数据类型或numpy数据类型)或与dtype对应的字符串标识,不声明的情况下会根据传入数据自动采用最合适的数据类型。

(2). 从tuple创建
a=np.array((1,2))

list是一样,listtuple的嵌套同理。

(3). 从dict创建(不合适)
In [138]: np.array({"x1":1,"x2":2})
Out[138]: array({'x1': 1, 'x2': 2}, dtype=object)

无法正常转换,整个dict会作为一个对象存入数组,可以尝试用pandas库去处理。

(4). 从其他类数组结构中创建,如PIL的图片类型
from PIL import Image
image= Image.open("D:\\test.jpg")
a=np.asarray(image)

np.asarray在多数情况下与np.array等效,区别在于np.asarray会避免没有必要的重复创建,当数据源同样是ndarraydtype无变化时,不会返回新的数组。

2. 快速填充

(1). 0/1填充
#填充0
a1=np.zeros((2,2))
#填充1
a2=np.ones((2,2))
#声明类型
a2=np.ones((2,2),dtype='int')

第一个参数shape为数组形状,必须赋值;
默认数据类型为float64,可通过dtype参数指定类型。

(2). 对角矩阵
In [151]: np.eye(3,3,0)
Out[151]: 
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])

In [150]: np.eye(3,3,1)
Out[150]: 
array([[ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 0.,  0.,  0.]])

第一个参数N为0轴长度,必须赋值;
第二个参数M为1轴长度,不赋值时与N值一致;
第一个参数k为对角线偏移量,默认0,正数向上偏移,负数向下偏移。
对角矩阵固定为二维数组。

(3).单位矩阵
In [5]: np.identity(1)
Out[5]: array([[ 1.]])

In [6]: np.identity(2)
Out[6]: 
array([[ 1.,  0.],
       [ 0.,  1.]])

第一个参数n为统一的轴长度,必须赋值;
单位矩阵固定为二维数组。

(4). 指定值填充
a=np.full((2,2),'a')

第一个参数shape为数组形状,必须赋值;
第二个参数fill_value为填充值,必须赋值。

(5). 空值填充
a=np.empty((2,2))

第一个参数shape为数组形状,必须赋值。
创建一个数组,但不初始化其中的值。

(6). 参考已有数组形状创建
a=np.ones((2,2))
a2=np.zeros_like(a)
a3=np.full_like(a,'a')
(7). 等差数列
#方法一
a1=np.arange(0,100,1)
#方法二
a2=np.linspace(0,99,100)

方法一,类似range函数,默认int类型,
三个参数分别为:开始,结束,步长(区间前闭后开);
方法二,线性空间,默认float类型,
range不一样,结束值默认是包含于区间的,
且第三个参数不是步长而是元素个数。

(8). 随机数
浮点数随机数
#[0,1]浮点随机数
a1=np.random.rand(5,5)
#标准正太分布随机数
a2=np.random.randn(5,5)

需要注意的是,上面的方法不通过shape而通过全部参数来定义数组形状。

整数随机数
#产生指定范围的整数随机数
a=np.random.randint(0,10,(4,3))

第一个参数low表示区间下限,必须赋值;
第二个参数high表示区间上限,未赋值时会将low作为上限,0作为下限;
第三个参数size表示数组形状,未赋值时函数会返回标量值。

正态分布随机数
a=np.random.normal(100,10,(4,3))

第一个参数loc表示分布的平均值;
第二个参数scale表示分布的标准偏差;
第三个参数size表示数组形状,未赋值时函数会返回标量值。

均匀分布随机数
a=np.random.uniform(0,10,(4,3))

第一个参数low表示区间下限,必须赋值;
第二个参数high表示区间上限,未赋值时会将low作为上限,0作为下限;
第三个参数size表示数组形状,未赋值时函数会返回标量值。

泊松分布随机数
a=np.random.poisson(1.0,(4,3))

第一个参数lam是lambda系数;
第二个参数size表示数组形状,未赋值时函数会返回标量值。

(9). 网格数据
In [26]: X,Y=np.mgrid[1:2:2j,1:3:3j]

In [27]: X
Out[27]: 
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.]])

In [28]: Y
Out[28]: 
array([[ 1.,  2.,  3.],
       [ 1.,  2.,  3.]])

In [33]: x=np.array([1,2,3])

In [34]: y=np.array([1,2])

In [35]: X,Y=np.meshgrid(x,y)

In [36]: X
Out[36]: 
array([[1, 2, 3],
       [1, 2, 3]])

In [37]: Y
Out[37]: 
array([[1, 1, 1],
       [2, 2, 2]])

np.mgrid通过索引选取的方式直接获得网格数据,j表示步数且包含停止值,去掉j该项代表步长且不包含停止值,第一个索引位声明X在轴0方向上的增长,第二个索引位声明Y在轴1方向上的增长,然后将XY广播至相同大小,存在更多索引位时以此类推。
np.meshgrid在已有数据基础上构造网格数据,跟mgrid不一样,第一个参数是表示第二个轴方向上增长的向量,第二个参数对应第一个轴,第三个参数对应第三个轴,第四个参数对应第四个轴,之后以此类推。
网格数据常用于绘图。

(10). 复制
a1=np.zeros((2,2))
a2=np.copy(a1)

三. 运算

1. 运算符

numpy对python中的运算符作了重载,可通过同样的用法实现数组运算。

数组与标量值的运算
In [191]: a=np.arange(0, 4)

In [192]: a
Out[192]: array([0, 1, 2, 3])

In [193]: a+1
Out[193]: array([1, 2, 3, 4])

In [193]: a*2
Out[194]: array([0, 2, 4, 6])
数组与数组的运算
In [199]: a1=np.arange(0, 4);a2=np.arange(4, 8)

In [200]: a1,a2
Out[200]: (array([0, 1, 2, 3]), array([4, 5, 6, 7]))

In [201]: a1+a2
Out[201]: array([ 4,  6,  8, 10])

In [202]: a2**a1
Out[202]: array([  1,   5,  36, 343], dtype=int32)

2. 标量值函数

标量值函数会对数组中每一个元素进行同样的计算。

一元函数
函数 作用 说明
np.abs 绝对值 计算浮点数/整数/复数的绝对值
np.fabs 绝对值 计算浮点数/整数的绝对值,速度更快(?)
np.sqrt 平方根 x^0.5
np.square 平方 x^2
np.log 自然对数 -
np.log2 2为底的对数 -
np.log10 10为底的对数
np.log1p x+1的自然对数 用于数值过小时保证计算的有效性
np.ceil 向上取整 -
np.floor 向下取整 -
np.rint 舍入取整 -
np.around 舍入指定位数 第二个参数decimals为舍入位数
np.exp 自然指数 e^x
np.sign 符号值 三种值:1(正)、0(0)、-1(负)
np.modf 拆分小数和整数部分 以两个独立的数组方式返回
np.isnan 判断是否为NaN 返回bool型数组
np.isfinite 判断是否是有穷 值非inf,非NaN;返回bool型数组
np.isinf 判断是否是有穷 值为inf或-inf;返回bool型数组
np.sin,np.sinh 正弦,双曲正弦 -
np.cos,np.cosh 余弦,双曲余弦 -
np.tan,np.tanh 正切,双曲正切 -
np.arcsin,np.arcsinh 反正弦,反双曲正弦 -
np.arccos,np.arccosh 反余弦,反双曲余弦 -
np.arctan,np.arctanh 反正切,反双曲正切 -
np.logical_not 逻辑非 -
多元函数
函数 作用 说明
np.add(a1,a2) 相加 a1+a2
np.sub(a1,a2) 相减 a1-a2
np.multiply(a1,a2) 相乘 a1*a2
np.divide(a1,a2) 相除 a1/a2
np.power(a1,a2) 乘幂 a1**a2
np.floor_divide(a1,a2) 整除 a1//a2
np.mod(a1,a2) 取模 a1%a2
np.maxinum(a1,a2,a3) 最大值 逐个元素进行比较,返回全部最大值的数组
np.fmax(a1,a2,a3) 最大值(忽略NaN) 逐个元素进行比较,返回全部最大值的数组
np.mininum(a1,a2,a3) 最小值 逐个元素进行比较,返回全部最小值的数组
np.fmin(a1,a2,a3) 最小值(忽略NaN) 逐个元素进行比较,返回全部最小值的数组
np.copysign(a1,a2) 复制符号 将a2的符号复制到a1中
np.greater(a1,a2) 大于 a1>a2
np.greater_equal(a1,a2) 大于等于 a1>=a2
np.less(a1,a2) 小于 a1<a2
np.less_equal(a1,a2) 小于等于 a1<=a2
np.equal(a1,a2) 等于 a1==a2
np.not_equal(a1,a2) 不等于 a1!=a2
np.logical_and(a1,a2) 逻辑与 -
np.logical_or(a1,a2) 逻辑或 -
np.logical_xor(a1,a2) 逻辑异或 -

3. 聚合函数

聚合函数会减少数组的维数,通常可以指定一个轴方向axis进行聚合,结果数组会减少一个维度,不指定方向时会在所有轴方向上聚合,结果为一个标量值。
大多数既可以静态调用,也可以直接调用ndarray对象的方法。

函数 作用 说明
np.sum 求和 -
np.mean 平均值 -
np.max 最大值 -
np.min 最小值 -
np.prod 连乘 -
np.any 至少一个为True 返回True/False
np.all 全部为True 返回True/False

np.maxnp.min有对应的np.argmaxnp.argmin的方法用于返回索引,详见查找章节。

(以下是部分示例)
np.sum
In [313]: a=np.array([[1,3],[4,2]])

In [314]: a
Out[314]: 
array([[1, 3],
       [4, 2]])

In [315]: a.sum()
Out[315]: 10

In [316]: a.sum(axis=0)
Out[316]: array([5, 5])

In [317]: a.sum(axis=1)
Out[317]: array([4, 6])
np.all
In [322]: a=np.array([[True,False],[True,True]])

In [323]: a
Out[323]: 
array([[ True, False],
       [ True,  True]], dtype=bool)

In [324]: a.all()
Out[324]: False

In [325]: a.all(axis=0)
Out[325]: array([ True, False], dtype=bool)

4. 复合统计函数

函数 作用 说明
np.cumsum 累加 -
np.cumprod 累乘 -
np.std 标准差 ((a-a.mean())**2).sum()/a.size
np.var 方差 np.sqrt(((a-a.mean())**2).sum()/a.size)
np.average 加权平均数 第三个参数weights为权重;ndarray无对应方法
np.bincount 分箱计数 只支持整数,分箱区间根据最大最小值自动生成,间隔为1
np.histogram 直方图统计 第二个参数bins指定分箱方式,比np.bincount更灵活
(以下是部分示例)
np.cumsum
In [317]: a=np.array([[1,3],[4,2]])

In [319]: a.cumsum()
Out[319]: array([ 1,  4,  8, 10], dtype=int32)

In [320]: a.cumsum(axis=0)
Out[320]: 
array([[1, 3],
       [5, 5]], dtype=int32)
np.average
In [331]: a=np.array([[1,3],[4,2]])

In [332]: w=np.array([[0.4,0.1],[0.2,0.3]])

In [333]: np.average(a)
Out[333]: 2.5

In [334]: np.average(a,weights=w)
Out[334]: 2.1000000000000001

5. 字符串函数

函数 作用 说明
np.char.add 字符串相加 逐个元素执行字符串相加
np.char.multiply 字符串重复 第二个参数i为重复次数
np.char.center 字符串居中 第二个参数width为长度,第三个参数fillchar为填充字符
np.char.capitalize 首字母大写 -
np.char.title 单词首字母大写 -
np.char.lower 转换为小写 -
np.char.upper 转换为大写 -
np.char.split 字符串分割 第二个参数sep为分隔符,返回list<str>数组
np.char.splitlines 行分割 以换行符分割,返回list<str>数组
np.char.strip 移除头尾指定字符 第二个参数chars为需要移除的字符
np.char.join 以指定分隔符拼接字符 第一个参数sep为分隔符
np.char.replace 替换字符串 第二个参数old为旧字符串,第三个参数new为新字符串
np.char.decode 解码 对每个元素调用str.decode
np.char.encode 编码 对每个元素调用str.encode
(以下是部分示例)
np.char.add
In [301]: a1=np.array(['a','b']);a2=np.array(['c','d'])

In [302]: np.char.add(a1,a2)
Out[302]: 
array(['ac', 'bd'],
      dtype='<U2')
np.char.multiply
In [303]: a=np.array(['a','b'])

In [304]: np.char.multiply(a,3)
Out[304]: 
array(['aaa', 'bbb'],
      dtype='<U3')
np.char.center
In [305]: a=np.array(['a','b'])

In [306]: np.char.center(a,10,'*')
Out[306]: 
array(['****a*****', '****b*****'],
      dtype='<U10')
np.char.split
In [307]: a=np.array(['a,b','c,d'])

In [308]: np.char.split(a,',')
Out[308]: array([list(['a', 'b']), list(['c', 'd'])], dtype=object)
np.char.join
In [309]: a=np.array(['ab','cd'])

In [310]: np.char.join(',',a)
Out[310]: 
array(['a,b', 'c,d'],
      dtype='<U3')

In [311]: a=np.array(['a,b','c,d'])

In [312]: np.char.join(',',np.char.split(a,','))
Out[312]: 
array(['a,b', 'c,d'],
      dtype='<U3')

注意,该方法无法实现多维数组的聚合计算。在数组元素为字符串时,会对字符串中的每个元素进行拼接;在数组元素为字符串序列时,会对序列中的字符串进行拼接。
与split互为逆运算。

6. 线性代数运算

函数 作用 说明
np.dot(a1,a2) 点乘 多维数组下会将a1的最后一个轴和a2的倒数第二个轴作为向量的维度,可视作向量的栈
np.vdot(a1,a2) 向量点乘 高维数组会被展开计算
np.inner(a1,a2) 向量内积 多维数组会将最后一个轴作为向量的维度
np.matmul(a1,a2) 矩阵乘积 多维数组下会将最后两个轴作为矩阵的维度,可视作元素是矩阵的数组
np.linalg.det(a) 行列式 行列式描述的是矩阵所表示的线性变换对“体积”的影响
np.linalg.solve(A,b) 求解线性方程组 求解线性方程组Ax = b,A为系数矩阵(方阵),b为常数矩阵
np.linalg.lstsq(A,b) 求解线性方程组 求线性方程组Ax = b的最小二乘解,A为系数矩阵,b为常数矩阵
np.linalg.inv(a) 逆矩阵 AB=BA=E,E为单位矩阵,则B为A的逆矩阵
np.linalg.pinv(a) 广义逆矩阵 可以输入非方阵
np.linalg.eig(a) 特征值和特征向量 返回两个数组
np.linalg.qr(a) 正交分解 -
np.linalg.svd(a) 奇异值分解 -
ndarray.T 转置 对一维数组转置是无效的
(以下是部分示例)
np.dot, np.vdot, np.inner, np.matmul
In [339]: a=np.array([1,2]);b=np.array([[3,4],[5,6]])

In [340]: np.dot(a,a), np.vdot(a,a), np.inner(a,a), np.matmul(a,a)
Out[340]: (5, 5, 5, 5)

In [341]: np.dot(a,b), np.dot(b,a)
Out[341]: (array([13, 16]), array([11, 17]))

In [342]: np.vdot(a,b)
Traceback (most recent call last):

  File "<ipython-input-358-f2388a21d848>", line 1, in <module>
    np.vdot(a,b)

ValueError: cannot reshape array of size 4 into shape (2,)

In [343]: np.inner(a,b), np.inner(b,a)
Out[343]: (array([11, 17]), array([11, 17]))

In [344]: np.matmul(a,b), np.matmul(b,a)
Out[344]: (array([13, 16]), array([11, 17]))

In [345]: np.dot(b,b)
Out[345]: 
array([[29, 36],
       [45, 56]])

In [346]: np.vdot(b,b)
Out[346]: 86

In [347]: np.inner(b,b)
Out[347]: 
array([[25, 39],
       [39, 61]])

In [348]: np.matmul(b,b)
Out[348]: 
array([[29, 36],
       [45, 56]])

这四个方法执行的运算都基于向量内积,非常相似,但在具体行为上有区别,很容易混淆。

dot是将数组看作向量的集合,第一个数组的最后一个轴和第二个数组的倒数第二个轴作为向量的轴,抽取每个向量两两匹配进行点积运算,因此这两个轴需要长度一致。例如,a.shape=(2,3,4)b.shape=(5,4,6),则np.dot(a,b).shape=(2,3,5,6)。在二维情况下,实现的计算即是矩阵乘法。

vdot的规则比较简单,会将数组展开为向量再计算点积,要求数组的size一致。

innerdot类似,但规则更简单,两个数组均以最后一个轴作为向量的轴,即最后一个轴长度要保持一致。例如,a.shape=(2,3,4)b.shape=(5,6,4),则np.dot(a,b).shape=(2,3,5,6)

matmul的计算针对矩阵,和dot一样在二维情况下表示矩阵乘法,二维以上视作元素为矩阵的数组,参与运算的两个数组均以最后两个轴作为矩阵的轴,逐个元素进行矩阵乘法并向其他轴方向上广播。例如,a.shape=(1,3,4)b.shape=(5,4,6),则np.dot(a,b).shape=(5,3,6),如果a.shape=(2,3,4),计算就会报错,因为不满足广播的规则。

7. 集合运算

函数 作用 说明
np.intersect1d(x, y) 交集 xy的公共元素
np.union1d(x, y) 并集 xy的所有元素
np.setdiff1d(x, y) 集合差 x中存在,y中不存在的元素
np.setxor1d(x, y) 集合异或 xy的独占元素

以上方法适用于一维数组。

(以下是部分示例)
In [810]: x=np.array([1,3,4])

In [811]: y=np.array([2,3,5])

In [812]: np.intersect1d(x,y)
Out[812]: array([3])

In [813]: np.union1d(x,y)
Out[813]: array([1, 2, 3, 4, 5])

In [814]: np.setdiff1d(x,y)
Out[814]: array([1, 4])

In [815]: np.setxor1d(x,y)
Out[815]: array([1, 2, 4, 5])

8. 位运算

函数 作用 说明
np.invert 按位取反 等效于~运算符
np.bitwise_and 按位与 等效于&运算符
np.bitwise_or 按位或 等效于|运算符
np.bitwise_xor 按位异或 等效于^运算符
np.left_shift 位左移 等效于<<运算符
np.right_shift 位右移 等效于>>运算符
(以下是部分示例)
In [858]: a=np.array([2,3,5,8,13])

In [859]: b=np.array([3,4,7,11,18])

In [860]: np.invert(a)
Out[860]: array([ -3,  -4,  -6,  -9, -14], dtype=int32)

In [862]: bin(a[4]),bin(~a[4])
Out[862]: ('0b1101', '-0b1110')

In [863]: np.bitwise_and(a,b)
Out[863]: array([2, 0, 5, 8, 0], dtype=int32)

bin(a[3]),bin(b[3]),bin(a[3]&b[3])
Out[865]: ('0b1000', '0b1011', '0b1000')

np.invert对于有符号整数,取对应二进制数的补码,然后 +1。二进制数形式的最高位为0表示正数,最高位为 1 表示负数。

9. 广播

numpy在进行不同形状的数组之间的计算时,会自动沿长度不足且长度为1的轴方向进行广播。当维数不一致时,会向前补齐,这要求后面的轴长度相同或是有一方长度等于1,即从后至前进行轴长比对,例如形状(a,b,c)与(b,c)可计算,(a,b,c)与(b,1)可计算,(a,b,c)与(a,b,1)可计算,(a,b,c)与(a,b)不可计算,(a,b,c)与(a,c,b)不可计算。

In [414]: a1=np.zeros((2,3,4))
         ...: a2=np.random.randint(0,10,(3,4))
         ...: a3=np.random.randint(0,10,(2,3))
         ...: a4=np.random.randint(0,10,(2,3,1))

In [418]: a1+a2
Out[418]: 
array([[[ 3.,  6.,  0.,  6.],
        [ 9.,  6.,  3.,  4.],
        [ 2.,  3.,  1.,  5.]],

       [[ 3.,  6.,  0.,  6.],
        [ 9.,  6.,  3.,  4.],
        [ 2.,  3.,  1.,  5.]]])

In [419]: a1+a3
Traceback (most recent call last):

  File "<ipython-input-419-d778f9717621>", line 1, in <module>
    a1+a3

ValueError: operands could not be broadcast together with shapes (2,3,4) (2,3) 

In [420]: a1+a4
Out[420]: 
array([[[ 0.,  0.,  0.,  0.],
        [ 9.,  9.,  9.,  9.],
        [ 1.,  1.,  1.,  1.]],

       [[ 4.,  4.,  4.,  4.],
        [ 0.,  0.,  0.,  0.],
        [ 2.,  2.,  2.,  2.]]])

10. 常量

属性名 常量名
np.e 自然指数 2.718281828459045
np.pi 圆周率 3.141592653589793
np.euler_gamma 欧拉常数 0.5772156649015329
np.inf 无穷大 -
np.nan 非数值 -

四. 查找

1. 索引

通过ndarray[index1,index2...]的形式指定索引。

(1). 定位元素
In [452]: a=np.array([[1,2],[3,4]])

In [453]: a[1,1]
Out[453]: 4
(2). 数据切片
In [454]: a[:,1]
Out[454]: array([2, 4])

In [455]: a[0:1,:]
Out[455]: array([[1, 2]])

通过low:high的形式限定索引范围,区间前闭后开,上下限留空表示不限制。
标量值索引会使对应维度消失,范围索引则不会。

(3). 倒序索引
In [457]: a[-1,:-1]
Out[457]: array([3])

-n表示倒数第n个。

(4). 按步长选取
In [458]: a[0:2:2]
Out[458]: array([[1, 2]])

通过low:high:step的形式限定索引范围和步长。

(5). 序列反转
In [459]: a[::-1]
Out[459]: 
array([[3, 4],
       [1, 2]])

等效于a[-1:-len(a)-1:-1]

(6). 布尔索引
In [460]: a>2
Out[460]: 
array([[False, False],
       [ True,  True]], dtype=bool)

In [461]: (a>2)&(a<4)
Out[461]: 
array([[False, False],
       [ True, False]], dtype=bool)

In [462]: a[(a>2)&(a<4)]
Out[462]: array([3])

ndarray应用逻辑运算符会得到布尔索引,布尔索引标识了每个元素符合逻辑判断条件的情况,使用该索引筛选数组将得到所有满足条件的元素构成的一维数组。

(7). 数组索引
In [463]: a[[0,1],[1]]
Out[463]: array([2, 4])

In [464]: a[[0,1],[1,0]]
Out[464]: array([2, 3])

将对应轴上需要选取的索引以数组形式传入,当在多个轴上传入数组索引时,索引会被一一对应。支持list,tuple,ndarray,支持倒序索引。

(8). 综合示例
获取第二列大于2的行的第一列
In [465]: a[a[:,1]>2,0]
Out[465]: array([3])
获取二维数组的顶角元素
In [466]: a[[0,-1]][:,[0,-1]]
Out[466]: 
array([[1, 2],
       [3, 4]])

In [467]: a[[0,0,-1,-1],[0,-1,0,-1]].reshape((2,2))
Out[467]: 
array([[1, 2],
       [3, 4]])

此处使用的reshape重塑方法会在重构章节讲到。

(9). 获取布尔索引指示的位置
In [473]: a>2
Out[473]: 
array([[False, False],
       [ True,  True]], dtype=bool)

In [474]: np.where(a>2)
Out[474]: (array([1, 1], dtype=int64), array([0, 1], dtype=int64))

In [476]: a[np.where(a>2)]
Out[476]: array([3, 4])

In [477]: np.where(a>2,a,-a)
Out[477]: 
array([[-1, -2],
       [ 3,  4]])

只输入数组时,内部会调用nonzero方法,返回值为一个tuple,包含每个轴上的索引值序列。
通过第二个参数x和第三个参数y,可以根据是否满足条件对元素进行不同的计算,返回同样形状的数组。

(10). 特定值查找
指定值序列
In [655]: a=np.arange(5)

In [656]: np.isin(a,[3,4])
Out[656]: array([False, False, False,  True,  True], dtype=bool)

第二个参数test_elements为指定值序列,返回布尔索引。

值排序搜索
In [683]: a=np.array([1,3,5,7,9])

In [684]: np.searchsorted(a,[3,6])
Out[684]: array([1, 3], dtype=int64)

第一个参数a为升序一维数组,非升序可通过第四个参数指定排序sorter=np.argsort(a),第二个参数v为需要被插入a的值或值序列,第三个参数side为判断方式,'left'表示a[i-1] < v <= a[i]'right'表示a[i-1] <= v < a[i]。该方法将逐个搜索v的每个元素在升序数组a中合适的插入位置,返回数组索引。

空值
In [661]: a=np.array([np.nan,1,2,3])

In [662]: np.isnan(a)
Out[662]: array([ True, False, False, False], dtype=bool)

返回布尔索引。

(11). 返回索引的方法
函数 作用 说明
np.argsort 返回排序后索引 -
np.argmax 返回最大值索引 -
np.argmin 返回最小值索引 -
np.argpartition 返回分区索引 第二个参数kth指定用于分区的元素索引
np.argwhere 返回符合条件的值索引 np.where类似,但返回值的形式不太适合索引
np.where 返回符合条件的值索引 -
np.isin 返回判断元素是否在指定列表中的布尔索引 第二个参数test_elements为指定的值序列
np.isnan 返回判断元素是否nan值的布尔索引 -
np.searchsorted 返回待插入值在升序序列中的插入位置 第一个参数a为升序一维数组,第二个参数v为待插入值
np.digitize 返回分箱后的索引 第二个参数bins指定分箱方式

2. 遍历

(1). 序列结构遍历
a=np.array([[1,2],[3,4]])

for buf1 in a:
    for buf2 in buf1:
        print(buf2)
(2). 索引遍历
for i in range(a.shape[0]):
    for j in range(a.shape[1]):
        print(a[i,j])

以上两种方法在遍历元素时,维数是多少就要嵌套多少层循环,效率不算高,但可以在每一层循环中嵌入额外的计算。

(3). 快速迭代器遍历
for item in np.nditer(a):
    print(item)

默认选择元素的顺序是和数组内存布局一致的,而不是使用标准C或者Fortran顺序。这是为了使用效率而设计的,这反映了默认情况下只需访问每个元素,而无需考虑其特定顺序。可通过order参数来指定特定的顺序来访问数组。

(4). 平铺迭代器遍历
for item in a.flat:
    print(item)

将数组转换为1-D的迭代器。

3. 抽样

(1). 无放回抽样
In [506]: a=np.array([[1,2],[3,4]])

In [507]: idx=np.random.choice(np.arange(a.shape[0]),size=1,replace=False)

In [508]: a[idx]
Out[508]: array([[3, 4]])
(2). 有放回抽样
In [511]: idx=np.random.randint(0,a.shape[0],size=3)

In [512]: a[idx]
Out[512]: 
array([[1, 2],
       [3, 4],
       [1, 2]])

也可使用np.random.choice(np.arange(a.shape[0]),size=3)生成索引。
以上都是在单个轴方向上抽样,如果想在多个轴方向上抽样,由于抽样后必然会破坏数组结构,建议先将用于抽样的轴展开,比如用reshape方法,见下面的示例,关于reshape方法的说明在重构章节。

(3). 多轴方向抽样
In [524]: a=np.array([[[1,2],[3,4]],[[5,6],[7,8]]])

In [525]: a2=a.reshape((2,4))

In [526]: idx=np.random.randint(0,a2.shape[0],size=2)

In [527]: a2[:,idx]
Out[527]: 
array([[1, 2],
       [5, 6]])

4. 视图

在对数组进行了索引切片后,返回的通常是原数组的一个视图,不会完整的拷贝数据,因此在这种情况下进行更新操作会影响到原数组和所有视图。
可通过ndarray.view()方法获得一个完整视图,等效于ndarray[:,:,...]
想将视图转换为拷贝可使用ndarray.copy()方法。

In [542]: a=np.array([[1,2],[3,4]])

In [543]: a2=a[1,:]

In [544]: a2
Out[544]: array([3, 4])

In [545]: a2[:]=0

In [546]: a
Out[546]: 
array([[1, 2],
       [0, 0]])

五. 变更

1. 更新

(1). 更新整个数组
In [555]: a=np.array([[1,2],[3,4]])

In [556]: a=a+1

In [557]: a
Out[557]: 
array([[2, 3],
       [4, 5]])

In [558]: a[:,:]=a-1

In [559]: a
Out[559]: 
array([[1, 2],
       [3, 4]])

两种方式都能更新整个数组,第一种将计算得到的新数组的引用重新赋给了a,第二种根据计算得到的新数组更新了原数组中相应位置的值。

(2). 更新指定位置
In [569]: a[a>2]+=1

In [570]: a
Out[570]: 
array([[1, 2],
       [4, 5]])

In [571]: a[a>2]=a[a>2]+1

In [572]: a
Out[572]: 
array([[1, 2],
       [5, 6]])

In [573]: a[0,:]=0

In [574]: a
Out[574]: 
array([[0, 0],
       [5, 6]])

In [575]: a[a>2]=[3,4]

In [576]: a
Out[576]: 
array([[0, 0],
       [3, 4]])

值数组形状需要与筛选后的原数组一致或遵循广播的规则。

(3). 定值填充
In [9]: a.fill(1)

In [10]: a
Out[10]: 
array([[1, 1],
       [1, 1]])

当填充值数据类型与数组数据类型不一致时,会尝试转换,失败时才会报错。

2. 扩增

(1). 插入
In [577]: a=np.array([[1,2],[3,4]])

In [578]: np.insert(a,1,[5,6],axis=0)
Out[578]: 
array([[1, 2],
       [5, 6],
       [3, 4]])

第二个参数obj是插入的位置索引,第三个参数values是待插入的值,需要与指定轴方向上的切片形状一致或满足广播规则,第四个参数axis是指定的轴。不影响原数组,返回的是一个拷贝。

(2). 附加
In [578]: np.append(a,[[5,6]],axis=0)
Out[578]: 
array([[1, 2],
       [3, 4],
       [5, 6]])

第二个参数values是待插入的值,需要与指定轴方向上的切片形状一致或满足广播规则,第三个参数axis是指定的轴。只能将新数据附加到数组末尾。不影响原数组,返回的是一个拷贝。

(3). 堆叠
In [589]: np.c_[a,a]
Out[589]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])

In [590]: np.column_stack((a,a))
Out[590]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])

In [591]: np.concatenate((a,a),axis=1)
Out[591]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])

In [592]: np.r_[a,a]
Out[592]: 
array([[1, 2],
       [3, 4],
       [1, 2],
       [3, 4]])

In [593]: np.row_stack((a,a))
Out[593]: 
array([[1, 2],
       [3, 4],
       [1, 2],
       [3, 4]])

In [594]: np.concatenate((a,a),axis=0)
Out[594]: 
array([[1, 2],
       [3, 4],
       [1, 2],
       [3, 4]])

In [595]: np.stack((a,a),axis=0)
Out[595]: 
array([[[1, 2],
        [3, 4]],

       [[1, 2],
        [3, 4]]])

np.c_np.column_stack是沿轴1进行堆叠,其他轴长度需要相同或满足广播规则,等效于np.concatenate(axis=1)
np.r_np.row_stack是沿轴0进行堆叠,其他轴长度需要相同或满足广播规则,等效于np.concatenate(axis=0)
np.stack是沿新轴进行堆叠,所有轴长度需要相同或满足广播规则。

(4). 重复
In [93]: a.repeat(3,axis=1)
Out[93]: 
array([[1, 1, 1, 2, 2, 2],
       [3, 3, 3, 4, 4, 4]])

In [96]: a.reshape((2,2,1)).repeat(3,axis=2)
Out[96]: 
array([[[1, 1, 1],
        [2, 2, 2]],

       [[3, 3, 3],
        [4, 4, 4]]])

In [98]: a.repeat(3).reshape((2,2,3))
Out[98]: 
array([[[1, 1, 1],
        [2, 2, 2]],

       [[3, 3, 3],
        [4, 4, 4]]])

In [99]: np.tile(a,2)
Out[99]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])

In [100]: np.tile(a,[2,2])
Out[100]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4],
       [1, 2, 1, 2],
       [3, 4, 3, 4]])

In [101]: np.tile(a.ravel(),2)
Out[101]: array([1, 2, 3, 4, 1, 2, 3, 4])

repeat方法将数组中的元素重复,可通过axis参数指定轴方向,默认会将数组展开后在唯一的轴方向上重复元素。可配合ndarray.reshape在新轴上复制元素。
tile方法将数组重复,注意,重复的是整个数组,不是单个元素,得到的结果中同元素不一定是紧挨着的。

3. 删除

(1). 索引筛选
In [616]: a[~(a[0]==1),:]
Out[616]: array([[3, 4]])

通过索引筛选可得到删除指定内容的数组。

(2). 删除方法
In [617]: np.delete(a,[0,1],axis=0)
Out[617]: array([], shape=(0, 2), dtype=int32)

通过相应方法获得删除指定索引位置内容的数组。第二个参数obj为索引位置,第三个参数axis为指定轴。

六. 重构

1. 类型转换

In [619]: a=np.array([[1,2],[3,4]])

In [620]: a.dtype
Out[620]: dtype('int32')

In [621]: a=a.astype('float64')

In [622]: a.dtype
Out[622]: dtype('float64')

In [623]: a=np.int32(a)

In [624]: a.dtype
Out[624]: dtype('int32')

使用ndarray.astype方法或是使用数据类型同名方法都可以转换类型,关于numpy支持的数据类型可以查看属性章节。转换类型后返回一个新数组。

2. 重塑

(1). 改变形状
In [625]: a=np.array([[1,2],[3,4]])

In [626]: a.reshape((1,4))
Out[626]: array([[1, 2, 3, 4]])

In [627]: a.reshape((-1,4))
Out[627]: array([[1, 2, 3, 4]])

使用tuple类型的参数声明新的形状。允许有一个新轴的大小为-1,表示自动计算。
改变前后元素数size需要保持一致。元素在轴上的排列是从最后一个轴开始往前面的轴方向上堆叠,见如下图示,可通过order参数指定其他排序方式。轴的相对位置不会改变,所以一些复杂的变形可能需要结合transposeswapaxes此类轴交换方法使用。

(2). 平铺
In [640]: a.ravel()
Out[640]: array([1, 2, 3, 4])

In [641]: a.flatten()
Out[641]: array([1, 2, 3, 4])

将数组平铺为向量,等效于reshape((-1,)),可通过order参数指定其他排序方式。

(3). 转置
In [643]: a.T
Out[643]: 
array([[1, 3],
       [2, 4]])

数组为一维时转置无效,为二维时即矩阵的转置,多于二维时交换第一个和最后一个轴。

(4). 轴交换
In [646]: a.swapaxes(0,1)
Out[646]: 
array([[1, 3],
       [2, 4]])

In [647]: a.transpose([1,0])
Out[647]: 
array([[1, 3],
       [2, 4]])

swapaxes一次只能指定两个轴进行交换,transpose可以重新为所有轴排序。

3. 排序

(1). 直接排序
In [734]: a=np.array([[2,3],[1,4]])

In [735]: np.sort(a,axis=None)
Out[735]: array([1, 2, 3, 4])

In [736]: a.sort(axis=0)

In [737]: a
Out[737]: 
array([[1, 3],
       [2, 4]])

ndarray.sort会直接在原数组上排序,可通过第一个参数axis指定排序的轴,会将沿着该轴方向的每个向量单独排序,默认-1,除沿最后一个轴外,指定其他轴都会在排序时生成数据的临时副本,因此沿最后一个轴排序最快。
等效方法np.sort,返回的是排序后的副本,还可指定axis=None,会将数组展开再排序。
当数组的维度具备实际含义时,直接排序会打乱数据结构,得到不被期望的结果,这种情况下需要使用间接排序。

(2). 间接排序
In [740]: a=np.array([[2,3,5],[1,1,4],[1,2,3]])

In [741]: a
Out[741]: 
array([[2, 3, 5],
       [1, 1, 4],
       [1, 2, 3]])

In [742]: idx1=np.argsort(a[:,0])

In [743]: a[idx1]
Out[743]: 
array([[1, 1, 4],
       [1, 2, 3],
       [2, 3, 5]])

In [744]: idx2=np.lexsort((a[:,0],a[:,2]))

In [745]: a[idx2]
Out[745]: 
array([[1, 2, 3],
       [1, 1, 4],
       [2, 3, 5]])

argsort可用于单键间接排序,lexsort可用于多键间接排序。

(3). 随机排序
In [763]: a=np.arange(12).reshape((3,4))

In [764]: a
Out[764]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [765]: np.random.shuffle(a)

In [766]: a
Out[766]: 
array([[ 4,  5,  6,  7],
       [ 0,  1,  2,  3],
       [ 8,  9, 10, 11]])

In [768]: idx=np.random.permutation(a.shape[1])

In [769]: a[:,idx]
Out[769]: 
array([[ 4,  5,  7,  6],
       [ 0,  1,  3,  2],
       [ 8,  9, 11, 10]])

方法一np.random.shuffle只能沿第一个轴进行随机排序,方法二是通过np.random.permutation,该方法也只能沿第一个轴随机排序,但在输入参数xint类型时,会对np.arange(x)进行随机排序,可以快速生成乱序索引,再通过索引查找得到乱序数组。

(4). 分区排序
In [799]: a=np.array([2,3,1,5,4,0,8])

In [800]: np.partition(a,1)
Out[800]: array([0, 1, 3, 5, 4, 2, 8])

In [801]: np.partition(a,4)
Out[801]: array([0, 1, 2, 3, 4, 5, 8])

分区排序是一种不完整的排序,用于不需要获取完整排序序列的情况下。该方法只保证kth指定位置的元素是正确排序的,其他小于该元素的元素前移,大于的后移。可用于快速找出第k大或第k小的元素。
也可通过ndarray.partition调用,axis参数指定轴方向,还有对应的np.argpartition用于获取分区后的索引。

4. 去重

In [775]: a=np.array([3,2,2,3,1,1,4])

In [776]: np.unique(a,return_index=True,return_inverse=True,return_counts=True,axis=None)
Out[776]: 
(array([1, 2, 3, 4]),
 array([4, 1, 0, 6], dtype=int64),
 array([2, 1, 1, 2, 0, 0, 3], dtype=int64),
 array([2, 2, 2, 1], dtype=int64))

In [777]: a=np.array([[1,3],[2,4],[1,3]])

In [778]: np.unique(a,axis=0)
Out[778]: 
array([[1, 3],
       [2, 4]])

axis指定去重的轴,默认None会将数组展开后再去重。
返回值一为去重后的有序值列表;
返回值二为唯一值在原数组中的索引,仅在return_index=True时提供;
返回值三为根据唯一值重建原数组的索引,仅在return_inverse=True时提供;
返回值四为唯一值的出现计数,仅在return_counts=True时提供。

5. 拆分

(1). 索引拆分

In [780]: a=np.arange(9).reshape((3,3))

In [781]: a[:2,:],a[2:,:]
Out[781]: 
(array([[0, 1, 2],
        [3, 4, 5]]), array([[6, 7, 8]]))

In [782]: a[a>2],a[~(a>2)]
Out[782]: (array([3, 4, 5, 6, 7, 8]), array([0, 1, 2]))

In [790]: idx=np.random.permutation(a.shape[0])

In [791]: sp_idx=int(a.shape[0]*0.8)

In [792]: a[idx[:sp_idx]],a[idx[sp_idx:]]
Out[792]: 
(array([[3, 4, 5],
        [6, 7, 8]]), array([[0, 1, 2]]))

最灵活的方式,复杂的拆分需要写较多的代码,可使用其他方法配合生成用于拆分的索引,比如使用np.digitize进行分箱。

(2). 拆分方法

In [802]: a=np.arange(8)

In [803]: np.split(a,2)
Out[803]: [array([0, 1, 2, 3]), array([4, 5, 6, 7])]

In [805]: np.split(a,[2,5])
Out[805]: [array([0, 1]), array([2, 3, 4]), array([5, 6, 7])]

In [806]: a=a.reshape((2,4))

In [807]: a
Out[807]: 
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

In [809]: np.split(a,[1,3],axis=1)
Out[809]: 
[array([[0],
        [4]]), array([[1, 2],
        [5, 6]]), array([[3],
        [7]])]

第二个参数indices_or_sections指定分割方式,int类型表示等分数量,一维数组类型表示用于分割的索引值,例如[2,5]表示分割为a[:2],a[2:5],a[5:]
第三个参数axis可以指定轴方向。

七. 读写

numpy 可使用专用的npynpz格式或常见的txt格式存储ndarray的数据。

1. 保存

In [803]: a=np.array([1,2,3,4])

In [804]: b=np.array([[5,6],[7,8]])

In [805]: np.save('D:\\out.npy',a)

In [806]: np.savez('D:\\out.npz',a,b=b)

In [807]: np.savetxt('D:\\out.txt',b,fmt="%d", delimiter=",")

拓展名可以省略,会自动补全。无法自动创建文件夹。
save用于保存单个数组为npy格式文件。
savez用于保存多个数组为npz格式文件,没有使用关键字参数传递的数组会自动命名为arr_0,arr_1,...
savetxt用于保存单个数组为txt格式文件,参数fmt指定保存时的字符串转换,参数delimiter指定分隔符,注意在读取时也需要指定分隔符。分隔符的设置对一维数组无效,二维以上的数组不适合用该方法保存。

2. 加载

In [835]: np.load('D:\\out.npy')
Out[835]: array([1, 2, 3, 4])

In [836]: npz=np.load('D:\\out.npz')

In [837]: npz['arr_0']
Out[837]: array([1, 2, 3, 4])

In [838]: npz['b']
Out[838]: 
array([[5, 6],
       [7, 8]])

In [841]: np.loadtxt('D:\\out.txt',dtype='int',delimiter=',')
Out[841]: 
array([[5, 6],
       [7, 8]])

np.load读取npz格式文件会得到一个NpzFile对象,之后通过保存时设置的名称进行[]索引可以取得每一个数组。

八. 其他

1. 矩阵类型

numpy提供了一个专用的矩阵对象matrix,是基于 ndarray作了进一步的封装得到的,能够更加快捷地进行一些矩阵相关的运算,但相比ndarray没有性能上的优势且维数限制在二维,并不推荐使用。

(1). 创建
In [79]: m1=np.matrix([[1,2],[3,4]])

In [81]: m1
Out[81]: 
matrix([[1, 2],
        [3, 4]])

创建方式与ndarray类似。

(2). 与 ndarray的相互转换
In [82]: a=np.array([[5,6],[7,8]])

In [83]: m2=np.matrix([[5,6],[7,8]])

In [84]: np.asmatrix(a)
Out[84]: 
matrix([[5, 6],
        [7, 8]])

In [85]: np.matrix(a)
Out[85]: 
matrix([[5, 6],
        [7, 8]])

In [88]: np.asarray(m2)
Out[88]: 
array([[5, 6],
       [7, 8]])

In [89]: np.array(m2)
Out[89]: 
array([[5, 6],
       [7, 8]])

In [90]: m2.base
Out[90]: 
array([[5, 6],
       [7, 8]])
(3). 矩阵运算
In [94]: m1*m2
Out[94]: 
matrix([[19, 22],
        [43, 50]])

In [95]: m1.I
Out[95]: 
matrix([[-2. ,  1. ],
        [ 1.5, -0.5]])

In [96]: m1.T
Out[96]: 
matrix([[1, 3],
        [2, 4]])

运算符*用在matrix上表示矩阵乘法,等效于np.dot(m1,m2),要实现元素相乘需要使用np.multiply(m1,m2)
matrix.T表示转置矩阵,matrix.I表示逆矩阵。
matrix可以使用大部分ndarray的方法,比如maxsumsort等。

2. 张量运算

张量是向量、矩阵这类概念的推广,标量是0阶张量,向量是1阶张量,矩阵是2阶张量。
numpy提供了广义的张量点积运算np.tensordot

In [2]: a=np.arange(1,9).reshape((2,2,2))

In [3]: b=np.arange(1,5).reshape((2,2))

In [4]: a
Out[4]: 
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

In [5]: b
Out[5]: 
array([[1, 2],
       [3, 4]])

In [8]: np.tensordot(a,b,axes=1)
Out[8]: 
array([[[ 7, 10],
        [15, 22]],

       [[23, 34],
        [31, 46]]])

In [9]: np.tensordot(a,b,axes=(-1,0))
Out[9]: 
array([[[ 7, 10],
        [15, 22]],

       [[23, 34],
        [31, 46]]])

In [10]: np.tensordot(a,b,axes=2)
Out[10]: array([30, 70])

In [12]: np.tensordot(a,b,axes=([-2,-1],[0,1]))
Out[12]: array([30, 70])

In [13]: np.dot(a,b)
Out[13]: 
array([[[ 7, 10],
        [15, 22]],

       [[23, 34],
        [31, 46]]])

In [14]: np.tensordot(a,b,axes=(-1,-2))
Out[14]: 
array([[[ 7, 10],
        [15, 22]],

       [[23, 34],
        [31, 46]]])

In [15]: np.inner(a,b)
Out[15]: 
array([[[ 5, 11],
        [11, 25]],

       [[17, 39],
        [23, 53]]])

In [16]: np.tensordot(a,b,axes=(-1,-1))
Out[16]: 
array([[[ 5, 11],
        [11, 25]],

       [[17, 39],
        [23, 53]]])

前两个参数ab为参与运算的两个张量。
第三个参数axes用于指定收缩的轴,完整格式形如([a_axis1,a_axis2,...],[b_axis1,b_axis2,...]),两个序列分别指定ab的轴,轴方向上的元素会按照被指定的顺序对应、相乘并相加;可使用(a_axis,b_axis)的形式仅指定一个轴;
可使用int类型快速指定a中最后N个轴和b中前N个轴用于收缩,即0等效于([],[]),对应张量积运算,1等效于([-1],[0]),对应张量内积运算,2等效于([-2,-1],[0,1]),对应张量双收缩运算,axes的默认值为2
np.dotnp.inner这类运算可视作该函数表示的几个特例,np.dot(a1,a2)等效于np.tensordot(a1,a2,axes=(-1,-2))np.inner(a1,a2)等效于np.tensordot(a1,a2,axes=(-1,-1))

3. 傅里叶变换

(1) 频率序列
In [12]: np.fft.fftfreq(10,1.0)
Out[12]: array([ 0. ,  0.1,  0.2,  0.3,  0.4, -0.5, -0.4, -0.3, -0.2, -0.1])

返回离散傅里叶变换采样频率,第一个参数n为窗口长度,int类型,第二个参数d为采样间距(采样率的倒数),默认为1.0,返回值单位与采样间距单位相对应。
返回值序列的计算方式:
如果n是偶数,f = [0, 1, ..., n/2-1, -n/2, ..., -1] / (d*n)
如果n是奇数,f = [0, 1, ..., (n-1)/2, -(n-1)/2, ..., -1] / (d*n)

(2) 快速傅里叶变换
In [13]: x=np.cos(np.linspace(0,2*np.pi,30))

In [14]: y=np.fft.fft(x)

In [15]: x2=np.fft.ifft(y)

In [16]: np.abs(x2-x).max()
Out[16]: 3.8864883384594504e-16

np.fftnp.ifft互为逆运算,用于一维快速傅里叶变换,经np.fft变换后的序列可通过np.ifft近似还原为原序列。
第二个参数n指定输出的变换轴长度,超长裁剪,不足补0;
第三个参数axis指定用于变换的轴,默认最后一个轴。

(3) 移频
In [21]: x=np.linspace(0,2*np.pi,8)

In [22]: y=np.fft.fft(x)

In [23]: y2=np.fft.fftshift(y)

In [24]: y3=np.fft.ifftshift(y2)

In [32]: np.abs(y3-y).max()
Out[32]: 0.0

np.fftshiftnp.ifftshift互为逆运算,用于将傅里叶变换输出中的直流分量移动到频谱的中央。第二个参数axis可指定用于转移的轴。

4. 图像处理

图像数据的存储方式是类似于数组的,可借助PIL库读取图片,再将图像数据转成ndarray进行计算处理。
以下提供一些使用numpy配合PIL处理图片数据的方法:

(1) 图片的创建、读取、缩放、保存
In [2]: from PIL import Image
   ...: image1= Image.open("D:\\test.jpg")

In [3]: image1.size
Out[3]: (1015, 610)

In [4]: image2=image1.resize((500,300))

In [5]: image2.save("D:\\test2.jpg")

In [6]: image0=Image.new('RGB',image2.size)

In [7]: image2
Out[7]: 

Image.open用于打开一张图片,mode可以设置读取模式,最常用的是'L'灰度图和'RGB'彩色图,一般会自动匹配不需要设置。
图片对象的resize方法可以缩放图片,大小参数以tuple类型(width,height)格式传入。
图片对象的save方法可以保存图片,通过保存路径中的文件拓展名或是format参数指定保存文件类型,quality参数可以设置保存图像的质量,取值1(最差)~ 95(最佳),默认75。
Image.new用于创建一个新的图片,mode参数指定模式,size指定大小。
IPython中,直接输入图片变量名就可以显示图片。

(2) 图片与数组之间的转换
In [7]: a=np.asarray(image2)

In [8]: a.shape
Out[8]: (300, 500, 3)

In [9]: image3=Image.fromarray(a,'RGB')

np.asarraynp.array可以将图片转换为ndarraynp.asarray返回图片数据的ndarray类型视图,不能更改。根据类型的不同,得到的数组形状也不一样,常见的两种,灰度图转换得到形状为(height,width)的数组,彩色图转换得到形状为(height,width,channel)的数组,channel即颜色通道,RGB模式下channel=3,分别对应红绿蓝。
Image.fromarray可以将ndarray转换为图片,可通过mode参数指定模式,默认会根据数组形状自动匹配,当指定某个模式时,数组形状也需要匹配。
注意,数组转图片需要是uint8数据类型,取值0~255,如不符合要进行另外的转换。

(3) 像素点绘制
In [10]: a0=np.array(image0)

In [11]: a0+=np.random.randint(0,256,a.shape,dtype='uint8')

In [12]: image4=Image.fromarray(a0)

In [13]: image4.putpixel((0,0),(255,255,255))

In [14]: image4 
Out[14]: 

图片对象的putpixel方法可以添加单个像素点,第一个参数xy(x,y)的形式声明添加像素点的位置,第二个参数value指定像素点的值,例如,L灰度图模式下为标量值,RGB彩色图模式下为(r,g,b)形式的tuple,取值均在0~255之间。该方法一次只能绘制一个像素点,效率低,在需要批量绘制时建议转换为ndarray处理。
上面的示例中使用ndarray的方法为新图片的每一个像素点添加了随机色彩。

(4) 灰度图和彩色图之间的转换
In [72]: a2=np.asarray(image2,dtype='float')

In [73]: a5=(11*a2[:,:,0]+16*a2[:,:,1]+5*a2[:,:,2])/32

In [74]: image5=Image.fromarray(np.uint8(a5))

In [75]: a6=a5.repeat(3).reshape(a5.shape+(3,))

In [76]: image6=Image.fromarray(np.uint8(a6))

In [77]: image5
Out[77]: 


彩色图转灰度图,L=11*R+16*G+5*B只是一种可行的公式,也有其他公式可用。
灰度图转彩色图,较为简单,即将灰度值拷贝到RGB3个通道上,转换后颜色还是灰色,因为灰度图不具备色彩方面的信息,即使先将彩色图转灰度图,再转换回彩色图,色彩信息同样会丢失。

(5) 图片的翻转、旋转、裁剪
In [84]:a2=np.asarray(image2)

In [85]:Image.fromarray(a2[::-1,::-1,:])
Out[85]: 
In [90]:Image.fromarray(a2.transpose([1,0,2]))
Out[90]: 
In [91]:Image.fromarray(a2[:150,:,:])
Out[91]: 

以上示例展示了 左右翻转+上下翻转,左右翻转+逆时针转90°,截取上半部分 三种情况,借助倒序索引和轴交换的组合可以得到90°倍数旋转和上下左右翻转的所有组合情形。精确的旋转需要使用矩阵运算,此处不作展开。

推荐阅读更多精彩内容