- NumPy 教程
- NumPy - 首页
- NumPy - 简介
- NumPy - 环境
- NumPy 数组
- NumPy - Ndarray 对象
- NumPy - 数据类型
- NumPy 创建和操作数组
- NumPy - 数组创建例程
- NumPy - 数组操作
- NumPy - 从现有数据创建数组
- NumPy - 从数值范围创建数组
- NumPy - 迭代数组
- NumPy - 重塑数组
- NumPy - 连接数组
- NumPy - 堆叠数组
- NumPy - 分割数组
- NumPy - 展平数组
- NumPy - 转置数组
- NumPy 索引和切片
- NumPy - 索引和切片
- NumPy - 高级索引
- NumPy 数组属性和操作
- NumPy - 数组属性
- NumPy - 数组形状
- NumPy - 数组大小
- NumPy - 数组步幅
- NumPy - 数组元素大小
- NumPy - 广播
- NumPy - 算术运算
- NumPy - 数组加法
- NumPy - 数组减法
- NumPy - 数组乘法
- NumPy - 数组除法
- NumPy 高级数组操作
- NumPy - 交换数组的轴
- NumPy - 字节交换
- NumPy - 复制与视图
- NumPy - 元素级数组比较
- NumPy - 过滤数组
- NumPy - 连接数组
- NumPy - 排序、搜索和计数函数
- NumPy - 搜索数组
- NumPy - 数组的并集
- NumPy - 查找唯一行
- NumPy - 创建日期时间数组
- NumPy - 二元运算符
- NumPy - 字符串函数
- NumPy - 数学函数
- NumPy - 统计函数
- NumPy - 矩阵库
- NumPy - 线性代数
- NumPy - Matplotlib
- NumPy - 使用 Matplotlib 绘制直方图
- NumPy - NumPy 的 I/O
- NumPy 排序和高级操作
- NumPy - 排序数组
- NumPy - 沿轴排序
- NumPy - 使用花式索引排序
- NumPy - 结构化数组
- NumPy - 创建结构化数组
- NumPy - 操作结构化数组
- NumPy - 字段访问
- NumPy - 记录数组
- Numpy - 加载数组
- Numpy - 保存数组
- NumPy - 将值追加到数组
- NumPy - 交换数组的列
- NumPy - 向数组插入轴
- NumPy 处理缺失数据
- NumPy - 处理缺失数据
- NumPy - 识别缺失值
- NumPy - 删除缺失数据
- NumPy - 估算缺失数据
- NumPy 性能优化
- NumPy - 使用数组进行性能优化
- NumPy - 使用数组进行矢量化
- NumPy - 数组的内存布局
- Numpy 线性代数
- NumPy - 线性代数
- NumPy - 矩阵库
- NumPy - 矩阵加法
- NumPy - 矩阵减法
- NumPy - 矩阵乘法
- NumPy - 元素级矩阵运算
- NumPy - 点积
- NumPy - 矩阵求逆
- NumPy - 行列式计算
- NumPy - 特征值
- NumPy - 特征向量
- NumPy - 奇异值分解
- NumPy - 求解线性方程组
- NumPy - 矩阵范数
- NumPy 元素级矩阵运算
- NumPy - 求和
- NumPy - 求平均值
- NumPy - 求中位数
- NumPy - 求最小值
- NumPy - 求最大值
- NumPy 集合运算
- NumPy - 唯一元素
- NumPy - 交集
- NumPy - 并集
- NumPy - 差集
- NumPy 有用资源
- NumPy 编译器
- NumPy - 快速指南
- NumPy - 有用资源
- NumPy - 讨论
NumPy - 复制与视图
在 NumPy 中,当您对数组执行操作时,结果可能是原始数据的副本或只是原始数据的视图。了解这两者之间的区别对于有效的内存管理以及避免代码中的意外副作用非常重要。
在 NumPy 中创建副本
我们可以使用 copy() 函数在 NumPy 中显式地创建数组的副本。此函数生成一个新数组,并将原始数组中的数据复制到此新数组中。
当您在 NumPy 中创建数组的副本时,数据将被完全复制。这意味着对副本进行的更改不会影响原始数组,反之亦然。当您需要使用修改后的数组版本而不更改原始数据时,副本很有用。
示例
在以下示例中,修改copied_array不会影响original_array,这证明了这两个数组的独立性:
import numpy as np # Original array original_array = np.array([1, 2, 3]) # Creating a copy copied_array = original_array.copy() # Modifying the copy copied_array[0] = 10 print("Original Array:", original_array) print("Copied Array:", copied_array)
获得以下输出:
Original Array: [1 2 3] Copied Array: [10 2 3]
浅拷贝与深拷贝
在 NumPy 数组的上下文中,浅拷贝和深拷贝之间的区别对于理解复制数据的方式很重要。
浅拷贝
数组的浅拷贝创建一个新的数组对象,但如果这些元素本身是数组或其他复杂对象,则它不会创建原始数组中包含的元素的副本。
相反,新数组仍然引用与原始数组相同的元素。这意味着对元素内容的更改将影响原始数组和复制数组。
- 数组级副本 - 在 NumPy 数组的情况下,浅拷贝意味着虽然复制了顶层数组对象,但底层数据缓冲区没有被复制。新数组只是同一数据的新的视图。
- 用法 - 当您需要一个新的数组对象但又想避免复制大量数据的开销时,浅拷贝很有用。
示例
在此示例中,修改shallow_copy也会修改original_array,因为它们共享相同的基础数据:
import numpy as np # Original array original_array = np.array([[1, 2, 3], [4, 5, 6]]) # Shallow copy shallow_copy = original_array.view() # Modify an element in the shallow copy shallow_copy[0, 0] = 100 print("Original Array:") print(original_array) print("\nShallow Copy:") print(shallow_copy)
这将产生以下结果:
Original Array: [[100 2 3] [ 4 5 6]] Shallow Copy: [[100 2 3] [ 4 5 6]]
深拷贝
另一方面,深拷贝会创建一个新的数组对象以及它包含的所有数据的副本。这意味着对新数组进行的任何更改都不会影响原始数组,反之亦然。新数组中的数据与原始数组中的数据完全独立。
- 完全复制 - 在 NumPy 的上下文中,深拷贝涉及复制数组的整个数据缓冲区,以确保新数组与原始数组完全分离。
- 用法 - 当您需要独立于原始数组使用数据时,深拷贝非常重要,尤其是在数据可能以不应影响原始数据的方式修改时。
示例
在这种情况下,修改deep_copy不会影响original_array,这证明了这两个数组的独立性:
import numpy as np # Original array original_array = np.array([[1, 2, 3], [4, 5, 6]]) # Deep copy deep_copy = original_array.copy() # Modify an element in the deep copy deep_copy[0, 0] = 100 print("Original Array:") print(original_array) print("\nDeep Copy:") print(deep_copy)
以下是上述代码的输出:
Original Array: [[1 2 3] [4 5 6]] Deep Copy: [[100 2 3] [ 4 5 6]]
复制子数组
为了避免在处理子数组时修改原始数组,您应该创建子数组的副本。当您需要独立于原始数据操作或分析子数组时,这很有用。
子数组只是现有 NumPy 数组的一部分。您可以使用切片技术提取子数组。
例如,如果您有一个二维数组,您可以通过沿着其行和列进行切片来提取一个较小的二维子数组。但是,默认情况下,切片会创建原始数组的视图,而不是单独的副本。这意味着对子数组的更改也将影响原始数组,除非您显式地创建副本。
示例
在下面的示例中,sub_array由于使用了 copy() 函数,因此是一个完全独立的数组:
import numpy as np # Original 2D array original_array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Creating a copy of the subarray sub_array = original_array[0:2].copy() sub_array[0] = 20 print("Original Array after subarray copy:", original_array) print("Subarray:", sub_array)
获得的输出如下所示:
Original Array after subarray copy: [[1 2 3] [4 5 6] [7 8 9]] Subarray: [[20 20 20] [ 4 5 6]]
在 NumPy 中创建视图
当您切片数组或执行某些操作(如重塑)时会创建视图。数据不会被复制;相反,新数组只是查看原始数据的一种不同方式。
换句话说,视图是一个新的数组对象,它查看与原始数组相同的数据。这意味着,如果您修改视图,更改将反映在原始数组中,反之亦然。
示例
在此示例中,修改view_array直接影响original_array,表明它们共享相同的数据:
import numpy as np # Original array original_array = np.array([1, 2, 3]) view_array = original_array[0:2] # Modifying the view view_array[0] = 30 print("Original Array after view modification:", original_array) print("View Array:", view_array)
执行上述代码后,我们将获得以下输出:
Original Array after view modification: [30 2 3] View Array: [30 2]
何时返回视图?
并非所有切片或操作都会导致视图。如果数组的内存布局发生变化,NumPy 可能会返回副本而不是视图。
来自切片的视图
NumPy 中返回视图的最常见情况是当您切片数组时。切片是通过指定一系列索引来提取数组一部分的方法。NumPy 返回一个视图,而不是创建一个具有自身数据的新数组,这意味着切片数组与原始数组共享相同的数据。
示例
在此示例中,view_array是original_array的视图。数据不会被复制,并且两个数组共享相同的底层内存。这意味着对“view_array”进行的任何更改也将影响“original_array”:
import numpy as np # Original array original_array = np.array([1, 2, 3, 4, 5]) # Creating a view by slicing the original array view_array = original_array[1:4] print("Original Array:") print(original_array) print("\nView Array (Sliced):") print(view_array)
产生的结果如下:
Original Array: [1 2 3 4 5] View Array (Sliced): [2 3 4]
来自重塑的视图
返回视图的另一种常见情况是当您重塑数组时。重塑会更改数组的形状(即每个维度中的元素数),而不更改底层数据。在可能的情况下,NumPy 会以新形状返回原始数组的视图。
示例
这里,reshaped_array是original_array的视图,只是以“2x3”格式呈现。数据保持不变,修改“reshaped_array”也会修改“original_array”:
import numpy as np # Original 1D array original_array = np.array([1, 2, 3, 4, 5, 6]) # Reshaping the array into a 2x3 matrix reshaped_array = original_array.reshape(2, 3) print("Original Array:") print(original_array) print("\nReshaped Array (View):") print(reshaped_array)
我们获得以下输出:
Original Array:[1 2 3 4 5 6] Reshaped Array (View): [[1 2 3] [4 5 6]]
来自转置的视图
转置数组涉及将其翻转过其对角线,将行转换为列,反之亦然。当您使用np.transpose()函数或.T属性转置数组时,NumPy 会在可能的情况下返回视图,而不是副本。
示例
在这种情况下,transposed_array是original_array的视图,但轴已交换。底层数据保持不变,对“transposed_array”的更改将反映在“original_array”中:
import numpy as np # Original 2D array original_array = np.array([[1, 2, 3], [4, 5, 6]]) # Transposing the array transposed_array = original_array.T print("Original Array:") print(original_array) print("\nTransposed Array (View):") print(transposed_array)
获得以下输出:
Original Array: [[1 2 3] [4 5 6]] Transposed Array (View): [[1 4] [2 5] [3 6]]
base 属性
在 NumPy 中,数组的base属性检查数组是另一个数组的视图还是副本。它是对从中派生当前数组的原始数组的引用。
如果当前数组是另一个数组的视图,“base”将指向该原始数组。如果当前数组不是视图(即它是原始数组或深拷贝),则“base”将为None。
示例:原始数组的 base 属性
当您创建数组时,其 base 属性将为 None,因为它就是原始数组:
import numpy as np # Creating an original array original_array = np.array([10, 20, 30, 40, 50]) # Checking the base attribute print("Base of original array:", original_array.base)
这将产生以下结果:
Base of original array: None
示例:视图的 base 属性
当您创建数组的视图(例如,通过切片)时,视图的 base 属性将指向原始数组:
import numpy as np # Creating an original array original_array = np.array([10, 20, 30, 40, 50]) # Creating a view of the original array view_array = original_array[1:4] # Checking the base attribute print("Base of view array:", view_array.base)
以下是上述代码的输出:
Base of view array: [10 20 30 40 50]
示例:副本的 base 属性
如果您创建数组的副本,则 base 属性将为 None,表示复制的数组独立于原始数组:
import numpy as np # Creating an original array original_array = np.array([10, 20, 30, 40, 50]) # Creating a copy of the original array copy_array = original_array.copy() # Checking the base attribute print("Base of copy array:", copy_array.base)
以下是上述代码的输出:
Base of copy array: None