NumPy教程:从基础到高级,玩转数组操作 – wiki基地

NumPy教程:从基础到高级,玩转数组操作

NumPy(Numerical Python)是Python中用于科学计算的核心库。它提供了强大的多维数组对象(ndarray),以及用于处理这些数组的各种函数。与Python内置的列表相比,NumPy数组在存储效率和计算性能上都有显著优势,尤其是在处理大规模数值数据时。这使得NumPy成为数据科学、机器学习和工程领域不可或缺的工具。

本教程将引导您从NumPy的基础知识,逐步深入到高级数组操作,帮助您全面掌握这一强大工具。

一、安装NumPy

在开始之前,您需要安装NumPy。最常用的安装方式有两种:

1. 使用pip安装 (推荐)

打开您的终端或命令行工具,运行以下命令:
bash
pip install numpy

2. 使用Conda安装 (推荐环境管理)

如果您使用Anaconda或Miniconda,可以通过conda进行安装。通常,建议为项目创建一个新的虚拟环境:

bash
conda create -n my_numpy_env python=3.9 # 创建一个名为my_numpy_env的Python 3.9环境
conda activate my_numpy_env # 激活环境
conda install numpy # 在激活的环境中安装NumPy

二、NumPy数组基础

NumPy的核心是其N维数组对象,即ndarray。它是一个同构的数据容器,意味着数组中的所有元素都必须是相同类型。

1. 创建数组

有多种方法可以创建NumPy数组:

  • 从Python列表或元组创建:

    “`python
    import numpy as np

    从列表创建一维数组

    arr_1d = np.array([1, 2, 3, 4, 5])
    print(“一维数组:”, arr_1d)
    print(“数据类型:”, type(arr_1d)) # 输出:

    从嵌套列表创建二维数组

    arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
    print(“二维数组:\n”, arr_2d)

    从元组创建数组

    arr_from_tuple = np.array((10, 20, 30))
    print(“从元组创建的数组:”, arr_from_tuple)
    “`

  • 创建全零、全一或空数组:

    “`python

    创建2×3的全零数组

    zeros_array = np.zeros((2, 3))
    print(“全零数组:\n”, zeros_array)

    创建3×2的全一数组

    ones_array = np.ones((3, 2))
    print(“全一数组:\n”, ones_array)

    创建2×2的空数组(内容随机)

    empty_array = np.empty((2, 2))
    print(“空数组:\n”, empty_array)
    “`

  • 创建序列数组:

    “`python

    使用arange创建等差序列(类似于Python的range)

    arange(start, stop_exclusive, step)

    range_array = np.arange(0, 10, 2) # 0, 2, 4, 6, 8
    print(“arange数组:”, range_array)

    使用linspace创建等间隔序列

    linspace(start, stop_inclusive, num_elements)

    linspace_array = np.linspace(0, 1, 5) # 0.0, 0.25, 0.5, 0.75, 1.0
    print(“linspace数组:”, linspace_array)
    “`

2. 数组属性

NumPy数组有许多有用的属性,可以帮助我们了解数组的结构和内容:

“`python
import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6]])

print(“数组:\n”, arr)
print(“维度数量 (ndim):”, arr.ndim) # 数组的轴(维度)的数量,例如:2表示二维数组
print(“数组形状 (shape):”, arr.shape) # 每个维度的大小,例如:(2, 3)表示2行3列
print(“元素总数 (size):”, arr.size) # 数组中元素的总数量
print(“数据类型 (dtype):”, arr.dtype) # 数组元素的类型,例如:int64
print(“单个元素字节大小 (itemsize):”, arr.itemsize) # 数组中每个元素占用的字节数
print(“数组总字节数 (nbytes):”, arr.nbytes) # 整个数组占用的总字节数
“`

三、数组索引与切片

NumPy数组的索引和切片功能强大且灵活,允许我们高效地访问和修改数组中的元素。

1. 单元素索引

通过指定索引来访问数组中的单个元素。对于多维数组,需要为每个维度提供一个索引。

“`python
arr = np.array([10, 20, 30, 40, 50])
print(“第一个元素:”, arr[0]) # 输出: 10
print(“最后一个元素 (负索引):”, arr[-1]) # 输出: 50

arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“数组:\n”, arr_2d)
print(“位于 (0, 1) 的元素:”, arr_2d[0, 1]) # 第一行第二列,输出: 2
print(“位于 (2, 0) 的元素:”, arr_2d[2, 0]) # 第三行第一列,输出: 7
“`

2. 切片 ([start:stop:step])

切片用于提取数组的子部分。其语法与Python列表切片类似,但可以应用于多个维度。

