【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.4 索引优化:避免意外复制的高效技巧

在这里插入图片描述

2.4 索引优化:避免意外复制的高效技巧

目录/提纲
索引优化:避免意外复制的高效技巧
索引返回视图的条件
.copy()最佳实践
内存占用检测方法
大数组处理技巧
意外复制预防

2.4.1 索引返回视图的条件
2.4.1.1 视图与副本的区别
2.4.1.2 索引操作返回视图的条件
2.4.2 .copy()最佳实践
2.4.2.1 .copy()方法的使用
2.4.2.2 何时使用.copy()方法
2.4.3 内存占用检测方法
2.4.3.1 使用 memory_profiler 检测内存占用
2.4.3.2 使用 tracemalloc 检测内存分配
2.4.4 大数组处理技巧
2.4.4.1 大数组的内存管理
2.4.4.2 使用生成器处理大数组
2.4.5 意外复制预防
2.4.5.1 常见的意外复制场景
2.4.5.2 如何预防意外复制

索引优化核心原理
视图与副本机制
内存管理策略
基本索引产生视图
高级索引产生副本
.copy()方法运用
内存检测工具
sys.getsizeof
array.nbytes
memory_profiler
显式复制控制

文章内容

NumPy 是一个强大的数值计算库,其中的索引和切片操作在处理大数组时尤为重要。然而,不当的索引操作可能会导致意外的数组复制,从而增加内存占用和影响性能。本文将详细介绍如何优化索引操作,避免意外复制,并提供一些高效的处理大数组的技巧。

2.4.1 索引返回视图的条件

2.4.1.1 视图与副本的区别

在 NumPy 中,索引操作可能会返回一个数组的视图(view)或副本(copy)。视图与副本的区别如下:

  • 视图:视图是对原数组的一个引用,不占用额外的内存。对视图的修改会反映到原数组上。
  • 副本:副本是原数组的一个独立拷贝,占用额外的内存。对副本的修改不会影响原数组。
原理说明
  • 视图:当索引操作返回的是一个视图时,NumPy 不会复制数据,只是创建一个指向原数据的新数组对象。
  • 副本:当索引操作返回的是一个副本时,NumPy 会复制数据,创建一个新数组对象。

基本切片操作原理

import numpy as np

# 创建连续内存数组
original = np.arange(10)  # 创建0-9的一维数组
view = original[2:6]      # 切片操作获取视图

print(view.base is original)  # 输出True,验证视图关系
print(view.flags.owndata)     # 输出False,不持有数据

内存结构示意图:

数据块
指针
Original Array
0 1 2 3 4 5 6 7 8 9
View Array
2 3 4 5
2.4.1.2 索引操作返回视图的条件

NumPy 的索引操作返回视图的条件如下:

  • 切片操作:一般情况下,切片操作返回的是视图。
  • 布尔索引:布尔索引操作通常返回副本。
  • 花哨索引:花哨索引(使用整数列表或数组进行索引)通常返回副本。
示例代码
import numpy as np

# 创建一个 NumPy 数组
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组

# 切片操作
slice_view = arr[1:4]  # 切片操作返回视图
print(slice_view)  # 输出 [2 3 4]

# 修改视图
slice_view[0] = 10  # 修改视图
print(arr)  # 输出 [ 1 10  3  4  5],原数组也被修改了

# 布尔索引
bool_view = arr[arr > 2]  # 布尔索引返回副本
print(bool_view)  # 输出 [10  3  4  5]

# 修改副本
bool_view[0] = 20  # 修改副本
print(arr)  # 输出 [ 1 10  3  4  5],原数组没有被修改

# 花哨索引
fancy_copy = arr[[1, 3]]  # 花哨索引返回副本
print(fancy_copy)  # 输出 [10  4]

# 修改副本
fancy_copy[0] = 30  # 修改副本
print(arr)  # 输出 [ 1 10  3  4  5],原数组没有被修改

# 创建二维数组
matrix = np.array([[1,2], [3,4], [5,6]])  # 3x2矩阵

