三.Numpy

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 使用 linspacelogspace 生成数组

linspacelogspace 函数生成的是线性间隔和对数间隔的数组,常用于数值分析和图形绘制。

# 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 的第三个参数是数组中的元素数量,而不是步长。
  • logspacebase 参数默认为 10,可以生成不同基数的对数间隔数组。

1.4 使用 oneszeros 创建特殊数组

oneszeros 函数可以快速创建元素全为 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.]]]

注意事项:

  • 默认情况下,oneszeros 创建的数组类型为浮点数。您可以通过 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,直到所有的数组维数都相同。
  2. 在任何一个维度上,如果一个数组的大小是1,而另一个数组的大小大于1,则第一个数组的形状会沿着这个维度扩展以匹配另一个数组的形状。
  3. 如果在任何维度上,两个数组的大小不一致且其中一个数组的大小不是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 模式按维度操作数组。

理解这部分内容的关键在于将处理过程按维度分解,并且对于不需要处理的维度统一使用 :... 来表示。分析操作时,首先需要注意逗号「,」的位置。处理的维度与 arangelinspace 等函数的使用方法相同。

⚠️ 注意:索引支持负数,即可以从数组的末尾开始计数。

以下是一些基本的索引和切片操作示例:

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.concatenatenp.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.concatenatenp.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.argmaxnp.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)
  • 48
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值