NumPy 魔术秀:一招搞定多维数据!

96 阅读7分钟

基础操作

创建数组

  • 从python列表或元组创建
list1 = [1, 2, 3, 4, 5]
arr1 = np.array(list1)
print(f"一维数组: {arr1}, dtype: {arr1.dtype}")

tuple1 = ((1, 2), (3, 4))
arr2 = np.array(tuple1)
print(f"二维数组:\n{arr2}, dtype: {arr2.dtype}")
  • 创建特定形状和初始值数组
# 创建 3x4 的全零数组
zeros_arr = np.zeros((3, 4)) 
# 创建 2x5 的全一整数数组
ones_arr = np.ones((2, 5), dtype=int)
# 创建 2x2 的全 7.0 数组
full_arr = np.full((2, 2), 7.0)
# 创建 2x3 的未初始化数组(值随机)
empty_arr = np.empty((2, 3)) 
  • 创建等差数列数组
# 从 0 到 10(不包含),步长为 2
arange_arr = np.arange(0, 10, 2) 
print(f"arange array: {arange_arr}")
# 从 0 到 1(包含),等间隔的 5 个数
linspace_arr = np.linspace(0, 1, 5)
print(f"linspace array: {linspace_arr}")

数组属性

arr = np.array([[1, 2, 3], [4, 5, 6]])
# (2, 3) - 2 行 3 列
print(f"Shape (维度): {arr.shape}") 
# int64 (取决于系统)
print(f"Data type (元素类型): {arr.dtype}")
# 2
print(f"Number of dimensions (维度数): {arr.ndim}")
# 6
print(f"Number of elements (元素总数): {arr.size}") 
# 8 (int64)
print(f"Size of each element (元素字节大小): {arr.itemsize}") 
print(f"Data buffer (数据缓冲区): {arr.data}")

数组索引和切片

arr = np.arange(10)
print(f"一维: {arr}")
print(f"第一个元素: {arr[0]}")
print(f"2到5索引元素: {arr[2:5]}")
print(f"以2为间隔: {arr[::2]}")

arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"\n2D array:\n{arr_2d}")
print(f"Element at row 0, column 1: {arr_2d[0, 1]}")
print(f"Row at index 1: {arr_2d[1]}")
print(f"Column at index 2: {arr_2d[:, 2]}")
print(f"Sub-array (rows 0-1, cols 1-2):\n{arr_2d[0:2, 1:3]}")

# 布尔索引
bool_arr = arr > 5
print(f"Boolean array (arr > 5): {bool_arr}")
print(f"Elements greater than 5: {arr[bool_arr]}")

# 整数数组索引
indices = [0, 2, 4]
print(f"Elements at indices [0, 2, 4]: {arr[indices]}")

数组形状操作

arr = np.arange(6)
print(f"Original array: {arr}, shape: {arr.shape}")

reshaped_arr = arr.reshape((2, 3))
print(f"转换数组 (2x3):\n{reshaped_arr}, shape: {reshaped_arr.shape}")

raveled_arr = reshaped_arr.ravel()
print(f"Raveled array (flattened): {raveled_arr}, shape: {raveled_arr.shape}")

flattened_arr = reshaped_arr.flatten() # 返回拷贝
print(f"Flattened array (copy): {flattened_arr}, shape: {flattened_arr.shape}")

transposed_arr = reshaped_arr.T
print(f"Transposed array:\n{transposed_arr}, shape: {transposed_arr.shape}")

数组运算

  • 元素级运算
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(f"a + b: {a + b}")
print(f"a - b: {a - b}")
print(f"a * b: {a * b}")
print(f"a / b: {a / b}")
print(f"a ** 2: {a ** 2}")
print(f"np.sin(a): {np.sin(a)}")
  • 广播(Broadcasting): 允许不同形状的数组进行运算
a = np.array([1, 2, 3])
scalar = 2
print(f"a + scalar: {a + scalar}")

b = np.array([[10, 20, 30], [40, 50, 60]])
c = np.array([1, 2, 3])
print(f"b + c:\n{b + c}") # c 会被广播到 b 的每一行
  • 聚合函数
arr = np.array([[1, 2, 3], [4, 5, 6]])

print(f"Sum of all elements: {np.sum(arr)}")
print(f"Sum along columns (axis=0): {np.sum(arr, axis=0)}")
print(f"Sum along rows (axis=1): {np.sum(arr, axis=1)}")
print(f"Mean: {np.mean(arr)}")
print(f"Max: {np.max(arr)}")
print(f"Min: {np.min(arr)}")
print(f"Argmax (flattened index): {np.argmax(arr)}")
print(f"Standard deviation: {np.std(arr)}")
  • 线性代数运算
arr = np.array([[1, 2, 3], [4, 5, 6]])

print(f"Sum of all elements: {np.sum(arr)}")
print(f"Sum along columns (axis=0): {np.sum(arr, axis=0)}")
print(f"Sum along rows (axis=1): {np.sum(arr, axis=1)}")
print(f"Mean: {np.mean(arr)}")
print(f"Max: {np.max(arr)}")
print(f"Min: {np.min(arr)}")
print(f"Argmax (flattened index): {np.argmax(arr)}")
print(f"Standard deviation: {np.std(arr)}")

常见问题

