文章目录
简介
NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库,包括:
- 一个强大的N维数组对象 ndarray
- 广播功能函数
- 整合 C/C++/Fortran 代码的工具
- 线性代数、傅里叶变换、随机数生成等功能
Numpy 是 Scipy、Pandas 等数据处理或科学计算库的基础
Ndarray
定义
NumPy 最重要的一个特点是其 N 维数组对象 ndarray,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引,每个元素在内存中都有相同存储大小的区域。ndarray 内部由以下内容组成:
- 一个指向数据的指针
- 数据类型或 dtype,描述在数组中的固定大小值的格子
- 一个表示数组形状(shape)的元组,表示各维度大小的元组。若 shape 为一个数字时,则表示一维数组的大小
- 一个跨度元组(stride),指为了前进到当前维度下一个元素需要“跨过”的字节数。跨度可以是负数,这样会使数组在内存中后向移动
ndarray 对象可以去掉元素间运算所需的循环,使得一维向量更想单个数据
# 计算 A*A + B*B,其中 A 和 B 是一维数组
import numpy as np
a = np.array([0, 1, 2, 3, 4])
b = np.array([9, 8, 7, 6, 5])
c = a**2 + b**2
ndarray 对象创建的一般形式为
numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
# object:数组或嵌套的数列
# dtype:数组元素的数据类型,可选
# copy:对象是否需要复制,可选
# order:创建数组的样式,C 为行方向,F 为列方向,A 为任意方向(默认)
# subok:默认返回一个与基类类型一致的数组
# ndmin:指定生成数组的最小维度
数据类型
基本数据类型
numpy 支持的元素类型要比 Python 内置的类型要多,基本上可以和 C 语言的数据类型对应上
- bool:布尔类型
- int_:默认的整数类型
- intc:与 C 语言中的 int 类型一致,一般为 int32 或 int64
- intp:用于索引的整数,与 C 语言中 ssize_t 一致,为 int32 或 int64
- int8、 int16、 int32、int64:分别为字节长度整数、16位长度整数、32位长度整数、64位长度整数
- uint8、uint16、uint32、uint64:分别为8位无符号整数、16位无符号整数、32位无符号整数、64位无符号整数
- float_:float64 类型的简写
- float16、 float32、float64:分别为16位半精度浮点数(1位符号位,5位指数、10位尾数)、32位半精度浮点数(1位符号位,8位指数、23位尾数)、64位半精度浮点数(1位符号位,11位指数、52位尾数)
- complex_:complex128 类型的简写
- complex64、complex128:复数类型,实部和虚部大小各占一半
数据类型对象
数据类型对象是用来描述与数组对应的内存区域如何使用,这依赖如下几个方面:
- 数据的类型(整数,浮点数或者 Python 对象)
- 数据的大小(例如, 整数使用多少个字节存储)
- 数据的字节顺序(小端法或大端法)。字节顺序是通过对数据类型预先设定"<“或”>“来决定的。”<“意味着小端法(最小值存储在最小的地址,即低位组放在最前面)。”>"意味着大端法(最重要的字节存储在最小的地址,即高位组放在最前面)。
- 在结构化类型的情况下,字段的名称、每个字段的数据类型和每个字段所取的内存块的部分
- 如果数据类型是子数组,它的形状和数据类型
dtype 对象创建的一般形式为
numpy.dtype(object, align, copy)
# object:要转换为的数据类型对象
# align:如果为 true,填充字段使其类似 C 的结构体
# copy:复制 dtype 对象 ,如果为 false,则是对内置数据类型对象的引用
import numpy as np
dt = np.dtype(np.int32)
print(dt) # int32
# int8, int16, int32, int64 四种数据类型可以使用字符串 'i1', 'i2','i4','i8' 代替
dt = np.dtype('<i4')
print(dt) # int32
student = np.dtype([('name','S20'), ('age', 'i1'), ('marks', 'f4')])
# 每个元组的第一个元素表示索引名称,第二个元素表示索引所指元素类型
a = np.array([('abc', 21, 50),('xyz', 18, 75)], dtype = student)
print(a['age']) # [21, 18]
每个内建类型都有一个唯一定义它的字符代码
- b:布尔型
- i:(有符号) 整型
- u:无符号整型 integer
- f:浮点型
- c:复数浮点型
- m:timedelta(时间间隔)
- M:datetime(日期时间)
- O:(Python) 对象
- S, a:(byte-)字符串
- U:Unicode
- V:原始数据 (void)
数组属性
ndarray 对象的属性有
- ndarray.ndim:秩(rank),它表示数组的维数。秩也是轴(axis)的数量,一维数组的秩为 1,二维数组的秩为 2。同时,第一个轴相当于是底层数组,第二个轴是底层数组里的数组。在对数组的操作中可以指明 axis,即表明操作遵循的方向,如 axis=0 表示沿着第 0 轴进行操作,即对每一列进行操作;axis=1 表示沿着第1轴进行操作,即对每一行进行操作
- ndarray.shape:数组的维度,对于矩阵为 n 行 m 列。它返回一个元组,这个元组的长度即为 ndim 属性。修改 ndarray.shape 属性可以用于调整数组大小,或者直接使用 reshape 函数,其参数为新的 shape
- ndarray.size:数组元素的总个数,相当于 .shape 中 n*m 的值
- ndarray.dtype:ndarray 对象的元素类型
- ndarray.itemsize:ndarray 对象中每个元素的大小,以字节为单位
- ndarray.flags:ndarray 对象的内存信息,包含以下属性,取值为 True 或 False:
- C_CONTIGUOUS:数据是在一个单一的 C 风格的连续段中
- F_CONTIGUOUS:数据是在一个单一的 Fortran 风格的连续段中
- OWNDATA:数组拥有它所使用的内存或从另一个对象中借用它
- WRITEABLE:数据区域可以被写入,将该值设置为 False,则数据为只读
- ALIGNED:数据和所有元素都适当地对齐到硬件上
- UPDATEIFCOPY:这个数组是其它数组的一个副本,当这个数组被释放时,原数组的内容将被更新
- ndarray.real:ndarray元素的实部
- ndarray.imag:ndarray 元素的虚部
- ndarray.data:包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性
创建数组
直接创建
ndarray 数组除了可以使用底层 ndarray 构造器来创建外,也可以通过以下几种方式来直接创建
- numpy.empty:创建一个数组,且数组元素为随机值,因为它们未初始化。一般形式为
numpy.empty(shape, dtype = float, order = 'C')
# shape:数组形状
# dtype:数据类型,可选
# order:有"C"和"F"两个选项,分别代表行优先和列优先,在计算机内存中的存储元素的顺序
- numpy.zeros:创建指定大小的数组,数组元素以 0 来填充。一般形式为
numpy.zeros(shape, dtype = float, order = 'C')
- numpy.ones:创建指定形状的数组,数组元素以 1 来填充。一般形式为
numpy.ones(shape, dtype = None, order = 'C')
- numpy.eye:创建一个 n*n 单位矩阵,对角元素为1,其余为0。一般形式为
numpy.eye(N, M = None, k = 0, dtype = float, order = 'C')
# N:矩阵的行数
# M:矩阵的列数,可选
# k:k=0则主对角线元素为1,k>0 时则为主对角线上面第 k 条对角线元素为1;k<0 则为主对角线下面第 k 条对角线元素为1,如
# np.eye(3, k=1)
# array([[0., 1., 0.],
# [0., 0., 1.],
# [0., 0., 0.]])
- numpy.full:创建一个指定元素的数组。一般形式为
numpy.full(shape, fill_value, dtype = None, order = 'C')
通过已有数组创建
- numpy.ones_like:根据参数数组的形状生成一个全1数组。一般形式为
numpy.ones_like(a, dtype = None, order = 'K', subok = True, shape = None)
# a:数组 a 的形状和数据类型决定返回的数组的形状和数据类型
# shape:重写数组类型,可选
- numpy.zeros_like:根据参数数组的形状生成一个全0数组。一般形式为
numpy.zeros_like(a, dtype=None, order='K', subok=True, shape=None)
- numpy.full_like:根据参数数组的形状生成一个指定元素的数组。一般形式为
numpy.full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None)
- numpy.asarray:类似 numpy.array,但 numpy.asarray 参数只有三个,一般形式为
numpy.asarray(a, dtype = None, order = None)
# a:任意形式的输入参数,可以是列表、列表的元组、元组、元组的元组、元组的列表、多维数组
- numpy.frombuffer:用于实现动态数组。numpy.frombuffer 接受 buffer 输入参数,以流的形式读入转化成 ndarray 对象。一般形式为
numpy.frombuffer(buffer, dtype = float, count = -1, offset = 0)
# buffer:可以是任意对象,会以流的形式读入。buffer 是字符串的时候,Python3 默认 str 是 Unicode 类型,所以要转成 bytestring 在原 str 前加上 b
# count:读取的数据数量,默认为-1,读取所有数据
# offset:读取的起始位置,默认为0
- numpy.fromiter:从可迭代对象中建立 ndarray 对象,返回一维数组。一般形式为
numpy.fromiter(iterable, dtype, count = -1)
# iterable:可迭代对象,如 range(5) 等
# count:读取的数据数量,默认为-1,读取所有数据
从数值范围创建数组
- numpy.arange:根据 start 与 stop 指定的范围以及 step 设定的步长,生成一个 ndarray。一般形式为
numpy.arange(start, stop, step, dtype)
# start:起始值,默认为0
# stop:终止值(不包含)
# step:步长,默认为1
- numpy.linspace:用于创建一个等差数列。一般形式为
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
# start:序列的起始值
# stop:序列的终止值
# num:要生成的等步长的样本数量,默认为50
# endpoint:该值为 True 时,数列中包含stop值,反之不包含,默认是True
# retstep:如果为 True 时,生成的数组中会显示间距,反之不显示
- numpy.logspace:用于创建一个等比数列。一般形式为
np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
# start:序列的起始值为:base ** start
# stop:序列的终止值为:base ** stop。如果endpoint为true,该值包含于数列中
# num:要生成的等步长的样本数量,默认为50
# endpoint:该值为 true 时,数列中中包含stop值,反之不包含,默认是True
# base:对数 log 的底数
- numpy.concatenate:将两个或多个数组合并成一个新的数组。一般形式为
numpy.concatenate((a1, a2, ...), axis=0, out=None)
# a1, a2, ...:这些数组除了 axis 方向的大小可以不同外,必须要有相同的形状
a = np.array([[1,2,3],[4,5,6]])
b = np.array([[11,21,31],[7,8,9]])
np.concatenate((a,b),axis=0)
# array([[ 1, 2, 3],
# [ 4, 5, 6],
# [11, 21, 31],
# [ 7, 8, 9]])
# 此时可以 a = np.array([[1,2,3],[4,5,6],[7,8,9]]),则结果为
# array([[ 1, 2, 3],
# [ 4, 5, 6],
# [ 7, 8, 9],
# [11, 21, 31],
# [ 7, 8, 9]])
np.concatenate((a,b),axis=1)
# array([[ 1, 2, 3, 11, 21, 31],
# [ 4, 5, 6, 7, 8, 9]])
数组操作
广播机制
广播(Broadcast)是 numpy 对不同形状的数组进行数值计算的方式。若两个数组形状相同,则两个数组的运算就是对应位的运算。而当运算中的两个数组的形状不同时,numpy 将自动触发广播机制
import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])
c = a * b # [ 10 40 90 160]
# 最简单例子
a = np.array([1,2,3,4])
a += 4 # [5,6,7,8]
a = np.array([[ 0, 0, 0],
[10,10,10],
[20,20,20],
[30,30,30]])
b = np.array([1,2,3])
c = a + b
# 此时先将数组 b 扩展为
# [[1 2 3]
# [1 2 3]
# [1 2 3]
# [1 2 3]]
# 再作运算,得到 c 为
# [[ 1 2 3]
# [11 12 13]
# [21 22 23]
# [31 32 33]]
a = np.array([[0],[1],[2],[3]])
b = nap.array([1,2,3])
c = a + b
# 此时同时发生两个广播
# 数组 a 扩展为
# [[0 0 0]
# [1 1 1]
# [2 2 2]
# [3 3 3]]
# 数组 b 扩展为
# [[1 2 3]
# [1 2 3]
# [1 2 3]
# [1 2 3]]
# 最后作运算得到 c
# [[1 2 3]
# [2 3 4]
# [3 4 5]
# [4 5 6]]
广播的规则
- 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐
- 输出数组的形状是输入数组形状的各个维度上的最大值
- 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错
- 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值
访问数组
索引
- 切片索引:ndarray 对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样。ndarray 数组可以基于 0 - n 的下标进行索引,切片对象可以通过内置的 slice 函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组
import numpy as np
a = np.arange(10)
s = slice(2,7,2) # [2 4 6]
b = a[2:7:2] # [2 4 6]
o = a[2] # [2]
c = np.array([[1,2,3],[3,4,5],[4,5,6]])
d = c[1:] # 在第一维切片
#[[3 4 5]
# [4 5 6]]
e = c[0][1:] # 在第二维切片
# [2 3]
- 整数数组索引:索引中数字由逗号隔开,第一个元素为行索引,第二个元素为列索引
import numpy as np
a = np.array([[0,1,2],[3,4,5],[6,7,8],[9,10,11]])
# 获取数组中(0,0)处元素
o = a[0,0] # 0
# 获取数组中(0,0),(1,1)和(2,0)位置处的元素
b = a[[0,1,2],[0,1,0]] # [0 4 6]
# 获取数组中的四个角的元素。行索引是 [0,0] 和 [3,3],而列索引是 [0,2] 和 [0,2]
c = a[[[0,0],[3,3]], [[0,2],[0,2]]]
# [[ 0 2]
# [ 9 11]]
# 整数数组索引与切片 : 结合
# 获取行索引从1到3,列索引从1到3的数组
d = a[1:3, 1:3]
# [[4 5]
# [7 8]]
# 获取行索引从1到3,列索引是1和2的数组
e = a[1:3,[1,2]]
# [[4 5]
# [7 8]]
# 获得行索引从1到3,列索引是1的数组
f = a[1:3,1] # [4,7]
# 获得列索引是1的数组
g = a[:,1] # [ 1, 4, 7, 10]
# 整数数组索引与Ellipsis ... 结合。 ... 和 [:] 都是指所有元素,因此用法相同
# 获得数组第2列元素
h = a[...,1