Numpy学习笔记
在日常工作中,Numpy 被广泛用于处理数值数据,尤其是在数据科学、机器学习和科学计算领域。
NumPy是一个运行速度非常快的数学库,主要用于数组计算。NumPy通常与SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用,这种组合广泛用于替代 MatLab,是一个强大的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。
SciPy是一个开源的 Python 算法库和数学工具包。 SciPy 包含的模块有最优化、线性代数、积分、插值特殊函数、快速傅里叶变换、信号处理和图像处理、常微分方程求解和其他科学与工程中常用的计算。
Matplotib 是 Python 编程语言及其数值数学扩展包,NumPy的可视化操作界面。它为利用通用的图形用户界面工具包,如 Tkinter,wxPython,Qt或 GTK+ 向应用程序嵌入式绘图提供了应用程序接口(API)。
1. 导入 Numpy
在使用 Numpy 之前,首先需要导入它:
import numpy as np
2. 创建数组
2.1 从列表或元组创建数组
arr = np.array([1, 2, 3, 4, 5]) # [1 2 3 4 5]
arr = np.array([[1,2],[3,4]])
"""
[[1 2]
[3 4]]
"""
arr = np.array([1,2,3,4,5],ndmin=2) # [[1 2 3 4 5]]
2.2 创建全零数组
zeros = np.zeros((3, 4)) # 创建一个 3x4 的全零数组
"""
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
"""
2.3 创建全一数组
ones = np.ones((2, 3)) # 创建一个 2x3 的全一数组
2.4 创建指定值的数组
full = np.full((2, 3), 7) # 创建一个 2x3 的数组,所有值为 7
2.5 创建单位矩阵
eye = np.eye(3) # 创建一个 3x3 的单位矩阵
"""
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]
"""
2.6 创建随机数组
random = np.random.random((2, 2)) # 创建一个 2x2 的随机数组,值在[0, 1)之间
"""
[[0.87368863 0.8534936 ]
[0.08163548 0.17707345]]
"""
arr = np.empty([3,2],dtype=int)
"""
[[-9223231307677340829 -8832184198205066449]
[ 2806322172944 2806322172976]
[ 15 45]]
"""
2.7 创建指定范围的数组
arr_range = np.arange(10, 20, 2) # 创建一个从 10 到 20,步长为 2 的数组
2.8 创建线性分布的数组
linspace = np.linspace(0, 1, 5) # 创建一个从 0 到 1 的数组,包含 5 个元素,均匀分布
# [0. 0.25 0.5 0.75 1. ]
arr = np.logspace(0, 2, num=10)
# [ 1. 1.66810054 2.7825594 4.64158883 7.74263683 12.91549665 21.5443469 35.93813664 59.94842503 100. ]
"""
这里 start 是 0,stop 是 2,表示我们希望生成的数值范围是从 10^0(即 1)到 10^2(即 100),这里的base默认是10
num=10 表示我们希望生成 10 个这样的数值。
"""
2.9 从已有的列表或元组创建数组
list_data = [1, 2, 3, 4, 5]
arr = np.asarray(list_data)
print(arr)
# [1 2 3 4 5]
2.10 NumPy 数组的重要属性
-
数组的秩 (Rank) 和轴 (Axis):
- 秩:数组的维数或维度的数量。例如,一维数组的秩为 1,二维数组的秩为 2,以此类推。
- 轴:指的是数组的维度(dimensions)。
-
ndarray 对象的重要属性:
属性 说明 ndarray.ndim
数组的秩,即轴的数量或维度的数量。 ndarray.shape
数组的维度。对于矩阵,n 行 m 列。 ndarray.size
数组元素的总个数,相当于 .shape
中 n*m 的值。ndarray.dtype
ndarray 对象的元素类型。 ndarray.itemsize
数组中每个元素的大小,以字节为单位。 ndarray.flags
ndarray 对象的内存信息。 ndarray.real
ndarray 元素的实部。 ndarray.imag
ndarray 元素的虚部。 ndarray.data
包含实际元素数据的缓冲区。由于一般通过数组的索引读取元素,所以通常不需要使用这个属性。
3. 数组的基本操作
3.1 数组形状
arr.shape # 获取数组的形状
3.2 调整数组形状
reshaped = arr.reshape((5, 1)) # 将数组重塑为 5x1 的形状
3.3 数组展平
flattened = arr.flatten() # 将数组展平为一维
3.4 数组转置
transposed = arr.T # 转置数组,T就是transpose,也可以写成arr.transpose
3.5numpy.swapaxes
函数
numpy.swapaxes
函数用于交换一个数组的两个指定轴(维度)。通过交换轴的位置,可以改变数据的排列方式。
3.5.1函数格式
numpy.swapaxes(arr, axis1, axis2)
arr
:输入的数组,必须是一个 ndarray。axis1
:需要交换的第一个轴的整数索引。axis2
:需要交换的第二个轴的整数索引。
3.5.2 示例代码
import numpy as np
# 创建一个三维的 ndarray
a = np.arange(8).reshape(2, 2, 2)
print('原数组是:')
print(a)
print('\n')
# 现在交换轴 0(深度方向)到轴 2(宽度方向)
print('调用 swapaxes 函数后的数组:')
print(np.swapaxes(a, 2, 0))
3.5.3. 示例解释
-
数组的初始状态:
a
是一个三维数组,形状为(2, 2, 2)
,表示它有三个轴:轴 0(深度方向),轴 1(高度方向),和轴 2(宽度方向)。- 通过
np.arange(8).reshape(2, 2, 2)
创建的数组a
:[[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
-
交换轴后的结果:
-
使用
np.swapaxes(a, 2, 0)
将轴 0 和轴 2 进行交换。结果数组的形状变为(2, 2, 2)
,但轴的顺序发生了改变:[[[0, 4], [2, 6]], [[1, 5], [3, 7]]]
-
交换轴 0 和轴 2 后,原来的深度方向上的元素被移动到宽度方向,而宽度方向上的元素被移动到深度方向。
-
3.6 维度的拓展与删除
3.6.1.numpy.expand_dims
函数
功能:
numpy.expand_dims
用于通过在指定位置插入一个新的轴来扩展数组的形状。
格式:
numpy.expand_dims(arr, axis)
arr
:输入的数组。axis
:要插入新轴的位置(索引)。
示例:
import numpy as np
x = np.array([[1, 2], [3, 4]])
print('数组 x:')
print(x)
print('\n')
y = np.expand_dims(x, axis=0)
print('数组 y:')
print(y)
print('\n')
print('数组 x 和 y 的形状:')
print(x.shape, y.shape)
"""
数组 x:
[[1 2]
[3 4]]
数组 y:
[[[1 2]
[3 4]]]
数组 x 和 y 的形状:
(2, 2) (1, 2, 2)
"""
输出:
- 原数组
x
是一个形状为(2, 2)
的二维数组。 - 使用
expand_dims
在轴 0 处插入一个新轴后,得到的新数组y
的形状为(1, 2, 2)
。
3.6.2. numpy.squeeze
函数
功能:
numpy.squeeze
用于从数组的形状中删除单维度条目,即删除形状为 1 的轴。
格式:
numpy.squeeze(arr, axis=None)
arr
:输入的数组。axis
(可选):要删除的轴。如果未指定,则删除所有单维度的轴。
示例:
import numpy as np
x = np.arange(9).reshape(1, 3, 3)
print('数组 x:')
print(x)
print('\n')
y = np.squeeze(x)
print('数组 y:')
print(y)
print('\n')
print('数组 x 和 y 的形状:')
print(x.shape, y.shape)
"""
数组 x:
[[[0 1 2]
[3 4 5]
[6 7 8]]]
数组 y:
[[0 1 2]
[3 4 5]
[6 7 8]]
数组 x 和 y 的形状:
(1, 3, 3) (3, 3)
"""
输出:
- 原数组
x
是一个形状为(1, 3, 3)
的三维数组。 - 使用
squeeze
删除形状为 1 的轴后,得到的新数组y
的形状为(3, 3)
。
3.7 数组拼接
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
concatenated1 = np.concatenate((arr1, arr2), axis=0) # 沿着第一个轴拼接
print(concatenated1)
concatenated2 = np.concatenate((arr1, arr2), axis=1) # 沿着第二个轴拼接
print(concatenated2)
"""
[[1 2]
[3 4]
[5 6]
[7 8]]
"""
"""
[[1 2 5 6]
[3 4 7 8]]
"""
3.8. 数组堆叠
3.8.1. numpy.stack
函数
-
功能:
numpy.stack
函数用于沿着新轴连接一系列数组。它可以在指定的轴上创建一个新数组,并将输入的数组堆叠到这个新轴上。 -
格式:
numpy.stack(arrays, axis=0)
arrays
:相同形状的数组序列。axis
:返回数组中的轴,输入数组沿着这个轴堆叠。默认是0
。
-
示例:
import numpy as np a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6], [7, 8]]) print('沿轴 0 堆叠两个数组:') print(np.stack((a, b), 0)) print('沿轴 1 堆叠两个数组:') print(np.stack((a, b), 1)) """ 沿轴 0 堆叠两个数组: [[[1 2] [3 4]] [[5 6] [7 8]]] 沿轴 1 堆叠两个数组: [[[1 2] [5 6]] [[3 4] [7 8]]] """
输出:
- 沿轴 0 (深度)堆叠时,结果是增加了一个新维度,将两个数组沿着该新轴堆叠在一起。
- 沿轴 1 堆叠时,结果是在现有的第一个轴的基础上增加一个新轴,并在此轴上进行堆叠。
3.8.2. numpy.hstack
函数
-
功能:
numpy.hstack
是stack
函数的变体,它通过水平(列方向)堆叠数组来生成新的数组。 -
格式:
numpy.hstack((a, b))
-
示例:
import numpy as np a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6], [7, 8]]) print('水平堆叠两个数组:') print(np.hstack((a, b))) """ 水平堆叠两个数组: [[1 2 5 6] [3 4 7 8]] """
输出:
- 数组
a
和b
被沿着列方向(即水平)堆叠在一起,形成一个新的数组。
- 数组
3.8.3 numpy.vstack
函数
-
功能:
numpy.vstack
是stack
函数的另一个变体,它通过垂直(行方向)堆叠数组来生成新的数组。 -
格式:
numpy.vstack((a, b))
-
示例:
import numpy as np a = np.array([[1, 2], [3, 4]]) b = np.array([[5, 6], [7, 8]]) print('垂直堆叠两个数组:') print(np.vstack((a, b))) """ 垂直堆叠两个数组: [[1 2] [3 4] [5 6] [7 8]] """
输出:
- 数组
a
和b
被沿着行方向(即垂直)堆叠在一起,形成一个新的数组。
- 数组
3.9 数组分割
splitted = np.split(arr, 2) # 将数组沿第一个轴分割为两个子数组
3.10 数组元素的添加与删除
3.10.1numpy.append
函数
功能:
numpy.append
函数用于在数组的末尾添加值。追加操作会分配整个新数组,并将原始数组的元素复制到新的数组中。
函数格式:
numpy.append(arr, values, axis=None)
arr
:输入数组。values
:要向arr
添加的值,需要和arr
形状相同(除了要添加的轴)。axis
:要追加的轴,默认值为None
。如果axis=None
,则values
将被展开并与arr
平铺连接,返回一个一维数组。
示例解析:
-
无轴添加(默认
axis=None
):a = np.array([[1, 2, 3], [4, 5, 6]])
np.append(a, [7, 8, 9])
- 输出:
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
- 解释:将
values
展开并平铺在arr
之后,返回一个一维数组。
-
沿轴 0 添加元素:
np.append(a, [[7, 8, 9]], axis=0)
- 输出:
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
- 解释:在轴 0(行)方向上添加新元素,
values
的形状必须匹配arr
在其他维度的形状。
-
沿轴 1 添加元素:
np.append(a, [[5, 5, 5], [7, 8, 9]], axis=1)
- 输出:
array([[1, 2, 3, 5, 5, 5], [4, 5, 6, 7, 8, 9]])
- 解释:在轴 1(列)方向上添加新元素,
values
的形状必须匹配arr
在其他维度的形状。
3.10.2 numpy.insert
函数
功能:
numpy.insert
函数用于在指定轴的特定位置插入值。与 append
函数不同的是,insert
函数允许你在数组的任意位置插入值,而不仅仅是在末尾。
函数格式:
numpy.insert(arr, obj, values, axis=None)
arr
:输入数组。obj
:表示索引的整数或整数数组,指定插入位置。如果axis
是None
,则obj
表示插入位置的索引(数组会被平展为一维数组)。values
:要插入的值。如果axis
指定了,values
需要与arr
在该轴上的形状兼容。axis
:要插入的轴。如果没有指定,则将数组展平为一维数组后插入值。
示例解析:
-
无轴插入(默认
axis=None
):import numpy as np a = np.array([1, 2, 3, 4, 5]) result = np.insert(a, 2, [99, 100]) print(result)
输出:
array([ 1, 2, 99, 100, 3, 4, 5])
- 解释:在一维数组
a
的索引2
处插入[99, 100]
,结果是新的数组中这些值插入到原始位置上,后面的元素右移。
- 解释:在一维数组
-
沿轴 0 插入元素:
import numpy as np a = np.array([[1, 2, 3], [4, 5, 6]]) result = np.insert(a, 1, [99, 100, 101], axis=0) print(result)
输出:
array([[ 1, 2, 3], [ 99, 100, 101], [ 4, 5, 6]])
- 解释:在
a
的索引1
处插入一行[99, 100, 101]
,其余的行向下移动。
- 解释:在
-
沿轴 1 插入元素:
import numpy as np a = np.array([[1, 2, 3], [4, 5, 6]]) result = np.insert(a, 1, [99, 100], axis=1) print(result)
输出:
array([[ 1, 99, 2, 3], [ 4, 100, 5, 6]])
- 解释:在
a
的每一行的索引1
处插入一个元素[99, 100]
,其他元素向右移动。
- 解释:在
注意事项:
- 如果
axis
未指定,数组将首先被展平为一维,然后执行插入操作。 - 插入的位置可以是单个索引,也可以是多个索引的数组,这允许在多个位置插入多个值。
- 插入操作会生成一个新数组,原数组不会被修改。
3.10.3numpy.delete
函数
功能:
numpy.delete
函数用于删除数组中指定的子数组或元素。与 append
和 insert
相反,delete
可以从数组中移除不需要的部分,并返回一个新的数组。
函数格式:
numpy.delete(arr, obj, axis=None)
arr
:输入数组。obj
:要删除的子数组或元素的位置。可以是整数、整数数组或切片。axis
:要删除的轴。如果没有指定,数组将展平为一维,然后删除指定位置的元素。
示例解析:
-
无轴删除(默认
axis=None
):import numpy as np a = np.array([1, 2, 3, 4, 5]) result = np.delete(a, 2) print(result)
输出:
array([1, 2, 4, 5])
- 解释:在一维数组
a
中删除索引2
处的元素3
,结果是剩下的元素形成一个新的数组。
- 解释:在一维数组
-
沿轴 0 删除元素:
import numpy as np a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) result = np.delete(a, 1, axis=0) print(result)
输出:
array([[1, 2, 3], [7, 8, 9]])
- 解释:在
a
的第二行(索引1
)删除整行,结果数组不再包含这一行。
- 解释:在
-
沿轴 1 删除元素:
import numpy as np a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) result = np.delete(a, 1, axis=1) print(result)
输出:
array([[1, 3], [4, 6], [7, 9]])
- 解释:在
a
的每一行中删除第二列(索引1
),结果数组不再包含这一列。
- 解释:在
-
使用切片删除:
import numpy as np a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) result = np.delete(a, np.s_[::2]) print(result)
输出:
array([1, 3, 5, 7, 9])
- 解释:使用切片
np.s_[::2]
,删除数组中所有偶数索引的元素,即删除索引0, 2, 4, 6, 8
处的元素。
- 解释:使用切片
注意事项:
- 如果未指定
axis
参数,delete
会将数组展平为一维数组,然后执行删除操作。 delete
操作会生成一个新的数组,原数组不被修改。obj
可以是单个索引,也可以是多个索引的数组或切片,允许删除多个位置的元素。
4. 索引和切片
4.1 基本索引
element = arr[1, 2] # 获取第二行第三列的元素
4.2 切片
sliced = arr[0:2, 1:3] # 获取第一到第二行,第二到第三列的子数组
4.3 布尔索引
bool_idx = arr[arr > 2] # 获取所有大于 2 的元素
4.4 花式索引
fancy_idx = arr[[0, 1, 2], [0, 1, 0]] # 获取 (0,0), (1,1), (2,0) 位置的元素
4.5 混合索引
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr[1, [0, 2]]) # 输出:[4 6]
5. NumPy 迭代数组(Iterator)
-
NumPy 迭代器对象
numpy.nditer
:- 提供了一种灵活访问一个或者多个数组元素的方式。可以通过迭代器轻松完成对数组元素的访问和操作。
-
基本用法:
- 使用
nditer
迭代数组元素时,可以控制迭代的顺序。
- 使用
-
代码示例:
import numpy as np a = np.arange(0, 60, 5) a = a.reshape(3, 4) print('原始数组是:') print(a) print('\n') print('以 C 风格顺序排序:') for x in np.nditer(a, order='C'): print(x, end=", ") print('\n') print('以 F 风格顺序排序:') for x in np.nditer(a, order='F'): print(x, end=", ") # 输出: """ 原始数组是: [[ 0 5 10 15] [20 25 30 35] [40 45 50 55]] 以 C 风格顺序排序: 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 以 F 风格顺序排序: 0, 20, 40, 5, 25, 45, 10, 30, 50, 15, 35, 55, """
-
迭代顺序:
- C 风格顺序 (
order='C'
):按行优先顺序迭代,即逐行访问数组元素。 - Fortran 风格顺序 (
order='F'
):按列优先顺序迭代,即逐列访问数组元素。
- C 风格顺序 (
6. 数学运算
6.1 数组元素级运算
arr_sum = arr + 2 # 数组每个元素加 2
arr_mult = arr * 2 # 数组每个元素乘 2
6.2 数组间运算
arr_sum = arr1 + arr2 # 数组相加
arr_mult = arr1 * arr2 # 数组对应元素相乘
6.3 数学函数
arr_sqrt = np.sqrt(arr) # 计算数组每个元素的平方根
arr_exp = np.exp(arr) # 计算数组每个元素的指数值
6.4 矩阵乘法
dot_product = np.dot(arr1, arr2) # 计算两个数组的矩阵乘积
7. 统计分析
7.1 最大值、最小值
max_val = np.max(arr) # 获取数组中的最大值
min_val = np.min(arr) # 获取数组中的最小值
7.2 求和、均值、方差
sum_val = np.sum(arr) # 数组元素求和
mean_val = np.mean(arr) # 计算数组的均值
var_val = np.var(arr) # 计算数组的方差
7.3 中位数、标准差
median_val = np.median(arr) # 计算数组的中位数
std_val = np.std(arr) # 计算数组的标准差
7.4 轴向操作
sum_axis0 = np.sum(arr, axis=0) # 按列求和
sum_axis1 = np.sum(arr, axis=1) # 按行求和
8. 线性代数
8.1 矩阵转置
transposed = np.transpose(arr) # 转置矩阵
8.2 矩阵逆
inverse = np.linalg.inv(arr) # 计算矩阵的逆
8.3 矩阵行列式
det = np.linalg.det(arr) # 计算矩阵的行列式
8.4 特征值与特征向量
eigvals, eigvecs = np.linalg.eig(arr) # 计算矩阵的特征值和特征向量
9. 随机数生成
9.1 生成均匀分布的随机数
uniform_random = np.random.rand(3, 3) # 生成一个 3x3 的均匀分布随机数数组
9.2 生成正态分布的随机数
normal_random = np.random.randn(3, 3) # 生成一个 3x3 的正态分布随机数数组
9.3 随机选择
random_choice = np.random.choice([1, 2, 3, 4, 5], size=3) # 从数组中随机选择 3 个元素
10. 高级操作
10.1 广播
广播(Broadcasting)是 NumPy 中用于处理不同形状数组之间算术运算的强大机制。广播允许 NumPy 在不复制数据的情况下,对形状不同的数组执行元素级的运算。
10.1.1. 广播的基本规则
广播机制遵循以下规则来确定两个数组能否进行运算:
-
如果两个数组的维度数不相同,形状较小的数组会在左边补 1,直到两个数组具有相同的维度数。
-
然后,从最后一个维度开始,NumPy 会依次比较每个维度:
- 如果两个维度相等,继续比较下一个维度。
- 如果两个维度中有一个是 1,则“扩展”这个维度,使其与另一个维度匹配。
- 如果两个维度不相等且没有维度为 1,则无法进行广播,抛出错误。
10.1.2. 广播的实际示例
-
示例 1:二维数组与一维数组的加法运算
import numpy as np A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) B = np.array([1, 0, 1]) C = A + B print(C)
输出:
[[ 2 2 4] [ 5 5 7] [ 8 8 10]]
在这个例子中,B 是一个一维数组,形状为
(3,)
,而 A 是一个二维数组,形状为(3, 3)
。广播机制将 B 的形状扩展为(3, 3)
,使得它的每一行都与 A 的对应行进行加法运算。 -
示例 2:二维数组与标量的乘法运算
import numpy as np A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) B = 2 C = A * B print(C)
输出:
[[ 2 4 6] [ 8 10 12] [14 16 18]]
在这个例子中,B 是一个标量,广播机制将 B 看作与 A 形状相同的数组,然后执行逐元素的乘法运算。
10.1.3. 广播的优点
- 内存效率:广播机制避免了不必要的数据复制,直接对不同形状的数组进行运算,从而节省内存。
- 简化代码:通过广播,代码更加简洁易懂,减少了手动调整数组形状的麻烦。
10.1.4. 注意事项
尽管广播机制非常方便,但在使用时要小心以下几点:
- 理解广播过程:确保对广播规则的理解,否则可能会产生意料之外的结果。
- 检查输出数组的形状:在广播运算后,结果数组的形状可能与预期不同,需注意确认。
10.2 矢量化运算
Numpy 中的大部分操作都是矢量化的,意味着它们可以快速地应用于整个数组,而无需使用显式的循环。
vectorized_mult = arr * 2 # 这个操作会比用 Python 的 for 循环来逐个元素乘以 2 快得多
11. 其他常用函数
11.1 排序
sorted_arr = np.sort(arr) # 对数组进行排序
11.2 唯一值
unique_vals = np.unique(arr) # 获取数组中的唯一值
11.3 条件过滤
filtered_arr = arr[arr > 3] # 获取所有大于 3 的元素
12.字符串函数
函数 | 描述 |
---|---|
add() | 对两个数组的逐个字符串元素进行连接。 |
multiply() | 返回元素多重连接后的字符串。 |
center() | 居中字符串。 |
capitalize() | 将字符串第一个字母转换为大写。 |
title() | 将字符串的每个单词的第一个字母转换为大写。 |
lower() | 数组元素转换为小写。 |
upper() | 数组元素转换为大写。 |
split() | 指定分隔符将字符串分割,并返回数组列表。 |
splitlines() | 返回元素中的行列表,以换行符分割。 |
strip() | 移除元素开头或者结尾的特定字符。 |
join() | 通过指定分隔符连接数组中的元素。 |
replace() | 使用新字符串替换字符串中的所有子字符串。 |
13.Matplotlib 与 NumPy 的结合使用
13.1. 简介
- Matplotlib 是 Python 中一个功能强大的绘图库,用于创建静态、动画和交互式可视化图表。
- NumPy 是一个用于科学计算的库,提供了支持大规模多维数组和矩阵的操作。
- 两者结合使用,可以有效地处理和可视化数据,尤其是数值数据的可视化,这在数据分析和科学计算中非常重要。
13.2. 基本绘图操作
-
使用 NumPy 生成数据,然后通过 Matplotlib 绘制图表,是常见的数据可视化流程。
示例代码:绘制简单的线性方程图
import numpy as np from matplotlib import pyplot as plt x = np.arange(1, 11) # 生成 1 到 10 的数组 y = 2 * x + 5 # 计算 y 值 plt.title("Matplotlib demo") plt.xlabel("x axis caption") plt.ylabel("y axis caption") plt.plot(x, y) plt.show()
解释:
np.arange(1, 11)
:生成 x 轴数据,从 1 到 10。y = 2 * x + 5
:计算 y 轴数据,表示线性方程y = 2x + 5
。plt.plot(x, y)
:绘制折线图。plt.title()
、plt.xlabel()
和plt.ylabel()
:为图表添加标题和轴标签。plt.show()
:显示图表。
13.3 散点图与格式化字符串
- Matplotlib 允许通过格式化字符串直接控制图表的样式,比如颜色、标记符号等。
13.3.1. 线型样式
字符 | 描述 |
---|---|
'-' | 实线样式 |
'--' | 短横线样式 |
'-.' | 点划线样式 |
':' | 虚线样式 |
13.3.2. 标记符号
字符 | 描述 |
---|---|
'.' | 点标记 |
',' | 像素标记 |
'o' | 圆标记 |
'v' | 倒三角标记 |
'^' | 正三角标记 |
'<' | 左三角标记 |
'>' | 右三角标记 |
'1' | 下箭头标记 |
'2' | 上箭头标记 |
'3' | 左箭头标记 |
'4' | 右箭头标记 |
's' | 正方形标记 |
'p' | 五边形标记 |
'*' | 星形标记 |
'h' | 六边形标记1 |
'H' | 六边形标记2 |
'+' | 加号标记 |
'x' | X 标记 |
'D' | 菱形标记 |
'd' | 窄菱形标记 |
`’ | '` |
'_' | 水平线标记 |
13.3.3 颜色
字符 | 描述 |
---|---|
'b' | 蓝色 |
'g' | 绿色 |
'r' | 红色 |
'c' | 青色 |
'm' | 品红色 |
'y' | 黄色 |
'k' | 黑色 |
'w' | 白色 |
这个表格总结了 Matplotlib 中常用的绘图格式化字符,包括线型样式、标记符号和颜色。这些字符可以在绘制图形时指定图形的样式,帮助更好地展示数据。
13.3.4 示例代码
使用格式化字符串绘制散点图
plt.plot(x, y, marker='o',color = 'g') # 使用绿色圆圈标记
plt.show()
解释:
marker='og'
:表示标记类型为绿色圆圈。
13.4. 绘制条形图
-
Matplotlib 的
bar()
函数用于绘制条形图,可以为不同数据集创建对比条形图。示例代码:绘制条形图
from matplotlib import pyplot as plt x = [5, 8, 10] y = [12, 16, 6] x2 = [6, 9, 11] y2 = [6, 15, 7] plt.bar(x, y, align='center') plt.bar(x2, y2, color='g', align='center') plt.title('Bar graph') plt.ylabel('Y axis') plt.xlabel('X axis') plt.show()
解释:
plt.bar(x, y)
:绘制第一个条形图。plt.bar(x2, y2, color='g')
:绘制第二个条形图,并设置颜色为绿色。
13.5. 绘制直方图
-
直方图是展示数据分布的常用图表类型,
hist()
函数用于创建直方图。示例代码:绘制直方图
from matplotlib import pyplot as plt import numpy as np a = np.array([22, 87, 5, 43, 56, 73, 55, 54, 11, 20, 51, 5, 79, 31, 27]) plt.hist(a, bins=[0, 20, 40, 60, 80, 100]) plt.title("Histogram") plt.show() """ 0-20区间有3个数,因此纵轴为3 """
解释:
bins
:指定直方图的分箱(数据范围区间),如[0, 20, 40, 60, 80, 100]
。
13.6. 多子图绘制
-
Matplotlib 提供了
subplot()
和subplots()
方法,用于在一个图表中绘制多个子图。示例代码:使用
subplots()
绘制多个子图import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 2 * np.pi, 400) y = np.sin(x**2) fig, ax = plt.subplots(2, 2, subplot_kw=dict(projection='polar')) ax[0, 0].plot(x, y) ax[1, 1].scatter(x, y) plt.show()
解释:
plt.subplots(2, 2)
:生成一个 2x2 的子图网格。subplot_kw=dict(projection='polar')
:这是一个关键字参数,用于指定子图的投影类型。这里设置为'polar'
,表示所有子图都使用极坐标系。np.linspace(0, 2 * np.pi, 100)
生成从 0 到2 * π
之间的 100 个均匀分布的点。这些点用于 x 轴数据。ax[0, 0].plot(x, y)
和ax[1, 1].scatter(x, y)
:分别在不同的子图中绘制折线图和散点图。
总结
- Matplotlib 与 NumPy 的结合使用,使得数据可视化变得非常高效,特别是在科学计算和数据分析领域。
- 通过简单的 NumPy 数组操作和 Matplotlib 的多功能图表绘制,可以快速生成各种有用的图表,如折线图、散点图、条形图、直方图和多子图。
- 灵活使用
plot()
、bar()
、hist()
、subplot()
等函数,可以满足不同的数据展示需求。
14. 副本与视图
在 NumPy 中,副本(copy) 和 视图(view) 是处理数组时的两个重要概念。理解它们有助于高效地操作数组数据,避免意外的修改或内存浪费。
14.1. 副本(Copy)
- 定义:当你对一个 NumPy 数组进行操作并创建一个副本时,你会得到一个新的数组对象,它与原数组有独立的内存空间。修改副本中的数据不会影响原数组,反之亦然。
- 特点:
- 副本拥有自己独立的内存空间。
- 修改副本不会影响原数组。
- 常用的创建副本的方法包括
np.copy()
函数,或者通过一些操作(如切片、赋值)自动创建。
示例
import numpy as np
a = np.array([1, 2, 3, 4])
b = a.copy() # 创建副本
b[0] = 99
print("原数组 a:", a) # 输出 [1, 2, 3, 4]
print("副本数组 b:", b) # 输出 [99, 2, 3, 4]
- 在这个例子中,
b
是a
的副本。修改b
不会影响a
。
14.2. 视图(View)
- 定义:视图是对原数组的一个不同的表示,它共享相同的数据内存。换句话说,视图是原数组的一个引用。修改视图中的数据会直接影响到原数组,反之亦然。
- 特点:
- 视图与原数组共享相同的内存空间。
- 修改视图会影响到原数组。
- 视图通常通过数组的切片、某些函数(如
reshape()
、ravel()
)等方式创建。
import numpy as np
a = np.array([1, 2, 3, 4])
b = a.view() # 创建视图
b[0] = 99
print("原数组 a:", a) # 输出 [99, 2, 3, 4]
print("视图数组 b:", b) # 输出 [99, 2, 3, 4]
- 在这个例子中,
b
是a
的视图。修改b
会影响a
,因为它们共享相同的数据内存。
14.3. 深拷贝与浅拷贝
- 浅拷贝(Shallow Copy):浅拷贝创建一个新的对象,但在对象中的元素还是引用原对象中的元素。因此,浅拷贝的某些操作会影响原对象。
- 深拷贝(Deep Copy):深拷贝创建一个全新的对象,并递归地复制所有的子对象,不再引用原对象中的元素。深拷贝完全独立,不会影响原对象。
14.4. 什么时候使用副本和视图?
-
副本:
- 在需要独立的数据时,使用副本。例如,当你不希望原数组因修改而受到影响时。
- 在处理大数据时,请注意副本会占用更多的内存。
-
视图:
- 当你需要一个数组的不同表示(如改变形状)而不需要独立的内存空间时,使用视图。
- 视图是高效的,因为它们避免了不必要的数据复制。
14.5. 检查数组是副本还是视图
- 你可以通过
base
属性来检查数组是否是另一个数组的视图。如果base
属性是None
,则表示该数组是独立的(即副本);如果base
指向另一个数组,则该数组是视图。
示例
import numpy as np
a = np.array([1, 2, 3, 4])
b = a.view() # 视图
c = a.copy() # 副本
print(b.base is a) # 输出 True,表示 b 是 a 的视图
print(c.base is a) # 输出 False,表示 c 是 a 的副本
14.6. 示例1
import numpy as np
a = np.arange(6)
print("数组 a:")
print(a)
print("调用id()函数:")
print(id(a))
print("a赋值给b")
b = a
print(b)
print("b拥有相同的id")
print(id(b))
print("修改 b 的形状:")
b.shape = 3,2
print("修改后的数组 b:")
print(b)
print("a的形状也被修改了")
print(a)
输出:
数组 a:
[0 1 2 3 4 5]
调用id()函数:
2670858791024
a赋值给b
[0 1 2 3 4 5]
b拥有相同的id
2670858791024
修改 b 的形状:
修改后的数组 b:
[[0 1]
[2 3]
[4 5]]
a的形状也被修改了
[[0 1]
[2 3]
[4 5]]
总结:简单的赋值不会创建数组对象的副本。相反,它使用原始数组的相同id()来访问它。
14.7 示例2
import numpy as np
arr = np.arange(12)
print('我们的数组:')
print(arr)
print('创建切片:')
a = arr[3:]
b = arr[3:]
a[1] = 123
b[2] = 234
print(arr)
print(id(a), id(b), id(arr[3:]))
"""
我们的数组:
[ 0 1 2 3 4 5 6 7 8 9 10 11]
创建切片:
[ 0 1 2 3 123 234 6 7 8 9 10 11]
2670858631760 2670858516016 2670858791024
"""
arr = np.arange(12)
-
创建数组:
np.arange(12)
生成一个包含从 0 到 11 的一维数组arr
,即:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
- 创建切片视图
a = arr[3:]
和b = arr[3:]
:- 这两行代码分别创建了数组
arr
的切片a
和b
。arr[3:]
表示从arr
的索引 3 开始到数组末尾的部分,即[3, 4, 5, 6, 7, 8, 9, 10, 11]
。 - 在 NumPy 中,切片创建的是原数组的视图,而不是副本。这意味着
a
和b
与arr
共享相同的内存空间,修改a
或b
的内容会直接影响到arr
。
- 这两行代码分别创建了数组
- 修改视图
a
和b
的内容
-
a[1] = 123
:- 修改
a
中索引为 1 的元素(对应arr
中索引为 4 的元素)。因此,arr
中的第 4 个元素会被修改为 123。
- 修改
-
b[2] = 234
:- 修改
b
中索引为 2 的元素(对应arr
中索引为 5 的元素)。因此,arr
中的第 5 个元素会被修改为 234。
- 修改
- 打印数组
arr
的结果
print(arr)
:- 打印
arr
的内容,此时数组arr
已被修改为:[ 0 1 2 3 123 234 6 7 8 9 10 11]
- 可以看到,
a[1]
和b[2]
的修改直接影响了原始数组arr
。
- 打印
- 检查内存地址
print(id(a), id(b), id(arr[3:]))
:id()
函数返回对象的唯一标识(通常是内存地址)。- 打印
a
、b
和重新创建的arr[3:]
的内存地址。结果表明,a
、b
和arr[3:]
占用不同的内存区域,但它们都是原数组arr
的视图,即都指向arr这个数组。