numpy中数组矩阵的奇妙变幻

NumPy 数组详解:从基础到高级的全方位指南

NumPy(Numerical Python)是 Python 科学计算的基础库,它提供了高性能的多维数组对象以及用于处理这些数组的工具。在数据分析、机器学习、科学计算等领域,NumPy 数组几乎是不可或缺的核心数据结构。本文将全面介绍 NumPy 数组的各种特性、操作方法和高级技巧,帮助你掌握这一强大工具。

一、NumPy 数组简介

1.1 什么是 NumPy 数组

NumPy 数组(ndarray,即 N-dimensional array)是一种多维的同构数据容器,它可以存储相同类型的元素。与 Python 的列表相比,NumPy 数组具有以下优势:

  • 更高的性能:NumPy 数组的运算速度通常比 Python 列表快 10-100 倍
  • 更简洁的语法:可以用简洁的方式表达复杂的数学运算
  • 支持广播机制:使不同形状的数组之间可以进行算术运算
  • 与其他科学计算库兼容:是 Pandas、Matplotlib、Scikit-learn 等库的基础

1.2 安装 NumPy

如果你还没有安装 NumPy,可以使用 pip 或 conda 进行安装:

python

运行

# 使用pip安装
!pip install numpy

# 使用conda安装
!conda install numpy

安装完成后,通常将其导入并简写为np

python

运行

import numpy as np

这个约定在 NumPy 社区中被广泛遵循,我们将在整篇文章中使用这个简写。

二、创建 NumPy 数组

NumPy 提供了多种创建数组的方法,适用于不同的场景。

2.1 从 Python 列表创建

最基本的创建数组的方法是使用np.array()函数,它可以将 Python 列表转换为 NumPy 数组:

python

运行

# 创建一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print("一维数组:")
print(arr1)
print("数组类型:", type(arr1))

# 创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n二维数组:")
print(arr2)

# 创建三维数组
arr3 = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print("\n三维数组:")
print(arr3)

输出结果:

plaintext

一维数组:
[1 2 3 4 5]
数组类型: <class 'numpy.ndarray'>

二维数组:
[[1 2 3]
 [4 5 6]
 [7 8 9]]

三维数组:
[[[1 2]
  [3 4]]

 [[5 6]
  [7 8]]]

可以通过ndmin参数指定数组的最小维度:

python

运行

# 创建至少3维的数组
arr = np.array([1, 2, 3], ndmin=3)
print("数组:")
print(arr)
print("数组维度:", arr.ndim)

输出结果:

plaintext

数组:
[[[1 2 3]]]
数组维度: 3

2.2 使用内置函数创建数组

NumPy 提供了许多实用函数来创建具有特定模式的数组。

2.2.1 等差数列

np.arange()函数类似于 Python 的range(),但返回的是一个 NumPy 数组:

python

运行

# 创建从0到9的数组
arr1 = np.arange(10)
print("arr1:", arr1)

# 创建从5到14的数组
arr2 = np.arange(5, 15)
print("arr2:", arr2)

# 创建从10到30,步长为5的数组
arr3 = np.arange(10, 31, 5)
print("arr3:", arr3)

输出结果:

plaintext

arr1: [0 1 2 3 4 5 6 7 8 9]
arr2: [ 5  6  7  8  9 10 11 12 13 14]
arr3: [10 15 20 25 30]

np.linspace()函数创建指定范围内的等间隔数字:

python

运行

# 创建从0到10的5个等间隔数字(包含终点)
arr1 = np.linspace(0, 10, 5)
print("arr1:", arr1)

# 创建从0到10的5个等间隔数字(不包含终点)
arr2 = np.linspace(0, 10, 5, endpoint=False)
print("arr2:", arr2)

# 查看步长
arr3, step = np.linspace(0, 10, 5, retstep=True)
print("arr3:", arr3)
print("步长:", step)

输出结果:

plaintext

arr1: [ 0.   2.5  5.   7.5 10. ]
arr2: [0. 2. 4. 6. 8.]
arr3: [ 0.   2.5  5.   7.5 10. ]
步长: 2.5
2.2.2 特殊数组

创建全零数组:

python

运行

# 创建3x4的全零数组
zeros = np.zeros((3, 4))
print("3x4的全零数组:")
print(zeros)

# 指定数据类型
zeros_int = np.zeros((2, 2), dtype=int)
print("\n2x2的整数全零数组:")
print(zeros_int)

输出结果:

plaintext

3x4的全零数组:
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

2x2的整数全零数组:
[[0 0]
 [0 0]]

创建全一数组:

python

运行

# 创建2x3的全一数组
ones = np.ones((2, 3))
print("2x3的全一数组:")
print(ones)

# 调整数据类型和形状
ones_custom = np.ones(5, dtype=np.int32).reshape(5, 1)
print("\n5x1的整数全一数组:")
print(ones_custom)

输出结果:

plaintext

2x3的全一数组:
[[1. 1. 1.]
 [1. 1. 1.]]

5x1的整数全一数组:
[[1]
 [1]
 [1]
 [1]
 [1]]

创建单位矩阵:

python

运行

# 创建3x3的单位矩阵
eye = np.eye(3)
print("3x3的单位矩阵:")
print(eye)

# 创建非方阵的单位矩阵
eye2 = np.eye(3, 5, k=1)
print("\n3x5的单位矩阵(偏移1):")
print(eye2)

输出结果:

plaintext

3x3的单位矩阵:
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]]

3x5的单位矩阵(偏移1):
[[0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 1. 0.]]

创建对角矩阵:

python

运行

# 从对角线元素创建对角矩阵
diag = np.diag([1, 2, 3, 4])
print("对角矩阵:")
print(diag)

# 提取矩阵的对角线元素
mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n原始矩阵:")
print(mat)
print("对角线元素:", np.diag(mat))

输出结果:

plaintext

对角矩阵:
[[1 0 0 0]
 [0 2 0 0]
 [0 0 3 0]
 [0 0 0 4]]

原始矩阵:
[[1 2 3]
 [4 5 6]
 [7 8 9]]
对角线元素: [1 5 9]
2.2.3 随机数组

NumPy 的random模块提供了多种生成随机数组的函数。

生成均匀分布的随机数:

python

运行

# 生成0到1之间的随机浮点数
rand = np.random.rand(3, 3)
print("3x3的随机数组(0-1):")
print(rand)

# 生成指定范围内的随机整数
randint = np.random.randint(10, 50, size=(2, 4))
print("\n2x4的随机整数数组(10-50):")
print(randint)

输出结果(每次运行结果不同):

plaintext