# 布尔索引产生副本
bool_copy = matrix[matrix > 3]            # 筛选大于3的元素
print(bool_copy.base is None)             # 输出True,独立副本

# 整数数组索引示例
index_copy = matrix[[0,2]]                # 选择第0行和第2行
print(index_copy.base is None)            # 输出True,产生副本

内存变化示意图:

基本切片
高级索引
原始数据
索引操作
索引类型判断
创建视图
创建副本

2.4.2 .copy()最佳实践

2.4.2.1 .copy()方法的使用

.copy()方法用于创建数组的副本。创建副本时,原数组的数据会被完全复制到新数组中,新数组与原数组独立。

示例代码
# 创建一个 NumPy 数组
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组

# 使用 .copy() 方法创建副本
arr_copy = arr.copy()  # 创建副本
print(arr_copy)  # 输出 [1 2 3 4 5]

# 修改副本
arr_copy[0] = 10  # 修改副本
print(arr)  # 输出 [1 2 3 4 5],原数组没有被修改
print(arr_copy)  # 输出 [10 2 3 4 5],副本被修改
2.4.2.2 何时使用.copy()方法
  • 需要独立操作:当需要对数组进行独立操作而不影响原数组时。
  • 避免内存泄漏:当原数组很大,且不希望占用额外的内存时。
  • 数据安全:当需要确保数据的安全性,避免意外修改时。
示例代码
# 大数组处理示例
large_arr = np.random.randint(0, 100, size=1000000)  # 创建一个大数组

# 使用 .copy() 创建副本
large_arr_copy = large_arr.copy()  # 创建副本
large_arr_copy[0] = -1  # 修改副本

# 检查原数组
print(large_arr[0])  # 输出 98(假设原数组的第一个元素是98)
print(large_arr_copy[0])  # 输出 -1,副本被修改

2.4.3 内存占用检测方法

2.4.3.1 使用 memory_profiler 检测内存占用

memory_profiler 是一个用于检测 Python 程序内存占用的工具。它可以详细记录每个函数的内存使用情况。

安装
pip install memory_profiler  # 安装 memory_profiler
示例代码
from memory_profiler import profile

@profile
def create_and_copy_large_array():
    large_arr = np.random.randint(0, 100, size=1000000)  # 创建一个大数组
    large_arr_copy = large_arr.copy()  # 创建副本
    return large_arr, large_arr_copy

create_and_copy_large_array()
2.4.3.2 使用 tracemalloc 检测内存分配

tracemalloc 是 Python 标准库中的一个内存分配跟踪模块,可以用来检测内存分配和释放的情况。

示例代码
import tracemalloc
import numpy as np

def create_and_copy_large_array():
    tracemalloc.start()  # 开始内存跟踪
    large_arr = np.random.randint(0, 100, size=1000000)  # 创建一个大数组
    large_arr_copy = large_arr.copy()  # 创建副本
    current, peak = tracemalloc.get_traced_memory()  # 获取当前和峰值内存占用
    tracemalloc.stop()  # 停止内存跟踪
    return large_arr, large_arr_copy, current, peak

large_arr, large_arr_copy, current, peak = create_and_copy_large_array()
print(f"Current memory usage: {current} bytes")
print(f"Peak memory usage: {peak} bytes")

2.4.4 大数组处理技巧

2.4.4.1 大数组的内存管理

处理大数组时,合理的内存管理可以显著提高程序的性能和稳定性。

  • 分块处理:将大数组分成多个小块进行处理。
  • 生成器:使用生成器按需生成数据,减少内存占用。
示例代码
# 分块处理大数组
def process_large_array_in_chunks(arr, chunk_size=100000):
    for i in range(0, len(arr), chunk_size):
        chunk = arr[i:i+chunk_size]  # 分块
        # 处理 chunk
        chunk_mean = np.mean(chunk)  # 计算均值
        print(f"Mean of chunk {i//chunk_size + 1}: {chunk_mean}")

large_arr = np.random.randint(0, 100, size=1000000)  # 创建一个大数组
process_large_array_in_chunks(large_arr)

