Numpy总结

Numpy

文章主要通过案例来理解广播机制、结合相应函数(rollaxis、swapaxes)来理解Axis、总结了相关函数 axis=1的操作规律

文章参考博客如下

文章结构参考网站: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查看数组维数

    python 复制代码
    arr = 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 所需的元素在两种形状中均相等则可重塑成任意形状

  • 元组传参,指定行数和列数

    python 复制代码
    e = np.array([[1, 2], [3, 4], [5, 6]])
    
    e = e.reshape((2, 3))  # (3,2)--->(2,3)
  • 从 1-D 到 3-D

    python 复制代码
    arr = 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] ] 
    ]
    """
  • 将多维数组展平成一维数组

    python 复制代码
    arr = 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 序列的终止值,如果endpointtrue,该值包含于序列中
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 结尾的。以下是存储和加载的函数:

  1. 存储
    np.save(fname,array) 或 np.savez(fname,array),其中,前者函数的扩展名是.npy,后者的扩展名是.npz,后者是经过压缩的
  2. 加载
    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,实际项目中需要注意

    python 复制代码
    arr = 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)

相关推荐
敲代码不忘补水2 天前
Python Matplotlib 数据可视化全面解析:选择它的七大理由与入门简介
开发语言·python·信息可视化·numpy·pandas·matplotlib
取个名字真难呐3 天前
2、PyTorch张量的运算API(上)
pytorch·python·numpy
敲代码不忘补水4 天前
pandas 机器学习数据预处理:从缺失值到特征切分的全面解析
人工智能·后端·python·机器学习·numpy·pandas·matplotlib
小青头8 天前
numpy学习笔记
笔记·学习·numpy
取个名字真难呐9 天前
矩阵乘法实现获取第i行,第j列值,矩阵大小不变
python·线性代数·矩阵·numpy
小锋学长生活大爆炸9 天前
【教程】Cupy、Numpy、Torch互相转换
pytorch·numpy·cupy
鱼灯几许11 天前
Python爬虫
爬虫·python·numpy
爱折腾的小码农12 天前
记一次宝塔centos出现Failed to start crond.service: Unit crond.service not found.解决
python·centos·numpy
正义的彬彬侠14 天前
XGBoost算法Python代码实现
python·决策树·机器学习·numpy·集成学习·boosting·xgboost
竹笋常青16 天前
《流星落凡尘》
django·numpy