Numpy库
0. NumPy的安装
- 使用Anaconda
访问 https://www.anaconda.com/distribution/ ,根据电脑的系统下载对应的软件,安装完成在软件中选择
JupyterLab
即可使用。
- 使用
pip
命令(保证您的Python环境已经配置完成)
在CMD中输入
python -m pip install --user numpy scipy matplotlib ipython jupyter pandas sympy nose
等待安装完成即可。
附:安装验证
输入下列代码能得到结果即代表着安装成功:
from numpy import *
eye(4)
输出:
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
1. NumPy库简介
NumPy是一个科学计算基础库其中提供了许多向量和矩阵操作,能让用户轻松完成最优化、线性代数、积分、插值、特殊函数、傅里叶变换、信号处理和图像处理、常微分方程求解以及其他科学与工程中常用的计算,不仅方便易用而且效率更高。
2. NumPy
NumPy数组的维数称为秩(rank),秩就是轴的数量,一位数组的秩为1,二维数组的秩为2,依次类推。在 NumPy中,每一个线性的数组称为是一个轴(axis),也就是维度(dimensions)。比如说,二维数组相当于是两个一维数组,其中第一个一维数组中每个元素又是一个一维数组。所以一维数组就是 NumPy 中的轴(axis),第一个轴相当于是底层数组,第二个轴是底层数组里的数组。而轴的数量——秩,就是数组的维数。很多时候可以声明 axis。axis=0,表示沿着第 0 轴进行操作,即对每一列进行操作;axis=1,表示沿着第1轴进行操作,即对每一行进行操作。
2.1 Ndarray
Ndarray
(N维数组)对象是NumPy最重要的一个特点,它是一系列同类型数据的集合,以0下标为开始进行集合中元素的索引。ndarray
对象是用于存放同类型元素的多维数组,其中的每个元素在内存中都有相同存储大小的区域,该对象具有矢量算术能力和复杂的广播能力。可以执行一些科学运算,ndarray
对象中定义了一些重要属性,如下表所示:
属性 | 说明 |
---|---|
ndarray.ndim | 秩,即轴的数量或维度的数量 |
ndarray.shape | 数组的维度,对于矩阵,n 行 m 列 |
ndarray.size | 数组元素的总个数,相当于 .shape 中 n*m 的值 |
ndarray.dtype | ndarray 对象的元素类型 |
ndarray.itemsize | ndarray 对象中每个元素的大小,以字节为单位 |
ndarray.flags | ndarray 对象的内存信息 |
ndarray.real | ndarray元素的实部 |
ndarray.imag | ndarray 元素的虚部 |
ndarray.data | 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性。 |
示例:
import numpy as np
data = np.arange(12).reshape(3, 4)
print("data=", data)
print("type(data)=", type(data))
print("data.ndim=", data.ndim)
print("data.shape=", data.shape)
print("data.size=", data.size)
print("data.dtype=", data.dtype)
输出:
data= [[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
type(data)= <class 'numpy.ndarray'>
data.ndim= 2
data.shape= (3, 4)
data.size= 12
data.dtype= int32
解析:
在代码示例中,第一行将numpy
库导入并将其取名为np
,第二行初始化一个三行四列的递增数组data
,第三行输出data
,第四行输出data
的类型,第五行输出了数组的维度个数,第六行输出了数组的维度,第七行输出了数组中元素的总个数,第八行输出了元素的类型。
2.1.1 常用属性
ndarray.shape
属性
ndarray.shape
表示数组的维度,返回一个元组,这个元组的长度就是维度的数目,即 ndim 属性(秩)。比如,一个二维数组,其维度表示"行数"和"列数"。ndarray.shape
也可以用于调整数组大小。
代码:
import numpy as np
data = np.arange(12)
print("更改前的数组:", data)
data.shape = (3, 4)
print("更改后的数组:", data)
输出:
更改前的数组: [ 0 1 2 3 4 5 6 7 8
9 10 11]
更改后的数组: [[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
reshape()
函数
reshape()
函数可用来调整数组大小,例如:
import numpy as np
data = np.arange(12)
print("更改前的数组:", data)
data.shape = (3, 4)
print("更改后的数组:", data)
print("reshape后数值:", data.reshape(2,6))
输出:
更改前的数组: [ 0 1 2 3 4 5 6 7 8
9 10 11]
更改后的数组: [[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
reshape后数值: [[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
ndarray.flags
属性
ndarray.flags
返回 ndarray 对象的内存信息,包含以下属性:
属性 | 描述 |
---|---|
C_CONTIGUOUS (C) | 数据是在一个单一的C风格的连续段中 |
F_CONTIGUOUS (F) | 数据是在一个单一的Fortran风格的连续段中 |
OWNDATA (O) | 数组拥有它所使用的内存或从另一个对象中借用它 |
WRITEABLE (W) | 数据区域可以被写入,将该值设置为 False,则数据为只读 |
ALIGNED (A) | 数据和所有元素都适当地对齐到硬件上 |
UPDATEIFCOPY (U) | 这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新 |
代码:
import numpy as np
x = np.array([1,2,3,4,5])
print (x.flags)
输出:
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
2.2 创建NumPy数组
2.2.1 使用array()
函数创建数组
语法:
array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0, /)
名称 | 描述 |
---|---|
object | 数组或嵌套的数列 |
dtype | 数组元素的数据类型,可选 |
copy | 对象是否需要复制,可选 |
order | 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认),可选 |
subok | 默认返回一个与基类类型一致的数组,可选 |
ndmin | 指定生成数组的最小维度,可选 |
示例:
import numpy as np
data = np.array([1, 2, 3])
data2 = np.array((1, 2, 3))
data3 = np.array(((1, 2, 3), (4, 5, 6)))
data4 = np.array([1, 2, 3, 4, 5], complex, ndmin=2)
print("data=", data)
print("data2=", data2)
print("data3=", data3)
print("data4=", data4)
输出:
data= [1 2 3]
data2= [1 2 3]
data3= [[1 2 3]
[4 5 6]]
data4= [[1.+0.j 2.+0.j 3.+0.j 4.+0.j 5.+0.j]]
解析:
可以通过array()
函数由列表、元组转化,由列表/元组转化为多维数组需要子元素为列表/元组的列表/元组,即:
[[1,2,3],[4,5,6]]
[(1,2,3),(4,5,6)]
([1,2,3],[4,5,6])
((1,2,3),(4,5,6))
都是可以的。
2.2.2 numpy
创建数组
empty()
函数
创建一个只分配内存空间的数组,其中的元素将随机填充。
语法:
empty(shape, dtype=float, order='C', /)
名称 | 描述 |
---|---|
shape | 数组的维度,例如(2,3)代表两行三列 |
dtype | 数组元素的类型,可选,默认为float64 |
order | 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认),可选 |
代码:
import numpy as np
data = np.empty((3, 5))
print(data)
输出:
[[6.23042070e-307 4.67296746e-307 1.69121096e-306 2.31424250e-306
1.89146896e-307]
[7.56571288e-307 3.11525958e-307 1.24610723e-306 1.37962320e-306
1.29060871e-306]
[2.22518251e-306 1.33511969e-306 1.78022342e-306 1.05700345e-307
2.76676762e-322]]
zeros()
和ones()
函数
zeros()
将创建一个用指定形状用0填充的数组。默认的dtype是float64。
ones()
将创建一个用指定形状用1填充的数组。默认的dtype是float64。
语法:
zeros(shape, dtype=float, order='C', /)
ones(shape, dtype=None, order='C')
名称 | 描述 |
---|---|
shape | 数组的维度,例如(2,3)代表两行三列 |
dtype | 数组元素的类型,可选 |
order | 创建数组的样式,C为行方向,F为列方向,A为任意方向(默认),可选 |
代码:
import numpy as np
data_0 = np.zeros((2, 3))
data_1 = np.ones((3, 2))
print("data_0=", data_0)
print("data_1=", data_1)
输出:
data_0= [[0. 0. 0.]
[0. 0. 0.]]
data_1= [[1. 1.]
[1. 1.]
[1. 1.]]
2.2.3 从数值范围创建数组
arange()
函数
arrange()
函数将创建具有有规律递增值的数组。
语法:
arange(start=None, stop, step=None,dtype=None, /)
名称 | 描述 |
---|---|
Start | 开始数值,可选,所输出的数组中包括开始数值 |
stop | 结束数值,若不定义则默认从0开始,单独使用时仅可使用正数,所输出的数组中不包括结束数值 |
step | 步长,可正可负可为小数,可选 |
dtype | 数组中的元素类型 |
代码:
import numpy as np
data = np.arange(10)
data1 = np.arange(1, 10)
data2 = np.arange(1, 10, 2)
data3 = np.arange(10, 1, -2)
dataf = np.arange(1, 10, 2, float)
print("data=", data)
print("data1=", data1)
print("data2=", data2)
print("data3=", data2)
print("dataf=", dataf)
输出:
data= [0 1 2 3 4 5 6 7 8 9]
data1= [1 2 3 4 5 6 7 8 9]
data2= [1 3 5 7 9]
data3= [1 3 5 7 9]
dataf= [1. 3. 5. 7. 9.]
linspace()
函数
linspace()
将创建具有指定数量元素的数组,并在指定的开始值和结束值之间平均间隔。该数组为指定起始值的等差数列。
语法:
linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
名称 | 描述 |
---|---|
start | 序列的起始值 |
stop | 序列的终止值 |
num | 序列的项数,默认为50项 |
endpoint | 是否包括stop 默认包括 |
retstep | 是否输出步长,默认不输出 |
dtype | 元素属性,默认为float64
|
axis | 操作维度,可选值0/-1,可选 |
代码:
import numpy as np
data = np.linspace(0,6,3,endpoint=False)
data_2 = np.linspace(0,6,3)
print(data)
print(data_2)
输出:
[0. 2. 4.]
[0. 3. 6.]
logspace()
函数
logspace()
用于创建一个等比数列。
语法:
np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
参数 | 描述 |
---|---|
start | 序列的起始值 |
stop | 序列的终止值 |
num | 要生成的等步长的样本数量,默认为50
|
endpoint | 该值为 true 时,数列中中包含stop 值,反之不包含,默认是True。 |
base | 对数 log 的底数,默认底数为10。 |
dtype |
ndarray 的数据类型,默认为float64 |
代码:
import numpy as np
a = np.logspace(0, 9, 10, base=2)
print(a)
输出:
[ 1. 2. 4. 8. 16. 32. 64. 128. 256. 512.]
2.2.4 从已有的数组创建数组
asarray()
函数
语法:
numpy.asarray(a, dtype = None, order = None)
参数 | 描述 |
---|---|
a | 任意形式的输入参数,可以是,列表, 列表的元组, 元组, 元组的元组, 元组的列表,多维数组 |
dtype | 数据类型,可选 |
order | 可选,有"C"和"F"两个选项,分别代表,行优先和列优先,在计算机内存中的存储元素的顺序。 |
代码:
import numpy as np
x = [1, 2, 3]
y = (4, 5, 6)
z = [(7, 8), (9, 10, 11)]
# 从列表创建数组
a = np.asarray(x)
# 从元组创建数组
b = np.asarray(y)
# 从元组列表创建数组
c = np.asarray(z)
print(a, b, c)
输出:
[1 2 3] [4 5 6] [(7, 8) (9, 10, 11)]
当然,asarray
也可以设置dtype
属性,例如:
import numpy as np
x = [1,2,3]
a = np.asarray(x, dtype = float)
print (a)
输出为:
[ 1. 2. 3.]
frombuffer()
函数
numpy.frombuffer
用于实现动态数组。numpy.frombuffer
接受 buffer 输入参数,以流的形式读入转化成 ndarray 对象。
语法:
numpy.frombuffer(buffer, dtype = float, count = -1, offset = 0)
注意:buffer 是字符串的时候,Python3 默认 str 是 Unicode 类型,所以要转成 bytestring 在原 str 前加上 b。
参数 | 描述 |
---|---|
buffer | 可以是任意对象,会以流的形式读入。 |
dtype | 返回数组的数据类型,可选 |
count | 读取的数据数量,默认为-1,读取所有数据。 |
offset | 读取的起始位置,默认为0。 |
代码:
import numpy as np
s = b'Hello World'
a = np.frombuffer(s, dtype = 'S1')
print (a)
输出:
[b'H' b'e' b'l' b'l' b'o' b' ' b'W' b'o' b'r' b'l' b'd']
2.3 数据类型
numpy 支持的数据类型比 Python 内置的类型要多很多,基本上可以和 C 语言的数据类型对应上,其中部分类型对应为 Python 内置的类型。下表列举了常用 NumPy 基本类型,参考链接:
菜鸟编程
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 位浮点数(实数部分和虚数部分) |
2.3.1 查看数据类型
通过ndarray.dtype
可以创建一个表示数据类型的对象,如果想要获取数据类型名称,需要访问name
属性进行获取,例如:
import numpy as np
data = np.array([[1, 2, 3], [4, 5, 6]])
print(data.dtype.name)
本段代码在64位Windows系统
将输出int32
,在64位Linux或MacOS
中将输出int64
2.3.2 创建数据类型
可以通过如下语法构造dtype
对象
numpy.dtype(object, align, copy)
- object - 要转换为的数据类型对象
- align - 如果为 true,填充字段使其类似 C 的结构体。
- copy - 复制 dtype 对象 ,如果为 false,则是对内置数据类型对象的引用
每一个NumPy内置的数据类型都有一个特征码,它能唯一标识一种数据类型,具体如表所示:
特征码 | 含义 | 特征码 | 含义 |
---|---|---|---|
b | 布尔型 | i | 有符号整型 |
u | 无符号整型 | f | 浮点型 |
c | 复数类型 | O | Python对象 |
S,a | 字节字符串 | U | unicode字符串 |
V | 原始数据 |
字节顺序是通过对数据类型预先设定"<"或">"来决定的。"<"意味着小端法(最小值存储在最小的地址,即低位组放在最前面)。">"意味着大端法(最重要的字节存储在最小的地址,即高位组放在最前面)。例如如下代码:
import numpy as np
# 创建car类型
car = np.dtype([('name', 'a3'), ('age', 'i1'), ('speed', 'f4')])
# 创建dtype为car类型的数组
a = np.array([('xz', 1, 20), ('ln', 2, 10)], dtype=car)
print(a)
输出为:
[(b'xz', 1, 20.) (b'ln', 2, 10.)]
2.3.3 转换数据类型
astype()
函数
该函数可以对ndarray
对象的数据类型进行转换。
代码:
import numpy as np
data = np.zeros((2, 3))
print("修改前的类型:", data.dtype)
int_data = data.astype(np.int64)
print("修改后的类型", int_data.dtype)
输出:
修改前的类型: float64
修改后的类型 int64
2.4 切片和索引
2.4.1 一维切片
在Python中接触过使用list
函数的切片操作,ndarray
对象内容可以同样通过索引或切片操作来访问和修改,基于0 - n
的下标进行索引,使用内置的slice
函数,设置start
,stop
,step
参数,从原数组中切割出一个新数组,其中slice
语法为:
slice(start: Any, stop: Any, step: Any=...)
slice(stop)
slice(start, stop[, step])
参数 | 说明 |
---|---|
start | 索引开始 |
stop | 索引结束 |
step | 索引步长 |
代码:
import numpy as np
a = np.arange(10)
s = slice(2, 7, 2) # 从索引 2 开始到索引 7 停止,间隔为 2
print(a[s])
输出:
[2 4 6]
当然也可以直接通过冒号分割切片参数start:stop:step来进行切片操作:
代码:
import numpy as np
a = np.arange(10)
b = a[2:7:2] # 从索引 2 开始到索引 7 停止,间隔为 2
print(b)
输出:
[2 4 6]
有关:
的解释
- 放置一个参数返回与该索引相对应的单个元素
[2]
,也可使用负数进行负索引
import numpy as np
a = np.arange(10)
print(a[2], " ", a[-2])
输出为:2 8
- 如果使用
[2:]
则输出从该索引(包括该元素)之后的所有项
import numpy as np
a = np.arange(10)
print(a[2:])
输出为:[2 3 4 5 6 7 8 9]
- 如果使用
[:7]
则输出从该索引(不包括该元素)之前的所有项
import numpy as np
a = np.arange(10)
print(a[:7])
输出为:[0 1 2 3 4 5 6]
- 如果使用
[2:7]
那么则提取两个索引(不包括停止索引)之间的项
import numpy as np
a = np.arange(10)
print(a[2:7])
输出为:[2 3 4 5 6]
- 同样可以使用负值对数组进行反转
[::-1]
import numpy as np
a = np.arange(10)
print(a[::-1])
输出为:[9 8 7 6 5 4 3 2 1 0]
- 多维数组同样适用上述索引提取方法
import numpy as np
a = np.array([[1, 2, 3], [3, 4, 5], [4, 5, 6]])
print(a)
# 从某个索引处开始切割
print('从数组索引 a[1:] 处开始切割')
print(a[1:])
输出为:
[[1 2 3]
[3 4 5]
[4 5 6]]
从数组索引 a[1:] 处开始切割
[[3 4 5]
[4 5 6]]
更多例子:
import numpy as np
a = np.arange(10)
print(a[1:9:2]) # 输出在索引为1和9之间(不包括9)步长为2的元素
print(a[9:1:-2]) # 反向输出在索引为9和1之间(不包括1)步长为2的元素
输出为:
[1 3 5 7]
[9 7 5 3]
2.4.2 多维切片
冒号分割函数在多维数组的处理上也很方便,在多维数组切片[x,y]
,x代表行号,y代表列号,例如下图中为二维数组中对应的冒号索引。
例如下列代码:
import numpy as np
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(a[2,1])
输出为10
,即为第2行第1列的元素。在多维数组的每一维同样可以使用:
索引,在讲解多维数组下的:
索引之前先引入一下省略号切片:
如图所示,如果需要输出第2行元素只需使用:
import numpy as np
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(a[2, ...])
print(a[2]) # 当省略号代表列时可省略
而输出第2列元素可以使用:
import numpy as np
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(a[..., 2])
多维数组下的:
索引
创建一个4×4
的二维数组,一些区域访问的例子如下图所示:
代码:
import numpy as np
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(a[1:3, 0])
print(a[2:, 1])
print(a[:3])
print(a[..., :3])
print(a[0::2, 2])
print(a[3:0:-2, 2])
print(a[0:2, 1:3])
print(a[::2, 0:])
print(a[::-2, 0:])
输出:
[5 9]
[10 14]
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
[[ 1 2 3]
[ 5 6 7]
[ 9 10 11]
[13 14 15]]
[ 3 11]
[15 7]
[[2 3]
[6 7]]
[[ 1 2 3 4]
[ 9 10 11 12]]
[[13 14 15 16]
[ 5 6 7 8]]
如果我们需要填充一个8×8
的棋盘,黑的为0
,白的为1
,可以使用:
索引迅速填充:
import numpy as np
chess = np.zeros((8, 8), int)
chess[::2, 1::2] = 1
chess[1::2, ::2] = 1
print(chess)
2.4.3 花式索引与布尔索引
花式索引的基本形式如下图所示:
花式索引import numpy as np
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(a[[0, 3]])
print(a[..., [0, 3]])
print(a[[0, 3], [1, 3]])
print(a[[0, 1, 2, 0, 3], [0, 2, 3, 1, 2]])
输出为:
[[ 1 2 3 4]
[13 14 15 16]]
[[ 1 4]
[ 5 8]
[ 9 12]
[13 16]]
[ 2 16]
[ 1 7 12 2 15]
当花式索引使用一个数组做索引,即a[[x,y]]
或a[..., [x,y]]
形式时,输出第x
,y
行/列的元素,而当使用两个数组做索引,即a[[x,y],[m,n]]
形式时,输出第x
行m
列和第y
行第n
列的元素。
np.ix_
np.ix_
代码:
import numpy as np
x = np.arange(32).reshape((8, 4))
print(x[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])])
print(x[[1, 5, 7, 2], [0, 3, 1, 2]])
输出:
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
[ 4 23 29 10]
布尔索引指的是将一个布尔数组作为数组索引,返回的数据是布尔数组中True
对应位置的值,比如一个简单的例子:
考试结束五个同学的成绩如下表:
zz | 98 | 64 | 87 |
---|---|---|---|
yxy | 100 | 92 | 76 |
wyf | 67 | 99 | 90 |
wcc | 80 | 100 | 99 |
zyh | 79 | 95 | 66 |
使用student
存储姓名,使用student_score
存储学生成绩,使用布尔索引输出姓名为zyh
的成绩
代码:
import numpy as np
student = np.array(['zz', 'yxy', 'wyf', 'wcc', 'zyh'])
student_score = np.array([[98, 64, 87], [100, 92, 76], [67, 99, 90],
[80, 100, 99], [79, 95, 66]])
print(student == 'zyh')
# 将布尔数组作为索引应用于存储成绩的数组,返回的数据是True值对应的行
print(student_score[student == 'zyh'])
# 布尔型数组与切片混合使用
print(student_score[student == 'zyh', 1:])
输出:
[False False False False True]
[[79 95 66]]
[[95 66]]
在使用布尔型索引获取值得时候,除了可以使用"=="运算符还可以使用"!="、"-"来进行否定、">"和"<"来判断数值大小以及"&"和"|"等符号来组合多个布尔条件。
2.5 数组运算
NumPy数组不需要循环遍历即可对每个元素执行批量的算术运算操作,这个过程叫矢量化运算。如果两个数组大小ndarray.shape
不同,则进行算术运算时会出现广播机制。同时,数组还支持算术运算符和标量进行运算。
2.5.1 矢量化运算
在NumPy中任何大小相等的数组之间的算术运算都会应用到元素级,即只用于位置相同的元素之间,所得的运算结果组成一个新的数组。
矢量化运算如图所示,两数组对齐以后会让相同位置的元素操作得到结果,并且结果的位置和操作数的位置相同,例如:
import numpy as np
a = np.array([4, 5, 3, 1])
b = np.array([1, 3, 2, 8])
print("a+b=", a + b)
print("a-b=", a - b)
print("a*b=", a * b)
print("a%b=", a % b)
输出为:
a+b= [5 8 5 9]
a-b= [ 3 2 1 -7]
a*b= [ 4 15 6 8]
a%b= [0 2 1 1]
2.5.2 数组广播
数组进行矢量化运算时,要求数组的形状是相等的。当形状不相等的数组执行算术运算时会出现广播机制,该机制会对数组进行扩展,使数组的shape
属性值一样,然后就可以进行矢量化运算。
如图所示,输入一个4行1列和一个1行3列的数组,在二者进行计算的时候会按照广播机制进行扩展使两者都成为4行3列的数组再进行矢量运算,代码为:
import numpy as np
a = np.array([[1], [4], [6], [8]])
b = np.array([3, 2, 7])
print("a+b=")
print(a + b)
输出为:
a+b=
[[ 4 3 8]
[ 7 6 11]
[ 9 8 13]
[11 10 15]]
广播的规则:
- 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
- 输出数组的形状是输入数组形状的各个维度上的最大值。
- 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
- 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。
2.5.3 数组与标量间的运算
数组与标量之间的运算类似于广播机制,标量会扩展为与输入相同大小的数组,所产生的数组中的所有元素均为该标量,例如:
import numpy as np
a = np.arange(10)
a.shape = (2, 5)
b = 1
print("a=", a)
print("a+b=", a + b)
输出为:
a= [[0 1 2 3 4]
[5 6 7 8 9]]
a+b= [[ 1 2 3 4 5]
[ 6 7 8 9 10]]
2.5.4 数组的操作
NumPy中包含了一些函数用于处理数组,如图所示:
NumPy数组操作修改数组形状
函数 | 描述 |
---|---|
reshape |
不改变数据的条件下修改形状 |
flat |
数组元素迭代器 |
flatten |
返回一份数组拷贝,对拷贝所做的修改不会影响原始数组 |
ravel |
返回展开数组 |
numpy.reshape
格式:
numpy.reshape(arr, newshape, order='C')
-
arr
:要修改形状的数组 -
newshape
:整数或者整数数组,新的形状应当兼容原有形状 - order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'k' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(6)
print("原始数组为:")
print(a, '\n')
b = a.reshape(2, 3)
print("修改后的数组为:")
print(b)
输出:
原始数组为:
[0 1 2 3 4 5]
修改后的数组为:
[[0 1 2]
[3 4 5]]
numpy.ndarray.flat
数组元素迭代器,对数组中每个元素都进行处理,可以使用flat属性。
代码:
import numpy as np
a = np.arange(6).reshape(2,3)
print("原始数组:")
for r in a:
print(r)
print("迭代后的数组:")
for e in a.flat:
print(e)
输出:
原始数组:
[0 1 2]
[3 4 5]
迭代后的数组:
0
1
2
3
4
5
numpy.ndarray.flatten
返回一份数组拷贝,对拷贝数组所做的修改不会影响原始数组。
格式:
ndarray.flatten(order='C')
参数说明:
- order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(8).reshape(2, 4)
print("原数组:")
print(a, '\n')
print("展开的数组:")
print(a.flatten(), '\n')
print("以F风格顺序展开的数组:")
print(a.flatten(order='F'), '\n')
输出:
原数组:
[[0 1 2 3]
[4 5 6 7]]
展开的数组:
[0 1 2 3 4 5 6 7]
以F风格顺序展开的数组:
[0 4 1 5 2 6 3 7]
numpy.ravel
numpy.ravel() 展平的数组元素,顺序通常是"C风格",返回的是数组视图,修改会影响原始数组。
语法:
numpy.ravel(a, order='C')
参数说明:
- order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序。
代码:
import numpy as np
a = np.arange(8).reshape(2, 4)
print("原数组:")
print(a, '\n')
print("使用ravel后:")
print(a.ravel(), '\n')
print("以F风格调用ravel后:")
print(a.ravel(order='F'), '\n')
输出:
原数组:
[[0 1 2 3]
[4 5 6 7]]
使用ravel后:
[0 1 2 3 4 5 6 7]
以F风格调用ravel后:
[0 4 1 5 2 6 3 7]
flatten
与ravel
import numpy as np
a = np.arange(8).reshape(2, 4)
a_2 = a
b = a.flatten()
c = a_2.ravel()
b[1] = 10
c[1] = 10
print("b=", b)
print("a=", a)
print("c=", c)
print("a_2=", a_2)
输出:
b= [ 0 10 2 3 4 5 6 7]
a= [[ 0 10 2 3]
[ 4 5 6 7]]
c= [ 0 10 2 3 4 5 6 7]
a_2= [[ 0 10 2 3]
[ 4 5 6 7]]
容易看出修改ravel
中的值对原数组元素是有影响的,而修改flatten
中的值对原数组无影响。
翻转数组
函数 | 描述 |
---|---|
transpose |
对换数组的维度 |
ndarray.T |
和 self.transpose() 相同 |
rollaxis |
向后滚动指定的轴 |
moveaxis |
移动轴的位置 |
swapaxes |
对换数组的两个轴 |
numpy.transpose
numpy.transpose() 函数用于对换数组的维度
语法:
numpy.transpose(arr, axes)
参数说明:
-
arr
:要操作的数组 -
axes
:整数列表,对应维度,通常所有维度都会对换。
代码:
import numpy as np
a = np.arange(12).reshape(3, 2, 2)
print('原数组:')
print(a, '\n')
print('置换数组:')
print(np.transpose(a))
print(np.transpose(a, (2, 1, 0)))
输出:
原数组:
[[[ 0 1]
[ 2 3]]
[[ 4 5]
[ 6 7]]
[[ 8 9]
[10 11]]]
置换数组:
[[[ 0 4 8]
[ 2 6 10]]
[[ 1 5 9]
[ 3 7 11]]]
[[[ 0 4 8]
[ 2 6 10]]
[[ 1 5 9]
[ 3 7 11]]]
有关参数axes
的解释
以上例为例,a = np.arange(12).reshape(3, 2, 2)
产生一个如图所示的数组:
对于transpose
函数中的axes如果不定义则默认全部转置,例如在三维数组中使用transpose
会将坐标轴由(x,y,z)转置为(z,y,x),通常的表达式为:np.transpose(a,(0,1,2))
其中0代表x轴,1代表y轴,2代表z轴,如果希望在新生成的数组中交换y轴和z轴,则只需输入np.transpose(a,(0,2,1)),如果是四维数组则第四个坐标对应的数字为3,即默认定义的四维数组的axes=(0,1,2,3)
numpy.rollaxis
numpy.rollaxis 函数向后滚动特定的轴到一个特定位置。
语法:
numpy.rollaxis(arr, axis, start)
参数说明:
-
arr
:数组 -
axis
:要向后滚动的轴,其它轴的相对位置不会改变 -
start
:默认为零,表示完整的滚动。会滚动到特定位置。
代码:
import numpy as np
a = np.arange(24).reshape(4, 3, 2)
print("滚动坐标轴:")
print(np.rollaxis(a, 2).shape)
输出:
滚动坐标轴:
(2, 4, 3)
(4, 3, 2)
通过一幅图来了解一下这个函数的机制:
rollaxis假设创建的一个数组arr为5维数组,即arr.ndim=5
,则axis
的取值为[-5,4],start
取值[-5,5],如果在函数中不定义start
值默认取0
,而axis
为要移动的轴,轴从0
开始编号,当然反向同样成立,对于一个n维数组,轴0
也是轴-n
,而轴a
为轴-n+a
,官方文档对start
的定义为The axis is rolled until it lies before this position. The default, 0, results in a “complete” roll.翻译过来就是轴滚动,直到它位于此位置之前。默认值 0 会导致"完整"滚动。通过对五维数组的尝试,可以发现,以axis
和-arr.ndim+axis
为分隔,当start
值小于等于axis
或-arr.ndim+axis
时,原来位于轴axis
或轴-arr.ndim+axis
的元素滚动至轴start
处,其余轴的相对位置保持不变,当start
值大于axis
或-arr.ndim+axis
时,原来位于轴axis
或轴-arr.ndim+axis
的元素滚动至轴start-1
处,其余轴的相对位置保持不变。
numpy.moveaxis
(版本 1.11.0 中的新增功能) 将数组的轴移动到新位置,其他轴保持原始顺序类似于numpy.rollaxis
,但是这个函数只需指定始位置与终位置即可,且可通过序列定义。
语法:
numpy.moveaxis(arr, source, destination)
参数说明:
-
arr
:输入的数组 -
source
:要移动的轴的原始位置。这些必须是唯一的。(整型int或整型数列[int1,int2,...,int n]) -
destination
:要移动的轴的目标位置。这些必须是唯一的。(整型int或整型数列[int1,int2,...,int n])
代码:
import numpy as np
a = np.zeros((1, 2, 3, 4, 5))
print(np.moveaxis(a, [1, 2], [-1, -5]).shape)
输出:
(3, 1, 4, 5, 2)
如果输入print(np.moveaxis(a, [1, 1], [-1, -5]).shape)
会报错,原因是moveaxis
第二个参数[1,1]
冲突。
numpy.swapaxes
numpy.swapaxes 函数用于交换数组的两个轴。
语法:
numpy.swapaxes(arr, axis1, axis2)
参数说明:
-
arr
:输入的数组 -
axis1
:对应第一个轴的整数 -
axis2
:对应第二个轴的整数
代码:
import numpy as np
a = np.ones((1, 2, 3, 4, 5))
print(np.swapaxes(a,0,-3).shape)
输出:
(3, 2, 1, 4, 5)
更改维度数
方法 | 描述 |
---|---|
atleast_1d(*arys) | 将输入转换为至少一维的数组。 |
atleast_2d(*arys) | 将输入视为至少具有二维的数组。 |
atleast_3d(*arys) | 以至少三个维度的数组形式查看输入。 |
broadcast | 产生模仿广播的对象。 |
broadcast_to(array, shape[, subok]) | 将数组广播为新形状。 |
broadcast_arrays(*args, **kwargs) | 互相广播任意数量的阵列。 |
expand_dims(a, axis) | 扩展数组的形状。 |
squeeze(a[, axis]) | 从数组形状中删除一维条目。 |
改变数组的种类
方法 | 描述 |
---|---|
asarray(a[, dtype, order]) | 将输入转换为数组。 |
asanyarray(a[, dtype, order]) | 将输入转换为ndarray,但通过ndarray子类。 |
asmatrix(data[, dtype]) | 将输入解释为矩阵。 |
asfarray(a[, dtype]) | 返回转换为浮点类型的数组。 |
asfortranarray(a[, dtype]) | 返回以Fortran顺序排列在内存中的数组(ndim> = 1)。 |
ascontiguousarray(a[, dtype]) | 返回内存中的连续数组(ndim> = 1)(C顺序)。 |
asarray_chkfinite(a[, dtype, order]) | 将输入转换为数组,检查NaN或Infs。 |
asscalar(a) | 将大小为1的数组转换为其等效的标量。 |
require(a[, dtype, requirements]) | 返回提供的类型满足要求的ndarray。 |
组合数组
方法 | 描述 |
---|---|
concatenate((a1, a2, …) | 沿现有轴连接一系列数组。 |
stack(arrays[, axis, out]) | 沿新轴连接一系列数组。 |
column_stack(tup) | 将一维数组作为列堆叠到二维数组中。 |
dstack(tup) | 沿深度方向(沿第三轴)按顺序堆叠数组。 |
hstack(tup) | 水平(按列)顺序堆叠数组。 |
vstack(tup) | 垂直(行)按顺序堆叠数组。 |
block(arrays) | 从块的嵌套列表中组装一个nd数组。 |
拆分数组
方法 | 描述 |
---|---|
split(ary, indices_or_sections[, axis]) | 将数组拆分为多个子数组,作为ary的视图。 |
array_split(ary, indices_or_sections[, axis]) | 将一个数组拆分为多个子数组。 |
dsplit(ary, indices_or_sections) | 沿第3轴(深度)将数组拆分为多个子数组。 |
hsplit(ary, indices_or_sections) | 水平(按列)将一个数组拆分为多个子数组。 |
vsplit(ary, indices_or_sections) | 垂直(行)将数组拆分为多个子数组。 |
平铺数组
方法 | 描述 |
---|---|
tile(A, reps) | 通过重复A代表次数来构造一个数组。 |
repeat(a, repeats[, axis]) | 重复数组的元素。 |
添加和删除元素
方法 | 描述 |
---|---|
delete(arr, obj[, axis]) | 返回一个新的数组,该数组具有沿删除的轴的子数组。 |
insert(arr, obj, values[, axis]) | 沿给定轴在给定索引之前插入值。 |
append(arr, values[, axis]) | 将值附加到数组的末尾。 |
resize(a, new_shape) | 返回具有指定形状的新数组。 |
trim_zeros(filt[, trim]) | 修剪一维数组或序列中的前导和/或尾随零。 |
unique(ar[, return_index, return_inverse, …]) | 查找数组的唯一元素。 |
重新排列元素
方法 | 描述 |
---|---|
flip(m[, axis]) | 沿给定轴颠倒数组中元素的顺序。 |
fliplr(m) | 左右翻转数组。 |
flipud(m) | 上下翻转阵列。 |
reshape(a, newshape[, order]) | 在不更改数据的情况下为数组赋予新的形状。 |
roll(a, shift[, axis]) | 沿给定轴滚动数组元素。 |
rot90(m[, k, axes]) | 在轴指定的平面中将阵列旋转90度。 |
2.6 函数
2.6.1 通用函数
在Numpy中提供了诸如sin,cos和exp等常见的数学函数,这些函数称为通用函数(ufunc),通用函数是针对ndarray数据执行元素级运算的函数,函数返回是一个新的数组,通常,将通用函数中接受一个数组参数的函数称为一元通用函数,接受两个数组参数的称为二元通用函数。
常用的一元通用函数
函数 | 描述 |
---|---|
abs、fabs | 计算整数、浮点数或辅助的绝对值 |
sqrt | 计算各元素的算术平方根 |
square | 计算各元素的平方 |
exp | 计算各元素的指数ex |
log、log10、log2、log1p | 分别为自然对数(底数为e),底数为10的对数,底数为2的对数,log(1+x) |
sign | 计算各元素的正负号:1(正数)、0(零)、-1(负数) |
ceil | 计算各元素的ceilling值,即大于或者等于该值的最小整数 |
floor | 计算个元素的floor值,即小于或者等于该值的最大整数 |
rint | 将各元素四舍五入到最接近的整数 |
modf | 将数组的小数和整数部分以两个独立数组的形式返回 |
isnan | 返回一个表示“哪些值是NaN”的布尔型数组 |
isfinite、isinf | 分别返回表示“哪些元素是有穷的”或“哪些元素是无穷的”的布尔型数组 |
sin、sinh、cos、cosh、tan、tanh | 普通型和双曲型三角函数 |
arcos、arccosh、arcsin | 反三角函数 |
常见的二元通用函数
函数 | 描述 |
---|---|
add | 将数组中对应的元素相加 |
subtract | 从第一个数组减去第二个数组中的元素 |
multiply | 两个数组元素相乘 |
divide、floor_divide | 除法或向下整除法(舍去余数) |
maximum、fmax | 元素级的最大值计算 |
minimum、fmin | 元素级的最小值计算 |
mod | 元素级的求模运算 |
copysign | 将第二个数组中的值得符号赋值给第一个数组中的值 |
greater、greater_equal、less、less_equal、equal、not_equal、logical_and、logical_or、logical_xor | 执行元素级的比较运算,最终产生布尔型数组,相当于运算符>、≥、<、≤、==、!=、逻辑和、逻辑或、逻辑异或 |
代码:
# 通用函数
import numpy as np
# 计算各元素的平方根
a = np.array([1, 4, 9])
print("数组平方根为:", np.sqrt(a))
# 返回数组的整数与小数
b = np.array([1.2, 2.5, 3])
c = np.modf(b)[0]
d = np.modf(b)[1]
print("b数组的小数部分为:", c, "\nb数组的整数部分为:", d)
# 符号赋予
e = np.ones(5)
e[::2] = -1
f = np.arange(5)
g = np.copysign(f, e)
print("符号赋予后的数组:", g)
# 元素级比较
print("e和g数组比较:", np.greater(e, g))
输出:
数组平方根为: [1. 2. 3.]
b数组的小数部分为: [0.2 0.5 0. ]
b数组的整数部分为: [1. 2. 3.]
符号赋予后的数组: [-0. 1. -2. 3. -4.]
e和g数组比较: [False False True False True]
2.6.2 统计函数
1. 将条件逻辑转换为数组运算
numpy的where()
函数是三元表达式x if condition else y
的矢量化版本,可以通过逻辑转化为数组运算,例如:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
con = np.array([True, False, True])
print(np.where(con, a, b))
输出为[1 5 3]
,可以看出,where
函数的第一个参数作为判定条件,如果元素为True
则选取第二个数组中对应位置的元素,如果为False
则选取第三个数组中对应位置的元素。当然判定数组也可以使用1
代表True
,0
代表False
,即con = np.array([1, 0, 1])
也能得到同样的结果。
2. 数组的统计运算
NumPy数组中与统计运算相关的方法
方法 | 描述 |
---|---|
sum | 对数组中全部或某个轴向的元素求和 |
mean | 算术平均值 |
min | 计算数组中的最小值 |
max | 计算数组中的最大值 |
argmin | 输出最小值索引 |
argmax | 输出最大值索引 |
cumsum | 所有元素的累计和 |
cumprod | 所有元素的累计积 |
代码:
import numpy as np
a = np.arange(10)
print("累计和为:", np.cumsum(a))
print("累计积为:", np.cumprod(a))
对于累计和如下图所示,累计积同理:
cumsum输出:
累计和为: [ 0 1 3 6 10 15 21 28 36 45]
累计积为: [0 0 0 0 0 0 0 0 0 0]
3. 数组排序
对数组中的元素进行排序,只需使用sort()
函数,例如:
import numpy as np
arr = np.array([[1, 5, 2, 3], [5, 8, 3, 1]])
arr.sort()
print(arr)
输出:
[[1 2 3 5]
[1 3 5 8]]
排序方法会修改原来的数组,函数中的参数代表着轴,例如:
import numpy as np
arr = np.array([[1, 5, 2, 3], [5, 8, 3, 1]])
arr.sort(0)
print(arr)
则得到的结果为:
[[1 5 2 1]
[5 8 3 3]]
排序
4. 检索数组元素
在NumPy中,all()
函数用于判断整个数组中的元素的值是否全部满足条件,如果全部满足条件则返回True
,否则返回False
。any()
函数用于判断整个数组中的元素至少有一个满足条件则返回True
,否则返回False
。
代码:
import numpy as np
arr = np.arange(12).reshape(3, 4)
print(np.any(arr > 10))
print(np.all(arr > 10))
输出:
True
False
5. 唯一化及其他集合逻辑
unique()
去除数组中的重复元素,保留唯一元素,即如果数组为[1,1,2,2]
则输出为[1,2]
,且排序输出数组
语法:
unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None)
参数说明:
-
ar
: 待操作数组 -
return_index
: 输出数组元素对应的索引(可选,默认False) -
return_inverse
: 输出重建数组元素索引(可选,默认False) -
return_count
: 输出每一元素出现的次数(可选,默认False) -
axis
:函数的操作维度(可选)
图解参数:
unique对于操作维度,如果不定义默认将数组降为一维,对一维数组进行unique
操作。
代码:
import numpy as np
arr = np.array([7, 2, 1, 1, 8, 2])
print(np.unique(arr, return_index=True))
print(np.unique(arr, return_inverse=True))
print(np.unique(arr, return_counts=True))
arr_2 = np.array([[0, 1, 0], [0, 1, 0], [2, 3, 4]])
print(np.unique(arr_2))
print(np.unique(arr_2, axis=0))
print(np.unique(arr_2, axis=1))
输出:
(array([1, 2, 7, 8]), array([2, 1, 0, 4],
dtype=int64))
(array([1, 2, 7, 8]), array([2, 1, 0, 0, 3, 1], dtype=int64))
(array([1, 2, 7, 8]), array([2, 2, 1, 1],
dtype=int64))
[0 1 2 3 4]
[[0 1 0]
[2 3 4]]
[[0 0 1]
[0 0 1]
[2 4 3]]
在二维数组中对1
轴使用unique
函数时,排序以输出的第一行为准,例如:
[[4 3 2 1]
[5 6 7 8]]
输出将为:
[[1 2 3 4]
[8 7 6 5]]
数组集合运算的常见函数
函数 | 描述 |
---|---|
unique(x) | 计算x中的唯一元素,并返回有序结果 |
intersect1d(x,y) | 计算x和y中的公共元素,并返回有序结果 |
union1d(x,y) | 计算x和y的并集,并返回有序结果 |
in1d(x,y) | 得到一个表示"x的元素是否包含y"的布尔型数组 |
setdiff1d(x,y) | 集合的差,即元素在x中且不再y中 |
setxor1d(x,y) | 集合的对称差,即存在于一个数组中但不同时存在于两个元素中的元素 |
2.6.3 线性代数相关函数
线性代数是数学运算中的一个重要工具,在numpy.linalg模块中有一组标准的矩阵分解运算以及诸如逆和行列式之类的东西,下面的例子代表矩阵的乘法:
import numpy as np
a = np.array([[1, 2, 3], [4, 5, 6]])
b = np.array([[1, 2], [3, 4], [5, 6]])
print(a.dot(b)) # 等价于np.dot(a,b)
输出为:
[[22 28]
[49 64]]
矩阵乘法
linalg模块常用函数
函数 | 描述 |
---|---|
dot | 矩阵乘法 |
diag | 以一维数组的形式返回方阵的对角线或将一维数组转为方阵 |
trace | 计算对角线元素和 |
det | 计算矩阵的行列式 |
eig | 计算方阵的特征值和特征向量 |
inv | 计算方阵的逆 |
qr | 计算qr分解 |
svd | 计算奇异值 |
solve | 解线性方程组Ax=b,其中A是一个方阵 |
lstsq | 计算Ax=b的最小二乘解 |
2.6.4 随机数相关函数
与Python的random模块相比,NumPy的random模块功能更多,它增加了一些可以高效生成多种概率分布的样本值函数。
import numpy as np
# 随机生成一个3行3列的二维数组
print(np.random.rand(3, 3))
# 随机生成一个的三维数组
print(np.random.rand(2, 3, 3))
输出:
[[0.51977788 0.78215477 0.03626778]
[0.53886531 0.84636492 0.20763375]
[0.62779302 0.01742627 0.21344803]]
[[[0.21952024 0.79324513 0.99029048]
[0.25493268 0.64863243 0.61611571]
[0.85816947 0.52925024 0.16129451]]
[[0.02905666 0.85701923 0.81112184]
[0.93242048 0.2237645 0.35160048]
[0.8884219 0.59137365 0.81497935]]]
random模块常见函数
函数 | 描述 |
---|---|
seed | 生成随机数种子 |
rand | 生成均匀分布的样本值 |
randint | 从给定的上下限范围内随机选取整数 |
normal | 产生正态分布的样本值 |
beta | 产生Beta分布的样本值 |
uniform | 产生在[0,1]中均匀分布的样本值 |