# 使用生成器处理大数组
def generate_large_array(size=1000000):
    for i in range(size):
        yield np.random.randint(0, 100)  # 生成随机整数

def process_large_array_with_generator(generator):
    for i, value in enumerate(generator):
        # 处理 value
        if i % 100000 == 0:
            print(f"Processing value {i}: {value}")

gen = generate_large_array(1000000)  # 创建生成器
process_large_array_with_generator(gen)  # 处理生成器

2.4.5 意外复制预防

2.4.5.1 常见的意外复制场景
  • 布尔索引:布尔索引操作通常返回副本。
  • 花哨索引:花哨索引操作通常返回副本。
  • 切片赋值:将视图赋值给一个新变量时,如果不注意可能会导致意外复制。
示例代码
# 布尔索引示例
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组
bool_index = arr > 2  # 布尔索引
print(bool_index)  # 输出 [False False  True  True  True]

# 得到的数组是副本
bool_view = arr[bool_index]
bool_view[0] = 10  # 修改副本
print(arr)  # 输出 [1 2 3 4 5],原数组没有被修改
print(bool_view)  # 输出 [10 4 5],副本被修改

# 花哨索引示例
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组
fancy_index = [1, 3]  # 花哨索引
print(fancy_index)  # 输出 [1, 3]

# 得到的数组是副本
fancy_view = arr[fancy_index]
fancy_view[0] = 10  # 修改副本
print(arr)  # 输出 [1 2 3 4 5],原数组没有被修改
print(fancy_view)  # 输出 [10 4],副本被修改

# 切片赋值示例
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组
slice_view = arr[1:4]  # 切片操作返回视图
print(slice_view)  # 输出 [2 3 4]

# 将视图赋值给新变量
new_arr = slice_view  # 新变量是一个视图,不是副本
new_arr[0] = 10  # 修改新变量
print(arr)  # 输出 [1 10 3 4 5],原数组也被修改了
print(new_arr)  # 输出 [10 3 4],新变量被修改
2.4.5.2 如何预防意外复制
  • 确认索引类型:使用 np.shares_memory 方法检查两个数组是否共享内存。
  • 显式使用 .copy():当需要一个独立的副本时,显式使用 .copy() 方法。
  • 使用视图操作:尽量使用切片操作,避免布尔索引和花哨索引。
示例代码
# 使用 np.shares_memory 检查共享内存
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组
slice_view = arr[1:4]  # 切片操作返回视图
bool_view = arr[arr > 2]  # 布尔索引返回副本

print(np.shares_memory(arr, slice_view))  # 输出 True,共享内存
print(np.shares_memory(arr, bool_view))  # 输出 False,不共享内存

# 显式使用 .copy() 方法
arr_copy = arr.copy()  # 创建副本
arr_copy[0] = 10  # 修改副本
print(arr)  # 输出 [1 2 3 4 5],原数组没有被修改
print(arr_copy)  # 输出 [10 2 3 4 5],副本被修改

# 使用视图操作
arr = np.array([1, 2, 3, 4, 5])  # 创建一个简单数组
view = arr[1:4]  # 切片操作返回视图
view[0] = 10  # 修改视图
print(arr)  # 输出 [1 10 3 4 5],原数组也被修改了

总结

通过本文的学习,读者将能够更好地理解 NumPy 的索引操作,并掌握如何优化索引操作以避免意外复制。索引操作返回视图的条件、.copy()方法的使用、内存占用检测方法以及大数组处理技巧都是提高性能和减少内存占用的关键点。最后,通过预防意外复制的技巧,读者可以在实际应用中更加高效地处理复杂的数据结构。

参考资料

