Numpy
1.创建和生成 NumPy 数组
1.1 从 Python 列表或元组创建
import numpy as np
使用 np.array()
函数,我们可以将 Python 的列表(list
)或元组(tuple
)转换成 NumPy
数组。这是最直接的创建方式的类型。
# 1.1从列表创建
list_array = np.array([1, 2, 3])
print("从列表创建的数组:\n", list_array)
# 输出:
从列表创建的数组:
[1 2 3]
# 1.2从列表创建二维数组
list_2d_array = np.array([[1, 2., 3], [4, 5, 6]])
print("从列表创建的二维数组:\n", list_2d_array)
# 输出:
从列表创建的二维数组:
[[1. 2. 3.]
[4. 5. 6.]]
# 1.3指定数据类型
typed_array = np.array([1, 2, 3], dtype=np.float16)
print("指定数据类型的数组:\n", typed_array)
# 输出:
指定数据类型的数组:
[1. 2. 3.]
# 1.4从元组创建
tuple_array = np.array((1.1, 2.2))
print("从元组创建的数组:\n", tuple_array)
# 输出:
从元组创建的数组:
[1.1 2.2]
# 1.5从元组创建二维数组
tuple_2d_array = np.array([(1.1, 2.2, 3.3), (4.4, 5.5, 6.6)])
print("从元组创建的二维数组:\n", tuple_2d_array)
# 输出:
从元组创建的二维数组:
[[1.1 2.2 3.3]
[4.4 5.5 6.6]]
注意事项:
- 当列表或元组中的元素类型不一致时,NumPy会尝试将它们转换成一个公共的类型,通常是更宽泛的类型(如整数会被转换为浮点数)。
- 您可以通过指定
dtype
参数来设置数组元素的数据类型。
1.2 使用 arange
生成数组
NumPy 的 arange
函数类似于 Python 的 range
函数,但它返回的是一个数组而不是列表。这个函数在需要创建数值范围时非常有用。
# 2.1创建一个 0 到 11 的数组
arange_array = np.arange(12)
print("使用 arange 创建的数组:\n", arange_array)
# 输出:
使用 arange 创建的数组:
[ 1 2 3 4 5 6 7 8 9 10 11]
# 2.2创建一个步长为 2 的数组
stepped_array = np.arange(100, 124, 2)
print("步长为 2 的数组:\n", stepped_array)
# 输出:
步长为 2 的数组:
[100 102 104 106 108 110 112 114 116 118 120 122]
1.3 使用 linspace
和 logspace
生成数组
linspace
和 logspace
函数生成的是线性间隔和对数间隔的数组,常用于数值分析和图形绘制。
# 3.1使用 linspace 创建等间隔数值数组
linear_space = np.linspace(0, 9, 10)
print("使用 linspace 创建的数组:\n", linear_space)
# 输出:
使用 linspace 创建的数组:
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
# 3.2使用 logspace 创建对数间隔数值数组
logarithmic_space = np.logspace(0, 9, 10, base=np.e)
print("使用 logspace 创建的数组:\n", logarithmic_space)
# 输出:
使用 logspace 创建的数组:
[1.00000000e+00 2.71828183e+00 7.38905610e+00 2.00855369e+01
5.45981500e+01 1.48413159e+02 4.03428793e+02 1.09663316e+03
2.98095799e+03 8.10308393e+03]
注意事项:
linspace
的第三个参数是数组中的元素数量,而不是步长。logspace
的base
参数默认为 10,可以生成不同基数的对数间隔数组。
1.4 使用 ones
和 zeros
创建特殊数组
ones
和 zeros
函数可以快速创建元素全为 1 或 0 的数组。这些函数在初始化参数或重置数据时特别有用。
# 4.1创建全 1 的数组
ones_array = np.ones((2, 3))
print("全 1 的数组:\n", ones_array)
# 输出:
全 1 的数组:
[[1. 1. 1.]
[1. 1. 1.]]
# 4.2创建全 0 的三维数组
zeros_array = np.zeros((2, 3, 4))
print("全 0 的三维数组:\n", zeros_array)
# 输出:
全 0 的三维数组:
[[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]]
# 4.3创建与另一个数组形状相同的全 0 数组
zeros_like_array = np.zeros_like(np.ones((2, 3, 3)))
print("与给定数组形状相同的全 0 数组:\n", zeros_like_array)
# 输出:
与给定数组形状相同的全 0 数组:
[[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
[[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]]
注意事项:
- 默认情况下,
ones
和zeros
创建的数组类型为浮点数。您可以通过dtype
参数指定其他类型。
1.5 使用 random
生成随机数数组
NumPy 的 random
模块包含了多种产生随机数的函数,非常适合生成测试数据或进行随机化操作。
# 5.1创建一个 2x3 的随机数组,元素值介于 0 到 1 之间
np.random.rand(2, 3)
# 输出:
array([[0.67985834, 0.69086849, 0.86309392],
[0.41802867, 0.90849351, 0.07485814]])
# 5.2创建一个随机整数数组,元素值介于 0 到 10 之间
np.random.randint(0, 10, (2, 3))
# 输出:
array([[2, 8, 3],
[1, 6, 5]])
# 5.3创建一个符合标准正态分布的数组
np.random.randn(2, 4)
# 输出:
array([[ 0.07952517, 0.05225985, -0.22883803, -0.23406145],
[-0.67722348, 0.05377112, 0.46357518, -0.34303437]])
# 从 1.17 版本后推荐使用 Generator 的方法来生成随机数
# rng 是个 Generator,可用于生成各种分布
rng = np.random.default_rng(42)
rng
# 输出:
Generator(PCG64) at 0x29232992820
# 连续均匀分布用法
rng.random((2, 3))
# 输出:
array([[0.64386512, 0.82276161, 0.4434142 ],
[0.22723872, 0.55458479, 0.06381726]])
# 可以指定上下界
rng.uniform(0, 5, (2, 3))
# 输出:
array([[3.82499429, 3.1735916 , 2.767897 ],
[2.7960358 , 1.51975049, 0.15408917]])
# 指定大小和上界
rng.integers(10, size=2)
# 输出:
array([0, 4], dtype=int64)
# 指定上下界
rng.integers(0, 10, (2, 3))
# 输出:
array([[7, 1, 4],
[1, 6, 4]], dtype=int64)
# 标准正态分布用法
rng.standard_normal((2, 4))
# 输出:
array([[-0.82448122, 0.65059279, 0.74325417, 0.54315427],
[-0.66550971, 0.23216132, 0.11668581, 0.2186886 ]])
# 高斯分布用法
rng.normal(0, 1, (3, 5))
# 输出:
array([[ 0.87142878, 0.22359555, 0.67891356, 0.06757907, 0.2891194 ],
[ 0.63128823, -1.45715582, -0.31967122, -0.47037265, -0.63887785],
[-0.27514225, 1.49494131, -0.86583112, 0.96827835, -1.68286977]])
1.6 从文件读取数组
NumPy 还提供了读写磁盘文件的功能。你可以将数组保存到文件中,然后在需要时再加载它们
# 将数组保存到文件
np.save('./my_array', np.array([[1, 2, 3], [4, 5, 6]]))
# 从文件加载数组
loaded_array = np.load('my_array.npy')
print("从文件加载的数组:\n", loaded_array)
# 输出:
从文件加载的数组:
[[1 2 3]
[4 5 6]]
注意事项:
- 使用
np.save
保存数组时,文件扩展名.npy
会自动添加。使用np.load
加载数组时需要包含此扩展名。
2.Numpy的基本操作
主要内容包括以下几个方面:
- 尺寸相关
- 最大值、最小值、中位数和分位数
- 平均值、求和和标准差等
这些都是描述性统计相关的指标,对于整体了解一个数组非常有帮助。其中,我们经常使用的是尺寸相关的"shape"属性,以及最大值、最小值、平均值和求和等。
在本节中,我们将使用一个随机生成的数组作为操作对象,并指定种子(seed),以确保每次运行时结果都是一样的。在训练模型时,通常需要指定种子(seed),以便在相同的条件下进行调参。
首先,让我们创建一个随机数生成器(generator):
# 创建一个随机数生成器
rng = np.random.default_rng(seed=42)
# 然后,我们使用均匀分布生成一个3行4列的数组:
# 生成一个均匀分布的数组
arr = rng.uniform(0, 1, (3, 4))
arr
# 输出:
array([[0.77395605, 0.43887844, 0.85859792, 0.69736803],
[0.09417735, 0.97562235, 0.7611397 , 0.78606431],
[0.12811363, 0.45038594, 0.37079802, 0.92676499]])
2.1 尺寸相关
在本小节中,我们将介绍数组的维度、形状和数据量等尺寸相关的属性。其中,形状(shape
)属性是我们经常使用的。
首先,让我们看看数组的维度
# 维度,数组是二维的(两个维度)
arr.ndim
# 输出
2
# 形状,返回一个元组
arr.shape
#输出
(3, 4)
# 数据量
arr.size
# 输出
12
2.2 最值和分位数
在本小节中,我们将介绍数组的最大值、最小值、中位数和其他分位数等属性。其中,最大值和最小值是我们经常使用的。
# 查看数组的最大值:
arr.max()
# 按指定的维度查找最大值,比如按列查找:
arr.max(axis=0)
# 按行查找最大值:
arr.max(axis=1)
# 按行查找最小值,并保持行的维度不变:
arr.min(axis=1, keepdims=True)
# 按列查找最小值并保持维度不变:
arr.min(axis=0, keepdims=True)
3.3 平均值、求和和标准差
在本小节中,我们将介绍数组的平均值、累计求和、方差和标准差等统计指标。其中,平均值是我们经常使用的。
# 查看数组的平均值:
np.average(arr) #np.mean()
# 按列计算:
np.average(arr, axis=0)
# 计算数组的求和:
np.sum(arr, axis=1)
# 保持维度不变,可以使用keepdims=True:
np.sum(arr, axis=1, keepdims=True)
# 按列进行累计求和
np.cumsum(arr, axis=0)
# 按行进行累计求和:
np.cumsum(arr, axis=1)
# 计算数组的标准差:
np.std(arr)
# 按列计算标准差:
np.std(arr, axis=0)
# 计算数组的方差:
np.var(arr, axis=1)
3. NumPy 广播机制
NumPy 的广播机制允许不同形状的数组在算术运算中进行兼容。当进行算术运算如加、减、乘、除等操作时,NumPy 试图让这些数组的形状匹配,如果可能的话,会“广播”较小数组的形状以匹配较大数组的形状。这让我们可以在不同形状的数组之间进行数学运算,而无需手动调整它们的大小。
3.1 直观认识
想象一下你有一个形状为 (3,) 的一维数组,也就是有3个元素的向量,和一个单独的数字(可以认为是形状为 (1,) 的数组)。如果你想要把这个单独的数字加到向量的每一个元素上,按照数学上的直觉,你可能需要写一个循环,逐个元素地进行加法。但是在NumPy中,你不需要写循环,你只需要简单地执行加法运算,NumPy会自动把那个单独的数字“扩展”或者说“广播”到向量的每一个元素上,然后逐个相加。
3.2 广播的规则
NumPy 在进行广播时遵循以下规则:
- 如果所有输入数组的维数不相同,将形状较小的数组的形状在前面补1,直到所有的数组维数都相同。
- 在任何一个维度上,如果一个数组的大小是1,而另一个数组的大小大于1,则第一个数组的形状会沿着这个维度扩展以匹配另一个数组的形状。
- 如果在任何维度上,两个数组的大小不一致且其中一个数组的大小不是1,则无法进行广播,NumPy将会抛出错误。
3.3 代码示例
# 创建一个随机数生成器
rng = np.random.default_rng(seed=42)
# 创建一个 3x4 的随机整数数组
a = rng.integers(1, 100, (3, 4))
## 现在,我们尝试将数组 a 与一个形状为 (4,) 的一维数组进行相加。这里,一维数组的形状将会在前面补1,变成 (1, 4)。然后,这个一维数组沿着第一个维度(行)广播,以匹配 a 的形状 (3, 4)
# 将 a 数组与一个一维数组 [1,2,3,4] 相加
# 一维数组的形状会被广播以匹配 a 的形状
b = np.array([1, 2, 3, 4])
b1 = np.array([[1, 2, 3, 4]])
b2 = np.array([[1, 2, 3, 4],[1, 2, 3, 4],[1, 2, 3, 4]])
a + b, a + b1, a + b2
# 输出:
(array([[ 10, 79, 68, 48],
[ 44, 88, 12, 74],
[ 21, 12, 56, 101]], dtype=int64),
array([[ 10, 79, 68, 48],
[ 44, 88, 12, 74],
[ 21, 12, 56, 101]], dtype=int64),
array([[ 10, 79, 68, 48],
[ 44, 88, 12, 74],
[ 21, 12, 56, 101]], dtype=int64))
结果是,一维数组 [1, 2, 3, 4] 被广播到每一行,与 a 的每一行相加。
## 在另一个例子中,我们将 a 与一个形状为 (3, 1) 的二维数组进行相加。这里,二维数组沿着第二个维度(列)广播,以匹配 a 的形状。
# 将 a 数组与一个形状为 (3, 1) 的二维数组相加
# 二维数组的形状会被广播以匹配 a 的形状
a + [[1], [2], [3]]
#输出:
array([[ 10, 78, 66, 45],
[ 45, 88, 11, 72],
[ 23, 13, 56, 100]], dtype=int64)
结果是,二维数组 [[1], [2], [3]] 被广播到每一列,与 a 的每一列相加。
通过这些示例,我们可以看到 NumPy 如何灵活地处理不同形状的数组,并在算术运算中自动应用广播机制。这大大简化了数组操作,使得代码更加简洁易读。
4.切片与索引
在这一节中,我们将深入了解 NumPy 数组的切片和索引功能。这是整个教程中最关键的部分,因为它揭示了 NumPy(以及 Python 语言)的强大功能。这种优雅的操作方式可能不是独一无二的,但在历史上绝对是开创性的。
本节内容将涵盖以下几个主题:
- 切片
- 索引
- 拼接、重复、分拆
切片和索引是这些主题中的核心,因为它们是基础且频繁使用的。我们强烈建议您熟练掌握这些技能。其他主题相对简单,只需记住一个 API 即可。
4.1 切片和索引
🌟 重点提示:切片和索引是在现有数组上操作以获取所需「部分」元素的方法。其核心是根据 start:stop:step
模式按维度操作数组。
理解这部分内容的关键在于将处理过程按维度分解,并且对于不需要处理的维度统一使用 :
或 ...
来表示。分析操作时,首先需要注意逗号「,
」的位置。处理的维度与 arange
、linspace
等函数的使用方法相同。
⚠️ 注意:索引支持负数,即可以从数组的末尾开始计数。
以下是一些基本的索引和切片操作示例:
rng = np.random.default_rng(42) # 创建一个随机数生成器实例
arr = rng.integers(0, 20, (5, 4)) # 创建一个 5x4 的数组,元素是 0 到 20 的整数
print(arr)
## 接下来,让我们看看如何使用切片和索引来访问和操作数组中的数据。
# 获取第 0 行的所有元素
print(arr[0])
# 输出:
[ 1 15 13 8]
# 获取第 0 行第 1 个元素
print(arr[0, 1])
# 获取第 1 到第 2 行的所有元素(不包括第 3 行)
print(arr[1:3])
# 获取第 1 行和第 3 行的所有元素(离散索引)
print(arr[[1, 3]])
# 获取第 1 到第 2 行的第 1 列元素
print(arr[1:3, 1])
# 获取第 1 行和第 3 行的第 0 列元素(离散索引)
print(arr[[1, 3], [0]])
# 获取第 3 行到最后一行的所有元素
print(arr[3:])
# 获取从开始到第 3 行的第 1 到第 2 列的元素
print(arr[:3, 1:3])
# 使用步长来获取元素,这里我们从第 1 行到第 4 行,每隔 2 行取一次,即第 1 行和第 3 行
print(arr[1:4:2])
# 我们也可以在列上使用步长,这里我们取第 1 行和第 3 行的第 0 列和第 2 列
print(arr[1:4:2, 0:3:2])
# 使用 ... 来代表多个冒号,这里我们获取第一列的所有元素
print(arr[..., 1])
# 直接使用冒号来获取第一列的所有元素,这是常见的用法
print(arr[:, 1])
4.2 拼接
在数据处理中,我们经常需要将多个数组合并成一个更大的数组。这可以通过拼接或堆叠完成。NumPy 提供了多种方法来实现这一点,我们将重点介绍 np.concatenate
和 np.stack
,这两个函数提供了合并数组的基本功能。
np.concatenate
np.concatenate
是 NumPy 中最常用的数组拼接函数之一。它可以沿着指定的轴连接数组序列。
# 创建两个形状相同的数组
arr1 = rng.random((2, 5))
arr2 = rng.random((2, 5))
# 默认情况下,`np.concatenate` 沿着第一个轴(axis=0,即行)进行连接
concatenated_arr = np.concatenate((arr1, arr2))
print(concatenated_arr_axis1)
print(concatenated_arr.shape)
# 可以指定 `axis` 参数来沿着不同的轴拼接数组,这里是沿着列(axis=1)
# 这将输出一个新数组,其中第二个数组的列被添加到第一个数组的右侧。
concatenated_arr_axis1 = np.concatenate((arr1, arr2), axis=1)
print(concatenated_arr_axis1)
print(concatenated_arr_axis1.shape)
np.stack
np.stack
是另一个用于堆叠数组的函数,不同于 np.concatenate
,np.stack
会创建一个新的轴。
# 使用 `np.stack` 来堆叠两个数组,它会在结果中添加一个新的轴
stacked_arr = np.stack((arr1, arr2))
print(stacked_arr)
print("Shape of stacked array:", stacked_arr.shape)
# 可以指定 `axis` 参数来沿着不同的轴堆叠数组,这里我们沿着最内侧的轴(axis=2)进行堆叠
stacked_arr_axis2 = np.stack((arr1, arr2), axis=2)
print(stacked_arr_axis2)
print("Shape of stacked array along axis 2:", stacked_arr_axis2.shape)
4.3 重复
有时我们需要将数组中的元素沿着指定的轴重复某些次数,np.repeat
函数正是为此设计的。
# 创建一个 3x4 的随机整数数组
arr = rng.integers(0, 10, (3, 4))
print(arr)
# 沿着 axis=0 重复每一行两次
repeated_arr_axis0 = np.repeat(arr, 2, axis=0)
print(repeated_arr_axis0)
# 沿着 axis=1 重复每一列三次
repeated_arr_axis1 = np.repeat(arr, 3, axis=1)
print(repeated_arr_axis1)
4.4 分拆
与拼接相反,分拆是将一个大数组分割成多个小数组的过程。np.split
提供了一个通用的分割方法。
# 创建一个 6x4 的随机整数数组
arr = rng.integers(1, 100, (6, 4))
print(arr)
# 默认情况下,`np.split` 沿着第一个轴(axis=0)分割数组,这里我们将其分割成 3 个相同大小的小数组
split_arr = np.split(arr, 3)
print(split_arr)
# 我们也可以沿着列(axis=1)分割数组,这里我们将其分割成 2 个相同大小的小数组
split_arr_axis1 = np.split(arr, 2, axis=1)
print(split_arr_axis1)
4.5 条件筛选
在处理数组时,我们经常根据条件选择或修改元素。np.where
是一个非常有用的函数,它返回满足条件的元素的索引。
# 创建一个 6x4 的随机整数数组
arr = rng.integers(1, 100, (6, 4))
print(arr)
# 使用条件筛选来创建一个布尔数组
condition = arr > 50
print(condition)
# 使用 `np.where` 来找到满足条件的元素的索引
indices = np.where(arr > 50)
print(indices)
# 使用 `np.where` 进行条件赋值,将所有小于等于 50 的元素替换为 -1
new_arr = np.where(arr > 50, arr, -1)
print(new_arr)
4.6 提取
有时我们需要从数组中提取满足特定条件的元素。
# 使用 `np.extract` 来提取大于 50 的元素
extracted_elements = np.extract(arr > 50, arr)
print(extracted_elements)
# 使用 `np.unique` 来获取数组中所有唯一的元素
unique_elements = np.unique(arr)
print(unique_elements)
4.7 最值 Index
在数据分析中,我们经常需要找到最大或最小元素的位置。np.argmax
和 np.argmin
可以帮助我们找到这些元素的索引。
# 找到整个数组中最大元素的索引
index_of_max = np.argmax(arr)
print(index_of_max)
# 沿着列找到每列最大元素的索引
indices_of_max_in_columns = np.argmax(arr, axis=0)
print(indices_of_max_in_columns)
# 沿着行找到每行最小元素的索引
indices_of_min_in_rows = np.argmin(arr, axis=1)
print(indices_of_min_in_rows)
5.形状和转换
在 numpy
中,数组(array)通常是多维的。我们通常将一维数组称为向量,二维数组称为矩阵,而超过二维的数组称为张量。由于数组可能具有多个维度,因此改变其形状和转换是非常基础且常见的操作。
在本节中,我们将探讨以下三个方面:
- 改变形状(Reshaping)
- 反序(Reversing)
- 转置(Transposing)
其中,改变形状和转置是非常常见的操作,值得深入学习和掌握。
5.1 改变形状
在本小节中,我们将介绍一些非常高频的 API,特别是用于扩展一维度的 expand_dims
和去除一维度的 squeeze
函数。这些函数在神经网络的架构中尤其常见。
需要特别注意的是,无论是扩展还是压缩维度,改变的维度大小必须是 1。例如,在使用 squeeze
函数时,如果指定了具体的维度,那么该维度的大小必须是 1。
# 创建一个随机整数数组
rng = np.random.default_rng(seed=42) # 创建一个随机数生成器,种子为 42
arr = rng.integers(1, 100, (3, 4)) # 生成一个 3x4 的整数数组,数值范围是 1 到 100
print(arr)
np.expand_dims
np.expand_dims
函数可以在指定位置增加一个维度。
# 在第二个维度位置增加一个维度
arr_expanded = np.expand_dims(arr, axis=1)
print(arr_expanded.shape)
# 在多个位置增加维度
expanded = np.expand_dims(arr, axis=(1, 3, 4))
print(expanded.shape)
# 注意:扩充维度时不能跳过已有的维度
# 下面的代码会出错,因为没有维度 8 可以扩展
# expanded = np.expand_dims(arr, axis=(1, 3, 8))
np.squeeze
np.squeeze
函数用于移除数组形状中大小为 1 的维度。
# 移除第二个维度,因为它的大小为 1
arr_squeezed = np.squeeze(expanded, axis=1)
print(arr_squeezed.shape)
# 移除所有大小为 1 的维度
squeezed = np.squeeze(expanded)
print(squeezed.shape)
np.reshape/arr.resize
reshape
函数可以改变数组的形状而不改变其数据。
# 将数组重塑为另一个形状
arr_reshaped = arr.reshape(2, 2, 3)
print(arr_reshaped)
# 使用 -1 可以自动计算该维度的大小
arr_reshaped = arr.reshape((4, -1))
print(arr_reshaped)
# 如果尝试将数组重塑为元素数量不匹配的形状,将会出错
# 下面的代码将会引发错误,因为原数组有 12 个元素,而新形状只能容纳 9 个元素
# arr_reshaped = arr.reshape(3, 3)
# 如果尝试将数组重塑为元素数量不匹配的形状,将会出错
# 下面的代码将会引发错误,因为原数组有 12 个元素,而新形状只能容纳 9 个元素
# arr_reshaped = arr.reshape(3, 3)
# 使用 resize 可以改变数组本身的形状
# 注意:resize 会直接修改原数组,而不是返回一个新数组
# 与 reshape 不同,resize 允许新形状的总元素数量与原数组不同
arr.resize((4, 3), refcheck=False)
print(arr)
# 使用 np.resize 可以创建一个新数组,它的行为略有不同
arr_resized = np.resize(arr, (5, 3))
print(arr_resized)
# 如果新形状的总元素数量多于原数组,则 np.resize 会截断原数组中的元素
# 输出的数组形状是 (2, 2),多余的元素被截断。
arr_resized = np.resize(arr, (2, 2))
print(arr_resized)
5.2 反序
反序是将数组中的元素顺序颠倒。在 numpy
中,我们可以使用切片的方式来实现反序。
如果给您一个字符串或数组让您反序,您可能会想到使用 reversed
函数,或者编写一个自定义函数,或者利用 Python 列表的切片功能。在 numpy
中,我们可以使用类似的切片方法来反序数组。
# 反序字符串
s = "uevol"
s_reversed = s[::-1]
print(s_reversed)
# 反序列表
lst = [1, "1", 5.2]
lst_reversed = lst[::-1]
print(lst_reversed)
# 反序 numpy 数组
# 默认情况下,使用切片 -1 可以反序数组的最外层维度
arr_reversed = arr[::-1]
print(arr_reversed)
# 可以在不同的维度上进行反序操作
# 例如,反序所有维度
arr_reversed_all_dims = arr[::-1, ::-1]
print(arr_reversed_all_dims)
5.3 转置
转置是线性代数中的一个基本操作,它将矩阵的行与列交换。在 numpy
中,这个操作可以很容易地通过 .T
属性或 np.transpose
函数来完成。
需要注意的是,一维数组的转置与原数组相同,因为它只有一个维度。
arr.T
arr.T
是转置的简便方法,适用于二维数组。对于多维数组,它会将所有维度的顺序倒置。
# 一维数组的转置
arr_1d = np.array([1, 2])
print(arr_1d)
print(arr_1d.T.shape)
print(arr_1d.T)
# 多维数组的转置
arr_transposed = arr.T
print(arr_transposed)
np.transpose
np.transpose
函数提供了更多的灵活性,允许您指定转置的维度顺序。
# 使用 np.transpose 进行转置
print(arr)
arr_transposed = np.transpose(arr)
print(arr_transposed)
6. Numpy中的视图操作
本节主要介绍Numpy中的视图操作。视图操作是指在不复制数组的情况下创建数组的新视图,这意味着新数组与原始数组共享相同的数据存储空间。视图操作在处理大型数组时非常有用,因为它们可以节省内存并提高性能。
什么是视图操作?
视图操作允许我们以不同的方式查看和操作数组的数据,而不会创建新的数组对象。通过创建数组的视图,我们可以改变数组的维度、形状、步长和数据类型,以满足不同的需求。
创建数组的视图
⭐ 在Numpy中,我们可以使用多种方式创建数组的视图。下面介绍了一些常用的方法:
6.1 切片操作
使用切片操作可以创建原始数组的一个子数组视图。切片操作通过指定起始索引、结束索引和步长来选择数组的子集。下面是一个简单的示例:
arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4] # 创建 arr 的一个视图,包含索引 1 到 3 的元素
print(view) # 输出: [2 3 4]
view[0]=99
arr # 输出: array([ 1, 99, 3, 4, 5])
## 在这个示例中,我们使用切片操作 arr[1:4] 创建了原始数组 arr 的一个视图 view,该视图包含了索引 1 到 3 的元素。通过打印视图,我们可以看到输出结果为 [2 3 4]。
6.2 reshape操作
使用reshape操作可以改变数组的形状,而不改变数组的数据。reshape操作返回一个新的数组视图,该视图具有与原始数组相同的数据,但具有不同的形状。下面是一个示例:
arr = np.array([1, 2, 3, 4, 5, 6])
view = arr.reshape(2, 3) # 创建一个 2x3 的数组视图
print(view)
print(arr)
# 输出:
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6]
# 在这个示例中,我们使用reshape操作 arr.reshape(2, 3) 创建了原始数组 arr 的一个 2x3 的数组视图 view。
6.3 flatten操作
使用flatten操作可以将多维数组转换为一维数组,返回一个新的数组视图。flatten操作不会改变数组的数据,只会改变数据的排列方式。下面是一个示例:
arr = np.array([[1, 2, 3], [4, 5, 6]])
view = arr.flatten() # 创建 arr 的一维数组视图
print(view) # 输出: [1 2 3 4 5 6]
# 在这个示例中,我们使用flatten操作 arr.flatten() 创建了原始数组 arr 的一维数组视图 view。
6.4 view操作
使用view操作可以创建一个新的数组视图,该视图与原始数组共享相同的数据存储空间。view操作可以改变数组的形状、步长和数据类型。下面是一个示例:
arr = np.array([1, 2, 3, 4, 5])
view = arr.view(dtype=np.int32) # 创建 arr 的一个视图,数据类型为int32
print(view) # 输出: [1 2 3 4 5]
# 在这个示例中,我们使用view操作 arr.view(dtype=np.int32) 创建了原始数组 arr 的一个视图 view,并指定了数据类型为int32。
7.矩阵和运算
这一节我们将聚焦矩阵和相关的运算,主要包括:
- 算术(四则运算及其他基础算术)
- 矩阵相关
这些内容在实际应用中非常普遍,我们经常使用这些操作,而且它们非常简单。虽然我们这里不涉及高维度的计算,但逻辑是一致的,只是更加复杂。
7.1 算术
⭐️ 所有的算术函数都可以直接应用于数组(array)。
rng = np.random.default_rng(42)
arr = rng.integers(1, 20, (3, 4))
arr
# 输出:
array([[ 2, 15, 13, 9],
[ 9, 17, 2, 14],
[ 4, 2, 11, 19]], dtype=int64)
# +-*/ 四则运算,就像计算两个数字一样
arr * 2
# 输出:
array([[ 4, 30, 26, 18],
[18, 34, 4, 28],
[ 8, 4, 22, 38]], dtype=int64)
# 平方
arr ** 2
# 输出:
array([[ 4, 225, 169, 81],
[ 81, 289, 4, 196],
[ 16, 4, 121, 361]], dtype=int64)
# 开方
np.sqrt(arr)
# 输出:
array([[1.41421356, 3.87298335, 3.60555128, 3. ],
[3. , 4.12310563, 1.41421356, 3.74165739],
[2. , 1.41421356, 3.31662479, 4.35889894]])
# 对数
np.log(arr)
# 超过5的都换成5
np.minimum(arr, 5)
# 低于5的都换成5
np.maximum(arr, 5)
# 四舍五入
np.round(np.sqrt(arr), 2)
# 取整
np.floor(np.sqrt(arr))
np.ceil(np.sqrt(arr))
# 取余 mod <=> x % 3
np.mod(arr, 3)
7.2 矩阵
⭐ 这一小节主要介绍线性代数中矩阵的处理,我们会介绍几个常用的矩阵操作。
rng = np.random.default_rng(42)
a = rng.integers(1, 10, (3, 2))
b = rng.integers(1, 10, (2, 4))
c = rng.integers(1, 10, (3, 3))
a, b, c
def matrix_multiply(matrix1, matrix2):
'''
矩阵乘法
matrix1:(3,4)
matrix2:(4,6)
则结果为:(3,6)
'''
rows1 = len(matrix1)
cols1 = len(matrix1[0])
rows2 = len(matrix2)
cols2 = len(matrix2[0])
if cols1 != rows2:
# 左列=右行
raise ValueError("The number of columns in matrix1 must be equal to the number of rows in matrix2.")
result = [[0 for _ in range(cols2)] for _ in range(rows1)]
for i in range(rows1):
for j in range(cols2):
for k in range(cols1):
result[i][j] += matrix1[i][k] * matrix2[k][j]
return result
arr.dot
# 数组乘法
np.dot(a, b)
或
a.dot(b)
np.matmul
# 矩阵乘法
np.matmul(a, b)
# 同上,写起来比较好看的方法
a @ b
# 内积
np.inner(a, a)
# 对,就是内积
a.dot(a.T)
# 行列式
print(c)
d = rng.integers(1, 10, (2,2))
print(d)
np.linalg.det(d)
# 逆矩阵(方阵)
np.linalg.inv(c)