pin_drop当前位置:知识文库 ❯ 图文

NumPy数组切片教程 - 视图与副本的区别详解

一、概述

NumPy数组切片是访问子数组的高效方式,语法与Python列表切片类似但功能更强大。切片返回的是原数组的视图(view)而非副本,修改切片会影响原数组,这一特性使得NumPy切片在内存效率上远优于Python列表切片。多维数组切片支持在每个维度上独立指定范围。


二、切片语法

一维切片

代码示例

arr[start:stop:step]

多维切片

代码示例

arr[row_start:row_stop:row_step, col_start:col_stop:col_step]

省略号切片

代码示例

arr[..., col_slice]

三、参数说明

参数 默认值 说明
start 0 起始索引(包含)
stop 末尾 结束索引(不包含)
step 1 步长

切片语法示例

语法 说明
arr[:] 全部元素
arr[2:] 从索引2到末尾
arr[:5] 从开头到索引5(不含)
arr[2:8] 索引2到7
arr[::2] 每隔一个取一个
arr[::-1] 反转数组
arr[1:6:2] 索引1到5,步长2

四、代码示例

示例1:一维数组切片

代码示例

import numpy as np

arr = np.arange(10)
print(f"原数组: {arr}")

# 基本切片
print(f"arr[2:6] = {arr[2:6]}")
print(f"arr[:5] = {arr[:5]}")
print(f"arr[5:] = {arr[5:]}")
print(f"arr[::2] = {arr[::2]}")
print(f"arr[::-1] = {arr[::-1]}")
print(f"arr[1:8:3] = {arr[1:8:3]}")

输出:

代码示例

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

示例2:二维数组切片

代码示例

import numpy as np

arr = np.arange(20).reshape(4, 5)
print(f"原数组:\n{arr}")

# 行切片
print(f"\n前两行:\n{arr[:2]}")

# 列切片
print(f"\n第2-4列:\n{arr[:, 1:4]}")

# 行列同时切片
print(f"\n第2-3行,第3-5列:\n{arr[1:3, 2:5]}")

# 特定行和列
print(f"\n第1行和第3行:\n{arr[[0, 2]]}")

输出:

代码示例

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

前两行:
[[0 1 2 3 4]
 [5 6 7 8 9]]

第2-4列:
[[ 1  2  3]
 [ 6  7  8]
 [11 12 13]
 [16 17 18]]

第2-3行,第3-5列:
[[ 7  8  9]
 [12 13 14]]

第1行和第3行:
[[ 0  1  2  3  4]
 [10 11 12 13 14]]

示例3:视图与副本

代码示例

import numpy as np

arr = np.arange(6)
print(f"原数组: {arr}")

# 切片是视图,修改会影响原数组
view = arr[2:5]
view[0] = 99
print(f"修改视图后原数组: {arr}")

# 使用copy()创建副本
arr2 = np.arange(6)
copy = arr2[2:5].copy()
copy[0] = 99
print(f"修改副本后原数组: {arr2}")

# 判断是否共享内存
arr3 = np.arange(6)
view3 = arr3[2:5]
copy3 = arr3[2:5].copy()
print(f"切片共享内存: {np.shares_memory(arr3, view3)}")
print(f"副本共享内存: {np.shares_memory(arr3, copy3)}")

输出:

代码示例

原数组: [0 1 2 3 4 5]
修改视图后原数组: [ 0  1 99  3  4  5]
修改副本后原数组: [0 1 2 3 4 5]
切片共享内存: True
副本共享内存: False

提示:理解切片是视图还是副本非常重要。视图共享内存,修改切片会影响原数组;副本是独立的数据,修改副本不会影响原数组。使用.copy()方法可以显式创建副本。


五、实际应用场景

  • 时间序列分析:通过切片提取特定时间段的数据

  • 图像处理:通过二维切片裁剪图像的感兴趣区域(ROI)

  • 数据预处理:通过切片分离训练集和测试集


六、注意事项

注意:NumPy切片返回视图而非副本,修改切片会修改原数组,这是与Python列表切片的重要区别。

注意:如需独立副本,务必使用.copy()方法。

注意:步长为负数时,start应大于stop,否则返回空数组。


七、切片与索引对比

操作 返回类型 是否视图 内存效率
整数索引 标量 - -
切片 ndarray 高(共享内存)
花式索引 ndarray 低(复制数据)
布尔索引 ndarray 低(复制数据)
.copy() ndarray 低(复制数据)

八、小结

  • 基本语法:切片语法为start:stop:step,各参数均可省略

  • 多维切片:多维数组在每个维度上独立切片,用逗号分隔

  • 视图特性:切片返回视图,修改切片会影响原数组,这是NumPy的重要特性

  • 副本创建:需要独立副本时使用.copy()方法

小贴士

NumPy切片返回视图的设计是为了性能优化,避免了不必要的数据复制。在处理大型数组时,这种设计可以显著节省内存。但这也意味着需要格外小心,意外修改切片可能导致原数组被修改。如果不确定是否需要视图还是副本,显式使用.copy()是最安全的做法。更多高级切片技巧可以参考NumPy索引文档


九、练习题

练习1

创建一个包含0到19的数组,使用切片提取索引5到15的元素,步长为2。

练习2

创建一个5×5的二维数组,使用切片提取中间3×3的子矩阵。

练习3

编写程序,验证NumPy切片是视图而非副本:修改切片后检查原数组是否变化。

常见问题

NumPy切片和Python列表切片有什么区别?

最主要的区别是:NumPy切片返回的是原数组的视图(共享内存),而Python列表切片返回的是新列表(独立副本)。这意味着修改NumPy切片会影响原数组,而修改列表切片不会影响原列表。NumPy的这种设计是为了提高内存效率,特别是在处理大型数组时。

如何在多维数组中使用省略号切片?

省略号...可以代表任意数量的完整维度。例如,对于三维数组arr[3,4,5],arr[..., 2]等价于arr[:, :, 2],arr[1, ...]等价于arr[1, :, :]。这在处理高维数组时可以简化代码。

步长为负数时切片如何使用?

步长为负数时,切片会从右向左取值,此时start应该大于stop。例如arr[5:1:-1]返回索引5、4、3、2的元素。arr[::-1]是最常用的反转数组方式。如果start小于stop且步长为负数,则返回空数组。

为什么有时候切片修改不影响原数组?

通常情况下NumPy切片返回视图,修改会影响原数组。但如果切片操作导致结果无法用视图表示(例如某些花式索引组合、或者经过多次复杂切片后),NumPy可能会返回副本。此外,某些高级索引操作(如布尔索引)总是返回副本。可以使用np.shares_memory()函数检查两个数组是否共享内存。

标签: 数组切片 NumPy 视图 副本 多维切片 内存优化

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: NumPy数组索引全面指南 - 布尔索引与花式索引 下一篇: NumPy数组形状操作教程 - reshape、flatten、transpose详解

poll相关推荐