“`python
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

print(“前5个元素:”, arr[:5]) # 输出: [0 1 2 3 4]
print(“从索引5开始到结束:”, arr[5:]) # 输出: [5 6 7 8 9]
print(“从索引2到7,步长为2:”, arr[2:8:2]) # 输出: [2 4 6]
print(“反转数组:”, arr[::-1]) # 输出: [9 8 7 6 5 4 3 2 1 0]

多维数组切片

arr_2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print(“二维数组:\n”, arr_2d)

print(“所有行,前两列:\n”, arr_2d[:, :2]) # 输出: [[ 1 2], [ 5 6], [ 9 10]]
print(“前两行,所有列:\n”, arr_2d[:2, :]) # 输出: [[1 2 3 4], [5 6 7 8]]
print(“特定行和列 (从索引1开始的行,从索引1到2的列):\n”, arr_2d[1:, 1:3])

输出: [[ 6 7], [10 11]]

``
**重要提示:** NumPy切片通常返回原始数组的**视图 (view)**,而不是副本。这意味着如果您修改切片,原始数组也会随之改变。如果需要独立副本,请使用
.copy()`方法。

四、数组操作与重塑

NumPy提供了丰富的函数来改变数组的形状、转置以及合并/拆分数组。

1. 重塑 (reshape(), flatten(), ravel())

  • reshape():改变数组的形状,而不改变其数据。
    “`python
    arr = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9]
    print(“原始数组:”, arr)

    重塑为3×3矩阵

    reshaped_arr = arr.reshape((3, 3))
    print(“重塑为3×3:\n”, reshaped_arr)

    使用-1自动推断维度

    reshaped_arr_auto = arr.reshape(-1, 3) # 自动计算行数
    print(“自动推断行数重塑:\n”, reshaped_arr_auto)
    “`

  • flatten():返回数组的扁平化(一维)副本。

  • ravel():返回数组的扁平化视图(如果可能)。

    python
    print("扁平化 (副本):", reshaped_arr.flatten())
    print("扁平化 (视图):", reshaped_arr.ravel())

2. 转置 (.T, np.transpose())

转置操作会交换数组的轴。对于二维数组,行变成列,列变成行。

“`python
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“原始二维数组:\n”, arr_2d)

transposed_arr = arr_2d.T # 或者 np.transpose(arr_2d)
print(“转置数组:\n”, transposed_arr)
“`

3. 堆叠/合并 (np.vstack(), np.hstack(), np.concatenate())

  • np.vstack():垂直堆叠(按行)。
  • np.hstack():水平堆叠(按列)。
  • np.concatenate():沿指定轴合并数组。

    “`python
    arr1 = np.array([1, 2, 3])
    arr2 = np.array([4, 5, 6])

    v_stack = np.vstack((arr1, arr2))
    print(“垂直堆叠:\n”, v_stack)

    h_stack = np.hstack((arr1, arr2))
    print(“水平堆叠:”, h_stack)

    concatenate要求所有数组除了连接轴以外的维度都相同

    所以需要先将一维数组重塑为二维数组

    concat_arr = np.concatenate((arr1.reshape(1, -1), arr2.reshape(1, -1)), axis=0)
    print(“沿轴0合并 (行合并):\n”, concat_arr)
    “`

4. 拆分 (np.hsplit(), np.vsplit(), np.split())

  • np.hsplit():水平拆分(按列)。
  • np.vsplit():垂直拆分(按行)。
  • np.split():沿指定轴拆分数组。

    “`python
    arr = np.arange(1, 10).reshape(3, 3)
    print(“原始数组:\n”, arr)

    h_split = np.hsplit(arr, 3) # 水平拆分为3个等宽数组
    print(“水平拆分:”, h_split)

    v_split = np.vsplit(arr, 3) # 垂直拆分为3个等高数组
    print(“垂直拆分:”, v_split)

    沿轴1(列)在索引1和2处进行拆分

    split_arr = np.split(arr, [1, 2], axis=1)
    print(“在特定索引处拆分:\n”, split_arr)
    “`

五、基本算术运算

NumPy数组支持所有标准的算术运算符,这些运算都是元素级的。

1. 元素级运算

“`python
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

print(“加法:”, arr1 + arr2) # [5 7 9]
print(“减法:”, arr1 – arr2) # [-3 -3 -3]
print(“乘法:”, arr1 * arr2) # [ 4 10 18]
print(“除法:”, arr1 / arr2) # [0.25 0.4 0.5 ]
print(“幂运算 (arr1^2):”, arr1 ** 2) # [1 4 9]
“`

2. 比较运算

比较运算符也作用于元素级,并返回一个布尔数组。

