番外篇之副本和视图的相爱💕相杀🔪

🖖导读🖖

学习NumPy已经有一段时间,感觉NumPy库真的是太方便了。但是唯独没有看到如何快速复制一个数组的这么一个概念,但是不能轻易放弃,好东西总是放在后面的,这不,本节我们就带来了副本和视图,让我们开始进行体验吧!

🎎 概念

什么是副本?

副本(copy)是一个新的数组对象,它是原始数组的一个独立的拷贝。对副本进行的任何更改都不会影响到原始数组。

什么是视图?

视图(view)是一个指向原始数组的引用,它是原始数组的一个别名。视图与原始数组共享数据,因此对视图进行的任何更改都会影响原始数组。

🧩 使用

我们先看一下传统Python的使用方式

python 复制代码
# -*- coding: utf-8 -*-
"""
@Created on : 2024/5/22 11:16
@creator : er_nao
@File :numpy_28_副本和视图.py
@Description :
"""

import numpy as np

# 传统数组之间相互赋值

x = np.array([1, 2, 3, 4, 5, 6, 7, 8])
print('原数组')
print(x)
# 获取数组在内存中的id
print('x数组的id: ')
print(id(x))
print('\n')
# 将x数组赋值给y
y = x
print('y数组的id:')
print(id(y))
print('\n')
# 修改y数组,测试x数组是否会发生变化
y.shape = 2, 4
print('打印y数组')
print(y)
print('\n')
print('打印x数组')
print(x)

# 输出结果
# 原数组
# [1 2 3 4 5 6 7 8]
# x数组的id: 
# 1955896948176
# 
# 
# y数组的id:
# 1955896948176
# 
# 
# 打印y数组
# [[1 2 3 4]
#  [5 6 7 8]]
# 
# 
# 打印x数组
# [[1 2 3 4]
#  [5 6 7 8]]

可以看到,数组y的变化也引起了x数值的变化,且两个数组的格式最后也是一样的,因为他们有共同的id,都是在内存中读取的。

接下来,我们看看NumPy给我们都提供了哪些函数?

首先我们看副本相关知识,副本这边NumPy给我们提供的函数是:copy()函数。copy()函数是先创建一个副本,然后对副本的数据进行修改,它主要的一点就是不会修改原数组的内容,这一点跟传统的数组之间的赋值非常的像。但是实践是检验的唯一真理,我们在编辑器中进行测试一下,用代码说话。

相关代码:

bash 复制代码
# copy()函数的使用

x = np.arange(16).reshape(2, 8)
print('原数组')
print(x)
# 获取数组在内存中的id
print('x数组的id: ')
print(id(x))
print('\n')
# 将x数组copy给y
y = x.copy()
print('y数组的id:')
print(id(y))
print('\n')

# 修改y数组,测试x数组是否会发生变化
y.shape = 4, 4
print('打印y数组')
print(y)
print('\n')
print('打印x数组')
print(x)

# # 输出结果
# 原数组
# [[ 0  1  2  3  4  5  6  7]
#  [ 8  9 10 11 12 13 14 15]]
# x数组的id: 
# 2507393856880
# 
# 
# y数组的id:
# 2507951925520
# 
# 
# 打印y数组
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]
# 
# 
# 打印x数组
# [[ 0  1  2  3  4  5  6  7]
#  [ 8  9 10 11 12 13 14 15]]

从上面的代码中可以看出,改变数组y的结构,并没有影响到数组x.因为它们在内存中的指向id都是不同的,它们不是共用的一个内存同一位置。

再来看看视图究竟是怎么个回事儿?同样的,NumPy库给我们也提供了view()函数。view()函数是指向原始数组的引用,从上面的概念可以看到,它是和原始数组共用数据的。检验一下吧!

相关代码:

bash 复制代码
# view()函数的使用

x = np.arange(16).reshape(2, 8)
print('原数组')
print(x)
# 获取数组在内存中的id
print('x数组的id: ')
print(id(x))
print('\n')
# 将x数组view给y
y = x.view()
print('y数组的id:')
print(id(y))
print('\n')

# 修改y数组,测试x数组是否会发生变化
y.shape = 4, 4
print('打印y数组')
print(y)
print('\n')
print('打印x数组')
print(x)

# 输出结果
# 原数组
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
# x数组的id: 
# 1875672167888
# 
# 
# y数组的id:
# 1875675695120
# 
# 
# 打印y数组
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]
# 
# 
# 打印x数组
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]

可以看到,使用view()函数也是没有影响到原数组的。

那么NumPy为什么给我们提供两种不同的函数,但返回的结果都是不影响原数组。它们到底有什么区别呢?

🎭 区别

副本和视图的应用它们的区别到底是什么,这得根据项目环境和当下环境来选择进行使用的。

主要区别在于空间的利用和时间的损耗上:

  • 当需要对数组进行操作,但不希望更改原始数组时,应使用副本。
  • 当需要共享数据以节省内存时,应使用视图。
  • 对于大型数组,创建副本可能会消耗大量内存和时间。在这种情况下,可以考虑使用视图。
  • 当需要将数组转换为不同的数据类型时,可以使用astype()方法创建视图。但是,请注意,这可能会导致数据截断或溢出。

这里我们主要测试一下内存的消耗和时间的情况下,两种类型的表现。

可以看到,copy()其实是新创建了一个数组,而view()是直接进行引用的,这占用内存的大小是相当恐怖的,而且在消耗时间上,copy函数消耗了283毫秒,而view函数几乎是不占用时间的。

这里做一个总结:虽然copy()的消耗比view()多得多,但是我们在项目中使用时,是根据情况而定的,并不是哪个消耗的少而频繁的进行使用。

相关推荐
职略2 小时前
负载均衡类型和算法解析
java·运维·分布式·算法·负载均衡
A22742 小时前
LeetCode 196, 73, 105
java·算法·leetcode
阿里巴巴P8资深技术专家3 小时前
Java常用算法&集合扩容机制分析
java·数据结构·算法
zengson_g3 小时前
当需要对大量数据进行排序操作时,怎样优化内存使用和性能?
java·数据库·算法·排序算法
爱上电路设计4 小时前
有趣的算法
开发语言·c++·算法
2401_858120264 小时前
探索sklearn文本向量化:从词袋到深度学习的转变
开发语言·python·机器学习
bigbearxyz6 小时前
Java实现图片的垂直方向拼接
java·windows·python
立秋67896 小时前
使用Python绘制堆积柱形图
开发语言·python
Kerry_66 小时前
2024年江苏省研究生数学建模科研创新实践大赛C题气象数据高精度融合技术研究论文和代码分析
算法·数学建模·matlab·数据分析
风啊雨6 小时前
刷题Day44|188.买卖股票的最佳时机IV、309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费
算法