3x3的随机数组(0-1):
[[0.46180183 0.26654741 0.60485547]
 [0.73354578 0.74647039 0.33420086]
 [0.93935347 0.96709572 0.36496975]]

2x4的随机整数数组(10-50):
[[23 45 12 37]
 [19 33 28 41]]

生成正态分布的随机数:

python

运行

# 生成标准正态分布(均值0,标准差1)的随机数
randn = np.random.randn(3, 3)
print("3x3的标准正态分布数组:")
print(randn)

# 生成指定均值和标准差的正态分布随机数
mu, sigma = 10, 2
normal = mu + sigma * np.random.randn(2, 4)
print(f"\n2x4的正态分布数组(均值{mu},标准差{sigma}):")
print(normal)

输出结果(每次运行结果不同):

plaintext

3x3的标准正态分布数组:
[[ 0.48375672 -0.53129892  0.26764573]
 [ 0.39326751  0.18841541 -0.08299631]
 [ 0.36533361  0.02635666 -0.04385225]]

2x4的正态分布数组(均值10,标准差2):
[[10.87636535  9.48547626 11.56360455  8.73663672]
 [ 9.76633121  9.35226147 10.29934463 10.98236156]]

2.3 从文件读取数组

NumPy 提供了读取文本文件和二进制文件的功能。

读取文本文件:

python

运行

# 首先创建一个示例文本文件
data = np.arange(12).reshape(3, 4)
np.savetxt('data.txt', data, fmt='%d', delimiter=',')
print("保存的文本文件内容:")
with open('data.txt', 'r') as f:
    print(f.read())

# 读取文本文件
loaded_data = np.loadtxt('data.txt', delimiter=',')
print("\n读取的数组:")
print(loaded_data)

输出结果:

plaintext

保存的文本文件内容:
0,1,2,3
4,5,6,7
8,9,10,11

读取的数组:
[[ 0.  1.  2.  3.]
 [ 4.  5.  6.  7.]
 [ 8.  9. 10. 11.]]

读取二进制文件(NumPy 专用格式):

python

运行

# 保存为NumPy二进制格式
np.save('data.npy', data)

# 读取NumPy二进制文件
loaded_npy = np.load('data.npy')
print("从npy文件读取的数组:")
print(loaded_npy)

# 保存多个数组到一个文件
data2 = np.arange(10, 22).reshape(3, 4)
np.savez('data.npz', arr1=data, arr2=data2)

# 读取多个数组
loaded_npz = np.load('data.npz')
print("\n从npz文件读取的数组1:")
print(loaded_npz['arr1'])
print("从npz文件读取的数组2:")
print(loaded_npz['arr2'])

输出结果:

plaintext

从npy文件读取的数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

从npz文件读取的数组1:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
从npz文件读取的数组2:
[[10 11 12 13]
 [14 15 16 17]
 [18 19 20 21]]

三、NumPy 数组的属性

NumPy 数组有许多重要的属性,可以帮助我们了解数组的特征。

python

运行

# 创建一个示例数组
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print("示例数组:")
print(arr)

# 数组的维度(ndim)
print("\n数组的维度:", arr.ndim)

# 数组的形状(shape)
print("数组的形状:", arr.shape)

# 数组的元素总数(size)
print("数组的元素总数:", arr.size)

# 数组的数据类型(dtype)
print("数组的数据类型:", arr.dtype)

# 每个元素的字节大小(itemsize)
print("每个元素的字节大小:", arr.itemsize)

# 数组的总字节大小(nbytes)
print("数组的总字节大小:", arr.nbytes)

# 数组的转置(T)
print("数组的转置:")
print(arr.T)

输出结果:

plaintext

示例数组:
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]

数组的维度: 2
数组的形状: (3, 4)
数组的元素总数: 12
数组的数据类型: int64
每个元素的字节大小: 8
数组的总字节大小: 96
数组的转置:
[[ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]
 [ 4  8 12]]

数据类型

NumPy 支持多种数据类型,比 Python 内置的类型更加丰富:

python

运行

# 创建不同数据类型的数组
arr_int = np.array([1, 2, 3], dtype=np.int32)
arr_float = np.array([1.1, 2.2, 3.3], dtype=np.float64)
arr_bool = np.array([True, False, True], dtype=np.bool_)
arr_complex = np.array([1+2j, 3+4j], dtype=np.complex_)
arr_str = np.array(['a', 'b', 'c'], dtype=np.str_)

print("整数数组类型:", arr_int.dtype)
print("浮点数数组类型:", arr_float.dtype)
print("布尔数组类型:", arr_bool.dtype)
print("复数数组类型:", arr_complex.dtype)
print("字符串数组类型:", arr_str.dtype)

# 数据类型转换
arr = np.array([1.5, 2.7, 3.9])
print("\n原始数组:", arr, "类型:", arr.dtype)

arr_int = arr.astype(np.int32)
print("转换为整数:", arr_int, "类型:", arr_int.dtype)

arr_str = arr.astype(np.str_)
print("转换为字符串:", arr_str, "类型:", arr_str.dtype)

输出结果:

plaintext

整数数组类型: int32
浮点数数组类型: float64
布尔数组类型: bool
复数数组类型: complex128
字符串数组类型: <U1

原始数组: [1.5 2.7 3.9] 类型: float64
转换为整数: [1 2 3] 类型: int32
转换为字符串: ['1.5' '2.7' '3.9'] 类型: <U32

四、数组的索引与切片

访问和修改数组元素是最基本的操作,NumPy 提供了灵活的索引和切片方式。

4.1 一维数组的索引与切片

一维数组的索引和切片与 Python 列表类似:

python

运行

arr = np.arange(10)
print("原始数组:", arr)

# 索引访问(从0开始)
print("\n索引为3的元素:", arr[3])
print("索引为-1的元素(最后一个):", arr[-1])

# 切片操作:start:end:step
print("\n从索引2到5的元素:", arr[2:5])  # 不包含end
print("从开始到索引5的元素:", arr[:5])
print("从索引5到结束的元素:", arr[5:])
print("每隔2个元素取一个:", arr[::2])
print("逆序数组:", arr[::-1])

# 修改元素
arr[2] = 99
print("\n修改索引2的元素后:", arr)

# 切片赋值
arr[5:8] = 100
print("修改索引5-7的元素后:", arr)

输出结果:

plaintext

原始数组: [0 1 2 3 4 5 6 7 8 9]

索引为3的元素: 3
索引为-1的元素(最后一个): 9