python
print("比较 (arr1 > 2):", arr1 > 2) # [False False True]
print("比较 (arr1 == arr2):", arr1 == arr2) # [False False False]

3. 通用函数 (Ufuncs)

NumPy提供了大量的通用函数(Universal Functions,ufuncs),它们是作用于ndarray对象的元素级数学函数。

“`python
arr = np.array([0, np.pi/2, np.pi]) # pi是NumPy中定义的圆周率常量

print(“正弦 (sin):”, np.sin(arr))
print(“余弦 (cos):”, np.cos(arr))
print(“指数 (exp):”, np.exp(arr))
print(“平方根 (sqrt):”, np.sqrt(arr))
print(“自然对数 (log):”, np.log(arr + 1e-9)) # 添加小值防止log(0)
“`

六、高级数组操作

NumPy的强大之处还在于其高级功能,如广播、线性代数、随机数生成等。

1. 广播 (Broadcasting)

广播是NumPy在不同形状的数组上执行算术运算的一种机制。它允许较小的数组在较大的数组上“广播”其值,而无需显式复制数据。

广播规则:
当对两个数组进行操作时,NumPy会逐元素地比较它们的形状(从末尾维度开始)。如果满足以下条件之一,则两个维度兼容:
1. 维度相等。
2. 其中一个维度为1。
如果不兼容,则会引发 ValueError

“`python
arr = np.array([[1, 2, 3], [4, 5, 6]]) # 形状 (2, 3)
scalar = 10 # 形状 ()

标量被广播到数组的每个元素

print(“数组 + 标量:\n”, arr + scalar)

vec = np.array([10, 20, 30]) # 形状 (3,)

向量被广播到数组的每一行

print(“数组 + 向量:\n”, arr + vec)

形状不兼容的示例(会引发错误)

vec_incompatible = np.array([10, 20]) # 形状 (2,)

print(arr + vec_incompatible)

ValueError: operands could not be broadcast together with shapes (2,3) (2,)

“`

2. 线性代数

NumPy的linalg模块提供了执行线性代数操作的函数。

“`python
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

矩阵乘法 (点积)

print(“矩阵乘法 (A @ B):\n”, A @ B) # 或者 np.dot(A, B)

元素级乘法

print(“元素级乘法 (A * B):\n”, A * B)

行列式

print(“A的行列式:”, np.linalg.det(A))

逆矩阵

print(“A的逆矩阵:\n”, np.linalg.inv(A))

特征值和特征向量

eigenvalues, eigenvectors = np.linalg.eig(A)
print(“A的特征值:”, eigenvalues)
print(“A的特征向量:\n”, eigenvectors)
“`

3. 随机数生成 (np.random)

np.random模块提供了各种生成随机数的函数。

“`python

生成0到1之间的随机浮点数

print(“单个随机浮点数 (0-1):”, np.random.rand())
print(“2×2随机浮点数 (0-1):\n”, np.random.rand(2, 2))

从标准正态分布(均值0,标准差1)生成随机数

print(“单个标准正态分布随机数:”, np.random.randn())
print(“2×2标准正态分布随机数:\n”, np.random.randn(2, 2))

生成指定范围的随机整数

print(“单个随机整数 (0-9):”, np.random.randint(10)) # 不包含10
print(“2×2随机整数 (0-9):\n”, np.random.randint(0, 10, size=(2, 2)))
“`

4. 排序 (np.sort(), np.argsort())

  • np.sort():返回数组的排序副本。可以指定沿哪个轴进行排序。
  • np.argsort():返回对数组进行排序的索引。

“`python
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])

sorted_arr = np.sort(arr)
print(“排序后的数组:”, sorted_arr)

arr_2d = np.array([[3, 2, 1], [6, 5, 4]])
print(“原始二维数组:\n”, arr_2d)
print(“沿行排序 (axis=1):\n”, np.sort(arr_2d, axis=1))
print(“沿列排序 (axis=0):\n”, np.sort(arr_2d, axis=0))

indices = np.argsort(arr)
print(“排序索引:”, indices)
print(“根据索引排序的数组:”, arr[indices])
“`

5. 统计操作

NumPy提供了丰富的统计函数,用于计算数组的均值、中位数、标准差、和、最大值、最小值等。

“`python
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

print(“均值 (Mean):”, np.mean(arr))
print(“中位数 (Median):”, np.median(arr))
print(“标准差 (Standard Deviation):”, np.std(arr))
print(“方差 (Variance):”, np.var(arr))
print(“总和 (Sum):”, np.sum(arr))
print(“最小值 (Minimum):”, np.min(arr))
print(“最大值 (Maximum):”, np.max(arr))

arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(“所有元素的均值:”, np.mean(arr_2d))
print(“沿列的均值 (axis=0):”, np.mean(arr_2d, axis=0)) # [2.5 3.5 4.5]
print(“沿行的均值 (axis=1):”, np.mean(arr_2d, axis=1)) # [2. 5.]
“`