资料名称链接
NumPy 官方文档https://numpy.org/doc/stable/
NumPy 视图与副本介绍https://numpy.org/doc/stable/reference/generated/numpy.ndarray.view.html
Python 数据科学手册https://jakevdp.github.io/PythonDataScienceHandbook/02.00-introduction-to-numpy.html
memory_profiler 文档https://pypi.org/project/memory-profiler/
tracemalloc 文档https://docs.python.org/3/library/tracemalloc.html
大数组处理技巧https://realpython.com/numpy-big-data/
NumPy 内存管理https://scipy-lectures.org/advanced/optimizing.NUMPy/index.html
Python 内存管理https://www.geeksforgeeks.org/python-memory-management/
NumPy 索引操作https://numpy.org/doc/stable/user/basics.indexing.html
高性能 Python 编程https://highperformancepython.com/
NumPy 切片操作https://www.tutorialspoint.com/numpy/numpy_slicing.htm
NumPy 布尔索引[https://www FluentPython.com/numpy-boolean-indexing]

希望本文的内容能够帮助读者在实际应用中更好地利用 NumPy 的索引功能,提高数据处理的效率和性能。这篇文章包含了详细的原理介绍、代码示例、源码注释以及案例等。希望这对您有帮助。如果有任何问题请随私信或评论告诉我。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963234.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言进阶(五)】指针进阶详解(下)

指针详解-下 1. 前言2. 函数指针数组2.1 函数指针数组的用途 3. 指向函数指针数组的指针3.1 回调函数 4. 库函数中qsort分析4.1 为什么这个函数需要我们自己实现?4.2 库函数qsort的使用 5. qsort函数的模拟实现5.1 大框架的实现5.2 比较函数的实现5.3 对于交换函数的思考5.4 交…

18.Word:数据库培训课程❗【34】

目录 题目 NO1.2.3.4 NO5设置文档内容的格式与样式 NO6 NO7 NO8.9 NO10.11标签邮件合并 题目 NO1.2.3.4 FnF12:打开"Word素材.docx”文件,将其另存为"Word.docx”在考生文件夹下之后到任务9的所有操作均基于此文件:"Word.docx”…

99.24 金融难点通俗解释:MLF(中期借贷便利)vs LPR(贷款市场报价利率)

目录 0. 承前1. 什么是MLF?1.1 专业解释1.2 通俗解释1.3 MLF的三个关键点: 2. 什么是LPR?2.1 专业解释2.2 通俗解释2.3 LPR的三个关键点: 3. MLF和LPR的关系4. 传导机制4.1 第一步:央行调整MLF4.2 第二步:银…

五. Redis 配置内容(详细配置说明)

五. Redis 配置内容(详细配置说明) 文章目录 五. Redis 配置内容(详细配置说明)1. Units 单位配置2. INCLUDES (包含)配置3. NETWORK (网络)配置3.1 bind(配置访问内容)3.2 protected-mode (保护模式)3.3 port(端口)配置3.4 timeout(客户端超时时间)配置3.5 tcp-keepalive()配置…

用 HTML、CSS 和 JavaScript 实现抽奖转盘效果

顺序抽奖 前言 这段代码实现了一个简单的抽奖转盘效果。页面上有一个九宫格布局的抽奖区域,周围八个格子分别放置了不同的奖品名称,中间是一个 “开始抽奖” 的按钮。点击按钮后,抽奖区域的格子会快速滚动,颜色不断变化&#xf…

【Linux系统】计算机世界的基石:冯诺依曼架构与操作系统设计

文章目录 一.冯诺依曼体系结构1.1 为什么体系结构中要存在内存?1.2 冯诺依曼瓶颈 二.操作系统2.1 设计目的2.2 系统调用与库函数 一.冯诺依曼体系结构 冯诺依曼体系结构(Von Neumann Architecture)是计算机的基本设计理念之一,由…

说说Redis的内存淘汰策略?

大家好,我是锋哥。今天分享关于【说说Redis的内存淘汰策略?】面试题。希望对大家有帮助; 说说Redis的内存淘汰策略? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis 提供了多种内存淘汰策略,用于在内存达到限制时决定如何…

【python】python基于机器学习与数据分析的手机特性关联与分类预测(源码+数据集)【独一无二】

👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 python基于机器学习与数据分析的手机特性关联与分类…

Flutter_学习记录_Tab的简单Demo~真的很简单

1. Tab的简单使用了解 要实现tab(选项卡或者标签视图)需要用到三个组件: TabBarTabBarViewTabController 这一块,我也不知道怎么整理了,直接提供代码吧: import package:flutter/material.dart;void main() {runApp(MyApp());…

JavaScript中的数组方法总结+详解

在JS中,数组方法是非常重要且常用的方法.在此整理总结一番. 1. javaScript常用数组方法 2.方法详解 1.push(); 功能: 在数组最后一位添加一个或多个元素,并返回新数组的长度,改变原数组.(添加多个元素用逗号隔开) var arr [1, 2, "c"];var rel arr.push(&q…

蓝桥杯之c++入门(二)【输入输出(上)】

目录 前言1.getchar和 putchar1.1 getchar()1.2 putchar() 2.scanf和 printf2.1 printf2.1.1基本用法2.1.2占位符2.1.3格式化输出2.1.3.1 限定宽度2.1.3.2 限定小数位数 2.2 scanf2.2.1基本用法2.2.2 占位符2.2.3 scanf的返回值 2.3练习练习1&#xff1a…

[EAI-028] Diffusion-VLA,能够进行多模态推理和机器人动作预测的VLA模型

Paper Card 论文标题:Diffusion-VLA: Scaling Robot Foundation Models via Unified Diffusion and Autoregression 论文作者:Junjie Wen, Minjie Zhu, Yichen Zhu, Zhibin Tang, Jinming Li, Zhongyi Zhou, Chengmeng Li, Xiaoyu Liu, Yaxin Peng, Chao…

3.5.5 基于横盘结构的分析体系——缠论(走势类型)

走势 缠论中走势的定义如下: 包含一个中枢的走势——盘整 包含两个或多个中枢的走势——趋势 方向 趋势(两中枢或多中枢) 盘整(一中枢) 上涨 下跌 表1-8 盘整和趋势类型的走势理论图。 趋势和中枢 …

使用PyQt5绘制带有刻度的温度计控件

前言:进入学习Python开发上位机界面的第二阶段,学习如何开发自定义控件,从常用的控件入手学习,本期主要学习如何使用PyQt5绘制带有刻度的温度计控件。 1. 先找到一篇参考文章 参考文章:Qt编写自定义控件5-柱状温度计…

DIFY源码解析

偶然发现Github上某位大佬开源的DIFY源码注释和解析,目前还处于陆续不断更新地更新过程中,为大佬的专业和开源贡献精神点赞。先收藏链接,后续慢慢学习。 相关链接如下: DIFY源码解析

赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。

佬们过年好呀~新年第一篇博客让我们来场赛博算命吧! 更多文章:个人主页 系列文章:JAVA专栏 欢迎各位大佬来访哦~互三必回!!! 文章目录 #一、文化背景概述1.文化起源2.起卦步骤 #二、卦象解读#三、just do i…

「AI学习笔记」深度学习的起源与发展:从神经网络到大数据(二)

深度学习(DL)是现代人工智能(AI)的核心之一,但它并不是一夜之间出现的技术。从最初的理论提出到如今的广泛应用,深度学习经历了几乎一个世纪的不断探索与发展。今天,我们一起回顾深度学习的历史…

AIGC技术中常提到的 “嵌入转换到同一个向量空间中”该如何理解

在AIGC(人工智能生成内容)技术中,“嵌入转换到同一个向量空间中”是一个核心概念,其主要目的是将不同类型的输入数据(如文本、图像、音频等)映射到一个统一的连续向量空间中,从而实现数据之间的…

单细胞分析基础-第一节 数据质控、降维聚类

scRNA_pipeline\1.Seurat 生物技能树 可进官网查询 添加链接描述 分析流程 准备:R包安装 options("repos"="https://mirrors.ustc.edu.cn/CRAN/") if(!require("BiocManager")) install.packages("BiocManager",update = F,ask =…

【13】WLC HA介绍和配置

1.概述 本文对AireOS WLC的HA进行介绍,和大多数网络架构设计一样,单台的WLC是无法保证设备的冗余性的,而且WLC也不是双引擎的设备,所以需要依靠High Available的技术来为WLC提供高可用性。 2.WLC HA类型 AireOS WLC的高可用性技术可以分为N+1的SSO的HA。不是所有的设备都…