从索引2到5的元素: [2 3 4]
从开始到索引5的元素: [0 1 2 3 4]
从索引5到结束的元素: [5 6 7 8 9]
每隔2个元素取一个: [0 2 4 6 8]
逆序数组: [9 8 7 6 5 4 3 2 1 0]

修改索引2的元素后: [ 0  1 99  3  4  5  6  7  8  9]
修改索引5-7的元素后: [  0   1  99   3   4 100 100 100   8   9]

4.2 多维数组的索引与切片

多维数组的索引和切片稍微复杂一些,需要为每个维度指定索引或切片:

python

运行

# 创建一个3x4的二维数组
arr = np.arange(12).reshape(3, 4)
print("原始数组:")
print(arr)

# 访问单个元素
print("\n第二行第三列的元素(索引从0开始):", arr[1, 2])
# 等价于
print("另一种方式:", arr[1][2])

# 切片操作
# 取前两行,前两列
print("\n前两行,前两列:")
print(arr[:2, :2])

# 取所有行,每隔一列取一列
print("\n所有行,每隔一列:")
print(arr[:, ::2])

# 取最后一行
print("\n最后一行:", arr[-1])

# 取最后一列
print("\n最后一列:", arr[:, -1])

# 取子数组
sub_arr = arr[1:3, 1:3]
print("\n子数组(行1-2,列1-2):")
print(sub_arr)

# 修改子数组会影响原始数组(因为是视图,不是副本)
sub_arr[:] = 0
print("\n修改子数组后的原始数组:")
print(arr)

输出结果:

plaintext

原始数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

第二行第三列的元素(索引从0开始): 6
另一种方式: 6

前两行,前两列:
[[0 1]
 [4 5]]

所有行,每隔一列:
[[ 0  2]
 [ 4  6]
 [ 8 10]]

最后一行: [ 8  9 10 11]

最后一列: [ 3  7 11]

子数组(行1-2,列1-2):
[[5 6]
 [9 10]]

修改子数组后的原始数组:
[[ 0  1  2  3]
 [ 4  0  0  7]
 [ 8  0  0 11]]

4.3 高级索引

除了基本的索引和切片,NumPy 还支持更高级的索引方式。

4.3.1 整数数组索引

可以使用整数数组来索引:

python

运行

arr = np.arange(10, 30)
print("原始数组:", arr)

# 使用整数数组索引
indices = [1, 3, 5, 7]
print("\n索引为1,3,5,7的元素:", arr[indices])

# 负数索引
print("索引为-2,-4的元素:", arr[[-2, -4]])

# 二维数组的整数索引
arr2d = np.arange(12).reshape(3, 4)
print("\n二维数组:")
print(arr2d)

# 取(0,0), (1,1), (2,2)位置的元素
print("\n特定位置的元素:", arr2d[[0, 1, 2], [0, 1, 2]])

# 取第0行和第2行的第1列和第3列
print("第0行和第2行的第1列和第3列:")
print(arr2d[[0, 2]][:, [1, 3]])

输出结果:

plaintext

原始数组: [10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29]

索引为1,3,5,7的元素: [11 13 15 17]
索引为-2,-4的元素: [27 25]

二维数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

特定位置的元素: [ 0  5 10]
第0行和第2行的第1列和第3列:
[[ 1  3]
 [ 9 11]]
4.3.2 布尔索引

可以使用布尔数组来筛选元素:

python

运行

arr = np.arange(10, 30)
print("原始数组:", arr)

# 创建布尔数组
mask = (arr > 15) & (arr < 25)
print("\n布尔掩码:", mask)

# 使用布尔索引筛选元素
print("筛选后的元素:", arr[mask])

# 直接在索引中使用条件
print("大于20的元素:", arr[arr > 20])

# 二维数组的布尔索引
arr2d = np.arange(12).reshape(3, 4)
print("\n二维数组:")
print(arr2d)

# 筛选出所有大于5的元素
print("所有大于5的元素:", arr2d[arr2d > 5])

# 按行筛选
row_mask = [True, False, True]
print("第一行和第三行:")
print(arr2d[row_mask])

输出结果:

plaintext

原始数组: [10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29]

布尔掩码: [False False False False False False  True  True  True  True  True  True
  True  True  True False False False False False]
筛选后的元素: [16 17 18 19 20 21 22 23 24]
大于20的元素: [21 22 23 24 25 26 27 28 29]

二维数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
所有大于5的元素: [ 6  7  8  9 10 11]
第一行和第三行:
[[ 0  1  2  3]
 [ 8  9 10 11]]

五、数组的形状操作

NumPy 提供了多种方法来改变数组的形状,而不改变其数据。

5.1 重塑数组

reshape()方法可以改变数组的形状:

python

运行

arr = np.arange(12)
print("原始数组:", arr)

# 重塑为3x4的二维数组
arr_3x4 = arr.reshape(3, 4)
print("\n3x4的数组:")
print(arr_3x4)

# 重塑为2x2x3的三维数组
arr_2x2x3 = arr.reshape(2, 2, 3)
print("\n2x2x3的数组:")
print(arr_2x2x3)

# 可以指定一个维度为-1,让NumPy自动计算该维度的大小
arr_auto = arr.reshape(2, -1)  # 2行,自动计算列数
print("\n2行的数组:")
print(arr_auto)

arr_auto2 = arr.reshape(-1, 3)  # 3列,自动计算行数
print("\n3列的数组:")
print(arr_auto2)

输出结果:

plaintext

原始数组: [ 0  1  2  3  4  5  6  7  8  9 10 11]

3x4的数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

2x2x3的数组:
[[[ 0  1  2]
  [ 3  4  5]]

 [[ 6  7  8]
  [ 9 10 11]]]

2行的数组:
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]]

3列的数组:
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]

5.2 展平数组

可以将多维数组展平为一维数组:

python

运行

arr = np.arange(12).reshape(3, 4)
print("原始数组:")
print(arr)

# 使用ravel()展平
flattened_ravel = arr.ravel()
print("\nravel()展平:", flattened_ravel)

# 使用flatten()展平
flattened_flatten = arr.flatten()
print("flatten()展平:", flattened_flatten)

# 修改展平后的数组,查看对原始数组的影响
flattened_ravel[0] = 99
print("\n修改ravel()结果后原始数组:")
print(arr)

flattened_flatten[1] = 99
print("修改flatten()结果后原始数组:")
print(arr)

输出结果:

plaintext

原始数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

ravel()展平: [ 0  1  2  3  4  5  6  7  8  9 10 11]
flatten()展平: [ 0  1  2  3  4  5  6  7  8  9 10 11]