6. 布尔索引 (Boolean Indexing)

布尔索引使用布尔数组(掩码)来选择满足特定条件的元素。

“`python
arr = np.array([10, 20, 30, 40, 50, 60])

mask = arr > 30 # 创建一个布尔掩码
print(“布尔掩码:”, mask) # [False False False True True True]

selected_elements = arr[mask] # 使用掩码选择元素
print(“选择大于30的元素:”, selected_elements) # [40 50 60]

直接使用条件进行布尔索引

print(“选择偶数元素:”, arr[arr % 2 == 0]) # [10 20 30 40 50 60]

二维数组的布尔索引

arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(“选择大于5的二维数组元素:”, arr_2d[arr_2d > 5]) # 返回一维数组: [6 7 8 9]
“`

7. 花式索引 (Fancy Indexing)

花式索引使用整数数组来选择特定行、列或元素。

“`python
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])

indices = np.array([0, 3, 5])
print(“选择特定索引的元素:”, arr[indices]) # [10 40 60]

二维数组的花式索引 (选择特定行)

arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
row_indices = np.array([0, 2])
print(“选择特定行:\n”, arr_2d[row_indices]) # 选择第0行和第2行

使用坐标对选择特定元素

row_coords = np.array([0, 1, 2])
col_coords = np.array([0, 1, 0])
print(“选择 (0,0), (1,1), (2,0) 处的元素:”, arr_2d[row_coords, col_coords]) # [1 5 7]
“`
注意: 与切片不同,花式索引总是返回数据的副本,而不是视图。

七、数组的保存与加载

NumPy提供了方便的函数来将数组保存到磁盘,以及从磁盘加载数组。

1. 保存和加载单个数组

  • np.save():将单个数组保存到二进制 .npy 格式文件。
  • np.load():从 .npy 文件加载数组。

“`python
data = np.arange(100).reshape(10, 10)
np.save(‘my_array.npy’, data)
print(“数组已保存到 my_array.npy”)

loaded_data = np.load(‘my_array.npy’)
print(“从 my_array.npy 加载的数组:\n”, loaded_data)
“`

2. 保存和加载多个数组

  • np.savez():将多个数组保存到未压缩的 .npz 文件中。您可以使用关键字参数为每个数组命名。
  • np.savez_compressed():将多个数组保存到压缩的 .npz 文件中,节省磁盘空间。
  • np.load():加载 .npz 文件时,它返回一个类似于字典的对象,可以通过键(即保存时的关键字参数)访问各个数组。

“`python
array1 = np.array([1, 2, 3])
array2 = np.array([[10, 20], [30, 40]])

np.savez(‘multiple_arrays.npz’, a=array1, b=array2)
print(“多个数组已保存到 multiple_arrays.npz”)

np.savez_compressed(‘compressed_arrays.npz’, x=array1, y=array2)
print(“多个数组已保存到 compressed_arrays.npz (压缩格式)”)

loaded_multi = np.load(‘multiple_arrays.npz’)
print(“加载的数组 ‘a’:”, loaded_multi[‘a’])
print(“加载的数组 ‘b’:\n”, loaded_multi[‘b’])

loaded_compressed = np.load(‘compressed_arrays.npz’)
print(“从压缩文件加载的数组 ‘x’:”, loaded_compressed[‘x’])
“`

3. 保存和加载文本文件

对于人类可读的格式,如CSV(逗号分隔值),可以使用 np.savetxt()np.loadtxt()

“`python
text_data = np.array([[1.1, 2.2], [3.3, 4.4]])
np.savetxt(‘my_data.csv’, text_data, delimiter=’,’)
print(“数组已保存到 my_data.csv”)

loaded_text_data = np.loadtxt(‘my_data.csv’, delimiter=’,’)
print(“从 my_data.csv 加载的数组:\n”, loaded_text_data)
“`

结语

NumPy是Python科学计算的基石,掌握其数组操作对于进行高效的数据处理和分析至关重要。从基本的数组创建、索引切片到高级的广播、线性代数和统计功能,NumPy都提供了强大且优化的工具。

本教程为您提供了NumPy从基础到高级的全面概览。鼓励您通过实践和探索NumPy的官方文档来进一步深入学习,充分发挥其在数据科学和机器学习项目中的巨大潜力。

滚动至顶部