Numpy
文章主要通过案例来理解广播机制、结合相应函数(rollaxis、swapaxes)来理解Axis、总结了相关函数 axis=1的操作规律。
文章参考博客如下:
-
大致参考:Python之Numpy详细教程。
-
线性代数部分:Numpy重要模块------linalg线性代数详细参数及演示。
-
常用函数:Numpy中常用函数。
-
随机数模块:使用Numpy中的random模块中常用函数的使用方法。
文章结构参考网站:NumPy教程(快速入门版)。
1. 简介
NumPy(Numerical Python 的缩写)是一个由多维数组对象和处理这些数组的函数集合组成的库。使用 NumPy 库,可以对数组执行数学运算和相关逻辑运算。NumPy 的底层主要用 C语言编写,因此它能够高速地执行数值计算。NumPy 还提供了多种数据结构,这些数据结构能够非常契合的应用在数组和矩阵的运算上。
1.1 使用需求
随着数据科学(Data Science,简称 DS,包括大数据分析与处理、大数据存储、数据抓取等分支)的蓬勃发展,像 NumPy、SciPy(Python科学计算库)、Pandas(基于NumPy的数据处理库) 等数据分析库都有了大量的增长,它们都具有较简单的语法格式。
在矩阵乘法与数组形状处理上,NumPy 有着非常不错的性能,再加上 NumPy 的计算速度很快,这些都是 NumPy 成为一款数据分析工具的重要原因。
1.2 应用场景
NumPy 通常与 SciPy 和 Matplotlib 等软件包组合使用,这种组合方式被用来广泛代替 MatLab 的使用。
MatLab 是一款强大的数学计算软件,广泛应用在数据分析、电子通信、深度学习、图像处理、机器视觉、量化金融等领域,但近些年随着 Python 语言的迅猛发展,Python 被看作是一种更适合代替 MatLab 的编程语言。您可以使用 NumPy、SciPy 与 Matplotlib 等 Python 工具包搭建科学计算环境,比如 Anaconda 就是一个开源的 Python 发行版本,它包含了 Python 、NumPy 等很多科学包及其依赖项。
2. ndarray对象
NumPy 定义了一个 n 维数组对象,简称 ndarray 对象。它是一个一系列相同类型元素组成的数组集合。ndarray 对象采用了数组的索引机制,将数组中的每个元素映射到大小相同的内存块上,并且按照一定的布局对内存块进行排列,常用的布局方式有两种,即按行(C)或按列(F)。
2.1 创建 ndarray
通过 NumPy 的内置函数 array() 可以创建 ndarray 对象。其语法格式如下:
python
np.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)
参数 | 参数说明 |
---|---|
object | 表示一个数组序列 |
dtype | 数组所需数据类型,可选 |
copy | 默认为true ,对象能否被复制,可选 |
order | C (按行)、F (按列)或A (任意,默认) |
subok | 默认情况下,返回的数组被强制为基类数组。 如果为true ,则返回子类 |
ndmin | 用于指定数组的维度 |
python
import numpy as np
# 使用列表构建数组
# 创建一维数组
a = np.array([1, 2, 3, 4])
print(a, " 类型为: ", type(a))
"""
输出结果:[1 2 3 4] 类型为: <class 'numpy.ndarray'>
"""
# 创建多维数组
b = np.array([[1, 2, 3], [4, 5, 6]])
-
ndim查看数组维数:
pythonarr = np.array([[1, 2, 3, 4], [4, 5, 6, 7], [9, 10, 11, 23]]) print(arr.ndim) # 维度为 2 # 创建时指定数组维度 a = np.array([1, 2, 3, 4, 5], ndmin=2) # [[1 2 3 4 5]]
2.2 ndarray.reshape()
Numpy 提供 reshape() 函数来对数组形状进行重塑。只要 reshape 所需的元素在两种形状中均相等则可重塑成任意形状。
-
元组传参,指定行数和列数:
pythone = np.array([[1, 2], [3, 4], [5, 6]]) e = e.reshape((2, 3)) # (3,2)--->(2,3)
-
从 1-D 到 3-D:
pythonarr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) newarr = arr.reshape(2,3,2) print(newarr) """ 最外层维度具有2个数组,其中包含3个小数组,每个小数组又包含2个元素 [ [ [ 1 2] [ 3 4] [ 5 6] ] [ [ 7 8] [ 9 10] [11 12] ] ] """
-
将多维数组展平成一维数组:
pythonarr = newarr.reshape(-1)
2.3 ndarray.shape
shape 属性的返回值是一个由数组维度构成的元组,比如 2 行 3 列的二维数组可以表示为(2,3)
,该属性也可用来调整数组维度的大小。
python
a = np.array([[2, 4, 6], [3, 5, 7]])
print(a.shape) # (2,3)
# 通过 shape 属性修改数组的形状大小
a.shape = (3,2) # 等价与 a = a.reshape(3,2)
2.4 ndarray.itemsize
返回数组中每个元素的大小(以字节 B 为单位) 。ndarray.size 则可查询数组元素个数。
python
# 数据类型为int8,代表1字节
x = np.array([1, 2, 3, 4, 5], dtype=np.int8)
print(x.itemsize) # 1B
2.5 ndarray.flags
返回 ndarray 数组的内存信息,比如 ndarray 数组的存储方式,以及是否是其他数组的副本等。
python
x = np.array([1, 2, 3, 4, 5])
print(x.flags)
# 判断是否共享内存
x.flags['OWNDATA'] # True:不共享
3. 数据类型
3.1 数据类型
数据类型 | 类型说明 | 唯一标识符 |
---|---|---|
bool | 用一个字节存储的布尔类型(True或False) | b |
int8 | 一个字节大小,-128 至 127 | i1 |
int16 | 整数,16 位整数(-32768 ~ 32767) | i2 |
int32 | 整数,32 位整数(-2147483648 ~ 2147483647) | i4 |
int64 | 整数,64 位整数(-9223372036854775808 ~ 9223372036854775807) | i8 |
uint8 | 无符号整数,0 至 255 | u1 |
uint16 | 无符号整数,0 至 65535 | u2 |
uint32 | 无符号整数,0 至 2^32^ - 1 | u4 |
uint64 | 无符号整数,0 至 2^64^ - 1 | u8 |
float16 | 半精度浮点数:16位,正负号1位,指数5位,精度10位 | f2 |
float32 | 单精度浮点数:32位,正负号1位,指数8位,精度23位 | f4 |
float64 | 单精度浮点数:64位,正负号1位,指数11位,精度52位 | f8 |
complex64 | 复数,分别用两个32位浮点数表示实部和虚部 | c8 |
complex128 | 复数,分别用两个64位浮点数表示实部和虚部 | c16 |
object_ | python 对象 | O |
string_ | 字符串 | S |
unicode_ | unicode类型 | U |
int_ | 默认整数类型,类似于 C 语言中的 long,取值为 int32 或 int64 | i4/i8 |
intc | 和 C 语言的 int 类型一样,一般是 int32 或 int 64 | i4/i8 |
intp | 用于索引的整数类型(类似于 C 的 ssize_t,通常为 int32 或 int64) | i4/i8 |
3.2 数据类型对象
数据类型对象(Data Type Object)又称 dtype 对象,主要用来描述数组元素的数据类型、大小及字节顺序。同时,也可用来创建结构化数据。其语法格式如下:
python
np.dtype(object)
创建一个 dtype 对象可以使用下列方法:
python
a= np.dtype(np.int64)
print(a) # 输出结果: int64
创建数组时指定数据类型:
python
# 简单定义数据类型
a = np.array([1, 2, 3, 4, 5], dtype='i1')
a = np.array([1, 2, 3, 4, 5], dtype=int32)
# 定义字段名score,以及数组数据类型 i1
dt = np.dtype([('score', 'i1')])
a = np.array([(55,), (75,), (85,)], dtype=dt)
print(a)
print(a.dtype)
print(a['score'])
"""
[(55,) (75,) (85,)]
[('score', 'i1')]
[55 75 85]
"""
结构化数据:
结构化数据使用字段的形式来描述某个对象的特征。以下示例描述一位老师的 姓名、年龄、工资的特征。该结构化数据包含以下字段:
- str 字段:name
- int 字段:age
- float 字段:salary
python
teacher = np.dtype([('name', 'S20'), ('age', 'i1'), ('salary', 'f4')])
# 输出结构化数据 teacher
print(teacher)
# 将其应用于 ndarray 对象
b = np.array([('zhangsan', 32, 5070.50), ('lisi', 28, 8560.0)], dtype=teacher)
print(b)
"""
[('name', 'S20'), ('age', 'i1'), ('salary', '<f4')]
[(b'ycs', 32, 6357.5) (b'jxe', 28, 6856.8)]
"""
3.3 查询数据类型
python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
d = np.array([Person('test1', 18), Person('test2', 20)])
print(d.dtype) # object
print(d[0].name) # test1
3.4 修改数据类型
python
f = a.astype('f2')
4. 数组操作
4.1 创建数组
4.1.1 np.empty()
该函数用来创建未初始化的数组。其语法格式如下:
python
np.empty(shape, dtype = float, order = 'C')
参数 | 参数说明 |
---|---|
shape | 指定数组形状 |
dtype | 数组元素的数据类型,默认值是 float,可选项 |
order | 指数组元素在计算机内存中的储存顺序,默认顺序是"C"(按行优先) |
python
arr_empty = np.empty((3, 2), dtype=int)
print(arr_empty)
"""
[[166998032 613]
[ 0 0]
[ 1 0]]
"""
np.empty() 返回的数组带有随机值,但这些数值并没有实际意义。
切记 :empty 并非创建空数组。
4.1.2 np.zeros()
该函数用来创建元素均为 0 的数组,可指定数组形状。其语法格式如下:
python
np.zeros(shape, dtype = float, order = 'C')
参数 | 参数说明 |
---|---|
shape | 指定数组形状 |
dtype | 数组元素的数据类型,默认值是 float,可选项 |
order | 指数组元素在计算机内存中的储存顺序,默认顺序是"C"(按行优先) |
python
arr_zeros = np.zeros((2, 3), dtype=[('x', 'i4'), ('y', 'i4')])
print(arr_zeros)
"""
[[(0, 0) (0, 0) (0, 0)]
[(0, 0) (0, 0) (0, 0)]]
"""
print(arr_zeros.itemsize)
# 8B = 4B + 4B
print(arr_zeros['x'])
"""
[[0 0 0]
[0 0 0]]
"""
4.1.3 np.ones()
该函数返回指定形状大小与数据类型的新数组,并且新数组中每项元素均用 1 填充。其语法格式如下:
python
numpy.ones(shape, dtype=None, order='C')
python
arr_ones = np.ones((2, 3), dtype=int)
print(arr_ones)
4.1.4 np.asarray()
该函数类似于np.array
,对于将 Python 序列转换为ndarray
非常有用。其语法格式如下:
python
np.asarray(arr, dtype = None, order = None)
参数 | 参数说明 |
---|---|
arr | 任意形式的输入参数,比如列表、列表的元组、元组、元组的元组、元组的列表 |
dtype | 输入数据的类型会应用到返回的ndarray |
order | 'C' 为按行的 C 风格数组,'F' 为按列的 Fortran 风格数组 |
python
x = (1,2,3)
a = np.asarray(x) # [1 2 3]
4.1.5 np.frombuffer()
该函数将缓冲区解释为一维数组。其语法格式如下:
python
np.frombuffer(buffer, dtype = float, count = -1, offset = 0)
参数 | 参数说明 |
---|---|
buffer | 将任意对象转换为流的形式读入缓冲区 |
dtype | 返回数组的数据类型,默认为float |
count | 需要读取的数据数量,默认为-1 ,读取所有数据 |
offset | 需要读取的起始位置,默认为0 |
python
s = 'Hello World'
a = np.frombuffer(s, dtype = 'S1')
# ['H' 'e' 'l' 'l' 'o' ' ' 'W' 'o' 'r' 'l' 'd']
4.1.6 np.fromiter()
该函数从任何可迭代对象构建一个ndarray
对象,返回一个新的一维数组。其语法格式如下:
python
np.fromiter(iterable, dtype, count = -1)
参数 | 参数说明 |
---|---|
iterable | 任何可迭代对象 |
dtype | 返回数组的数据类型 |
count | 需要读取的数据数量,默认为-1 ,读取所有数据 |
python
list = range(5)
it = iter(list)
# 使用迭代器创建 ndarray
x = np.fromiter(it, dtype = float)
# [0. 1. 2. 3. 4.]
4.1.7 np.arange()
该函数返回ndarray
对象,包含给定范围内的等间隔值。其语法格式如下:
python
np.arange(start, stop, step, dtype)
参数 | 参数说明 |
---|---|
start | 范围的起始值,默认为0 |
stop | 范围的终止值(不包含) |
step | 两个值的间隔,默认为1 |
dtype | 返回ndarray 的数据类型,如果没有提供,则会使用输入数据的类型 |
python
# 创建 0-10 步数为 2 的数组 结果为 [0,2,4,6,8]
b = np.arange(0,10,2)
4.1.8 np.linspace()
该函数类似于arange()
函数。 在此函数中,指定了范围之间的均匀间隔数量,而不是步长。其语法格式如下:
python
np.linspace(start, stop, num, endpoint, retstep, dtype)
参数 | 参数说明 |
---|---|
start | 序列的起始值 |
stop | 序列的终止值,如果endpoint 为true ,该值包含于序列中 |
num | 要生成的等间隔样例数量,默认为50 |
endpoint | 序列中是否包含stop 值,默认为ture |
retstep | 如果为true ,返回样例,以及连续数字之间的步长 |
dtype | 输出数组的数据类型,如果没有提供,则取决于其它参数 |
python
x = np.linspace(10, 20, 5, endpoint = False)
# [10. 12. 14. 16. 18.]
x = np.linspace(1,2,5, retstep = True)
# (array([ 1. , 1.25, 1.5 , 1.75, 2. ]), 0.25)
4.1.9 np.logspace()
该函数返回一个ndarray
对象,其中包含在对数刻度上均匀分布的数字。 刻度的开始和结束端点是某个底数的幂,通常为 10。其语法格式如下:
python
np.logspace(start, stop, num, endpoint, base, dtype)
参数 | 参数说明 |
---|---|
start | 起始值是base ** start |
stop | 终止值是base ** stop |
num | 范围内的数值数量,默认为50 |
endpoint | 如果为true ,终止值包含在输出数组当中 |
base | 对数空间的底数,默认为10 |
dtype | 输出数组的数据类型,如果没有提供,则取决于其它参数 |
python
a = np.logspace(1, 10, num = 10, base = 2)
# [2. 4. 8. 16. 32. 64. 128. 256. 512. 1024.]
4.1.10 补充
python
# full : 全部为指定值的 N 行 N 列数组
arr_full = np.full((2,3), 9)
# eye : 生成一个在主对角线上元素均为 1, 其他元素都为 0 的 N 行 N 列矩阵
arr_eye = np.eye(4)
4.2 遍历数组
NumPy 提供了一个 nditer 迭代器对象,它可以配合 for 循环完成对数组元素的遍历。
python
a = np.arange(0, 60, 5).reshape((3, 4))
# 使用 nditer 迭代器, 并使用 for 进行遍历
for x in np.nditer(a):
print(x, end=' ')
4.2.1 遍历顺序
在内存中,Numpy 数组提供了两种存储数据的方式,分别是 C-order(行优先顺序)与 Fortrant-order(列优先顺序)。那么 nditer 迭代器又是如何处理具有特定存储顺序的数组呢?其实它选择了一种与数组内存布局一致的顺序,之所以这样做,是为了提升数据的访问效率。
在默认情况下,当遍历数组中元素的时候,不需要考虑数组的存储顺序,这一点可以通过遍历数组的转置数组来验证。
python
a = np.arange(0, 60, 5).reshape((3, 4))
# 使用 nditer 迭代器,并使用 for 进行遍历
for x in np.nditer(a):
print(x, end=',')
b = a.T # a的转置数组
for x in np.nditer(b):
print(x, end=",")
a 和 a.T 的遍历顺序是一样的,也就是说,它们在内存中的存储顺序是一样的。
下面以 'C' 样式访问转置数组的副本。示例如下:
python
a = np.arange(0, 60, 5).reshape((3, 4))
# 使用 nditer 迭代器, 并使用 for 进行遍历
for x in np.nditer(a):
print(x, end=', ')
print('\n')
b = a.T # a 的转置数组
for x in np.nditer(b):
print(x, end=", ")
print('\n')
# copy 方法生成数组副本
for x in np.nditer(a.T.copy(order='C')): # 默认是'F'
print (x, end=", " )
4.2.2 指定遍历顺序
通过 nditer 对象的order
参数来指定数组的遍历顺序。示例如下:
python
a = np.arange(0, 60, 5).reshape((3, 4))
for x in np.nditer(a, order='C'):
print(x, end=", ")
print('\n')
for x in np.nditer(a, order='F'):
print(x, end=", ")
4.2.3 外部循环使用
nditer 对象的构造函数有一个"flags"参数,它可以接受以下参数值:
参数 | 参数说明 |
---|---|
c_index | 可以跟踪 C 顺序的索引。 |
f_index | 可以跟踪 Fortran 顺序的索引。 |
multi_index | 每次迭代都会跟踪一种索引类型。 |
external_loop | 返回的遍历结果是具有多个值的一维数组。 |
python
a = np.arange(0,60,5).reshape(3,4)
print("原数组",a)
# 修改后数组
for x in np.nditer(a, flags = ['external_loop'], order = 'F'):
print(x)
"""
原数组:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
修改后的一维数组
[ 0 20 40]
[ 5 25 45]
[10 30 50]
[15 35 55]
"""
4.2.4 迭代多个数组
如果两个数组都能被广播,那么 nditer 对象就可以同时对它们迭代。
假设数组 a 的维度是 3 * 4,另一个数组 b 的维度是 1*4 (即维度较小的数组 b 可被广播到数组 a 中),示例如下:
python
a = np.arange(0, 60, 5).reshape(3, 4)
print(a)
b = np.array([1, 2, 3, 4], dtype=int)
print(b)
# 广播迭代
for x, y in np.nditer([a, b]):
print("%d:%d" % (x, y), end=", ")
"""
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
[1 2 3 4]
0:1, 5:2, 10:3, 15:4, 20:1, 25:2, 30:3, 35:4, 40:1, 45:2, 50:3, 55:4,
"""
4.3 修改数组元素
4.3.1 nditer的op_flags
nditer 对象提供了一个可选参数op_flags
,它表示能否在遍历数组时对元素值进行修改。它提供了三种模式,如下所示:
-
read-only:只读模式,在这种模式下,遍历时不能修改数组中的元素。
-
read-write:读写模式,遍历时可以修改元素值。
-
write-only:只写模式,在遍历时可以修改元素值。
python
a = np.arange(0, 60, 5).reshape(3, 4)
print(a)
for x in np.nditer(a, op_flags=['readwrite']):
x[...] = 2 * x
print('修改后的数组是:\n', a)
"""
注意:切片还可以使用省略号"...", 如果在行位置使用省略号, 那么返回值将包含所有行元素, 反之,则包含所有列元素
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
修改后的数组是:
[[ 0 10 20 30]
[ 40 50 60 70]
[ 80 90 100 110]]
"""
4.3.2 索引替换
- numpy.argwhere():该函数返回数组中非 0 元素的索引,若是多维数组则返回行、列索引组成的索引坐标。
python
# 利用索引可以做值的替换,把满足条件位置的值替换成其他值
# 创建数组元素值为 [0,10) 随机数的 3 行 5 列数组
a3 = np.random.randint(0,10,size=(3,5))
# 将 a3 数组第一行数据全部更换为 0
a3[1] = 0
# 将 a3 数组第一行数据更换为 [1,2,3,4,5] -- 数据个数要对应
a3[1] = np.array([1,2,3,4,5])
x = np.arange(1, 7).reshape(2, 3)
# 返回所有大于4的元素索引
y = np.argwhere(x > 4)
# 将 x 中元素值大于 4 的统一设定为 4
x[y[:, 0], y[:, 1]] = 4
4.3.3 条件索引替换
python
# 数组中值小于 3 的元素全部替换为 1
a3[a3 < 3] = 1
4.3.4 函数替换
python
# 将 a3 数组中小于 5 的值替换为 0,剩余值替换为 1
result = np.where(a3 < 5, 0, 1)
4.4 修改数组形状
4.4.1 数组变维
函数名称 | 函数说明 |
---|---|
reshape | 将原数组转换成指定的形状,然后返回转换后的结果,原数组形状不被修改 |
resize | 将原数组转换成指定的形状,会直接修改原数组本身,且不会返回任何值 |
flat | 返回一个迭代器,可以用 for 循环遍历其中的每一个元素 |
flatten | 以一维数组的形式返回一份数组的副本,对副本的操作不会影响到原数组 |
ravel | 展平,但与 flatten不同,它返回的是数组视图(修改视图会影响原数组) |
reshape与resize
python
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
# 验证: reshape 是否改变原数组形状(不改变)
arr1 = arr.reshape((2, 3, 2))
print(arr1) # (2, 3, 2)
arr2 = arr.reshape((3, 4))
print(arr2) # (3, 4)
print(arr.shape) #(12,)
# 验证: 能否通过修改 reshape 后数组元素值修改原数组元素值(得分类)
arr2[0, 0] = 100
print(arr2, '\n', arr) # arr[0] = 100 了
# 验证: resize 是否改变原数组形状(改变)
arr1 = arr.resize((3, 4))
print(arr1) # None
print(arr) # arr形状直接被修改了
"""
注意: reshape 返回值可能是一个view,或是一个copy。
相应的条件为:
1.返回一个view条件:数据区域连续
2.反之,则返回一个copy
"""
np.ndarray.flat
返回一个数组迭代器,示例如下:
python
arr = np.arange(1, 16).reshape((3, 5))
for row in arr:
print(row, end=", ")
print('\n')
for ele in arr.flat:
print(ele, end=", ")
"""
[1 2 3 4 5], [ 6 7 8 9 10], [11 12 13 14 15],
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
"""
np.ndarray.flatten()
该函数表示返回一份数组副本,对副本修改不会影响原始数组。其语法格式如下:
python
ndarray.flatten(order='C')
python
arr = np.arange(1,16).reshape((3,5))
print(arr.reshape(-1)) # 默认 C 顺序查看数组
arr_flatten = arr.flatten(order='F')
print(arr_flatten) # F顺序查看数组
"""
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
[ 1 6 11 2 7 12 3 8 13 4 9 14 5 10 15]
"""
arr_flatten[0] = 100
print(arr_flatten) # arr_flatten[0]=100
print(arr) # arr[0] = 1 (不变) --- 深拷贝copy
np.ravel()
该函数表示将多维数组中的元素以一维数组的形式展开,该方法返回数组的视图(view),如果修改,则会影响原始数组。其语法格式如下:
python
numpy.ravel(a, order='C')
示例如下:
python
a = np.arange(8).reshape(2, 4)
print('原数组:\n', a)
print('调用 ravel 函数后:\n', a.ravel())
print('F 风格顺序调用 ravel 函数之后:\n', a.ravel(order='F'))
a.ravel()[0] = 10
"""
b = a.ravel() # 浅拷贝 view
b[0] = 10
"""
print(a)
输出结果如下:
python
原数组:
[[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]
[[10 1 2 3]
[ 4 5 6 7]]
4.4.2 数组转置
函数名称 | 函数说明 |
---|---|
transpose | 将数组的维度值进行对换,如二维数组维度(2,4),使用该方法后为(4,2) |
ndarray.T | 与 transpose 方法相同 |
np.transpose()
该函数用于对换多维数组的维度,如二维数组使用此方法可以实现矩阵转置。其语法格式如下:
python
numpy.transpose(arr, axes)
参数 | 参数说明 |
---|---|
arr | 要操作的数组 |
axes | 可选参数,元组或者整数列表,将会按照该参数进行转置 |
python
arr = np.arange(1, 16).reshape((3, 5))
arr_transpose = arr.transpose()
arr_transpose = np.transpose(arr_transpose)
4.4.3 连接与分割
类型 | 函数名称 | 函数说明 |
---|---|---|
连接数组方法 | concatenate | 沿指定轴连接两个或者多个相同形状的数组 |
stack | 沿着新的轴连接一系列数组 | |
hstack | 沿水平方向堆叠 | |
vstack | 沿垂直方向堆叠 | |
分割数组方法 | split | 将一个数组分割为多个子数组 |
hsplit | 将一个数组水平分割为多个子数组 | |
vsplit | 将一个数组垂直分割为多个子数组 |
np.concatenate()
该函数表示沿指定轴连接相同形状的两个或多个数组。其语法格式如下:
python
numpy.concatenate((a1, a2, ...), axis)
参数 | 参数说明 |
---|---|
a1, a2, ... | 表示一系列相同类型的数组 |
axis | 沿着该参数指定的轴连接数组,默认为 0 |
python
arr1 = np.arange(1, 7).reshape((2, 3))
arr2 = np.arange(11, 17).reshape((2, 3))
# 沿水平方向堆叠
arr = np.concatenate((arr1, arr2), axis=1)
print(arr)
arr = np.hstack([arr1, arr2])
print(arr)
# 沿垂直方向堆叠
arr = np.concatenate((arr1, arr2), axis=0)
print(arr)
arr = np.vstack([arr1, arr2])
print(arr)
"""
0---按列---列数不变---沿垂直方向---vstack
1---按行---行数不变---沿水平方向---hstack
"""
数组连接操作至少需要两个维度相同的数组,才允许对它们进行垂直或者水平方向上的操作。
np.split()
该函数表示沿指定的轴将数组分割为多个子数组。其语法格式如下:
python
numpy.split(arr, indices_or_sections, axis)
参数 | 参数说明 |
---|---|
arr | 被分割的数组 |
axis | 默认为0,表示按列切分(同列数);为1时表示按行切分(同行数) |
indices_or_sections | 若是一个整数,代表用该整数平均切分,若是一个数组,则代表沿轴切分的位置(左开右闭) |
python
arr = np.arange(1, 19).reshape((3, 6))
print(arr)
# 水平方向平均分为 2 份
arr1 = np.hsplit(arr, 2)
# 垂直方向分为 2,1 列(在下标为 2 处切割)
arr2 = np.vsplit(arr, (2,))
# 按列平均切割
arr3 = np.split(arr, 3, axis=1)
# 按行平均切割
arr4 = np.split(arr, 3, axis=0)
print(arr1, '\n', arr2, '\n', arr3, '\n', arr4)
输出结果如下:
python
# 原数组
[[ 1 2 3 4 5 6]
[ 7 8 9 10 11 12]
[13 14 15 16 17 18]]
# 水平方向平均分为 2 份
[array([[ 1, 2, 3],
[ 7, 8, 9],
[13, 14, 15]]), array([[ 4, 5, 6],
[10, 11, 12],
[16, 17, 18]])]
# 垂直方向分为 2,1 列(在下标为 2 处切割)
[array([[ 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12]]), array([[13, 14, 15, 16, 17, 18]])]
# 按列平均切割
[array([[ 1, 2],
[ 7, 8],
[13, 14]]), array([[ 3, 4],
[ 9, 10],
[15, 16]]), array([[ 5, 6],
[11, 12],
[17, 18]])]
# 按行平均切割
[array([[1, 2, 3, 4, 5, 6]]), array([[ 7, 8, 9, 10, 11, 12]]), array([[13, 14, 15, 16, 17, 18]])]
小结
python
数组的连接:
0---按列---列数不变---沿垂直方向---vstack
1---按行---行数不变---沿水平方向---hstack
数组的切割:
0---按列---行数不变---沿垂直方向---vstack
1---按行---列数不变---沿水平方向---hstack
4.5 增删数组元素
函数名称 | 描述说明 |
---|---|
append | 将元素值添加到数组的末尾 |
insert | 沿规定的轴将元素值插入到指定的元素前 |
delete | 删掉某个轴上的子数组,并返回删除后的新数组 |
unique | 用于删除数组中重复的元素,并按元素值由大到小返回一个新数组 |
4.5.1 np.append()
该函数表示在数组的末尾添加值,它返回一个一维数组。其语法格式如下:
python
numpy.append(arr, values, axis=None)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
axis | 默认为 None,返回一维数组;当 axis =0 时,追加的值会被添加到行(按列添加,列数不变),若 axis=1 则恰好相反(按行添加,行数不变) |
values | 向 arr 数组中添加的值,需要和 arr 数组的形状保持一致 |
python
a = np.array([[1, 2, 3], [4, 5, 6]])
# 向数组a添加元素
print(np.append(a, [7, 8, 9]))
# 按列添加元素---列数不变
print(np.append(a, [[7, 8, 9]], axis=0))
# 按行添加元素---行数不变
print(np.append(a, [[5, 5, 5], [7, 8, 9]], axis=1))
输出结果如下:
python
# 向数组a添加元素
[1 2 3 4 5 6 7 8 9]
# 沿轴 0 添加元素
[[1 2 3]
[4 5 6]
[7 8 9]]
# 沿轴 1 添加元素
[[1 2 3 5 5 5]
[4 5 6 7 8 9]]
4.5.2 np.insert()
该函数表示沿指定的轴,在给定索引值的前一个位置插入相应的值,如果没有提供轴,则输入数组被展开为一维数组。其语法格式如下:
python
numpy.insert(arr, obj, values, axis)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
obj | 表示索引值,在该索引值之前插入 values 值 |
axis | 指定的轴,如果未提供,则输入数组会被展开为一维数组 |
values | 要插入的值 |
python
a = np.array([[1, 2], [3, 4], [5, 6]])
# 不提供axis的情况,会将数组展开
print(np.insert(a, 3, [11, 12]))
# 按列插入元素---垂直方向---列数不变
print(np.insert(a, 1, 11, axis=0))
# 按行插入元素---水平方向---行数不变
print(np.insert(a, 1, 11, axis=1))
输出结果如下:
python
# 不提供 axis 参数
[ 1 2 3 11 12 4 5 6]
# 沿轴 0
[[ 1 2]
[11 11]
[ 3 4]
[ 5 6]]
# 沿轴 1
[[ 1 11 2]
[ 3 11 4]
[ 5 11 6]]
4.5.3 np.delete()
该函数表示从输入数组中删除指定的子数组,并返回一个新数组。它与 insert() 函数相似,若不提供 axis 参数,则输入数组被展开为一维数组。其语法格式如下:
python
numpy.delete(arr, obj, axis)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
obj | 整数或整数数组,表示要被删除数组元素或子数组 |
axis | axis=0 表示按行删除,其他的大部分函数都是 axis=1 来表示按行 |
python
a = np.arange(12).reshape(3, 4)
print(a)
# 不提供axis参数情况
print(np.delete(a, 5))
# 删除第二列:axis=1 可简记沿水平方向删除
print(np.delete(a, 1, axis=1))
# 删除经切片后的数组
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(np.delete(a, np.s_[::2]))
输出结果如下:
python
# a数组
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
# 无 axis 参数
[ 0 1 2 3 4 6 7 8 9 10 11]
# 删除第二列
[[ 0 2 3]
[ 4 6 7]
[ 8 10 11]]
# 删除经过切片的数组
[ 2 4 6 8 10]
4.5.4 np.unique()
该函数用于删除数组中重复的元素。其语法格式如下:
python
numpy.unique(arr, return_index, return_inverse, return_counts)
参数 | 参数说明 |
---|---|
arr | 输入的数组,若是多维数组则以一维数组形式展开 |
return_index | 如果为 True,则返回新数组元素在原数组中的位置(索引) |
return_inverse | 如果为 True,则返回原数组元素在新数组中的位置(索引) |
return_counts | 如果为 True,则返回去重后的数组元素在原数组中出现的次数 |
python
a = np.array([5, 2, 6, 2, 7, 5, 6, 8, 2, 9])
print(a)
# 对a数组的去重
uq = np.unique(a)
print(uq)
u, indices = np.unique(a, return_index=True)
# 新数组元素在原数组中的位置
print(indices)
ui, indices = np.unique(a, return_inverse=True)
# 原数组元素在新数组中的位置
print(indices)
# 返回去重元素的重复数量
uc, indices = np.unique(a, return_counts=True)
# 元素出现次数
print(indices)
输出结果如下:
python
# a数组
[5 2 6 2 7 5 6 8 2 9]
# 去重后的a数组
[2 5 6 7 8 9]
# 新数组元素在原数组中的位置
[1 0 2 4 7 9]
# 原数组在新数组中的下标
[1 0 2 0 3 1 2 4 0 5]
# 统计重复元素出现次数
[3 2 2 1 1 1]
5. 索引和切片
在 NumPy 中,如果想要访问或修改数组中的元素,可以采用索引或切片的方式,比如使用从 0 开始的索引依次访问数组中的元素,这与 Python 的 list 列表是相同的。
NumPy 提供了多种类型的索引方式,常用方式有两种:基本切片与高级索引。
5.1 基本切片
NumPy 内置函数 slice() 可以用来构造切片对象,该函数需要传递三个参数值,分别是 start(起始索引)、stop(终止索引) 和 step(步长) ,通过它可以实现从原数组上切割出一个新数组。
python
a = np.arange(10)
# 生成切片对象
s = slice(2, 9, 3) # 从 索引2 开始到 索引9 停止,间隔步长为 2
# 等价于: b = a[2:9:2]
print(a[s])
"""
[2 5 8]
"""
一维数组
python
# 1. 一维数组的索引和切片
a1 = np.arange(10)
# 1.1 进行索引操作
print(a1[4]) # 结果为:4
# 1.2 进行切片操作
print(a1[4:6]) # 结果为:[4 5]
# 1.3 使用步长
print(a1[::2]) # 结果为:[0 2 4 6 8]
# 1.4 使用负数作为索引
print(a1[-1]) # 结果为:9
二维数组
python
# 2. 多维数组
# 通过中括号来索引和切片,在中括号中使用逗号进行分割
# 逗号前面的是行,逗号后面的是列,如果多维数组中只有一个值,那么这个值就是行
a2 = np.random.randint(0,10,size=(4,6))
# 获取第 0 行数据
print(a2[0])
# 获取第 1,2 行数据
print(a2[1:3])
# 获取多行数据 例 0,2,3 行数据
print(a2[[0,2,3]])
# 获取第二行第一列数据
print(a2[2,1])
# 获取多个数据 例:第一行第四列、第二行第五列数据
print(a2[[1,2],[4,5]])
#获取多个数据 例:第一、二行的第四、五列的数据
print(a2[1:3,4:6])
#获取某一列数据 例:第一列的全部数据
print(a2[:,1])
#获取多列数据 例:第一、三列的全部数据
print(a2[:,[1,3]])
5.2 高级索引
布尔数组索引
当输出的结果需要经过布尔运算(如比较运算)时,此时会使用到另一种高级索引方式,即布尔数组索引。
python
# 返回所有大于 6 的数字组成的数组
x = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]])
x[x > 6] # [ 7 8 9 10 11]
# 使用补码运算符去除 NaN(即非数字元素)
a1 = np.array([np.nan, 1, 2, np.nan, 3, 4, 5])
a1[~np.isnan(a1)]
# 删除数组中整数元素
a2 = np.array([1, 2 + 6j, 5, 3.5 + 5j])
a2[np.iscomplex(a2)]
# 生成 0~23 的 4 行 6 列的二维数组
a3 = np.arange(24).reshape((4, 6))
a3[(a3 < 5) | (a3 > 10)]
-
小结:
(1) 布尔索引是通过相同数据上的 True 还是 False 来进行提取的。
(2) 提取条件可以为一个或多个,当提取条件为多个时使用 & 代表且,使用 | 代表或
(3) 当提取条件为多个时,每个条件要使用圆括号括起来
花式索引
花式索引也可以理解为整数数组索引,但是它们之间又略有不同,下面通过示例做简单讲解。花式索引也会生成一个新的副本。
当原数组是一维数组时,使用一维整型数组作为索引,索引结果就是相应索引位置上的元素。
python
x = np.array([1, 2, 3, 4])
print(x[0]) # 1
如果原数组是二维数组,那么索引数组也需要是二维的,索引数组的元素值与被索引数组的每一行相对应,示例如下:
python
x = np.arange(32).reshape((8, 4))
# 分别对应 第4行数据、第2行数据、第1行数据、第7行数据项
print(x[[4, 2, 1, 7]])
还可以同时使用多个索引数组,但这种情况下需要添加np.ix_
。
python
x = np.arange(32).reshape((8, 4))
print(x[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])])
其中 [1,5,7,2] 代表行索引,而 [0,3,1,2] 表示与行索引相对应的列索引值,也就是行中的元素值会按照列索引值排序。比如,第一行元素,未排序前的顺序是 [4,5,6,7],经过列索引排序后变成了 [4,7,5,6]。
6. 广播机制
NumPy 中的广播机制(Broadcast)旨在解决不同形状数组之间的算术运算问题。当两个数组的维数不相同时,元素到元素的操作是不可以的。 然而,在 NumPy 中仍然可以对形状不相似的数组进行操作,因为它拥有广播功能。 较小的数组会广播到较大数组的大小,以便使它们的形状可兼容。
广播机制的核心就是对形状较小的数组,在横向或纵向上进行一定次数的重复,使其与形状较大的数组拥有相同的维度。
python
a1 = np.arange(1, 9).reshape((2, 4))
# 数组形状一致时 各个元素相加减(满足数组广播机制)
a2 = np.arange(11, 19).reshape((2, 4))
print("", a1, "\n\n", a2, "\n\n", a1 + a2)
# 形状不一致的数组不能相加减(不满足数组广播机制)
a3 = np.random.randint(0, 5, size=(3, 4))
# a1+a3 报错
# 两个数组行数相同,其中一个数组列数为 1 (满足数组广播机制)
np.random.seed(100)
a4 = np.random.randint(2, 18, size=(2, 1))
# 两个数组列数相同,其中一个数组行数为 1 (满足数组广播机制)
np.random.seed(100)
a5 = np.random.randint(2, 12, size=(1, 4))
a6 = a1 + a4
a7 = a1 + a5
print("", a4, "\n\n", a5, "\n\n", a6, "\n\n", a7)
下图 1 :数组 a 、b 的运算展示了广播机制的实现流程。
4x3 的二维数组 a 与 1x3 的一维数组 b 相加,本质上可以理解为 b 数组在纵向上 向下拓展 3 次(将第一行重复 3 次),从而生成与 a 数组相同形状的数组,之后再与 a 数组进行运算。
python
a = np.array([[1], [2], [3]])
b = np.array([4, 5, 6])
print(a.shape, " ", b.shape, "\n\n", a, "\n\n", b, "\n\n", a + b)
"""
(3, 1) (3,)
[[1]
[2]
[3]]
[4 5 6]
[[5 6 7]
[6 7 8]
[7 8 9]]
"""
分析:形状较小的数组(3,)--->(1,3),(1,3)与(3,1)相比,(1,3)的行数1--->3--->(3,3);再与(3,1)相比,(3,1)的列1--->3--->(3,3)。从而形状相同可加减。
广播的规则:
- 让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
- 输出数组的形状是输入数组形状的各个维度上的最大值。
- 如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
- 当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。
简单理解:对两个数组,分别比较他们的每一个维度(若其中一个数组没有当前维度则忽略),满足:
- 数组拥有相同形状。
- 当前维度的值相等。
- 当前维度的值有一个是 1。
若条件不满足,抛出 "ValueError: frames are not aligned" 异常。
6.1 相关方法
函数名称 | 函数说明 |
---|---|
broadcast | 生成一个模拟广播的对象。 |
broadcast_to | 将数组广播为新的形状。 |
6.1.1 np.broadcast()
该函数以两个数组作为输入参数,返回值是数组被广播后的对象,示例如下:
python
a = np.array([[1], [2], [3]])
b = np.array([[4, 5, 6]])
print(a.shape, " ", b.shape)
# 使用broadcast将a与b相加
e = np.broadcast(a, b) # type(e): <class 'numpy.broadcast'>
f = np.empty(e.shape)
f.flat = [x + y for x, y in e]
print(f)
d = np.broadcast(a, b)
for r, c in d:
print(r, " ", c)
# 对b广播a
d = np.broadcast(a, b)
# d 拥有 iterator 属性
r, c = d.iters # type(r): <class 'numpy.flatiter'>
print(type(r),type(c))
for i in r:
print(i,end=" ") # r:[1 1 1 2 2 2 3 3 3]
输出结果如下:
python
(3, 1) (1, 3)
[[5. 6. 7.]
[6. 7. 8.]
[7. 8. 9.]]
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
6.1.2 np.broadcast_to()
该函数将数组广播到新形状中,它在原数组的基础上返回一个只读视图。 如果新形状不符合 NumPy 的广播规则,则会抛出 ValueError 异常。其语法格式如下:
python
numpy.broadcast_to(array, shape, subok)
python
a = np.arange(4).reshape(4, 1)
print("原数组\n", a)
print('调用 broadcast_to 函数之后:')
print(np.broadcast_to(a, (4, 5)))
print(np.broadcast_to(a, (5, 5))) # 报错
输出结果如下:
python
原数组
[[0]
[1]
[2]
[3]]
调用 broadcast_to 函数之后
[[0 0 0 0 0]
[1 1 1 1 1]
[2 2 2 2 2]
[3 3 3 3 3]]
7. Axis 理解
7.1 Axis
简单来说,最外面的括号代表着 axis=0,依次往里的括号对应的 axis 的计数就依次加 1。
操作方式:如果指定轴进行相关的操作,那么它会使用轴下的每一个直接子元素的第0个,第1个,第2个...分别进行相关的操作。
7.1.1 案例分析
- 用 np.max 求 axis=0 和 axis=1 两种情况下的最大值:
python
np.random.seed(100)
x = np.random.randint(1, 10, size=(3,5))
"""
[[9 9 4 8 8]
[1 5 3 6 3]
[3 3 2 1 9]]
"""
x.max(axis=0) # 结果为 [9, 9, 4, 8, 9]
x.max(axis=1) # 结果为 [9, 6, 9]
分析:按照 axis=0 进行求最大值,那么就会在最外层轴里找直接子元素,然后将每个子元素的第 0 个值放在一起求最大值,将第 1 个值放在一起求最大值,以此类推。而如果 axis=1,那么就是拿到每个直接子元素,然后求每个子元素中的最大值。
- 用 np.delete 在 axis=0 和 axis=1 两种情况下删除元素:
python
np.random.seed(100)
x = np.random.randint(1,10,size=(3,5))
# 输出结果为:
#[[9 9 4 8 8]
# [1 5 3 6 3]
# [3 3 2 1 9]]
# 删除第0行
#[[1, 5, 3, 6, 3],
# [3, 3, 2, 1, 9]]
np.delete(x,0,axis=0)
分析:np.delete是个例外,按照 axis=0 的方式进行删除,那么会首先找到最外面的括号下的直接子元素的第0个,然后直接删掉,剩下最后一行的数据。同理,如果我们按照 axis=1 进行删除,那么会把第一列的数据删掉。
7.2 相关函数
函数名称 | 函数说明 |
---|---|
rollaxis | 沿着指定的轴向后滚动至规定的位置 |
swapaxes | 对数组的轴进行对换 |
expand_dims | 扩展数组的形状。 |
squeeze | 从数组的形状中删除一维项。 |
7.2.1 np.rollaxis()
可参考博客:numpy的rollaxis和swapaxes全面解析。
该函数表示将数组 arr 所对应的 axis 轴 放在 start 轴前面,start轴 往后移一"列",其它轴的相对位置不改变。其语法格式如下:
python
numpy.rollaxis(arr, axis, start)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
axis |
要向后滚动的轴,其它轴的相对位置不会改变 |
start |
默认为0,表示完整的滚动。会滚动到特定位置 |
python
a = np.arange(24).reshape(2, 3, 4)
print('原数组:\n', a)
print(np.rollaxis(a, 2).shape) # (4, 2, 3)
数组下标与值对应如下表:
0(000) | 1(001) | 2(002) | 3(003) |
---|---|---|---|
4(010) | 5(011) | 6(012) | 7(013) |
8(020) | 9(021) | 10(022) | 11(023) |
12(100) | 13(101) | 14(102) | 15(103) |
16(110) | 17(111) | 18(112) | 19(113) |
20(120) | 21(121) | 22(122) | 23(123) |
程序运行 np.rollaxis(a, 2) 时,从轴0 开始,将 轴2 滚动到轴0 前面,数组下标排序由 0,1,2 变成了 2,0,1;其他轴相对位置不变:0在1前(不变)。根据新的下标前后位序更新原数组下标:
0(000)-->0(000) | 1(001)-->1(100) | 2(002)-->2(200) | 3(003)-->3(300) |
---|---|---|---|
4(010)-->4(001) | 5(011)-->5(101) | 6(012)-->6(201) | 7(013)-->7(301) |
8(020)-->8(002) | 9(021)-->9(102) | 10(022)-->10(202) | 11(023)-->11(302) |
12(100)-->12(010) | 13(101)-->13(110) | 14(102)-->14(210) | 15(103)-->15(310) |
16(110)-->16(011) | 17(111)-->17(111) | 18(112)-->18(211) | 19(113)-->19(311) |
20(120)-->20(012) | 21(121)-->21(112) | 22(122)-->22(212) | 23(123)-->23(312) |
python
print(np.rollaxis(a, 2, 1).shape) # (2, 4, 3)
程序运行 np.rollaxis(a, 2, 1) 时,从轴1 开始,将 轴2 滚动到轴1 前面,数组下标排序由 0,1,2 变成了 0,2,1;其他轴相对位置不变:0在1、2前(不变)。根据新的下标前后位序更新原数组下标:
0(000)-->0(000) | 1(001)-->1(010) | 2(002)-->2(020) | 3(003)-->3(030) |
---|---|---|---|
4(010)-->4(001) | 5(011)-->5(011) | 6(012)-->6(021) | 7(013)-->7(031) |
8(020)-->8(002) | 9(021)-->9(012) | 10(022)-->10(022) | 11(023)-->11(032) |
12(100)-->12(100) | 13(101)-->13(110) | 14(102)-->14(120) | 15(103)-->15(130) |
16(110)-->16(101) | 17(111)-->17(111) | 18(112)-->18(121) | 19(113)-->19(131) |
20(120)-->20(102) | 21(121)-->21(112) | 22(122)-->22(122) | 23(123)-->23(132) |
规律总结:滚动之后,数组下标索引存在,则对应的值不变,如:(0,1,2)--->(0,2,1)时,8(020)--->8(002)(002在原下标索引中已存在);若下标索引是新出现的,则对应的值也不变,即滚动变换只更新了数组值对应的下标索引。具体如何快速判断变换后的值是什么,需要不断运用,掌握其规律。
7.2.2 np.swapaxes()
该函数用于交换数组的两个轴。其语法格式如下:
python
numpy.swapaxes(arr, axis1, axis2)
python
arr = np.arange(1, 16).reshape((3, 5))
print(np.swapaxes(arr, 0, 1)) # 等价于二维数组转置
a = np.arange(24).reshape(2, 3, 4)
# 对换0轴与1轴
print(np.swapaxes(a, 0, 1))
# 对换1轴与2轴
print(np.swapaxes(a, 1, 2)) # 等价于: np.rollaxis(a, 2, 1)
# 对换0轴与2轴
print(np.swapaxes(a, 0, 2))
0(000) | 1(001) | 2(002) | 3(003) |
---|---|---|---|
4(010) | 5(011) | 6(012) | 7(013) |
8(020) | 9(021) | 10(022) | 11(023) |
12(100) | 13(101) | 14(102) | 15(103) |
16(110) | 17(111) | 18(112) | 19(113) |
20(120) | 21(121) | 22(122) | 23(123) |
对换 0轴 与 1轴 之后:(2,3,4)--->(3,2,4)
0(000)-->0(000) | 1(001)-->1(001) | 2(002)-->2(002) | 3(003)-->3(003) |
---|---|---|---|
4(010)-->4(100) | 5(011)-->5(101) | 6(012)-->6(102) | 7(013)-->7(103) |
8(020)-->8(200) | 9(021)-->9(201) | 10(022)-->10(202) | 11(023)-->11(203) |
12(100)-->12(010) | 13(101)-->13(011) | 14(102)-->14(012) | 15(103)-->15(013) |
16(110)-->16(110) | 17(111)-->17(111) | 18(112)-->18(112) | 19(113)-->19(113) |
20(120)-->20(210) | 21(121)-->21(211) | 22(122)-->22(212) | 23(123)-->23(213) |
对换 1轴 与 2轴 之后:(2,3,4)--->(2,4,3)
0(000)-->0(000) | 1(001)-->1(010) | 2(002)-->2(020) | 3(003)-->3(030) |
---|---|---|---|
4(010)-->4(001) | 5(011)-->5(011) | 6(012)-->6(021) | 7(013)-->7(031) |
8(020)-->8(002) | 9(021)-->9(012) | 10(022)-->10(022) | 11(023)-->11(032) |
12(100)-->12(100) | 13(101)-->13(110) | 14(102)-->14(120) | 15(103)-->15(130) |
16(110)-->16(101) | 17(111)-->17(111) | 18(112)-->18(121) | 19(113)-->19(131) |
20(120)-->20(102) | 21(121)-->21(112) | 22(122)-->22(122) | 23(123)-->23(132) |
对换 0轴 与 2轴 之后:(2,3,4)--->(4,3,2)
0(000)-->0(000) | 1(001)-->1(100) | 2(002)-->2(200) | 3(003)-->3(300) |
---|---|---|---|
4(010)-->4(010) | 5(011)-->5(110) | 6(012)-->6(210) | 7(013)-->7(310) |
8(020)-->8(020) | 9(021)-->9(120) | 10(022)-->10(220) | 11(023)-->11(320) |
12(100)-->12(001) | 13(101)-->13(101) | 14(102)-->14(201) | 15(103)-->15(301) |
16(110)-->16(011) | 17(111)-->17(111) | 18(112)-->18(211) | 19(113)-->19(311) |
20(120)-->20(021) | 21(121)-->21(121) | 22(122)-->22(221) | 23(123)-->23(321) |
规律总结:与滚动一样,数组下标索引存在,则对应的值不变,如:对换 0轴 与 2轴时,12(100)--->12(001)(001在原下标索引中已存在);若下标索引是新出现的,则对应的值也不变,即变换只更新了数组值对应的下标索引。
7.2.3 np.expand_dims()
在指定位置插入新的轴,从而扩展数组的维度。语法格式如下:
python
numpy.expand_dims(arr, axis)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
axis |
新轴插入的位置 |
python
x = np.array(([1, 2], [3, 4]))
# 在 0 轴处插入新的轴
y = np.expand_dims(x, axis=0)
print('数组 y:\n', y)
# 在 1 轴处插入新的轴
z = np.expand_dims(x, axis=1)
print('数组 z:\n', z)
print('数组 y 和 z 的形状:')
print(y.shape, z.shape)
"""
数组 y:
[[[1 2]
[3 4]]]
数组 z:
[[[1 2]]
[[3 4]]]
数组 y 和 z 的形状:
(1, 2, 2) (2, 1, 2)
"""
7.2.4 np.squeeze()
该函数删除数组中维度为 1 的项。例如,一个数组的 shape 是 (5,1),经此函数后,shape 变为 (5,) 。其语法格式如下:
python
numpy.squeeze(arr, axis)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
axis |
取值为整数或整数元组,用于指定需要删除的维度所在轴,指定的维度值必须为 1 ,否则将会报错,若为 None,则删除数组维度中所有为 1 的项 |
python
x = np.array([[[0], [1], [2]]])
# x.shape(1, 3, 1)
np.squeeze(x).shape # (3,):[0 1 2]
np.squeeze(x, axis=(2,)).shape # (1, 3):[[0 1 2]]
np.squeeze(x, axis=(0,)).shape # (3, 1):[[0] [1] [2]]
另一组示例:
python
a = np.arange(9).reshape(1, 3, 3)
b = np.squeeze(a)
print(a, '\n', b)
print('数组 a 和 b 的形状:')
print(a.shape, b.shape)
"""
[[[0 1 2]
[3 4 5]
[6 7 8]]]
[[0 1 2]
[3 4 5]
[6 7 8]]
数组 a 和 b 的形状:
(1, 3, 3) (3, 3)
"""
7.3 小结
对于二维数组来说,axis=1 表示沿着水平方向,axis=0 表示沿着垂直方向。
python
1. 最外面的括号代表着 axis=0,依次往里的括号对应的 axis 的计数就依次加1
2. 操作方式:如果指定轴进行相关的操作,那么它会使用轴下面的每个直接子元素的第0个,第1个,第2个...分别进行相关的操作
3. np.delete 是直接删除指定轴下的第几个直接子元素
数组的连接:
0---按列---列数不变---沿垂直方向---vstack
1---按行---行数不变---沿水平方向---hstack
数组的切割:
0---按列---行数不变---沿垂直方向---vstack
1---按行---列数不变---沿水平方向---hstack
数组的删除:
0---删除某行
1---删除某列
- 补充:np.c_()和 np.r_()
np.r_ 是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等。
np.c_ 是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等。
8. 数组运算
8.1 位运算
函数 | 位运算符 | 函数说明 |
---|---|---|
bitwise_and | & | 计算数组元素之间的按位与运算。 |
bitwise_or | | | 计算数组元素之间的按位或运算。 |
invert | ~ | 计算数组元素之间的按位取反运算。 |
left_shift | << | 将二进制数的位数向左移。 |
right_shift | >> | 将二进制数的位数向右移。 |
8.1.1 bitwise_and()
该函数对数组中整数的二进制数进行"按位与"运算。示例如下:
python
a = 10
b = 12
print("a的二进制数:", bin(a))
print("b的二进制数:", bin(b))
print("将a与b执行按位与操作:", np.bitwise_and(a, b))
输出结果如下:
python
a的二进制: 0b1010
b的二进制: 0b1100
a与b执行按位与操作: 8
8.1.2 invert()
该函数对数组中整数做按位取反运算,也就是 0 变成 1,1 变为 0。若是有符号的负整数,取其二进制数的补码,并执行 +1 操作。
对于有符号二进制数,其最高位为 0, 表示正数;最高位为 1, 表示负数。
python
# 数据类型为无符号整型uint8
arr = np.array([20], dtype=np.uint8)
print("二进制表示:", np.binary_repr(20, 8))
print(np.invert(arr))
# 进行取反操作
print("二进制表示: ", np.binary_repr(235, 8))
输出结果如下:
python
二进制表示:00010100
[235]
二进制表示:11101011
注意:上述示例中,np.binary_repr 函数用来设置二进制数的位数。
8.1.3 right_shift()
该函数将数组中元素的二进制数向右移动到指定位置,其返回值对应的二进制数会从左侧追加相等数量的 0。该函数使用与 left_shift() 恰好相反。
python
# 将40右移两位后返回值
print(np.right_shift(40, 2))
# 移动前40的二进制数
print(np.binary_repr(40, width=8))
# 移动后返回值的二进制数
print(np.binary_repr(10, width=8))
输出结果如下:
python
# 将40右移两位后返回值
10
# 移动前40的二进制数
00101000
# 移动后返回值的二进制数
00001010
8.2 字符处理
函数名称 | 函数说明 |
---|---|
add() | 对两个数组相应位置的字符串做连接操作 |
multiply() | 返回多个字符串副本,比如将字符串" hello"乘以3,则返回字符串" hello hello hello" |
center() | 用于居中字符串,并将指定的字符,填充在原字符串的左右两侧 |
capitalize() | 将字符串第一个字母转换为大写 |
title() | 标题样式,将每个字符串的第一个字母转换为大写形式 |
lower() | 将数组中所有的字符串的大写转换为小写 |
upper() | 将数组中所有的字符串的小写转换为大写 |
split() | 通过指定分隔符对字符串进行分割,并返回一个数组序列,默认分隔符为空格 |
splitlines() | 以换行符作为分隔符来分割字符串,并返回数组序列 |
strip() | 删除字符串开头和结尾处的空字符 |
join() | 返回一个新的字符串,该字符串是以指定分隔符来连接数组中的所有元素 |
replace() | 用新的字符串替换原数组中指定的字符串 |
decode() | 用指定的编码格式对数组中元素依次执行解码操作 |
encode() | 用指定的编码格式对数组中元素依次执行编码操作 |
np.char.multiply()
该函数将指定的字符串进行多次拷贝,并将拷贝结果返回,示例如下:
python
print (np.char.multiply('c.biancheng.net', 3))
输出结果如下:
python
c.biancheng.net c.biancheng.net c.biancheng.net
np.char.center()
该函数用于居中字符串。其语法格式如下:
python
np.char.center(string, width, fillchar)
string:代表字符串,width:表示长度,fillchar:要填充的字符
python
print(np.char.center("c.biancheng.net", 20, '*'))
# **c.biancheng.net***
np.char.split()
该函数通过指定分隔符对字符串进行分割,并返回数组序列。默认情况下,分隔符为空格。
python
print(np.char.split("Welcome To Python"),sep = " ")
# ['Welcome', 'To', 'Python']
encode()与decode()
默认以utf-8
的形式进行编码与解码,示例如下:
python
# cp500国际编码
encode_str = np.char.encode("Welcome to China", 'cp500')
decode_str = np.char.decode(encode_str, 'cp500')
print(encode_str)
print(decode_str)
输出结果如下:
python
b'\xa6\x85\x93\x83\x96\x94\x85@\xa3\x96@\xc3\x88\x89\x95\x81'
Welcome to China
8.3 算数运算
做算术运算时,输入数组必须具有相同的形状,或者符合数组的广播规则,才可以执行运算。
8.3.1 加减乘除
python
a = np.arange(9, dtype=np.float_).reshape(3, 3)
print(a)
b = np.array([10, 10, 10])
print(b)
# 数组加法运算
print(np.add(a, b)) # 先广播,再对应位置元素相加
# 数组减法运算
print(np.subtract(a, b))
# 数组乘法运算
print(np.multiply(a, b)) # 点乘 <---> a*b
# 数组除法运算
print(np.divide(a, b)) # 点除
8.3.2 np.reciprocal()
该函数对数组中的每个元素取倒数,并以数组的形式返回。当数组元素的数据类型为整型(int)时,对于绝对值小于 1 的元素,返回值为 0;而当数组中包含 0 元素时,返回值将出现 overflow(inf) 溢出提示。示例如下:
python
# 注意此处有0
a = np.array([0.25, 1.33, 1, 0, 100])
# 对数组a使用求倒数操作
print(np.reciprocal(a))
# b数组的数据类型为整形int
b = np.array([100], dtype=int)
print(np.reciprocal(b)) # 0
8.3.3 np.power()
该函数将 a 数组中的元素作为底数,把 b 数组中与 a 相对应的元素作为幂 ,最后以数组形式返回两者的计算结果。示例如下:
python
a = np.array([10, 100, 1000])
b = np.array([1, 2, 3])
print(np.power(a, 0.5))
print(np.power(a, b))
"""
[ 3.16227766 10. 31.6227766 ]
[ 10 10000 1000000000]
"""
8.3.4 np.mod()
该函数返回两个数组相对应位置上元素相除后的余数,它与 np.remainder() 的作用相同 。
python
a = np.array([11, 22, 33])
b = np.array([3, 5, 7])
# a与b相应位置的元素做除法
print(np.mod(a, b)) # [2 2 5]
# remainder方法一样
print(np.remainder(a, b)) # [2 2 5]
8.3.5 复数函数
NumPy 提供了诸多处理复数类型数组的函数,主要有以下几个:
- numpy.real() 返回复数数组的实部;
- numpy.imag() 返回复数数组的虚部;
- numpy.conj() 通过更改虚部的符号,从而返回共轭复数;
- numpy.angle() 返回复数参数的角度,该函数的提供了一个 deg 参数,如果 deg=True,则返回的值会以角度制来表示,否则以以弧度制来表示。
python
a = np.array([-5.6j, 0.2j, 11., 1 + 1j])
# 实部
print(np.real(a))
# 虚部
print(np.imag(a))
# conj(): 通过更改虚部的符号,从而返回共轭复数
print(np.conj(a))
# angle()
print(np.angle(a))
# angle() 带参数deg
print(np.angle(a, deg=True))
输出结果如下:
python
# a数组
[0. - 5.6j 0. + 0.2j 11. + 0.j 1. + 1.j]
# real()
[0. 0. 11. 1.]
# imag()
[-5.6 0.2 0. 1.]
# conj()
[0. + 5.6j 0. - 0.2j 11. - 0.j 1. - 1.j]
# angle()
[-1.57079633 1.57079633 0. 0.78539816]
# angle(a, deg=True)
[-90. 90. 0. 45.]
8.4 统计运算
8.4.1 求最值
numpy.amin() 和 numpy.amax():这两个函数用于计算数组沿指定轴的最小值与最大值。
- amin() 沿指定的轴,查找数组中元素的最小值,并以数组形式返回;
- amax() 沿指定的轴,查找数组中元素的最大值,并以数组形式返回。
python
a = np.array([[3, 7, 5], [8, 4, 3], [2, 4, 9]])
# axis=1---按行
print(np.amax(a, axis=1))
# axis=0---按列
print(np.amax(a, axis=0))
8.4.2 np.ptp()
该函数用于计算数组元素中最值之差值,也就是(最大值 - 最小值)。
python
a = np.array([[2, 10, 20], [80, 43, 31], [22, 43, 10]])
print("原数组", a)
print("沿着axis 1:", np.ptp(a, 1))
print("沿着axis 0:", np.ptp(a, 0))
输出结果如下:
python
原数组 array:
[[ 2 10 20]
[80 43 31]
[22 43 10]]
沿着 axis 1: [18 49 33]
沿着 axis 0: [78 33 21]
8.4.3 np.percentile()
百分位数,是统计学中使用的一种度量单位。该函数表示沿指定轴,计算数组中任意百分比分位数。其语法格式如下:
python
numpy.percentile(arr, q, axis)
参数 | 参数说明 |
---|---|
arr | 输入的数组 |
q | 要计算的百分位数,在 0~100 之间 |
axis |
沿着指定的轴计算百分位数 |
python
a = np.array([[2, 10, 20], [80, 43, 31], [22, 43, 10]])
print("数组a:", a)
print("沿着axis=0计算百分位数", np.percentile(a, 10, 0))
print("沿着axis=1计算百分位数", np.percentile(a, 10, 1))
输出结果如下:
python
# 数组a
[[ 2 10 20]
[80 43 31]
[22 43 10]]
# 沿着axis=0计算百分位数
[ 6. 16.6 12. ]
# 沿着axis=1计算百分位数
[ 3.6 33.4 12.4]
8.4.4 np.median()
该函数用于计算 a 数组元素的中位数(中值)。
python
a = np.array([[30, 65, 70], [80, 95, 10], [50, 90, 60]])
print(a)
print(np.median(a, axis=1))
"""
[[30 65 70]
[80 95 10]
[50 90 60]]
[65. 80. 60.]
"""
8.4.5 np.mean()
该函数表示沿指定的轴,计算数组中元素的算术平均值(即元素之总和除以元素数量)。
python
a = np.array([[30, 65, 70], [10, 20, 30], [50, 70, 90]])
print(a)
print(np.mean(a, axis=1))
"""
[[30 65 70]
[10 20 30]
[50 70 90]]
[55. 20. 70.]
"""
8.4.6 np.average()
加权平均值是将数组中各数值乘以相应的权数,然后再对权重值求总和,最后以权重的总和除以总的单位数(即因子个数)。
numpy.average() 根据在数组中给出的权重,计算数组元素的加权平均值。该函数可以接受一个轴参数 axis,如果未指定,则数组被展开为一维数组。
下面举一个简单的示例:现有数组 [1,2,3,4] 和相应的权重数组 [4,3,2,1],它的加权平均值计算如下:加权平均值=(1 * 4 + 2 * 3 + 3 * 2 + 4 * 1)/(4 + 3 + 2 + 1)。
python
a = np.array([1, 2, 3, 4])
# 若不指定权重相当于对数组求均值
print(np.average(a)) # 2.5
w = np.array([4, 3, 2, 1])
print(np.average(a, weights=w)) # 2.0
#returned 为Ture,则返回权重的和
print(np.average([1, 2, 3, 4], weights=w, returned=True)) # (2.0,10.0)
在多维数组中,也可以指定 axis 轴参数。示例如下:
python
a = np.arange(6).reshape(3, 2)
wt = np.array([3, 5])
print(np.average(a, axis=1, weights=wt))
print(np.average(a, axis=1, weights=wt, returned=True))
"""
[0.625 2.625 4.625]
(array([0.625, 2.625, 4.625]), array([8., 8., 8.]))
"""
8.4.7 np.var()
方差,在统计学中也称样本方差。如何求得方差呢?首先要知道全体样本的的平均值,然后再求得每个样本值与均值之差的平方和,最后对差的平方和求均值,公式如下(其中 n 代表元素个数):
python
print (np.var([1,2,3,4])) # 1.25
8.4.8 np.std()
标准差是方差的算术平方根,用来描述一组数据平均值的分散程度 。若一组数据的标准差较大,说明大部分的数值和其平均值之间差异较大;若标准差较小,则代表这组数值比较接近平均值。NumPy 中使用 np.std() 计算标准差。
python
print (np.std([1,2,3,4])) # 1.1180339887498949
9. 副本和视图
对 NumPy 数组执行某些函数操作时,其中一部分函数会返回数组的副本,而另一部分函数则返回数组的视图。
其实从内存角度来说,副本就是对原数组进行深拷贝(copy),新产生的副本与原数组具有不同的存储位置。而视图(view)可理解为对数组的引用,它和原数组有着相同的内存位置。
9.1 不拷贝
直接赋值,栈区没有拷贝,只是用同一个栈区定义了不同的名称。
赋值操作
赋值操作是数组引用的一种方法。比如,将 a 数组赋值给变量 b,被赋值后的变量 b 与 a 数组具有相同的内存 id。因此,无论操作 a、b 中哪个数组,另一个数组也会受到影响。(不拷贝)
python
a = np.arange(12)
# 这种情况不会进行拷贝
b = a
# 返回True, 说明 b 和 a 是相同的
print(b is a)
print(id(a),' ',id(b)) # 地址相同
print(np.may_share_memory(a, b)) # True
9.2 浅拷贝
只拷贝栈区,栈区指定的堆区并没有拷贝。
ndarray.view()
该函数返回一个原数组的视图,对该视图的操作,会影响到原数组。
python
a = np.arange(1, 16).reshape((3, 5))
b = a.view()
# 判断是否共享内存
print("方式一: ", np.may_share_memory(a, b), " 方式二: ", b.flags['OWNDATA'])
b[0, 0] = 100
print(b, '\n', a)
"""
方式一: True 方式二: False
[[100 2 3 4 5]
[ 6 7 8 9 10]
[ 11 12 13 14 15]]
[[100 2 3 4 5]
[ 6 7 8 9 10]
[ 11 12 13 14 15]]
"""
切片创建视图
使用切片可以创建视图数组,若要修改视图就会影响到原数组。示例如下:
python
arr = np.arange(10)
print('数组arr: ', arr)
# 创建切片修改原数组arr
a = arr[3:]
b = arr[3:]
a[1] = 123
b[2] = 234
print(a, b, '\n', arr)
"""
数组arr: [0 1 2 3 4 5 6 7 8 9]
[ 3 123 234 6 7 8 9] [ 3 123 234 6 7 8 9]
[ 0 1 2 3 123 234 6 7 8 9]
"""
9.3 深拷贝
栈区和堆区都拷贝。
ndarray.copy()
该方法返回原数组的副本,对副本的修改不会影响到原数组。示例如下:
python
a = np.arange(1, 16).reshape((3, 5))
b = a.copy()
# 判断是否共享内存
print("方式一: ", np.may_share_memory(a, b), " 方式二: ", b.flags['OWNDATA'])
b[0, 0] = 100
print(b, '\n', a)
"""
方式一: False 方式二: True
[[100 2 3 4 5]
[ 6 7 8 9 10]
[ 11 12 13 14 15]]
[[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]]
"""
10. 文件操作
10.1 操作 CSV 文件
10.1.1 保存文件
python
np.savetxt(frame, array, fmt="%.18e", delimiter=None)
参数 | 参数说明 |
---|---|
frame | 文件、字符串或产生器,可以是 .gz 或 .bz2 的压缩文件 |
array | 存入文件的数组 |
fmt | 写入文件的格式,例如:%d %.2f %.18e |
delimter | 分割字符串,默认是空格 |
python
scores = np.random.randint(0, 100, size=(10, 2))
# 保存csv文件
np.savetxt("./datas/score.csv", scores, fmt="%d", delimiter=",", header="English,Math", comments="")
10.1.2 读取文件
python
np.loadtxt(frame, dtype=np.float, delimiter=None, unpack=False)
参数 | 参数说明 |
---|---|
frame | 文件、字符串或产生器,可以是 .gz 或 .bz2 的压缩文件 |
dtype | 数据类型,可选 |
delimter | 分割字符串,默认是空格 |
skiprows | 跳过前面 x 行 |
usecols | 读取指定的列,用元组组合 |
unpack | 如果True,读取出来的数组是转置后的 |
python
# 读取 csv 文件 跳过第一行的表头
b = np.loadtxt("./datas/score.csv", dtype=np.int_, delimiter=",", skiprows=1)
10.2 独有存储解决方案
numpy 中还有一种独有的存储解决方案。文件名是以 .npy 或者 npz 结尾的。以下是存储和加载的函数:
- 存储
np.save(fname,array) 或 np.savez(fname,array),其中,前者函数的扩展名是.npy,后者的扩展名是.npz,后者是经过压缩的 - 加载
np.load(fname)
python
c = np.random.randint(0,10,size=(2,3))
# 存储
np.save("c",c)
# 读取
c1 = np.load("c.npy")
10.3 总结
python
1. np.savetxt 和 np.loadtxt 一般用来操作 CSV 文件,可以设置 header,但是不能存储3维以上的数组。
2. np.save 和 np.load 一般用来存储非文本类型的文件,不可以设置header,但是可以存储3维以上的数组
3. 如果想专门的操作 csv 文件,还存在另一个模块叫做 csv,这个模块是 python 内置的,不需要安装
11. 排序与搜索
11.1 排序
11.1.1 np.sort()
该函数对输入数组进行从小到大排序,并返回一个数组副本。其语法格式如下:
python
numpy.sort(arr, axis, kind, order)
参数 | 参数说明 |
---|---|
arr | 待排数组 |
axis | 沿着指定轴进行排序,如果没有指定 axis,默认在最后一个轴上排序,若 axis=0 表示按列排序,axis=1 表示按行排序 |
kind | 默认为 quicksort(快速排序),(heapsort、mergesort) |
order | 若数组设置了字段,则 order 表示要排序的字段 |
python
a = np.array([[3, 7], [9, 1]])
# 按列排序:
print(np.sort(a, axis=0))
# 设置在sort函数中排序字段
dt = np.dtype([('name', 'S10'), ('age', int)])
a = np.array([("raju", 21), ("anil", 25), ("ravi", 17), ("amar", 27)], dtype=dt)
# 按name字段排序
print(np.sort(a, order='name'))
"""
[[3 1]
[9 7]]
[(b'amar', 27) (b'anil', 25) (b'raju', 21) (b'ravi', 17)]
"""
# 降序排序
#方式一:使用负号
-np.sort(-a)
# 方式二:使用 sort 和 argsort 以及 take
# 排序后的结果就是降序的
indexes = np.argsort(-a)
# 从 a 中根据下标提取相应的元素
np.take(a,indexes)
11.1.2 np.argsort()
该函数沿着指定轴,对输入数组的元素值进行排序,并返回排序后的元素索引数组。参数同np.sort。
python
arr = np.array([90, 29, 89, 12])
arr_argsort = np.argsort(arr)
print("排序元素索引值: ", arr_argsort)
# 使用索引数组对原数组排序
sort_a = arr[arr_argsort]
print("排序数组: ", sort_a)
"""
排序元素索引值: [3 1 2 0]
排序数组: [12 29 89 90]
"""
11.1.3 np.lexsort()
该函数按键序列对数组进行排序,返回一个已排序的索引数组,类似于 numpy.argsort()。
python
a = np.array(['a', 'b', 'c', 'd', 'e'])
b = np.array([12, 90, 380, 12, 211])
ind = np.lexsort((a, b)) # a是键key,b是值value---ind将两者绑定
# 排序元素的索引数组
print(ind)
# 使用索引数组对数组进行排序
for i in ind:
print(a[i], b[i])
"""
[0 3 1 4 2]
a 12
d 12
b 90
e 211
c 380
"""
11.2 搜索
11.2.1 np.nonzero()
该函数从数组中查找非零元素的索引位置。
python
b = np.array([12, 90, 380, 12, 211])
print("非0元素的索引位置: ",b.nonzero())
# 非0元素的索引位置: (array([0, 1, 2, 3, 4], dtype=int64),)
11.2.2 np.where()
该函数的返回值是满足了给定条件的元素索引值。其语法格式如下:
python
numpy.where(condition, x, y) | numpy.where(condition)
- 满足 condition 条件的输出 x,不满足输出 y。
- 只有 condition,没有 x 和 y,则输出满足条件(即非0)元素的下标 (类似于np.nonzero)。
python
arr = np.array([[1, 2, 3], [1, 2, 3]])
print(np.where(arr < 2, 0, 10))
print(np.where(arr == 2))
"""
[[ 0 10 10]
[ 0 10 10]]
(array([0, 1], dtype=int64), array([1, 1], dtype=int64))
"""
注意:
-
np.where()[0] 和 np.where()[1] :np.where()[0] 表示行索引,np.where()[1]表示列索引。
-
输入的不能直接是list,需要转为 numpy.ndarray 才行。
-
当 np.where()搜索不到时会返回[],但 len(arr)==1,实际项目中需要注意。
pythonarr = np.array([[1, 2, 3], [1, 2, 3]]) arr1 = np.where(arr == 5) print(arr1) print(len(arr1)) print(len(arr1[0]),' ',len(arr1[1])) """ (array([], dtype=int64), array([], dtype=int64)) 2 0 0 """
11.2.3 np.extract()
该函数的返回值是满足了给定条件的元素值。其语法格式如下:
python
numpy.extract(condition, arr)
参数 | 参数说明 |
---|---|
condition | 搜索的条件 |
arr | 要搜索的数组 |
python
x = np.arange(12.).reshape(3, 4)
print(x)
# 设置条件选择偶数元素
condition = np.mod(x, 3) == 0
# 输出布尔值数组
print(condition)
# 按condition提取满足条件的元素值
print(np.extract(condition, x))
"""
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
[[ True False False True]
[False False True False]
[False True False False]]
[0. 3. 6. 9.]
"""
11.2.4 np.argmax()
该函数返回最大值的的索引,与其相反的函数是 argmin() 求最小值索引。
python
a = np.array([[30, 40, 70], [80, 20, 10], [50, 90, 60]])
print(a)
print(np.argmax(a)) # 不指明轴,则展平再搜索--->7:90
# 将数组以一维展开
print(a.flatten())
# 沿轴 0 的最大值索引:
maxindex = np.argmax(a, axis=0)
print(maxindex)
# 沿轴 1 的最大值索引
maxindex = np.argmax(a, axis=1)
print(maxindex)
"""
[[30 40 70]
[80 20 10]
[50 90 60]]
7
[30 40 70 80 20 10 50 90 60]
[1 2 0]
[2 0 1]
"""
12. 线性代数
NumPy 提供了 numpy.linalg 模块,该模块中包含了一些常用的线性代数计算方法。
函数名称 | 函数说明 |
---|---|
dot | 两个数组的叉乘 |
vdot | 两个向量的点积 |
inner | 两个数组的内积 |
matmul | 两个数组的矩阵积 |
det | 计算矩阵的行列式 |
solve | 求解线性矩阵方程 |
inv | 计算矩阵的逆矩阵,逆矩阵与原始矩阵相乘,会得到单位矩阵 |
12.1 np.dot()
该函数主要用于矩阵的乘法运算,其中包括:向量内积、多维矩阵乘法和矩阵与向量的乘法。
python
"""
1. 向量内积
向量其实是一维的矩阵,两个向量进行内积运算时,需要保证两个向量包含的元素个数是相同的
(计算过程就是将向量中对应元素相乘,再相加所得.即普通的向量乘法运算)
x = np.array([1, 2, 3, 4, 5, 6, 7])
y = np.array([2, 3, 4, 5, 6, 7, 8])
result = np.dot(x, y)
print(result) # 168
2. 矩阵与向量乘法(不满足交换律)
矩阵 x 为 m×n 阶,向量 y 为 n 阶向量,则矩阵 x 和向量 y 可以进行乘法运算,结果为 m 阶向量.
进行运算时,会首先将后面一项进行自动转置操作,之后再进行乘法运算.
样例:
x = np.array([[1, 2, 3], [3, 4, 4]])
y = np.array([1, 2, 3])
result = np.dot(x, y)
print(result)
print("x阶数:" + str(x.shape))
print("y阶数:" + str(y.shape))
print("result阶数:" + str(result.shape))
结果为:
[14 23]
x阶数:(2, 3)
y阶数:(3,)
result阶数:(2,)
"""
12.2 np.vdot()
该函数用于计算两个向量的点积结果,与 dot() 函数不同。
矩阵点积计算公式:两个矩阵对应位置元素乘积之和。
python
a = np.array([[4, 3], [5, 6]])
b = np.array([[10, 11], [12, 13]])
c = np.vdot(a, b)
print(c) # 矩阵点积计算公式: 两个矩阵对应位置元素乘积之和
print(a.shape, ' ', b.shape)
"""
211
(2, 2) (2, 2)
"""
d = np.dot(a,b)
print(d)
"""
[[ 76 83]
[122 133]]
"""
12.3 np.inner()
该函数用于计算数组之间的内积。当计算的数组是一维数组时,它与 dot() 函数相同;若输入多维数组则两者存在不同。
python
A = [1, 2, 3]
B = [4, 5, 6]
print(np.dot(A, B))
print(np.inner(A, B))
a = np.array([[100, 200],
[23, 12]])
b = np.array([[10, 20],
[12, 21]])
print(np.dot(a, b))
print(np.inner(a, b))
"""
32
32
[[3400 6200]
[ 374 712]]
[[5000 5400]
[ 470 528]]
"""
inner() 函数的计算过程是 A 数组的每一行与 B 数组的每一行相乘再相加,如下所示:
python
[[100*10+200*20 100*12+200*21 ]
[23*10+12*20 23*12+12*21]]
dot() 则表示是 A 数组每一行与 B 数组的每一列相乘。
12.4 np.matmul()
该函数返回两个矩阵的乘积(叉乘),假如两个矩阵的维度不一致,就会产生错误。
python
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([[23, 23, 12], [2, 1, 2], [7, 8, 9]])
print(np.matmul(a, b)) # print(np.dot(a, b))结果一致
输出结果如下:
python
[[ 48 49 43]
[144 145 112]
[240 241 181]]
12.5 np.multiply()
该函数对两个数组的对应位置的元素进行相乘,因此它要求这两个数组有相同的大小(shape相同),相同则是计算内积。如果shape不同的话,会将小规格的矩阵延展成与另一矩阵一样大小,再求两者内积。
python
array1 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], ndmin=2)
array2 = np.array([[9, 8, 7], [6, 5, 4], [3, 2, 1]], ndmin=2)
result = np.multiply(array1, array2)
print(result)
""""
[[ 9 16 21]
[24 25 24]
[21 16 9]]
"""
12.6 np.linalg.det()
该函数使用对角线元素来计算矩阵的行列式。
python
a = np.array([[1, 2], [3, 4]])
print(np.linalg.det(a))
# -2.0000000000000004
b = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 9]])
print(np.linalg.det(b))
# 9.000000000000002
12.7 np.linalg.solve()
该函数用于求解线性矩阵方程组,并以矩阵的形式表示线性方程的解,如下所示:
python
3X + 2Y + Z = 10
X + Y + Z = 6
X + 2Y - Z = 2
首先将上述方程式转换为矩阵的表达形式:
python
方程系数矩阵:
3 2 1
1 1 1
1 2 -1
方程变量矩阵:
X
Y
Z
方程结果矩阵:
10
6
2
如果用 m 、x、n 分别代表上述三个矩阵,其表示结果如下:
python
m*x=n 或 x=n/m
将系数矩阵 与结果矩阵传递给 numpy.solve() 函数,即可求出线程方程的解,如下所示:
python
m = np.array([[3,2,1],[1,1,1],[1,2,-1]])
n = np.array([[10],[6],[2]])
x = np.linalg.solve(m,n)
print (x)
输出结果如下:
python
x为线性方程的解:
[[1.]
[2.]
[3.]]
12.8 np.linalg.inv()
该函数用于计算矩阵的逆矩阵,逆矩阵与原矩阵相乘得到单位矩阵。
python
a = np.array([[1, 2], [3, 4]])
print("原数组:\n", a)
b = np.linalg.inv(a)
print("求逆:\n", b)
输出结果如下:
python
原数组:
[[1 2]
[3 4]]
求逆:
[[-2. 1. ]
[ 1.5 -0.5]]
12.9 np.linalg.eig()
该函数用于计算矩阵特征值,特征向量。函数返回特征值和特征向量。
python
# 给出判断矩阵
arr = np.array([[1, 2, 5], [1 / 2, 1, 2], [1 / 5, 1 / 2, 1]])
# 矩阵的特征值和特征向量(均为实数)
eig_val, eig_vector = np.linalg.eig(arr)
print(eig_val)
print('='*30)
print(eig_vector)
print('='*30)
# 矩阵的最大特征值
max_eig_val = np.max(eig_val)
# 矩阵最大特征值对应的特征向量(只要实部)
max_eig_vector = eig_vector[:, np.argmax(eig_val)].real
print(max_eig_vector)
"""
特征值:
[ 3.00553511e+00+0.j -2.76755587e-03+0.12895082j
-2.76755587e-03-0.12895082j]
==============================
[[-0.89021421+0.j -0.89021421+0.j -0.89021421-0.j ]
[-0.41320083+0.j 0.20660042+0.35784242j 0.20660042-0.35784242j]
[-0.19179084+0.j 0.09589542-0.16609574j 0.09589542+0.16609574j]]
==============================
(3.0055351117384994+0j)
==============================
[-0.89021421 -0.41320083 -0.19179084]
==============================
"""
13. NAN 与 INF
13.1 介绍
NAN:Not A number,不是一个数字的意思,但是它是浮点类型的,所以想要进行数据操作的时候需要注意它的类型。
python
data = np.random.randint(0, 10, size=(3, 5))
data = data.astype(np.float)
# 将数组中某个位置的值设置为NAN
data[0, 1]=np.NAN
NAN特点:
- NAN和NAN不相等。比如 np.NAN != np.NAN 这个条件是成立的。
- NAN和任何值做运算,结果都是NAN。
INF:Infinity,代表的是无穷大的意思,也是属于浮点类型。np.inf 表示正无穷大,-np.inf 表示负无穷大,一般在出现除数为 0 的时候为无穷大,比如 2/0。
13.2 缺失值处理
13.2.1 删除缺失值
将数组中的 NAN 删掉,可以只提取不为NAN的值。
python
# 第一种方式: 删除所有 NAN 的值,因为删除了值后数组将不知道该怎么变化,所以会被变成一维数组
data[~np.isnan(data)]
# 第二种方式: 删除 NAN 所在行
# 获取哪些行有 NAN
lines = np.where(np.isnan(data))[0]
# 使用 delete 方法删除指定的行,lines 表示删除的行号,axis=0 表示删除行
np.delete(data,lines,axis=0)
13.2.2 用其他值替代
python
# 从文件中读取数据
scores = np.loadtxt("scores.csv", delimiter=",", skiprows=1, dtype=np.str)
# 将空数据转换成NAN
scores[scores == ""] = np.NAN
# 转化成float类型
scores1 = scores.astype(np.float)
# 将NAN替换为0
scores1[np.isnan(scores1)] = 0
# 除了delete用axis=0表示行以外,其他的大部分函数都是axis=1来表示行
# 对指定轴求和 axis=1按行
scores1.sum(axis=1)
# 将空值替换为均值
# 对scores进行深拷贝
scores2 = scores.astype()
# 循环遍历每一列
for x in range(score2.shape[1]):
col = scores2[:, x]
# 去除该列中值为NAN
non_nan_col = col[~np.isnan(col)]
# 求平均值
mean = non_nan_col.mean()
# 将该列中值为NAN的数值替换为平均值
col[np.isnan(col)] = mean
scores2
13.3 总结
python
1)NAN:Not A Number的简写,不是一个数字,但是是属于浮点类型
2)INF:无穷大,在除数为0的情况下会出现INF
3)NAN和所有的值进行计算结果都是等于NAN
4)NAN != NAN
5)可以通过np.isnan来判断某个值是不是NAN
6)处理值的时候,可以通过删除NAN的形式进行处理,也可以通过值的替换进行处理
7)np.delete比较特殊,通过axis=0来代表行,而其他大部分函数通过axis=1来代表行
14. random 模块
14.1 np.random.seed()
该函数用于指定随机数生成时所用算法开始的整数值,如果使用相同的 seed()值,则每次生成的随机数都相同,如果不设置这个值,则系统根据时间来自己选择这个值,此时每次生成的随机数因时间差异不同。一般没有特殊要求不用设置。
python
flag = np.random.seed(1)
print(flag) # None
a = np.random.rand()
# 打印其他的值,因为随机数种子只对下一次随机数的产生有影响
b = np.random.rand()
print(a, ' ', b)
np.random.seed(1)
c = np.random.rand()
print(a, ' ', c) # 因为随机数种子一致,所以 a==c
"""
None
0.417022004702574 0.7203244934421581
0.417022004702574 0.417022004702574
"""
14.2 np.random.rand()
该函数生成一个值为 [0,1) 之间的数组,形状由参数指定,若无参数,将返回一个随机值。
python
# 产生随机数
a = np.random.rand()
# 产生随机数组 两行三列
arr = np.random.rand(2,3)
14.3 np.random.randn()
该函数生成 均值(μ)为 0,标准差(σ)为 1 的标准正态分布的值。
python
# 生成一个2行3列的数组,数组中的值都满足标准正态分布
data = np.random.randn(2,3)
14.4 np.random.normal()
该函数返回一个或一组样本,具有标准正态分布。其语法格式如下:
python
numpy.random.normal (loc= , scale= , size= )
参数 | 参数说明 |
---|---|
loc | 正态分布的均值 |
scale | 正态分布的方差 |
size | 返回数组的形状大小 |
python
# 生成 10 个服从标准正太分布样本点
arr = np.random.randn(10)
data = np.random.normal(size=(10,), loc=0, scale=1)
14.5 np.random.uniform()
该函数用于从一个均匀分布的区域中随机采样。其语法格式如下:
python
numpy.random.uniform(low, high, size)
参数 | 参数说明 |
---|---|
low | 采样区域的下界,float类型 或 int类型 或 数组类型 或 迭代类型,默认值为0 |
high | 采样区域的上界,float类型 或 int类型 或 数组类型 或 迭代类型,默认值为1 |
size | 输出样本的数目 (int类型 或 tuple类型或 迭代类型) |
python
# 均匀分布区域:[1,10)
X = np.random.uniform(1, 10, (3, 4))
print(X)
"""
[[6.16311538 1.02583294 6.55430422 3.93980412]
[5.74352292 8.97347889 4.21542784 9.17681636]
[6.61024104 1.14239119 9.3649351 7.21807226]]
"""
14.6 np.random.randint()
该函数生成指定范围内的随机数,并且可以通过 size 参数指定维度。
python
# 生成值在0-10之间,3行5列的数组
data1 = np.random.randint(10, size=(3, 5))
# 生成值在1-20之间,3行6列的数组
data2 = np.random.randint(1, 20, size=(3, 6))
14.7 np.random.choice()
该函数会从一个列表或者数组中,随机进行采样。或者是从指定的区间中进行采样,采样个数可以通过参数设定。其语法格式如下:
python
numpy.random.choice(arr, size=None, replace=True, p=None)
参数 | 参数说明 |
---|---|
arr | (ndarray、list、tuple,但必须是一维的)中随机抽取数字 |
size | 组成指定大小的数组 |
replace | True表示可以取相同数字,False表示不可以取相同数字 |
p | 与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同 |
python
a1 = np.random.choice(5) # 从[0, 5)中随机输出一个随机数
# 相当于np.random.randint(0, 5)
a2 = np.random.choice(5, 3) # 在[0, 5)内随机选出3个数字并组成一维数组(ndarray)
# 相当于np.random.randint(0, 5, 3)
li = [1, 2, 3, 4, 5] #list列表
tu = (2, 4, 6, 2) #tuple元组
arr1 = np.array([4, 2, 1]) # numpy,array数组,必须是一维的
arr2 = np.arange(1, 11).reshape(2, 5) #二维数组会报错
a3 = np.random.choice(li, 5)
a4 = np.random.choice(tu, 5)
a5 = np.random.choice(arr1, 5)
# a6 = np.random.choice(arr2, 5) #如果是二维数组,会报错:ValueError: 'a' must be 1-dimensional
aa_milne_arr = ['pooh', 'rabbit', 'piglet', 'Christopher']
a7 = np.random.choice(aa_milne_arr, 5, p=[0.5, 0.1, 0.1, 0.3])
print(a7) # ['pooh' 'pooh' 'rabbit' 'pooh' 'Christopher']
# 可以看到,'pooh'被选取的概率明显比其他几个高很多
14.8 np.random.shuffle()
该函数会把原来数组元素的位置打乱 。np.random.permutation()则是深拷贝,permutation(arr) 不会修改 arr 的顺序。
python
a = np.arange(10)
# 将数组a的元素的位置都会进行随机更换
# shuffle没有返回值,直接打乱原数组位置
np.random.shuffle(a)
15 常用函数汇总
15.1 一元函数
函数名称 | 函数说明 |
---|---|
np.abs | 绝对值 |
np.sqrt | 开方(负数开方结果为NAN) |
np.square | 平方 |
np.exp | 计算指数(e^x) |
np.log,np.log10,np.log2,np.log1p | 求以e为底,以10为底,以2为底,以(1+x为底的对数 |
np.sign | 将数组中的值标签化,大于0的变成1,等于0的变成0,小于0的变成-1 |
np.ceil | 朝着无穷大的方向取整,比如5.1会变成6,-6.3会变成-6 |
np.floor | 朝着负无穷大的方向取整,比如5.1会变成5,-6.3会变成-7 |
np.rint,np.round | 返回四舍五入后的值 |
np.modf | 将整数和小数分割开来形成两个数组 |
np.isnan | 判断是否是nan |
np.isinf | 判断是否是inf |
np.cos,np.cosh,np.sinh,np.tan,np.tanh | 三角函数 |
np.arccos,np.arcsin,np.arctan | 反三角函数 |
15.2 二元函数
函数名称 | 函数说明 |
---|---|
np.add | 加法运算(即1+1=2),相当于+ |
np.subtract | 减法运算(即3-2=1),相当于- |
np.negative | 复数运算(即-2)。相当于加个负号 |
np.multiply | 乘法运算(即2_3=6),相当于_ |
np.divide | 除法运算(即3/2=1.5),相当于/ |
np.floor_divide | 取整运算,相当于// |
np.mod | 取余运算,相当于% |
greater,greater_equal,less,less_equal,equal,not_equal | >,>=,<,<=,=,!=的函数表达式 |
logical_and | 且运算符函数表达式 |
logical_or | 或运算符函数表达式 |
15.3 聚合函数
函数名称 | 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.sum 或者是 a.sum 即可实现。并且在使用的时候,可以指定具体哪个轴。同样 python 中也内置了 sum 函数,但是 python 内置的sum 函数执行效率没有 np.sum 高。
15.4 布尔数组函数
函数名称 | 函数说明 |
---|---|
np.any | 验证任何一个元素是否为真 |
np.all | 验证所有元素是否为真 |
python
# 查看数组中是不是所有元素都为0
# 方式一
np.all(a==0)
# 方式二
(a==0).all()
# 查看数组中是否有等于0的数
# 方式一
np.any(a==0)
# 方式二
(a==0).any()
16. 其他函数汇总
16.1 np.pad()
应用: 在卷积神经网络中,为了避免因为卷积运算导致输出图像缩小和图像边缘信息丢失,常常采用图像边缘填充技术,即在图像四周边缘填充0,使得卷积运算后图像大小不会缩小,同时也不会丢失边缘和角落的信息。在Python的numpy库中,常常采用numpy.pad()进行填充操作。
python
numpy.pad(arr, pad_width, mode,**kwargs)
参数 | 参数说明 |
---|---|
arr | 表示需要填充的数组 |
pad_width | 表示每个轴(axis)边缘需要填充的数值数目。 |
mode | 表示填充的方式(取值:str字符串或用户提供的函数),总共有11种填充模式 |
填充方式 | 说明 |
---|---|
'constant' | 表示连续填充相同的值,每个轴可以分别指定填充值,constant_values=(x, y)时前面用x填充,后面用y填充,缺省值填充0 |
'edge' | 表示用边缘值填充 |
'linear_ramp' | 表示用边缘递减的方式填充 |
'maximum' | 表示最大值填充 |
'mean' | 表示均值填充 |
'median' | 表示中位数填充 |
'minimum' | 表示最小值填充 |
'reflect' | 表示对称填充 |
'symmetric' | 表示对称填充 |
'wrap' | 表示用原数组后面的值填充前面,前面的值填充后面 |
python
"""
pad_width------表示每个轴(axis)边缘需要填充的数值数目。
参数输入方式为:((before_1, after_1), ... (before_N, after_N))
其中(before_1, after_1)表示第1轴两边缘分别填充before_1个和after_1个数值.取值为:{sequence, array_like, int}
#在二维数组A的边缘填充constant_values指定的数值
#(3,2)表示在axis = 0轴填充
#(2,3)表示在axis = 1轴填充
"""
a = np.arange(95, 99).reshape(2, 2) # 原始输入数组
b = np.pad(a, pad_width=1)
print(b)
# constant_values表示填充值,且(before,after)的填充值等于(0,0)
c = np.pad(a, ((3, 2), (2, 3)), 'constant', constant_values=(0, 0))
print(c)
# 0轴和1轴分别填充不同的值,先填充0轴,后填充1轴,存在1轴填充覆盖0轴填充的情形
d = np.pad(a, ((3, 2), (2, 3)), 'constant', constant_values=((0, 0), (1, 2)))
print(d)
16.2 np.apply_along_axis
该函数沿着某个轴执行指定的函数。
python
# 求数组 a 按行求平均值,并且要去掉最大值和最小值
# 函数
def get_mean(x):
# 排除最大值和最小值后求平均值
y=x[np.logical_and(x!=x.max,x!=x.min)].mean()
return y
#方式一:调用函数
np.apply_along_axis(get_mean,axis=1,arr=c)
#方式二:lambda表达式
np.apply_along_axis(lambda x:x[np.logical_and(x!=x.max,x!=x.min)].mean(),axis=1,arr=c)