NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。现在通过这篇笔记简单整理一下numpy库的一些简单使用方法。主要参考了NumPy 教程 | 菜鸟教程 (runoob.com)教程。
NumPy 最重要的一个特点是其 N 维数组对象 ndarray,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引。
通常来说,ndarray 内部由以下内容组成:
numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
其中,object表示内容,即我们赋予的数组;dtype表示数组元素的数据类型,如需要明确数据类型我们可以使用dtype关键字。(注意numpy智能定义同一类型的数据,如果数据不匹配,Numpy会向上转换);copy表示对象是否需要复制,可选;order表示创建数组的样式,C为行方向,F为列方向,A为任意方向(默认);subok表示默认返回一个与基类类型一致的数组;ndmin表示指定生成数组的最小维度。
下面看一段简单的代码来直观感受
import numpy as np #调用numpy库
x1=np.array([1,2,3]) #创建一个一维数组,数据类型默认为整型
x2=np.array([1,2,3],dtype='float32') #创建一个一维数组,数据类型定义为浮点型
x3=np.array([[1,2,3],[4,5,6]]) #创建一个二维数组
x4=np.array([1,2,3],ndmin=2) #定义最小维度为2
打印结果后显示如下
[1 2 3];
[1. 2. 3.];
[[1 2 3]
[4 5 6]];
[[1 2 3]];
numpy定义的数组通常有以下几种属性:
属性 | 说明 |
---|---|
ndarray.ndim | 数组的维度 |
ndarray.shape | 数组的维度大小,如数组为n行m列 |
ndarray.size | 数组元素的总个数,相当于 .shape 中 n*m 的值 |
ndarray.dtype | 数组对象的数据类型 |
ndarray.itemsize | 数组中每个元素的大小,以字节为单位 |
ndarray.nbytes | 数组总字节大小(也就是itemsize×size) |
我们可以定义一个数组来看看对应的属性
import numpy as np #调用numpy库
x=np.array([[1,2,3],[4,5,6]]) #创建一个二维数组
print(x.ndim,x.shape,x.size,x.itemsize) #打印数组的属性
结果如下
2 (2, 3) 6 4
可以看到,我们定义的数组是2维的,大小为2行3列,共有6个元素,由于元素默认为int型,故每个元素占4个字节。
我们还可以利用shape来调整数组维度大小,如下所示
x.shape=(3,2) #将之前创建的数组改为3行2列
print(x)
结果如下
[[1 2]
[3 4]
[5 6]]
我们还可以使用reshape来调整,如下所示
x=x.reshape(3,2)
print(x)
结果相同。
NUMPY库中数据类型:
名称 | 描述 |
---|---|
bool_ | 布尔型数据类型(True 或者 False) |
int_ | 默认的整数类型(类似于 C 语言中的 long,int32 或 int64) |
intc | 与 C 的 int 类型一样,一般是 int32 或 int 64 |
intp | 用于索引的整数类型(类似于 C 的 ssize_t,一般情况下仍然是 int32 或 int64) |
int8 | 字节(-128 to 127) |
int16 | 整数(-32768 to 32767) |
int32 | 整数(-2147483648 to 2147483647) |
int64 | 整数(-9223372036854775808 to 9223372036854775807) |
uint8 | 无符号整数(0 to 255) |
uint16 | 无符号整数(0 to 65535) |
uint32 | 无符号整数(0 to 4294967295) |
uint64 | 无符号整数(0 to 18446744073709551615) |
float_ | float64 类型的简写 |
float16 | 半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位 |
float32 | 单精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位 |
float64 | 双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位 |
complex_ | complex128 类型的简写,即 128 位复数 |
complex64 | 复数,表示双 32 位浮点数(实数部分和虚数部分) |
complex128 | 复数,表示双 64 位浮点数(实数部分和虚数部分) |
上面讲到了使用array创建给定数组,numpy中还提供了一些特殊数组的创建方式,具体如下:
创建一个未初始化的数组
emp_x=np.empty((3,2),dtype = int) #创建一个大小为3*2,数据类型为int型的空矩阵(默认为浮点数型)
print(emp_x)
创建全为0的数组
zero_x=np.zeros((1,5),dtype='float64') #创建一个大小为1*5,数据类型为float64型的全零矩阵
print(zero_x)
结果如下
[[0. 0. 0. 0. 0.]]
创建全为1的数组
one_x=np.ones((2,2),dtype=np.int) #创建一个大小为2*2,数据类型为int型的全一矩阵
print(one_x)
结果如下
[[1 1]
[1 1]]
创建指定大小的单位矩阵
eye_x=np.eye(5,dtype=np.int) #创建一个大小为5*5,数据类型为int的单位矩阵
print(eye_x)
结果如下
[[1 0 0 0 0]
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]]
创建一维等差数组
x2=np.arange(5) #创建0到4的一维数组
print(x2)
结果如下
[0 1 2 3 4]
创建二维等差数组
x3=np.arange(6).reshape(2,3) ##创建0到6的一维数组并使用reshape函数更改大小
print(x3)
结果如下
[[0 1 2]
[3 4 5]]
创建随机整数数组
x4=np.random.randint(5, size = (2, 3)) #创建大小为2*3的随机数组,元素数值在5以下
print(x4)
结果如下
[[0 1 4]
[0 4 4]]
ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样。这需要用到numpy内置的slic函数,话不多说,直接上代码。
x5 = np.arange(10) # 创建一个0到9的一维数组
x6 = slice(1,9,3) # 索引从 1 开始到 9 停止,步长为3(若不设置默认为1)
print (x5[x6])
结果如下
[1 4 7]
可以看到,我们从一个含有0到9元素的数组按照我们给定的条件分离出了另一个数组。通常的,我们进行索引切片设置的三个参数,分别对应 start, stop 及 step。我们还可以利用冒号对三个参数进行分隔,这样做的好处是当我们只设置一个参数时,若不添加冒号则返回对应下标的元素,添加则代表进行索引,可以较好的区分开,示例代码如下
x5 = np.arange(10)
x6 = x5[3] #返回x5中下标为3的元素
x7 = x5[3:] #从数组x5下标3开始索引
print (x6,'\n',x7)
结果如下
3
[3 4 5 6 7 8 9]
可以看到两个结果截然不同。
多维数组同样适用上述索引提取方法。我们先来看一段代码
x8=np.arange(9).reshape(3,3) #创建一个3*3的等差数组
x9=x8[1:2] #从数组第2行开始索引
print(x9)
结果如下
[[3 4 5]
[6 7 8]]
需要注意的是,如果我们的代码是这样的
x8=np.arange(9).reshape(3,3)
x9=x8[1,2]
print(x9)
那我们索引的就是第2行第3列的那个元素。
切片还可以使用省略号 …,使用方法如下
x8=np.arange(9).reshape(3,3)
print(x8)
print(x8[1,...]) #打印第2行的元素
print(x8[...,1]) #打印第2列的元素
结果如下
[[0 1 2]
[3 4 5]
[6 7 8]]
[3 4 5]
[1 4 7]
广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行。
如果两个数组shape相同,那么结果就是两个数组的对应位进行运算,示例代码如下
x1=np.array([1,2,3])
x2=np.array([4,5,6])
x3=x1*x2
print(x3)
结果如下
[ 4 10 18]
numpy的广播机制,当进行运算的数组不同的时候,nunpy会调整数组格式,如下所示
a = np.array([[ 0, 0, 0],
[10,10,10],
[20,20,20],
[30,30,30]]) #创建一个4*3的数组
b = np.array([1,2,3]) #创建一个1*3的数组
c=a*b
print(c)
结果如下
[[ 0 0 0]
[10 20 30]
[20 40 60]
[30 60 90]]
Numpy的广播遵循一系列严格的规则:
让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面重复补齐。
输出数组的形状是输入数组形状的各个维度上的最大值。
如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。
简单理解:对两个数组,分别比较他们的每一个维度(若其中一个数组没有当前维度则忽略),满足:
数组拥有相同形状。
当前维度的值相等。
当前维度的值有一个是 1。
若条件不满足,抛出 “ValueError: frames are not aligned” 异常。
示例:两个数组任何一维度都匹配不上
a = np.array([[ 0, 0, 0],
[10,10,10],
[20,20,20],
[30,30,30]])
b = np.array([10])
c=a*b
print(c)
结果如下
[[ 0 0 0]
[100 100 100]
[200 200 200]
[300 300 300]]
在这过程中,大小为1 * 1的数组b为了匹配大小为3 * 4的数组a,自动扩展为大小为3 * 4且每个元素都为10的数组而后两个数组进行运算。
简单来说,当一个数组无法扩展为与另一数组相同时,两个数组就无法进行运算。
numpy.append
numpy.append 函数在数组的末尾添加值。 追加操作会分配整个数组,并把原来的数组复制到新数组中。 此外,输入数组的维度必须匹配否则将生成ValueError。
代码如下:
x = np.array([[1,2,3],[4,5,6]])
print (x,'\n')
print (np.append(x, [7,8,9]),'\n')
print (np.append(x, [[7,8,9]],axis = 0),'\n')
print (np.append(x, [[0,0,0],[0,0,0]],axis = 1),'\n')
结果如下
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6 7 8 9]
[[1 2 3]
[4 5 6]
[7 8 9]]
[[1 2 3 0 0 0]
[4 5 6 0 0 0]]
append函数分别有三个参数:数组,向数组添加的值,添加的方向axis。若未定义axis,会返回一维数组,若axis为0,将沿0轴添加,若axis为1,沿1轴添加。添加的值需要和数组形状相匹配。如:沿0轴,列数要与数组相同;沿1轴,行数要与数组相同。可视情况判断。
numpy.insert
numpy.insert 函数在给定索引之前,沿给定轴在输入数组中插入值。
如果值的类型转换为要插入,则它与输入数组不同。 插入没有原地的,函数会返回一个新数组。 此外,如果未提供轴,则输入数组会被展开。
先看代码
x = np.array([[1,2,3],[4,5,6]]) #创建一个二维数组
x1 = np.insert(x,3,[11,12])
看看结果
[ 1 2 3 11 12 4 5 6]
可以看到,数组x被拆分为了一维数组,并且在下标3处插入了[11,12]的元素。
insert函数分别有四个参数:数组,向数组添加值的位置,向数组添加的值,添加的方向axis。此处axis同append的一样表示添加方向,若未定义数组将被拆分为一维数组。再来看看下面的代码
x = np.array([[1,2,3],[4,5,6]])
x1 = np.insert(x,1,[0],axis = 0)
print (x,'\n')
print(x1)
结果如下
[[1 2 3]
[4 5 6]]
[[1 2 3]
[0 0 0]
[4 5 6]]
可以看到,在第2行插入了我们给定的值。当axis为1时则插入列。同样的,插入的值也满足广播规则。
numpy.delete
numpy.delete 函数返回从输入数组中删除指定子数组的新数组。 与 insert() 函数的情况一样,如果未提供轴参数,则输入数组将展开。
来看代码
x = np.array([[1,2,3],[4,5,6]])
x1 = np.delete(x,1,axis = 0)
print (x,'\n')
print(x1)
结果如下
[[1 2 3]
[4 5 6]]
[[1 2 3]]
可以看到,数组的第2行被删除了。delete函数与insert函数类似,共有三个参数:数组,删除的维度,删除的轴axis。若axis未被定义,则数组会被拆分为一维数组。
至此,numpy库的介绍和简单使用先告一段落了。numpy库有十分强大且丰富的功能,限于篇幅无法完全写下。这篇笔记仅记录的我个人的学习经历,关于numpy还有很多知识没有讲到,包括其他的关于数组的操作方法还有数组的运算等等,无法一语概之,有机会我会再补充。
本人才疏学浅,如有不足之处欢迎批评指正。