什么是 NumPy?它与 Python 列表有什么主要区别?

  • NumPy 是 Python 中用于科学计算的基础库,提供了高性能的多维数组对象 ndarray。
  • 主要区别
    • 数据类型: NumPy 数组中的元素必须是相同的数据类型,而 Python 列表可以包含不同类型的元素。
    • 性能: NumPy 数组在内存中是连续存储的,并且针对数值运算进行了优化(向量化操作),通常比 Python 列表快得多。
    • 功能: NumPy 提供了丰富的数学函数、线性代数操作、广播机制等,方便进行复杂的数值计算,而 Python 列表的功能相对有限。
    • 大小: NumPy 数组在创建时大小通常是固定的(虽然可以创建新的调整大小的数组),而 Python 列表的大小是动态的。

解释 NumPy 的广播 (Broadcasting) 机制。

  • 广播是 NumPy 强大的特性,允许对形状不完全相同的数组进行算术运算。NumPy 会自动“扩展”维度较小的数组,使其形状与维度较大的数组兼容,而无需显式地复制数据。广播遵循一定的规则,例如:
    • 如果两个数组的维度数不同,形状较小的数组会在前面补 1。
    • 如果两个数组在某个维度上的大小相等,或者其中一个数组在该维度上的大小为 1,则它们在该维度上是兼容的。
    • 如果两个数组在所有维度上都兼容,则可以进行广播。
    • 广播之后,两个数组就好像具有相同的形状,运算会按元素进行。

reshape(), ravel(), flatten() 方法的区别是什么?

  • reshape(new_shape): 在不改变数据的情况下,改变数组的形状。返回的是原始数组的视图 (view),这意味着修改返回的数组可能会影响原始数组。
  • ravel(): 将多维数组展平成一维数组。返回的是原始数组的视图 (view),如果原始数组在内存中不是连续的,则返回拷贝。
  • flatten(): 将多维数组展平成一维数组。总是返回原始数组的拷贝 (copy)。

如何在 NumPy 数组中进行索引和切片?NumPy 的切片与 Python 列表的切片有什么不同?

  • NumPy 数组的索引和切片与 Python 列表类似,使用方括号 []。对于多维数组,可以使用逗号分隔每个维度的索引或切片。
  • 主要区别
    • 多维切片: NumPy 允许在多个维度上进行切片,例如 arr[0:2, 1:3]。Python 列表的切片主要针对一维列表。
    • 返回视图 vs. 拷贝: NumPy 的切片操作通常返回的是原始数组的视图 (view),修改切片会影响原始数组。Python 列表的切片操作返回的是原始列表的拷贝 (copy)。如果需要对 NumPy 数组进行拷贝切片,可以使用 .copy() 方法。

解释 NumPy 的向量化 (Vectorization) 操作。它为什么比使用 Python 循环更快?

  • 向量化是指对整个 NumPy 数组执行操作,而不是逐个元素地进行循环。NumPy 的许多内置函数和运算符都经过优化,可以高效地处理整个数组。
  • 速度更快的原因
    • 底层实现: NumPy 的向量化操作通常在底层使用高度优化的 C 或 Fortran 代码实现。
    • 避免 Python 解释器开销: Python 循环需要在解释器中逐个执行操作,开销较大。向量化操作将整个操作交给底层代码一次性执行。
    • SIMD (Single Instruction, Multiple Data): 底层实现可以利用 CPU 的 SIMD 指令,同时对多个数据元素执行相同的操作。
    • 内存连续性: NumPy 数组在内存中是连续存储的,这使得向量化操作可以更高效地访问和处理数据。

如何在 NumPy 中处理缺失值 (NaN)?

  • NumPy 使用 np.nan (Not a Number) 来表示缺失值。
  • 常用操作
    • 检测 NaN: 使用 np.isnan(arr) 返回一个布尔数组,指示哪些元素是 NaN。
    • 替换 NaN: 可以使用布尔索引将 NaN 值替换为其他值,例如 arr[np.isnan(arr)] = 0。
    • 忽略 NaN 的聚合函数: NumPy 提供了一些忽略 NaN 值的聚合函数,例如 np.nanmean(), np.nansum(), np.nanmax(), np.nanmin() 等。

如何合并 (concatenate) 多个 NumPy 数组?

  • 可以使用 np.concatenate((arr1, arr2, ...), axis=) 函数,其中 axis 指定了连接的轴(0 表示按行连接,1 表示按列连接)。还可以使用 np.vstack() (垂直堆叠) 和 np.hstack() (水平堆叠) 等函数进行特定方向的合并。需要确保合并的数组在非连接轴上的形状是匹配的。

什么是 NumPy 的视图 (view)?它与拷贝 (copy) 有什么区别?

  • 视图 (view): 是对原始数组数据的引用,不会创建新的数据副本。修改视图会影响原始数组,反之亦然。例如,通过切片操作获得的数组通常是视图。
  • 拷贝 (copy): 是原始数组数据的完整副本,存储在不同的内存位置。修改拷贝不会影响原始数组,反之亦然。可以使用 .copy() 方法显式地创建拷贝。

如何在 NumPy 中进行布尔索引 (Boolean Indexing)?

  • 布尔索引允许我们根据一个布尔数组来选择 NumPy 数组中的元素。布尔数组的形状必须与要索引的数组的形状兼容(通常是相同的形状或可以广播)。只有布尔数组中 True 对应的位置的元素会被选中。

解释 np.where() 函数的用法。

  • np.where(condition, x, y) 函数是三元运算符的向量化版本。它返回一个数组,其中的元素来自 x 或 y,具体取决于 condition 中对应位置的元素是 True 还是 False。如果只提供 condition,则返回 condition 为 True 的元素的索引。

如果对你有帮助, 请点赞+关注鼓励下