最后一次更新日期: 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.int32
,float
对应np.float64
,complex
对应np.complex128
。
dtype与type的转换
获取dtype的type:dtype.type
通过type创建dtype:dtype(type)
dtype与type可以直接进行比较
np.dtype('int')==np.int32
dtype的字节顺序
np.dtype('
字节顺序是通过对数据类型预先设定"<"或">"来决定的。"<"意味着小端法(最小值存储在最小的地址,即低位组放在最前面)。">"意味着大端法(最重要的字节存储在最小的地址,即高位组放在最前面)。一般情况下采用默认设置即可。
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
可以显式声明数组的数据类型,可传入dtype
或type
(python基础数据类型或numpy数据类型)或与dtype
对应的字符串标识,不声明的情况下会根据传入数据自动采用最合适的数据类型。
(2). 从tuple创建
a=np.array((1,2))
与list
是一样,list
与tuple
的嵌套同理。
(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
会避免没有必要的重复创建,当数据源同样是ndarray
且dtype
无变化时,不会返回新的数组。
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方向上的增长,然后将X
和Y
广播至相同大小,存在更多索引位时以此类推。
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 |
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.max
和np.min
有对应的np.argmax
和np.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 |
np.char.splitlines | 行分割 | 以换行符分割,返回list |
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='
np.char.multiply
In [303]: a=np.array(['a','b'])
In [304]: np.char.multiply(a,3)
Out[304]:
array(['aaa', 'bbb'],
dtype='
np.char.center
In [305]: a=np.array(['a','b'])
In [306]: np.char.center(a,10,'*')
Out[306]:
array(['****a*****', '****b*****'],
dtype='
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='
注意,该方法无法实现多维数组的聚合计算。在数组元素为字符串时,会对字符串中的每个元素进行拼接;在数组元素为字符串序列时,会对序列中的字符串进行拼接。
与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 "", line 1, in
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
一致。
inner
与dot
类似,但规则更简单,两个数组均以最后一个轴作为向量的轴,即最后一个轴长度要保持一致。例如,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) | 交集 | x 和y 的公共元素 |
np.union1d(x, y) | 并集 | x 和y 的所有元素 |
np.setdiff1d(x, y) | 集合差 | x 中存在,y 中不存在的元素 |
np.setxor1d(x, y) | 集合异或 | x 和y 的独占元素 |
以上方法适用于一维数组。
(以下是部分示例)
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 "", line 1, in
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
参数指定其他排序方式。轴的相对位置不会改变,所以一些复杂的变形可能需要结合transpose
或swapaxes
此类轴交换方法使用。
(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
,该方法也只能沿第一个轴随机排序,但在输入参数x
为int
类型时,会对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 可使用专用的npy
和npz
格式或常见的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
的方法,比如max
、sum
、sort
等。
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]]])
前两个参数a
和b
为参与运算的两个张量。
第三个参数axes
用于指定收缩的轴,完整格式形如([a_axis1,a_axis2,...],[b_axis1,b_axis2,...])
,两个序列分别指定a
和b
的轴,轴方向上的元素会按照被指定的顺序对应、相乘并相加;可使用(a_axis,b_axis)
的形式仅指定一个轴;
可使用int
类型快速指定a
中最后N个轴和b
中前N个轴用于收缩,即0
等效于([],[])
,对应张量积运算,1
等效于([-1],[0])
,对应张量内积运算,2
等效于([-2,-1],[0,1])
,对应张量双收缩运算,axes
的默认值为2
。
np.dot
、np.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.fft
和np.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.fftshift
和np.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.asarray
或np.array
可以将图片转换为ndarray
,np.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
只是一种可行的公式,也有其他公式可用。
灰度图转彩色图,较为简单,即将灰度值拷贝到
RGB
3个通道上,转换后颜色还是灰色,因为灰度图不具备色彩方面的信息,即使先将彩色图转灰度图,再转换回彩色图,色彩信息同样会丢失。
(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°倍数旋转和上下左右翻转的所有组合情形。精确的旋转需要使用矩阵运算,此处不作展开。