修改ravel()结果后原始数组:
[[99  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
修改flatten()结果后原始数组:
[[99  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

注意:ravel()返回的是数组的视图(如果可能),修改它会影响原始数组;而flatten()总是返回一个新的数组(副本),修改它不会影响原始数组。

5.3 数组转置

数组转置是交换数组的维度,对于二维数组来说就是行列互换:

python

运行

arr = np.arange(12).reshape(3, 4)
print("原始数组:")
print(arr)

# 使用T属性转置
transposed_T = arr.T
print("\n使用T属性转置:")
print(transposed_T)

# 使用transpose()方法转置
transposed_transpose = arr.transpose()
print("使用transpose()方法转置:")
print(transposed_transpose)

# 三维数组的转置
arr3d = np.arange(24).reshape(2, 3, 4)
print("\n三维数组:")
print(arr3d)
print("形状:", arr3d.shape)

# 交换轴0和轴1
transposed3d = arr3d.transpose(1, 0, 2)
print("\n交换轴0和轴1后的三维数组:")
print(transposed3d)
print("形状:", transposed3d.shape)

# 使用swapaxes()交换轴
swapped = arr3d.swapaxes(0, 2)
print("\n交换轴0和轴2后的三维数组:")
print(swapped)
print("形状:", swapped.shape)

输出结果:

plaintext

原始数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

使用T属性转置:
[[ 0  4  8]
 [ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]]
使用transpose()方法转置:
[[ 0  4  8]
 [ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]]

三维数组:
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
形状: (2, 3, 4)

交换轴0和轴1后的三维数组:
[[[ 0  1  2  3]
  [12 13 14 15]]

 [[ 4  5  6  7]
  [16 17 18 19]]

 [[ 8  9 10 11]
  [20 21 22 23]]]
形状: (3, 2, 4)

交换轴0和轴2后的三维数组:
[[[ 0 12]
  [ 4 16]
  [ 8 20]]

 [[ 1 13]
  [ 5 17]
  [ 9 21]]

 [[ 2 14]
  [ 6 18]
  [10 22]]

 [[ 3 15]
  [ 7 19]
  [11 23]]]
形状: (4, 3, 2)

5.4 数组的合并与分割

5.4.1 合并数组

可以使用concatenate()vstack()hstack()等函数合并数组:

python

运行

# 创建两个数组
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
print("arr1:")
print(arr1)
print("\narr2:")
print(arr2)

# 使用concatenate()在轴0上合并(垂直方向)
concat_axis0 = np.concatenate([arr1, arr2], axis=0)
print("\n在轴0上合并:")
print(concat_axis0)

# 使用concatenate()在轴1上合并(水平方向)
concat_axis1 = np.concatenate([arr1, arr2], axis=1)
print("在轴1上合并:")
print(concat_axis1)

# 使用vstack()垂直合并(等价于axis=0)
vstacked = np.vstack([arr1, arr2])
print("\nvstack垂直合并:")
print(vstacked)

# 使用hstack()水平合并(等价于axis=1)
hstacked = np.hstack([arr1, arr2])
print("hstack水平合并:")
print(hstacked)

# 合并一维数组
arr3 = np.array([1, 2, 3])
arr4 = np.array([4, 5, 6])
print("\n一维数组合并:", np.concatenate([arr3, arr4]))

# 使用dstack()按深度合并
dstacked = np.dstack([arr1, arr2])
print("\ndstack按深度合并:")
print(dstacked)
print("形状:", dstacked.shape)

输出结果:

plaintext

arr1:
[[1 2]
 [3 4]]

arr2:
[[5 6]
 [7 8]]

在轴0上合并:
[[1 2]
 [3 4]
 [5 6]
 [7 8]]
在轴1上合并:
[[1 2 5 6]
 [3 4 7 8]]

vstack垂直合并:
[[1 2]
 [3 4]
 [5 6]
 [7 8]]
hstack水平合并:
[[1 2 5 6]
 [3 4 7 8]]

一维数组合并: [1 2 3 4 5 6]

dstack按深度合并:
[[[1 5]
  [2 6]]

 [[3 7]
  [4 8]]]
形状: (2, 2, 2)
5.4.2 分割数组

可以使用split()vsplit()hsplit()等函数分割数组:

python

运行

# 创建一个6x4的数组
arr = np.arange(24).reshape(6, 4)
print("原始数组:")
print(arr)

# 使用split()在轴0上分割为3个部分
split_axis0 = np.split(arr, 3, axis=0)
print("\n在轴0上分割为3个部分:")
for i, part in enumerate(split_axis0):
    print(f"部分{i+1}:")
    print(part)

# 使用split()在指定位置分割
split_pos = np.split(arr, [2, 4], axis=0)
print("\n在轴0上的2和4位置分割:")
for i, part in enumerate(split_pos):
    print(f"部分{i+1}:")
    print(part)

# 使用vsplit()垂直分割(等价于axis=0)
vsplit_result = np.vsplit(arr, 3)
print("\nvsplit垂直分割为3个部分:")
print("部分1的形状:", vsplit_result[0].shape)

# 使用hsplit()水平分割(等价于axis=1)
hsplit_result = np.hsplit(arr, 2)
print("\nhsplit水平分割为2个部分:")
for i, part in enumerate(hsplit_result):
    print(f"部分{i+1}:")
    print(part)

# 分割一维数组
arr1d = np.arange(10)
split_1d = np.split(arr1d, [3, 7])
print("\n一维数组分割:")
for i, part in enumerate(split_1d):
    print(f"部分{i+1}:", part)

输出结果:

plaintext

原始数组:
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]

在轴0上分割为3个部分:
部分1:
[[0 1 2 3]
 [4 5 6 7]]
部分2:
[[ 8  9 10 11]
 [12 13 14 15]]
部分3:
[[16 17 18 19]
 [20 21 22 23]]

在轴0上的2和4位置分割:
部分1:
[[0 1 2 3]
 [4 5 6 7]]
部分2:
[[ 8  9 10 11]
 [12 13 14 15]]
部分3:
[[16 17 18 19]
 [20 21 22 23]]

vsplit垂直分割为3个部分:
部分1的形状: (2, 4)

hsplit水平分割为2个部分:
部分1:
[[ 0  1]
 [ 4  5]
 [ 8  9]
 [12 13]
 [16 17]
 [20 21]]
部分2:
[[ 2  3]
 [ 6  7]
 [10 11]
 [14 15]
 [18 19]
 [22 23]]

一维数组分割:
部分1: [0 1 2]
部分2: [3 4 5 6]
部分3: [7 8 9]

六、数组的运算

NumPy 数组支持丰富的算术和数学运算,并且这些运算都是向量化的,即不需要编写循环就能对数组中的所有元素进行操作。

6.1 基本算术运算

数组的算术运算会对每个元素执行相应的操作:

python

运行

arr1 = np.array([10, 20, 30, 40])
arr2 = np.array([1, 2, 3, 4])
print("arr1:", arr1)
print("arr2:", arr2)

# 加法
print("\narr1 + arr2:", arr1 + arr2)
# 等价于
print("np.add(arr1, arr2):", np.add(arr1, arr2))

# 减法
print("\narr1 - arr2:", arr1 - arr2)
# 等价于
print("np.subtract(arr1, arr2):", np.subtract(arr1, arr2))

# 乘法
print("\narr1 * arr2:", arr1 * arr2)
# 等价于
print("np.multiply(arr1, arr2):", np.multiply(arr1, arr2))

# 除法
print("\narr1 / arr2:", arr1 / arr2)
# 等价于
print("np.divide(arr1, arr2):", np.divide(arr1, arr2))

# 整数除法
print("\narr1 // arr2:", arr1 // arr2)

# 取余
print("\narr1 % arr2:", arr1 % arr2)

# 幂运算
print("\narr1 ** arr2:", arr1 ** arr2)
# 等价于
print("np.power(arr1, arr2):", np.power(arr1, arr2))

输出结果:

plaintext

arr1: [10 20 30 40]
arr2: [1 2 3 4]

arr1 + arr2: [11 22 33 44]
np.add(arr1, arr2): [11 22 33 44]

arr1 - arr2: [ 9 18 27 36]
np.subtract(arr1, arr2): [ 9 18 27 36]

arr1 * arr2: [ 10  40  90 160]
np.multiply(arr1, arr2): [ 10  40  90 160]

arr1 / arr2: [10. 10. 10. 10.]
np.divide(arr1, arr2): [10. 10. 10. 10.]

arr1 // arr2: [10 10 10 10]

arr1 % arr2: [0 0 0 0]

arr1 ** arr2: [    10    400   27000 2560000]
np.power(arr1, arr2): [    10    400   27000 2560000]

6.2 广播机制

广播(Broadcasting)是 NumPy 中一种强大的机制,它允许不同形状的数组之间进行算术运算。基本规则是:如果两个数组的维度不匹配,NumPy 会自动扩展其中一个或两个数组,使它们的形状兼容。

python

运行

# 标量与数组的运算(标量被广播到数组的每个元素)
arr = np.array([1, 2, 3, 4])
print("数组:", arr)
print("数组 + 5:", arr + 5)
print("数组 * 2:", arr * 2)

# 不同形状的数组运算
arr1 = np.array([[0, 0, 0], [10, 10, 10], [20, 20, 20]])
arr2 = np.array([1, 2, 3])
print("\narr1:")
print(arr1)
print("arr2:", arr2)
print("arr1 + arr2:")
print(arr1 + arr2)

# 另一个广播的例子
arr3 = np.array([[0], [10], [20]])
print("\narr3:")
print(arr3)
print("arr3 + arr2:")
print(arr3 + arr2)

# 不兼容的形状(无法广播)
try:
    arr4 = np.array([1, 2])
    print("\n尝试将arr1与arr4相加:")
    print(arr1 + arr4)
except ValueError as e:
    print("错误:", e)

输出结果:

plaintext

数组: [1 2 3 4]
数组 + 5: [6 7 8 9]
数组 * 2: [2 4 6 8]

arr1:
[[ 0  0  0]
 [10 10 10]
 [20 20 20]]
arr2: [1 2 3]
arr1 + arr2:
[[ 1  2  3]
 [11 12 13]
 [21 22 23]]

arr3:
[[ 0]
 [10]
 [20]]
arr3 + arr2:
[[ 1  2  3]
 [11 12 13]
 [21 22 23]]

尝试将arr1与arr4相加:
错误: operands could not be broadcast together with shapes (3,3) (2,) 

广播的规则可以总结为:

  1. 如果两个数组的维度数不同,维度较少的数组会在前面添加新维度(形状为 1)
  2. 对于每个维度,如果两个数组的形状在该维度上不相等且都不为 1,则无法广播,会抛出错误
  3. 对于每个维度,如果两个数组的形状在该维度上相等或其中一个为 1,则可以广播,结果的形状取该维度上的较大值

6.3 通用函数(ufunc)

通用函数(Universal Functions)是对数组进行元素级操作的函数,它们可以处理标量和数组,并且支持广播。

6.3.1 数学函数

python

运行

arr = np.array([1, 4, 9, 16])
print("原始数组:", arr)

# 平方根
print("\n平方根:", np.sqrt(arr))

# 指数
print("指数:", np.exp(arr))

# 对数
print("自然对数:", np.log(arr))
print("以10为底的对数:", np.log10(arr))

# 三角函数
angles = np.array([0, np.pi/2, np.pi])
print("\n角度(弧度):", angles)
print("正弦:", np.sin(angles))
print("余弦:", np.cos(angles))
print("正切:", np.tan(angles))

# 取整函数
arr = np.array([1.2, 1.5, 1.7, -1.2, -1.5, -1.7])
print("\n原始数组:", arr)
print("向上取整:", np.ceil(arr))
print("向下取整:", np.floor(arr))
print("四舍五入:", np.round(arr))
print("截断小数部分:", np.trunc(arr))

输出结果:

plaintext

原始数组: [ 1  4  9 16]

平方根: [1. 2. 3. 4.]
指数: [2.71828183e+00 5.45981500e+01 8.10308393e+03 8.88611052e+06]
自然对数: [0.         1.38629436 2.19722458 2.77258872]
以10为底的对数: [0.         0.60205999 0.95424251 1.20411998]

角度(弧度): [0.         1.57079633 3.14159265]
正弦: [0.0000000e+00 1.0000000e+00 1.2246468e-16]
余弦: [ 1.0000000e+00  6.1232340e-17 -1.0000000e+00]
正切: [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]

原始数组: [ 1.2  1.5  1.7 -1.2 -1.5 -1.7]
向上取整: [ 2.  2.  2. -1. -1. -1.]
向下取整: [ 1.  1.  1. -2. -2. -2.]
四舍五入: [ 1.  2.  2. -1. -2. -2.]
截断小数部分: [ 1.  1.  1. -1. -1. -1.]
6.3.2 统计函数

python

运行

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("原始数组:")
print(arr)

# 求和
print("\n总和:", arr.sum())
print("按行求和:", arr.sum(axis=1))
print("按列求和:", arr.sum(axis=0))

# 平均值
print("\n平均值:", arr.mean())
print("按行平均值:", arr.mean(axis=1))
print("按列平均值:", arr.mean(axis=0))

# 标准差和方差
print("\n标准差:", arr.std())
print("方差:", arr.var())

# 最大值和最小值
print("\n最大值:", arr.max())
print("最小值:", arr.min())
print("每行最大值:", arr.max(axis=1))
print("每列最小值:", arr.min(axis=0))

# 最大值和最小值的索引
print("\n最大值索引:", arr.argmax())
print("最小值索引:", arr.argmin())
print("每行最大值索引:", arr.argmax(axis=1))
print("每列最小值索引:", arr.argmin(axis=0))

# 累积和和累积积
print("\n累积和:", arr.cumsum())
print("按行累积和:")
print(arr.cumsum(axis=1))

输出结果:print (arr.cumsum (axis=1))

plaintext


输出结果:

原始数组:
[[1 2 3]
[4 5 6]
[7 8 9]]

总和: 45
按行求和: [6 15 24]
按列求和: [12 15 18]

平均值: 5.0
按行平均值: [2. 5. 8.]
按列平均值: [4. 5. 6.]

标准差: 2.581988897471611
方差: 6.666666666666667

最大值: 9
最小值: 1
每行最大值: [3 6 9]
每列最小值: [1 2 3]

最大值索引: 8
最小值索引: 0
每行最大值索引: [2 2 2]
每列最小值索引: [0 0 0]

累积和: [1 3 6 10 15 21 28 36 45]
按行累积和:
[[ 1 3 6]
[ 4 9 15]
[ 7 15 24]]

plaintext


## 七、数组的迭代

虽然NumPy鼓励使用向量化操作来避免显式循环,但在某些情况下,我们仍然需要迭代数组的元素。

### 7.1 基本迭代

对于一维数组,迭代与Python列表类似:

```python
# 一维数组迭代
arr1d = np.array([1, 2, 3, 4, 5])
print("一维数组迭代:")
for element in arr1d:
    print(element, end=' ')
print()

输出结果:

plaintext

一维数组迭代:
1 2 3 4 5 

对于多维数组,默认迭代的是第一轴(行):

python

运行

# 二维数组迭代
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n二维数组默认迭代(行):")
for row in arr2d:
    print(row)

# 迭代每个元素
print("\n二维数组迭代每个元素:")
for row in arr2d:
    for element in row:
        print(element, end=' ')
print()

输出结果:

plaintext

二维数组默认迭代(行):
[1 2 3]
[4 5 6]
[7 8 9]

二维数组迭代每个元素:
1 2 3 4 5 6 7 8 9 

7.2 使用 flat 属性迭代

flat属性可以将数组展平为一维迭代器:

python

运行

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("使用flat属性迭代每个元素:")
for element in arr2d.flat:
    print(element, end=' ')
print()

输出结果:

plaintext

使用flat属性迭代每个元素:
1 2 3 4 5 6 7 8 9 

7.3 使用 nditer 迭代器

np.nditer是一个灵活的迭代器,可以控制迭代的方式:

python

运行

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("原始数组:")
print(arr2d)

# 基本迭代(C风格,行优先)
print("\nC风格迭代(行优先):")
for element in np.nditer(arr2d, order='C'):
    print(element, end=' ')
print()

# Fortran风格迭代(列优先)
print("Fortran风格迭代(列优先):")
for element in np.nditer(arr2d, order='F'):
    print(element, end=' ')
print()

# 修改数组元素(需要指定op_flags)
print("\n修改数组元素:")
arr_copy = arr2d.copy()
for element in np.nditer(arr_copy, op_flags=['readwrite']):
    element[...] = element * 2
print(arr_copy)

# 同时迭代多个数组
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
print("\n同时迭代两个数组:")
for x, y in np.nditer([arr1, arr2]):
    print(f"{x} * {y} = {x * y}", end='; ')
print()

输出结果:

plaintext

原始数组:
[[1 2 3]
 [4 5 6]
 [7 8 9]]

C风格迭代(行优先):
1 2 3 4 5 6 7 8 9 

Fortran风格迭代(列优先):
1 4 7 2 5 8 3 6 9 

修改数组元素:
[[ 2  4  6]
 [ 8 10 12]
 [14 16 18]]

同时迭代两个数组:
1 * 5 = 5; 2 * 6 = 12; 3 * 7 = 21; 4 * 8 = 32; 

八、数组的高级操作

8.1 数组的复制

在 NumPy 中,数组的复制需要特别注意,因为简单的赋值通常只是创建一个引用,而不是真正的副本:

python

运行

# 简单赋值(引用,不是副本)
arr = np.array([1, 2, 3, 4])
arr_ref = arr
arr_ref[0] = 99
print("简单赋值后原数组:", arr)  # 原数组被修改

# 使用view()创建浅拷贝(视图)
arr = np.array([1, 2, 3, 4])
arr_view = arr.view()
arr_view[0] = 99
print("\n修改视图后原数组:", arr)  # 原数组被修改
print("视图的形状修改不影响原数组:")
arr_view.shape = (2, 2)
print("视图形状:", arr_view.shape)
print("原数组形状:", arr.shape)

# 使用copy()创建深拷贝
arr = np.array([1, 2, 3, 4])
arr_copy = arr.copy()
arr_copy[0] = 99
print("\n修改副本后原数组:", arr)  # 原数组不受影响
print("副本:", arr_copy)

输出结果:

plaintext

简单赋值后原数组: [99  2  3  4]

修改视图后原数组: [99  2  3  4]
视图的形状修改不影响原数组:
视图形状: (2, 2)
原数组形状: (4,)

修改副本后原数组: [1 2 3 4]
副本: [99  2  3  4]

总结:

  • 简单赋值(arr_ref = arr):创建引用,两者指向同一数据
  • view()方法:创建浅拷贝(视图),共享数据但可以有不同的形状
  • copy()方法:创建深拷贝,完全独立的数组

8.2 数组的排序

NumPy 提供了多种排序方法:

python

运行

# 一维数组排序
arr1d = np.array([3, 1, 4, 1, 5, 9, 2, 6])
print("一维数组:", arr1d)
print("排序后:", np.sort(arr1d))
print("原数组(未改变):", arr1d)  # sort()返回新数组,不修改原数组

# 原地排序(修改原数组)
arr1d.sort()
print("原地排序后:", arr1d)

# 二维数组排序
arr2d = np.array([[3, 1, 4], [1, 5, 9], [2, 6, 5]])
print("\n二维数组:")
print(arr2d)

# 按行排序
print("按行排序:")
print(np.sort(arr2d, axis=1))

# 按列排序
print("按列排序:")
print(np.sort(arr2d, axis=0))

# 按特定方式排序
dt = np.dtype([('name', 'S10'), ('age', int)])
people = np.array([
    ('Alice', 25),
    ('Bob', 20),
    ('Charlie', 30)
], dtype=dt)
print("\n结构化数组:")
print(people)
print("按年龄排序:")
print(np.sort(people, order='age'))

输出结果:

plaintext

一维数组: [3 1 4 1 5 9 2 6]
排序后: [1 1 2 3 4 5 6 9]
原数组(未改变): [3 1 4 1 5 9 2 6]
原地排序后: [1 1 2 3 4 5 6 9]

二维数组:
[[3 1 4]
 [1 5 9]
 [2 6 5]]
按行排序:
[[1 3 4]
 [1 5 9]
 [2 5 6]]
按列排序:
[[1 1 4]
 [2 5 5]
 [3 6 9]]

结构化数组:
[(b'Alice', 25) (b'Bob', 20) (b'Charlie', 30)]
按年龄排序:
[(b'Bob', 20) (b'Alice', 25) (b'Charlie', 30)]

8.3 条件操作

np.where()函数是一个强大的条件操作工具,类似于三元表达式:

python

运行

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
print("原始数组:", arr)

# 基本用法:where(condition, x, y) 满足条件返回x,否则返回y
result = np.where(arr % 2 == 0, "偶数", "奇数")
print("\n奇偶判断:", result)

# 对满足条件的元素进行修改
arr2 = arr.copy()
arr2[np.where(arr2 % 2 == 0)] = 0
print("偶数替换为0:", arr2)

# 二维数组的条件操作
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n二维数组:")
print(arr2d)

# 将大于5的元素替换为10,否则替换为0
result2d = np.where(arr2d > 5, 10, 0)
print("条件替换后:")
print(result2d)

# 找到满足条件的元素的索引
indices = np.where(arr2d > 5)
print("大于5的元素的索引:", indices)
print("大于5的元素:", arr2d[indices])

输出结果:

plaintext

原始数组: [1 2 3 4 5 6 7 8 9]

奇偶判断: ['奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数']
偶数替换为0: [1 0 3 0 5 0 7 0 9]

二维数组:
[[1 2 3]
 [4 5 6]
 [7 8 9]]
条件替换后:
[[ 0  0  0]
 [ 0  0 10]
 [10 10 10]]
大于5的元素的索引: (array([1, 2, 2, 2]), array([2, 0, 1, 2]))
大于5的元素: [6 7 8 9]

8.4 集合操作

NumPy 提供了一些集合操作函数:

python

运行

arr1 = np.array([1, 2, 3, 4, 5])
arr2 = np.array([4, 5, 6, 7, 8])
print("arr1:", arr1)
print("arr2:", arr2)

# 交集
intersection = np.intersect1d(arr1, arr2)
print("\n交集:", intersection)

# 并集
union = np.union1d(arr1, arr2)
print("并集:", union)

# 差集(arr1 - arr2)
difference = np.setdiff1d(arr1, arr2)
print("差集(arr1 - arr2):", difference)

# 对称差集(并集 - 交集)
symmetric_diff = np.setxor1d(arr1, arr2)
print("对称差集:", symmetric_diff)

# 判断元素是否在另一个数组中
isin = np.isin(arr1, arr2)
print("\narr1中的元素是否在arr2中:", isin)
print("arr1中在arr2中的元素:", arr1[isin])

输出结果:

plaintext

arr1: [1 2 3 4 5]
arr2: [4 5 6 7 8]

交集: [4 5]
并集: [1 2 3 4 5 6 7 8]
差集(arr1 - arr2): [1 2 3]
对称差集: [1 2 3 6 7 8]

arr1中的元素是否在arr2中: [False False False  True  True]
arr1中在arr2中的元素: [4 5]

九、总结与展望

NumPy 数组是 Python 科学计算的基石,它提供了高效的多维数组操作能力,为数据分析、机器学习、科学计算等领域提供了强大的支持。本文详细介绍了 NumPy 数组的创建、属性、索引与切片、形状操作、运算、迭代以及高级操作等内容,涵盖了从基础到进阶的各个方面。

掌握 NumPy 数组的操作不仅能够提高数据处理的效率,还能为学习更高级的库(如 Pandas、Scikit-learn 等)打下坚实的基础。NumPy 的功能远不止本文所介绍的这些,还有许多高级特性和优化技巧值得深入学习和探索。

随着数据科学领域的不断发展,NumPy 作为基础库也在持续更新和完善。建议读者在实际应用中多实践、多探索,充分利用 NumPy 的强大功能来解决实际问题。

希望本文能帮助你更好地理解和使用 NumPy 数组,在数据科学的道路上走得更远!

<think>首先,用户的问题是:“我想理解numpy数组矩阵和向量的区别 请问numpy教程 数组 矩阵 向量 区别”。这需要我解释NumPy数组矩阵和向量的区别。 根据系统指令,我必须: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考提供的引用,但它们是关于NumPy数组矩阵的,不是用户问题的直接引用,所以我要基于知识来回答。 关键点:NumPy中的数组(array)、矩阵(matrix)和向量(vector)。 从引用中: - 引用[1]:提到数组排序、函数等,但没有直接解释区别。 - 引用[2]:明确指出数组的*运算符表示元素对应相加,矩阵的*运算符表示矩阵乘法。数组可以使用np.dot实现矩阵乘法。 - 引用[3]:提到numpy用于数组、向量、矩阵运算,并创建数组的方法。 实际知识: - NumPy中的数组(ndarray)是多维数组,用于存储数值数据。 - 矩阵(matrix)是ndarray的子类,专门用于矩阵运算。在NumPy中,matrix类在较新版本中不推荐使用,推荐使用ndarray进行矩阵运算。 - 向量(vector)通常是一维数组或二维数组中的行或列向量。在NumPy中,向量通常表示为1D数组。 区别: 1. **数组(ndarray)**:灵活的多维数组,支持各种操作,如元素级运算、广播等。 2. **矩阵(matrix)**:继承自ndarray,但重载了运算符(如*用于矩阵乘法)。在NumPy中,matrix类在v1.15后已弃用,建议使用ndarray和@运算符进行矩阵乘法。 3. **向量(vector)**:在NumPy中,向量通常表示为1D数组。例如,一个向量可以是形状为(n,)的数组。 在NumPy中,向量是1D数组矩阵是2D数组(可以是行向量或列向量),但矩阵类提供了特定行为。 回答结构: 1. **介绍**:简述NumPy的核心数据结构。 2. **详细解释区别**: - 数组:定义、特性、用途。 - 矩阵:历史背景、为什么弃用、与数组的区别。 - 向量:如何表示、在数组中的形式。 3. **示例代码**:展示创建和使用数组矩阵、向量的例子。 4. **总结**:关键区别。 5. **生成相关问题**:基于内容生成几个相关问题。 使用中文回答。 引用标识:在回答中,如果引用提供的文本,需添加[^1], [^2], [^3]。例如,引用[2]提到运算符区别,我可以说“如引用[2]所示”。 最终输出: - 回答部分。 - 相关问题部分。 确保LaTeX正确:例如,数学表达式用$...$,如$A \times B$。 大纲: 1. 介绍NumPy和核心概念。 2. 解释数组:多维数组。 3. 解释矩阵:作为子类,但弃用。 4. 解释向量:1D数组。 5. 区别总结:运算符、维度、推荐用法。 6. 示例代码。 7. 相关问题。</think>### NumPy数组矩阵和向量的区别 在NumPy中,数组(array)、矩阵(matrix)和向量(vector)是核心数据结构,但它们在定义、运算和用途上有显著差异。理解这些区别对高效使用NumPy至关重要。以下我将逐步解释它们的区别,并辅以示例代码。 #### 1. **数组(array)** - **定义**:数组NumPy的核心数据结构,称为`ndarray`(n-dimensional array),表示多维数据容器。它可以是一维(如向量)、二维(如矩阵)或更高维度的数据。 - **特性**: - 支持广播机制(broadcasting),允许不同形状的数组进行算术运算。 - 运算符(如`*`、`+`)执行元素级(element-wise)运算,而非矩阵乘法。 - 推荐用于大多数数值计算,因为它更灵活且高效。 - **示例代码**: ```python import numpy as np # 创建一维数组(向量) arr1d = np.array([1, 2, 3]) # 形状:(3,) # 创建二维数组(类似矩阵) arr2d = np.array([[1, 2], [3, 4]]) # 形状:(2, 2) # 元素级乘法 result = arr2d * arr2d # 输出:[[1, 4], [9, 16]] ``` 在元素级运算中,$a_{ij} \times b_{ij}$ 直接应用于每个元素。 #### 2. **矩阵(matrix)** - **定义**:矩阵NumPy中`matrix`类的对象,继承自数组,但专门设计用于线性代数运算。它本质上是二维的(2D array)。 - **特性**: - 运算符(如`*`)被重载为矩阵乘法,而非元素级运算。例如,$A \times B$ 表示矩阵乘法,而非 $A_{ij} \times B_{ij}$。 - **重要提示**:在NumPy v1.15后,`matrix`类已弃用(deprecated),官方推荐使用数组配合`@`运算符或`np.dot()`进行矩阵运算[^2]。 - 矩阵运算更符合数学习惯,但灵活性不如数组。 - **示例代码**: ```python # 创建矩阵(不推荐,仅用于演示) mat = np.matrix([[1, 2], [3, 4]]) # 矩阵乘法 result = mat * mat # 输出:[[ 7, 10], [15, 22]],对应矩阵乘法公式 $C = AB$ # 使用数组替代矩阵乘法 arr2d = np.array([[1, 2], [3, 4]]) result_dot = np.dot(arr2d, arr2d) # 或 arr2d @ arr2d ``` 矩阵乘法公式为:$C_{ij} = \sum_{k} A_{ik} B_{kj}$,与数组的元素级运算不同。 #### 3. **向量(vector)** - **定义**:向量在NumPy中没有独立的数据类型,通常表示为**一维数组**(1D array)。数学上,向量可以是行向量(row vector)或列向量(column vector),但在NumPy中,它们都是形状为`(n,)`的数组。 - **特性**: - 维度处理:1D数组在运算时可能被自动广播为行或列向量。例如,在矩阵乘法中,1D数组会被视为行向量或列向量,具体取决于上下文。 - 与矩阵的区别:向量不是矩阵的子类,而是数组的特殊形式。强行转换为矩阵会引入额外维度。 - **示例代码**: ```python # 创建向量(一维数组) vector = np.array([1, 2, 3]) # 形状:(3,) # 与矩阵乘法(需显式处理维度) mat = np.array([[1, 2], [3, 4], [5, 6]]) # 形状:(3, 2) result = vector @ mat # 输出:[22, 28],解释为行向量乘以矩阵 # 转换为列向量(增加维度) col_vector = vector.reshape(3, 1) # 形状:(3, 1) ``` #### 4. **关键区别总结** | 特性 | 数组 (array) | 矩阵 (matrix) | 向量 (vector) | |--------------|----------------------------|---------------------------|---------------------------| | **维度** | 任意维度(1D, 2D, 等) | 仅二维(2D) | 一维(1D数组) | | **核心类** | `numpy.ndarray` | `numpy.matrix`(弃用) | `numpy.ndarray`(1D) | | **运算符行为** | `*` 为元素级运算 | `*` 为矩阵乘法 | 同数组,但广播时自动调整 | | **矩阵乘法** | 需用`np.dot()`或`@`运算符 | 直接使用`*` | 需用`@`或`np.dot()` | | **推荐用法** | 通用数值计算,优先使用 | 不推荐新代码使用 | 作为1D数组处理 | | **广播机制** | 支持 | 有限支持 | 支持,但需注意维度对齐 | 例如,在元素级乘法中: - 数组:$ \begin{bmatrix} a & b \\ c & d \end{bmatrix} * \begin{bmatrix} e & f \\ g & h \end{bmatrix} = \begin{bmatrix} a e & b f \\ c g & d h \end{bmatrix} $ - 矩阵:$ \begin{bmatrix} a & b \\ c & d \end{bmatrix} * \begin{bmatrix} e & f \\ g & h \end{bmatrix} = \begin{bmatrix} a e + b g & a f + b h \\ c e + d g & c f + d h \end{bmatrix} $ #### 5. **最佳实践建议** - **优先使用数组**:现代NumPy推荐`ndarray`进行所有操作,矩阵运算用`@`运算符(如`A @ B`),元素级运算用`*`[^2][^3]。 - **避免矩阵类**:由于弃用,使用数组能保证代码兼容性和性能。 - **向量处理**:保持为1D数组,必要时用`reshape()`调整维度(如`.reshape(-1, 1)`转换为列向量)。 - **性能考虑**:数组在底层由C实现,处理大数据时比Python列表快得多[^3]。 通过理解这些区别,你可以更高效地使用NumPy进行科学计算。如果有特定场景(如线性代数),可进一步学习`numpy.linalg`模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值