1 NumPy入门¶
import numpy as np
# jupyter输出显示设置
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
1.1 理解Python和NumPy的数据类型¶
1.1.1 从Python列表创建数组¶
# 创建整型数组
np.array([1, 2, 3, 4, 5])
array([1, 2, 3, 4, 5])
# 类型不匹配时自动转换
np.array([1.0, 2, 3, 4, 5])
array([1., 2., 3., 4., 5.])
# 指定数组的数据类型
np.array([1, 2, 3, 4, 5], dtype=np.float32)
array([1., 2., 3., 4., 5.], dtype=float32)
# 初始化多维数组
np.array([range(i, i+3) for i in [2, 4, 6]])
array([[2, 3, 4], [4, 5, 6], [6, 7, 8]])
1.1.2 从头创建数组¶
# 创建指定大小的全0数组
np.zeros((3, 4), dtype=np.int32)
array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
# 创建指定大小的全1数组
np.ones((3, 5), dtype=np.float32)
array([[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.]], dtype=float32)
# 创建指定大小、指定数值的数组
np.full((3, 5), 3.14)
array([[3.14, 3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14, 3.14]])
# 创建线性序列,参数为开始、结束、步长(类似于range)
np.arange(0, 20, 2)
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
# 创建均匀切割的数组,参数为开始、结束、数量
np.linspace(0, 1, 5)
array([0. , 0.25, 0.5 , 0.75, 1. ])
# 创建指定大小、0~1之间分布的随机数矩阵
np.random.random((3, 3))
array([[0.52300173, 0.6099551 , 0.90392997], [0.68513022, 0.40606534, 0.12176085], [0.50479174, 0.67819969, 0.60743873]])
# 创建指定大小、服从指定参数的正态分布的随机数矩阵
np.random.normal(0, 1, (3, 3))
array([[ 0.39088633, 0.01229742, -0.10358749], [ 0.17884759, 1.87676579, 0.48339031], [ 0.70697702, 0.75920357, -1.29395961]])
# 创建指定大小、指定区间内随机数的一维数组
np.random.randint(0, 10, (3, 3))
array([[5, 2, 9], [2, 7, 7], [9, 3, 3]])
# 创建指定大小的单位矩阵
np.eye(3)
array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
# 创建指定大小未初始化的矩阵
np.empty((3, 5))
array([[3.14, 3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14, 3.14], [3.14, 3.14, 3.14, 3.14, 3.14]])
1.1.3 NumPy标准数据类型¶
数据类型 | 说明 |
---|---|
bool_ | 布尔值,1字节存储 |
int_ | 默认整型,int64或int32 |
intc | 同C语言int |
intp | 用作索引的整型,同C语言ssize_t,通常为int64或int32 |
int8 | 字节,-128~127 |
int16 | 整型 |
int32 | 整型 |
int64 | 整型 |
uint8 | 无符号整型 |
uint16 | 无符号整型 |
uint32 | 无符号整型 |
uint64 | 无符号整型 |
float_ | float64简化形式 |
float16 | 半精度浮点型,5比特指数、10比特尾数 |
float32 | 单精度浮点型,8比特指数、23比特尾数 |
float64 | 双精度浮点型,11比特指数、52比特尾数 |
complex_ | complex128简化形式 |
complex64 | 复数,两个32位浮点数表示 |
complex128 | 复数,两个64位浮点数表示 |
# 数据类型的指定有两种方式:字符串、NumPy对象
np.zeros(5, dtype='int32')
np.zeros(5, dtype=np.int32)
array([0, 0, 0, 0, 0])
array([0, 0, 0, 0, 0])
a = np.random.randint(0, 10, size=(10))
b = np.random.randint(0, 10, size=(3, 4))
# 数组的维度
b.ndim
# 数组每个维度的大小
b.shape
# 数组的总大小
b.size
# 数组的数据类型
b.dtype
# 数组元素的字节大小
b.itemsize
2
(3, 4)
12
dtype('int32')
4
1.2.2 数组索引¶
# 中括号索引
a[0]
# 负值索引
a[-2]
# 逗号分隔维度
b[0, 1]
5
4
3
【注】 由于NumPy数组是固定类型的,因此如果修改的值与原类型不对应则会执行转换操作
1.2.3 数组切片¶
切片的语法大致为x[start:stop:step]
# 一维子数组
a
a[5:]
a[::2]
a[1::2]
a[::-1] # 经典的逆排序操作
array([5, 5, 5, 3, 8, 9, 9, 7, 4, 3])
array([9, 9, 7, 4, 3])
array([5, 5, 8, 9, 4])
array([5, 3, 9, 7, 3])
array([3, 4, 7, 9, 9, 8, 3, 5, 5, 5])
# 多维子数组,逗号分隔不同维度
b
b[:2, :3]
b[:2, ::3]
b[::-1, ::-1] # 经典的逆排序操作
array([[0, 3, 0, 4], [0, 9, 5, 6], [7, 0, 5, 9]])
array([[0, 3, 0], [0, 9, 5]])
array([[0, 4], [0, 6]])
array([[9, 5, 0, 7], [6, 5, 9, 0], [4, 0, 3, 0]])
# 获取数组的行和列
b[:, 0]
b[0, :]
array([0, 0, 7])
array([0, 3, 0, 4])
由于NumpPy数组切片返回的是数组数据的视图而非数值数据的副本(类似于引用关系),因此需要注意NumPy数组与Python数组的区别。
c = b[0, :]
c[0] = 100
b # 可以看到改变c的值同时也改变了b的值
array([[100, 3, 0, 4], [ 0, 9, 5, 6], [ 7, 0, 5, 9]])
# 创建数组的副本
c = b[0, :].copy()
c[0] = 1000
b # 改变c的值同时不改变b的值
array([[100, 3, 0, 4], [ 0, 9, 5, 6], [ 7, 0, 5, 9]])
1.2.4 数组变形¶
使用reshape函数进行数组变形时, 如果原始数组大小与变形后数组大小一致,则可以得到原始数组的非副本视图。然而在非连续数据缓存情况下,返回非副本试图往往不能实现。
# 使用reshape()函数
a = np.arange(1, 10)
b = a.reshape((3, 3))
b
b[1, 1] = 100
a # 改变b的值,则a的值也发生改变
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
array([ 1, 2, 3, 4, 100, 6, 7, 8, 9])
将一维数组转换为二维矩阵时,可以在切片中使用newaxis关键字或reshape函数
# 使用newaxis关键字
a[np.newaxis, :]
# 使用reshape函数
a.reshape(1, 9)
array([[ 1, 2, 3, 4, 100, 6, 7, 8, 9]])
array([[ 1, 2, 3, 4, 100, 6, 7, 8, 9]])
1.2.5 数组拼接与分裂¶
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.array([[7], [8]])
数组的拼接或连接主要由np.concatenate
、np.vstack
、np.hstack
和np.dstack
实现
# np.concatenate将数组元组或数组列表作为第一参数
np.concatenate([a, b])
array([1, 2, 3, 4, 5, 6])
# np.concatenate也可以拼接多维数组,使用axis控制拼接维度
x = np.array([[1, 2, 3], [4, 5, 6]])
np.concatenate([x, x])
np.concatenate([x, x], axis=1)
array([[1, 2, 3], [4, 5, 6], [1, 2, 3], [4, 5, 6]])
array([[1, 2, 3, 1, 2, 3], [4, 5, 6, 4, 5, 6]])
沿着固定维度处理数据时,使用np.vstack
和np.hstack
(垂直栈和水平栈)会更加简洁
# 垂直栈
np.vstack([x, a])
# 水平栈
np.hstack([x, c])
array([[1, 2, 3], [4, 5, 6], [1, 2, 3]])
array([[1, 2, 3, 7], [4, 5, 6, 8]])
数组的分裂通过np.split
、np.hsplit
、np.vsplit
和np.dsplit
实现
x = [1, 2, 3, 4, 5, 6, 7, 8]
x
# 使用np.split函数时,向函数传递一个记录分裂点位置的索引列表
x1, x2, x3 = np.split(x, [3, 5])
x1
x2
x3
[1, 2, 3, 4, 5, 6, 7, 8]
array([1, 2, 3])
array([4, 5])
array([6, 7, 8])
x = np.arange(16).reshape((4, 4))
x
# np.vsplit函数
x1, x2 = np.vsplit(x, [2])
x1
x2
# np.hsplit函数
x1, x2 = np.hsplit(x, [2])
x1
x2
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15]])
array([[0, 1, 2, 3], [4, 5, 6, 7]])
array([[ 8, 9, 10, 11], [12, 13, 14, 15]])
array([[ 0, 1], [ 4, 5], [ 8, 9], [12, 13]])
array([[ 2, 3], [ 6, 7], [10, 11], [14, 15]])
1.3 NumPy数组计算:通用函数¶
使NumPy计算变快的关键是利用向量化操作,通常在NumPy通用函数实现。
1.3.1 通用函数介绍¶
NumPy为很多类型的操作提供了方便的、静态类型的、可编译程序的接口,也称为向量操作。向量操作被用于将循环推送至NumPy之下的编译层,以达到更高的执行效率。
x = np.arange(9).reshape((3, 3))
2**x
array([[ 1, 2, 4], [ 8, 16, 32], [ 64, 128, 256]], dtype=int32)
1.3.2 探索NumPy的通用函数¶
通用函数有两种存在形式:一元通用函数(对单个输入操作)、二元通用函数(对两个输入操作)
x = np.arange(4)
# 数组的四则运算
x
x + 5 # np.add
x - 5 # np.subtract
x * 2 # np.multiply
x / 2 # np.divide
x // 2 # np.floor_divide
array([0, 1, 2, 3])
array([5, 6, 7, 8])
array([-5, -4, -3, -2])
array([0, 2, 4, 6])
array([0. , 0.5, 1. , 1.5])
array([0, 0, 1, 1], dtype=int32)
# 数组的其他运算
-x # np.negative
x ** 2 # np.power
x % 2 # np.mod
array([ 0, -1, -2, -3])
array([0, 1, 4, 9], dtype=int32)
array([0, 1, 0, 1], dtype=int32)
# 绝对值
x = np.array([1, 2, -1, -3, 2, 2])
y = np.array([3-4j, 3+4j, 2, -1])
# 实数数组求绝对值
np.abs(x)
# 复数数组求模长
np.abs(y)
array([1, 2, 1, 3, 2, 2])
array([5., 5., 2., 1.])
# 三角函数
a = np.linspace(0, np.pi, 3)
b = np.linspace(-1, 1, 3)
np.sin(a)
np.cos(a)
np.tan(a)
np.arcsin(b)
np.arccos(b)
np.arctan(b)
array([0.0000000e+00, 1.0000000e+00, 1.2246468e-16])
array([ 1.000000e+00, 6.123234e-17, -1.000000e+00])
array([ 0.00000000e+00, 1.63312394e+16, -1.22464680e-16])
array([-1.57079633, 0. , 1.57079633])
array([3.14159265, 1.57079633, 0. ])
array([-0.78539816, 0. , 0.78539816])
# 指数与对数
x = np.array([1, 2, 3])
np.exp(x)
np.exp2(x)
np.power(3, x)
np.log(x)
np.log2(x)
np.log10(x)
array([ 2.71828183, 7.3890561 , 20.08553692])
array([2., 4., 8.])
array([ 3, 9, 27], dtype=int32)
array([0. , 0.69314718, 1.09861229])
array([0. , 1. , 1.5849625])
array([0. , 0.30103 , 0.47712125])
# 专用的通用函数,由子模块scipy.special提供
from scipy import special
x = [1, 5, 10]
special.gamma(x)
special.beta(x, 2)
x = np.array([0, 0.3, 0.6, 0.9])
special.erf(x)
special.erfc(x)
special.erfinv(x)
array([1.0000e+00, 2.4000e+01, 3.6288e+05])
array([0.5 , 0.03333333, 0.00909091])
array([0. , 0.32862676, 0.60385609, 0.79690821])
array([1. , 0.67137324, 0.39614391, 0.20309179])
array([0. , 0.27246271, 0.59511608, 1.16308715])
1.3.3 高级的通用函数特性¶
所有的通用函数都可以通过out参数指定计算结果的存放位置,而不同于创建临时数组
# 指定输出,通过out字段指定存储位置
x = np.arange(6)
y = np.arange(6)
np.multiply(x, 10, out=y)
y
# 该特性也可以用在数组视图
y = np.zeros(12)
np.power(2, x, out=y[::2])
y
array([ 0, 10, 20, 30, 40, 50])
array([ 0, 10, 20, 30, 40, 50])
array([ 1., 2., 4., 8., 16., 32.])
array([ 1., 0., 2., 0., 4., 0., 8., 0., 16., 0., 32., 0.])
二元通用函数有些非常有趣的聚合功能,这些聚合可以直接在对象上计算
# 聚合
x = np.arange(1, 6)
np.add.reduce(x)
np.multiply.reduce(x)
# 如果需要存储每次计算的中间结果,则可使用accumulate
np.add.accumulate(x)
15
120
array([ 1, 3, 6, 10, 15], dtype=int32)
任何通用函数都可以用outer函数获取两个不同输入数组所有元素对的函数运算结果
# multiply与outer函数实现外积的计算
x = np.arange(1, 6)
np.multiply.outer(x, x)
array([[ 1, 2, 3, 4, 5], [ 2, 4, 6, 8, 10], [ 3, 6, 9, 12, 15], [ 4, 8, 12, 16, 20], [ 5, 10, 15, 20, 25]])
1.4 聚合:最小值、最大值和其他值¶
# 数组值求和
x = np.arange(10)
np.sum(x)
45
# 最大值与最小值
x = np.arange(10)
np.min(x)
np.max(x)
0
9
NumPy的聚合默认操作范围为整个数组,因此如需指定维度则用axis
字段进行约束
# 多维度聚合
x = np.random.randint(0, 10, size=(4, 4))
x
x.sum()
x.sum(axis=0)
x.sum(axis=1)
array([[2, 0, 8, 9], [4, 8, 6, 1], [3, 1, 8, 5], [7, 9, 3, 5]])
79
array([16, 18, 25, 20])
array([19, 19, 17, 24])
NumPy的其他聚合函数以及NaN安全版本如下表所示(NaN版本在计算时会忽略所有缺失值NaN)
函数名称 | NaN安全版本 | 描述 |
---|---|---|
np.sum | np.nansum | 计算元素之和 |
np.prod | np.nanprod | 计算元素之积 |
np.mean | np.nanmean | 计算元素平均值 |
np.std | np.nanstd | 计算元素标准差 |
np.var | np.nanvar | 计算元素方差 |
np.min | np.nanmin | 计算最小值 |
np.max | np.nanmax | 计算最大值 |
np.argmin | np.nanargmin | 找出最小值索引 |
np.argmax | np.nanargmax | 找出最大值索引 |
np.median | np.nanmedian | 计算中位数 |
np.percentile | np.nanpercentile | 计算基于元素排序的统计值 |
np.any | N/A | 验证任何一个元素是否为真 |
np.all | N/A | 验证所有元素是否为真 |
1.5 数组的计算:广播¶
NumPy通过通用函数的向量化操作减少缓慢的Python循环。NumPy还提供了另一种向量化操作:利用NumPy的广播功能。广播功能可以理解为用于不同大小数组的二进制通用函数(加减乘等)的一组规则
1.5.1广播简介¶
# 加减乘
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
a + b
# 数组与标量相加
a + 5
array([5, 7, 9])
array([6, 7, 8])
广播操作可以理解为将标量或数组进行扩展,然后执行相应的操作
# 多维数组的广播机制
a = np.ones((2, 3))
b = np.arange(3)
a
b
a + b
a * b
array([[1., 1., 1.], [1., 1., 1.]])
array([0, 1, 2])
array([[1., 2., 3.], [1., 2., 3.]])
array([[0., 1., 2.], [0., 1., 2.]])
有时可能发生两个数组同时广播,此时情况更加复杂
# 两个数组同时发生广播
a = np.arange(3)
b = np.arange(3)[:, np.newaxis]
a
b
a + b
array([0, 1, 2])
array([[0], [1], [2]])
array([[0, 1, 2], [1, 2, 3], [2, 3, 4]])
1.5.2 广播的规则¶
广播的规则如下:
- 如果两个数组的维度数量不同,则小维度数组的形状将会在最左边补1
- 如果两个数组的形状在任何一个维度上都不匹配,则数组的形状会沿着维度为1的维度扩展以匹配另外一个数组的形状
- 如果两个数组的形状在任何一个维度上都不匹配且没有任何一个维度等于1,则引发异常
广播规则对于任任意二进制通用函数都是适用的,例如logaddexp
等。
1.5.3 广播的实际应用¶
# 数组的归一化
x = np.random.random((10, 3))
xmean = x.mean(0) # 沿着第一个维度聚合
x - xmean
array([[-0.25235933, -0.40876055, -0.29193434], [ 0.20706869, -0.40842629, 0.04335263], [ 0.32411636, 0.07717028, 0.08544746], [ 0.07033109, 0.46299066, -0.14045554], [-0.15462263, -0.05521411, -0.35915212], [-0.12088064, -0.18449039, 0.03603256], [ 0.20239461, -0.19075211, -0.19478297], [ 0.50400593, -0.09198252, -0.17657219], [-0.41733717, 0.45233086, 0.5438025 ], [-0.3627169 , 0.34713416, 0.45426202]])
# 绘制二维函数
import matplotlib.pyplot as plt
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 50)[:, np.newaxis]
z = np.sin(x)**10+np.cos(10+y*x)*np.cos(x)
x.shape, y.shape, z.shape
%matplotlib inline
plt.imshow(z, origin='lower', extent=[0, 5, 0, 5], cmap='viridis')
plt.colorbar()
((50,), (50, 1), (50, 50))
<matplotlib.image.AxesImage at 0x144eaa640d0>
<matplotlib.colorbar.Colorbar at 0x144eb12b310>
1.6 比较、掩码和布尔逻辑¶
当想基于某些准则抽取、修改、计数或对一个数组中的值进行其他操作时,掩码可以派上用场。
传统的统计方式是对所有数据循环,当碰到数据落在区间时计数器加1,但这是一种非常低效的方法。NumPy的通用函数能够替代循环,快速实现数组的逐元素运算。同样,我们也可以用其他通用函数实现数组的逐元素比较。
1.6.1 与通用函数类似的比较操作¶
x = np.array([0, 1, 2, 3, 4, 5])
# 比较操作
x < 3 # np.less
x > 3 # np.greater
x <= 3 # np.less_equal
x >= 3 # np.greater_equal
x != 3 # np.not_equal
x == 3 # np.equal
array([ True, True, True, False, False, False])
array([False, False, False, False, True, True])
array([ True, True, True, True, False, False])
array([False, False, False, True, True, True])
array([ True, True, True, False, True, True])
array([False, False, False, True, False, False])
1.6.2 操作布尔数组¶
x = np.random.randint(0, 10, size=(3, 4))
x
# 统计True的个数
np.count_nonzero(x < 6) # 统计布尔数组True的个数
np.sum(x < 6)
np.sum(x < 6, axis=1) # sum函数能够指定维度进行统计
# 统计是否全为True
np.all(x < 6)
np.any(x < 6)
np.all(x < 6, axis=1)
np.any(x < 6, axis=1)
array([[2, 2, 4, 1], [5, 1, 2, 6], [2, 5, 4, 9]])
10
10
array([4, 3, 3])
False
True
array([ True, False, False])
array([ True, True, True])
# NumPy使用通用函数重载逻辑运算符
np.sum((x > 3) & (x < 8))
np.sum(~((x <= 3) | (x >= 8))) # 与上式等价
5
5
逐位的布尔运算符与对应的通用函数对应关系如下表:
运算符 | 对应通用函数 |
---|---|
& | np.bitwise_and |
| | np.bitwise_or |
^ | np.bitwise_xor |
~ | np.bitwise_not |
1.6.3 将布尔数组作为掩码¶
x = np.random.randint(0, 10, size=(3, 4))
# 布尔数组作为掩码,选出对应元素
x
x < 5
x[x < 5]
array([[9, 2, 5, 9], [0, 7, 3, 6], [6, 2, 4, 5]])
array([[False, True, False, False], [ True, False, True, False], [False, True, True, False]])
array([2, 0, 3, 2, 4])
1.7 花哨的索引¶
在之前的小节中索引使用的都是简单的索引值、切片和布尔掩码,本节将使用索引数组快速获得并修复复杂的数组值的子数据集。
1.7.1 探索花哨的索引¶
需要注意的是,花哨的索引的结果与索引数组的形状一致,而不是被索引数组的形状。
x = np.random.randint(0, 10, size=(12))
y = x.reshape(-1, 4)
x
y
# 通过传递索引的单个列表或数组获取元素
index = [0, 2]
x[index]
y[index]
# 花哨的索引的结果与索引数组的形状一致(而不是被索引数组的形状)
index = np.array([[1, 1], [2, 2]]) # 注:这里用的是ndarray类型而不是list
x[index]
array([5, 4, 3, 4, 8, 1, 3, 3, 1, 9, 4, 1])
array([[5, 4, 3, 4], [8, 1, 3, 3], [1, 9, 4, 1]])
array([5, 3])
array([[5, 4, 3, 4], [1, 9, 4, 1]])
array([[4, 4], [3, 3]])
# 数组索引对多维数组同样适用,注意每个维度构建一个列表
row, col = [1, 2], [2, 3]
y[row, col]
array([3, 1])
# 数组索引时同样可以适用广播机制
row, col = np.array([1, 2]), np.array([2, 3])
y[row[:, np.newaxis], col]
array([[3, 3], [4, 1]])
1.7.2 组合索引¶
组合索引,即将多种索引方式组合适用
x = np.arange(12).reshape(-1, 4)
x
# 数组索引与简单索引组合
x[2, [1, 2]]
x[1:, [0, 3]]
array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
array([ 9, 10])
array([[ 4, 7], [ 8, 11]])
# 数组索引与掩码索引组合
mask = np.array([1, 0, 1, 0], dtype=np.bool8)
x[np.array([0, 2])[:, np.newaxis], mask]
array([[ 0, 2], [ 8, 10]])
1.7.3 用花哨的索引修改值¶
x = np.arange(10)
x
# 用花哨的索引修改值
index = np.array([1, 2, 3])
x[index] -= 9
x
x[index] = 11
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
array([ 0, -8, -7, -6, 4, 5, 6, 7, 8, 9])
array([ 0, 11, 11, 11, 4, 5, 6, 7, 8, 9])
需要注意的是,操作中出现重复的索引可能导致意外:
x = np.zeros(10)
i = [1, 1, 2, 2, 2, 3, 3, 3, 3]
# 重复的索引导致意外的结果
x[i] += 1
x
array([0., 1., 1., 1., 0., 0., 0., 0., 0., 0.])
为了避免这种情况,可以使用at
函数或reduceat
函数
x = np.zeros(10)
i = [1, 1, 2, 2, 2, 3, 3, 3, 3]
# at函数:为给定位置、给定数值进行给定操作
np.add.at(x, i, 1)
x
array([0., 2., 3., 4., 0., 0., 0., 0., 0., 0.])
1.8 数组的排序¶
1.8.1 快速排序:np.sort和np.argsort¶
x = np.random.randint(0, 100, size=(12))
x
# np.sort排序元素值
np.sort(x)
# np.argsort排序索引值
np.argsort(x)
array([11, 0, 23, 92, 58, 57, 79, 28, 78, 5, 41, 92])
array([ 0, 5, 11, 23, 28, 41, 57, 58, 78, 79, 92, 92])
array([ 1, 9, 0, 2, 7, 10, 5, 4, 8, 6, 3, 11], dtype=int64)
x = x.reshape(3, 4)
x
# 多维数组的排序
np.sort(x, axis=1)
array([[11, 0, 23, 92], [58, 57, 79, 28], [78, 5, 41, 92]])
array([[ 0, 11, 23, 92], [28, 57, 58, 79], [ 5, 41, 78, 92]])
1.8.2 部分排序:分隔¶
有时我们不希望对整个数组进行排序,仅仅希望找到数组中第K小的值。np.partition
函数提供了这一功能,其输入是数组和数字K,输出结果是一个新数组,最小的K个元素排列于数组前端,其余元素排列于数组后端,分隔开的两个数组内部无排序。
x = np.random.randint(0, 100, size=(20))
x
# np.partition
np.partition(x, 10)
# np.partition可沿着特定维度操作
x = x.reshape(4, -1)
np.partition(x, 2, axis=1)
array([92, 19, 75, 56, 98, 24, 9, 68, 99, 67, 41, 13, 52, 79, 81, 97, 56, 94, 92, 63])
array([19, 63, 56, 56, 52, 24, 9, 13, 41, 67, 68, 75, 81, 79, 92, 92, 97, 94, 98, 99])
array([[19, 56, 75, 92, 98], [ 9, 24, 67, 99, 68], [13, 41, 52, 79, 81], [56, 63, 92, 94, 97]])
正如np.sort
对应有np.partition
,np.argsort
也对应有np.argpartition
。
1.9 结构化数据:NumPy的结构化数组¶
NumPy的结构化数组和记录数组为复合的、异构的数据提供了有效的存储。
# 示例
name = ['A', 'B', 'C', 'D']
age = [11, 12, 13, 14]
weight = [55, 56, 57, 58]
data = np.zeros(4, dtype={
'names': ('name', 'age', 'weight'),
'formats': ('U10', 'i4', 'f8')
})
data['name'], data['age'], data['weight'] = name, age, weight
data
data['name']
data[0]
data[0]['name']
data[data['age'] < 13]['name']
array([('A', 11, 55.), ('B', 12, 56.), ('C', 13, 57.), ('D', 14, 58.)], dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f8')])
array(['A', 'B', 'C', 'D'], dtype='<U10')
('A', 11, 55.)
'A'
array(['A', 'B'], dtype='<U10')
1.9.1 生成结构化数组¶
# 采用字典
np.dtype({
'names': ('name', 'age', 'weight'),
'formats': ('U10', 'i4', 'f8')
})
dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f8')])
# 数值的类型可使用Python或NumpPy的类型指定
np.dtype({
'names': ('name', 'age', 'weight'),
'formats': ((np.str_, 10), int, np.float32)
})
dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
# 也可以采用元组列表
np.dtype([('name', 'U10'), ('age', 'i4'), ('weight', 'f8')])
dtype([('name', '<U10'), ('age', '<i4'), ('weight', '<f8')])
# 可省略类型名称
np.dtype('U10,i4,f8')
dtype([('f0', '<U10'), ('f1', '<i4'), ('f2', '<f8')])
NumPy数据类型简写规则:
- 字符\<和>分别表示小端和大端
- 字符u、i、f等表示数据类型
- 数字4、8等表示类型字节大小
NumPy数据类型符号 | 描述 |
---|---|
b | 字节型 |
i | 有符号整型 |
u | 无符号整型 |
f | 浮点型 |
c | 复数浮点型 |
S、a | 字符串 |
U | Unicode编码字符串 |
V | 原生数据raw |
1.9.2 更高级的复合类型¶
有时使用更高级的矩阵代替多维数组或Python字典,因为NumPy的dtype直接映射到C结构的定义,因此数组内容能够直接在C程序中使用。
# 更高级的复合类型
t = np.dtype([('id', 'i8'), ('mat', 'f8', (3, 3))])
x = np.zeros(4, dtype=t)
x[0]
x['mat']
x['mat'][0]
(0, [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
array([[[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]])
array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
1.9.3 记录数组:结构化数组的扭转¶
NumPy提供了np.recarray
类,其具有独特的性质为:域可以像属性一样获取,而不是像字典的键那样获取。记录数组的缺点在于会造成额外的开销。
data = np.zeros(4, dtype={
'names': ('name', 'age', 'weight'),
'formats': ('U10', 'i4', 'f8')
})
data['age']
# 记录数组
data_rec=data.view(np.recarray)
data_rec.age
array([0, 0, 0, 0])
array([0, 0, 0, 0])