【python】如何包装 numpy 的数组

一、说明

        Numpy的数组是强大的对象,通常用作更复杂的对象(如pandas或xarray)的基本数据结构。话虽如此,您当然也可以在自己的类中使用numpy的强大数组 - 为此,您基本上有2种方法:

  • 子类方法:创建从 Numpy.ndarray 继承的类
  • 容器方法:创建将数组作为属性的类

在本文中,我们将看到如何使用容器方法来包装 numpy 的数组来正确创建自己的自定义类。

二、 物理格式的项目

        让我们举一个示例项目:我们想创建一个简单的项目来处理物理单位和尺寸,创建长度或重量的数组,然后使用这些数组来计算平均身高或[身体质量指数](https://en.wikipedia.org/wiki/Body_mass_index)。我们希望依靠 numpy 来完成繁重的数值计算(如加法、减法、幂),但我们也希望能够处理像 numpy 数组这样的实例,比如 or .[1, 2, 3] meter [55 65 8] kilogramnp.sort(weights)np.min(heights)

        为此,我们将创建一个使用容器方法来包装 numpy 数组的新类。数值将存储为普通 numpy 数组,物理维度存储为字符串:

import numpy as npclass Physical():def __init__(self, value, unit=""):self.value = value # store the numerical value as a plain numpy arrayself.unit = unitdef __repr__(self):return f"<Physical:({self.value}, {self.unit})"def __str__(self):return f"{self.value} {self.unit}"weights = Physical(np.array([55.6, 45.7, 80.3]), "kilogram")
print(weights) # [55.6 45.7 80.3] kilogram

物理阵列的第一个实现

这将简单地打印:。同样,此字符串后面的数字列表是存储在 中的实际 numpy 数组。[55.6 45.7 80.3] kilogramself.value

现在这是毫无用处的:我们不能让这个对象与其他任何东西交互,所以我们添加了基本操作,如加法或乘法与其他实例:Physical

import numpy as npclass Physical():def __init__(self, value, unit=""):self.value = valueself.unit = unitdef __repr__(self):return f"<Physical:({self.value}, {self.unit})"def __str__(self):return f"{self.value} {self.unit}"def __add__(self, other):if self.unit == other.unit:return Physical(self.value + other.value, self.unit)else:raise ValueError("Physical objects must have same unit to be added.")def __sub__(self, other):if self.unit == other.unit:return Physical(self.value - other.value, self.unit)else:raise ValueError("Physical objects must have same unit to be subtracted.")def __mul__(self, other):return Physical(self.value * other.value, f"{self.unit}*{other.unit}")def __truediv__(self, other):return Physical(self.value / other.value, f"{self.unit}/{other.unit}")def __pow__(self, powfac):return Physical(self.value**powfac, f"{self.unit}^{powfac}")weights = Physical(np.array([55.6, 45.7, 80.3]), "kilogram")
heights = Physical(np.array([1.64, 1.85, 1.77]), "meter")
print(weights) # [55.6 45.7 80.3] kilogram
print(heights) # [1.64 1.85 1.77] meter
print(heights + heights) # [3.28 3.7  3.54] meter
# print(height + weights) # raises ValueError
print(heights**2) # [2.6896 3.4225 3.1329] meter^2

现在可以将物理阵列与其他物理阵列相加或相乘。

请注意,在添加或减去物理之前,我们首先检查它们是否具有相同的单位:您不能用重量添加长度(或土豆加胡萝卜,或马用驴)。

这很好,我们现在可以计算一个身体质量指数 (BMI) 数组,给定一个以米为单位的高度数组和一个以公斤为单位的重量数组。BMI 只是通过将重量除以平方高度来给出的,即:

BMI =体重(公斤)/身高(米)^2

weights = Physical(np.array([55.6, 45.7, 80.3]), "kilogram")
heights = Physical(np.array([1.64, 1.85, 1.77]), "meter")
bmi = weights/heights**2
print(bmi) # [20.67221892 13.35281227 25.63120432] kilogram/meter^2

        万岁!我们使用身高数组和身高数组计算了一个身体质量指数数组,使用后面的numpy数组来执行实际的数值计算。但是numpy的数组还有很多东西可以提供,这就是它变得非常有趣的地方。

三、实现 numpy 函数支持

Numpy 提供了许多用于数组的有用函数。仅举几例:

  • np.sinnp.cosnp.tan
  • np.expnp.lognp.log10
  • np.addnp.multiplynp.divide
  • np.minnp.maxnp.argminnp.argmax
  • np.floornp.ceilnp.trunc
  • np.concatenatenp.vstack

等等。您可以在他们的网站上找到numpy提供的所有内容:Routines — NumPy v1.25 Manual。

让我们尝试在我们的类中使用其中之一:

np.mean(bmi)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
...
<ipython-input-10-538e4626f6f7> in __truediv__(self, other)31 32     def __truediv__(self, other):
---> 33         return Physical(self._value / other._value, f"{self._unit}/{other._unit}")34 35     def __pow__(self, powfac):AttributeError: 'numpy.int64' object has no attribute '_value'

尝试调用我们的物理实例会引发一个 因为 numpy 依赖于整数的加法和除法,而我们的类没有正确实现这种操作。所以我们必须在某个地方告诉 numpy 我们想要如何表现。np.meanbmiAttributeErrornp.mean(bmi)

这就是界面发挥作用的地方。__array_function__

该接口只是一个规范化过程,用于重载(某些)numpy 函数如何处理类中的参数。__array_function__

让我们看一个轻量级的例子来处理我们的调用:np.mean(bmi)

import numpy as np# FIRST
HANDLED_FUNCTIONS = {}class Physical():def __init__(self, value, unit=""):self._value = valueself._unit = unit# ... other methods here, see above# SECONDdef __array_function__(self, func, types, args, kwargs):if func not in HANDLED_FUNCTIONS:return NotImplemented# Note: this allows subclasses that don't override# __array_function__ to handle MyArray objectsif not all(issubclass(t, Physical) for t in types):return NotImplementedreturn HANDLED_FUNCTIONS[func](*args, **kwargs)# THIRD
def implements(numpy_function):"""Register an __array_function__ implementation for Physical objects."""def decorator(func):HANDLED_FUNCTIONS[numpy_function] = funcreturn funcreturn decorator# FOURTH
@implements(np.mean)
def np_mean_for_physical(x, *args, **kwargs):# first compute the numerical value, with no notion of unitmean_value = np.mean(x._value, *args, **kwargs)# construct a Physical instance with the result, using the same unitreturn Physical(mean_value, x._unit)weights = Physical(np.array([55.6, 45.7, 80.3]), "kilogram")
heights = Physical(np.array([1.64, 1.85, 1.77]), "meter")
bmi = weights/heights**2print(np.mean(bmi)) # 19.885411834844252 kilogram/meter^2

使用 __array_function__ 接口实现 np.mean 支持

再次欢呼,返回我们物理数组的“平均值”,这确实是一个物理量,单位为“千克/米^2”。np.mean(bmi)

让我们回顾一下我们添加到代码中以实现此目的的内容。有 4 点需要注意:

  1. 首先,我们在类定义之上创建一个空字典,称为 。HANDLED_FUNCTION = {}
  2. 其次,我们在类中添加一个调用的方法,该方法采用一个名为 .我们将在一分钟内回到此方法的内容。__array_function__func
  3. 第三,我们创建一个装饰器构造函数:这是一个返回装饰器的函数(即,另一个将函数作为参数的函数)。我们的装饰器只是在我们的字典中创建 numpy 函数和 之间的对应关系,后者是我们版本的 numpy 函数。implementsHANDLED_FUNCTIONfunc
  4. 第四,我们实现了 numpy 处理物理实例的平均值,当调用时是物理实例。它具有与 大致相同的签名,并执行以下操作:np.mean(x)xnp.mean
  • 使用 x 的值计算数值平均值,这是一个普通数组。x._value
  • 然后使用平均值作为值和输入的单位作为单位创建新的物理实例。
  • 最后,我们在该函数上使用装饰器。implements

那么当我们打电话时会发生什么?np.mean(bmi)

好吧,由于 numpy 无法计算平均值,正如我们上面看到的,它检查是否有方法,并使用在 上使用的函数调用它,即 : 。bmi__array_function__bminp.meanbmi.__array_function__(np.mean, *args, **kwargs)

由于已经在 中注册,我们用它来调用我们的版本:这里相当于 。np.meanHANDELED_FUNCTIONSnp.meanHANDLED_FUNCTIONS[np.mean](*args, **kwargs)np_mean_for_physical(*args, **kwargs)

这就是如何使 numpy 的函数与您的自定义类一起工作。

不幸的是,这并不完全正确。此接口仅适用于某些 numpy 函数,但不适用于全部。

还记得上面的功能列表吗?好吧,我们可以将它们分为 2 个子列表:常规 numpy 函数和 numpy 通用函数 — 或简称“ufuncs”:

  • Numpy 函数 : , , , , ,np.minnp.maxnp.argminnp.argmaxnp.concatenatenp.vstack.
  • Numpy ufuncs : , , ,, , ,, , , , ,np.sinnp.cosnp.tannp.expnp.lognp.log10np.addnp.multiplynp.dividenp.floornp.ceilnp.trunc

我们看到了如何使用 实现 numpy 函数支持。在下一篇文章中,我们现在将看到如何使用该接口添加对“ufuncs”的支持。__array_function____array_ufunc__

四、总结一下 

  • 使用 numpy 数组的容器方法包括在自定义类实例中将数组设置为属性(与子类化数组相反)。
  • 要使类使用 numpy 函数调用(如 ),必须在类中实现接口。np.mean(my_array_like_instance)__array_function__
  • 这基本上是通过向类添加一个方法,编写自己的包装器(就像我们对 ) 所做的那样,并将它们链接在一起(就像我们对查找字典所做的那样)。__array_function__np_mean_for_physicalHANDLED_FUNCTIONS
  • 请注意,这仅适用于“常规”numpy 函数。对于numpy的“通用”函数,您还需要实现该接口。__array_ufunc__

这个主题相当广泛,所以这里有几个链接,你应该阅读,以更好地掌握利害关系:

  • 容器方法:Writing custom array containers — NumPy v1.25 Manual
  • __array_function__参考资料: Standard array subclasses — NumPy v1.25 Manual
  • 参考资料: Universal functions (ufunc) — NumPy v1.25 Manual

以下是我们在本文中编写的完整代码:

import numpy as npHANDLED_FUNCTIONS = {}class Physical():def __init__(self, value, unit=""):self._value = valueself._unit = unitdef __repr__(self):return f"<Physical:({self._value}, {self._unit})"def __str__(self):return f"{self._value} {self._unit}"def __add__(self, other):if self._unit == other._unit:return Physical(self._value + other._value, self._unit)else:raise ValueError("Physical objects must have same unit to be added.")def __sub__(self, other):if self._unit == other._unit:return Physical(self._value - other._value, self._unit)else:raise ValueError("Physical objects must have same unit to be subtracted.")def __mul__(self, other):return Physical(self._value * other._value, f"{self._unit}*{other._unit}")def __truediv__(self, other):return Physical(self._value / other._value, f"{self._unit}/{other._unit}")def __pow__(self, powfac):return Physical(self._value**powfac, f"{self._unit}^{powfac}")def __array_function__(self, func, types, args, kwargs):if func not in HANDLED_FUNCTIONS:return NotImplemented# Note: this allows subclasses that don't override# __array_function__ to handle Physical objectsif not all(issubclass(t, Physical) for t in types):return NotImplementedreturn HANDLED_FUNCTIONS[func](*args, **kwargs)def implements(numpy_function):"""Register an __array_function__ implementation for Physical objects."""def decorator(func):HANDLED_FUNCTIONS[numpy_function] = funcreturn funcreturn decorator@implements(np.mean)
def np_mean_for_physical(x, *args, **kwargs):# first compute the numerical value, with no notion of unitmean_value = np.mean(x._value, *args, **kwargs)# construct a Physical instance with the result, using the same unitreturn Physical(mean_value, x._unit)weights = Physical(np.array([55.6, 45.7, 80.3]), "kilogram")
heights = Physical(np.array([1.64, 1.85, 1.77]), "meter")
print(weights)
print(heights)
print(heights + heights)
print(heights**2)
ratio = weights/heights
print(ratio)
bmi = weights/heights**2
print(bmi)
print(np.mean(bmi))

干杯!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【动手学深度学习】--12.深度卷积神经网络AlexNet

文章目录 深度卷积神经网络AlexNet1.AlexNet2.模型设计3.激活函数4.模型实现5.读取数据集6.训练AlexNet 深度卷积神经网络AlexNet 学习视频&#xff1a;深度卷积神经网络 AlexNet【动手学深度学习v2】 官方笔记&#xff1a;深度卷积神经网络&#xff08;AlexNet&#xff09; …

Android 中 app freezer 原理详解(一):R 版本

基于版本&#xff1a;Android R 0. 前言 在之前的两篇博文《Android 中app内存回收优化(一)》和 《Android 中app内存回收优化(二)》中详细剖析了 Android 中 app 内存优化的流程。这个机制的管理通过 CachedAppOptimizer 类管理&#xff0c;为什么叫这个名字&#xff0c;而不…

【Linux | Shell】结构化命令2 - test命令、方括号测试条件、case命令

目录 一、概述二、test 命令2.1 test 命令2.2 方括号测试条件2.3 test 命令和测试条件可以判断的 3 类条件2.3.1 数值比较2.3.2 字符串比较 三、复合条件测试四、if-then 的高级特性五、case 命令 一、概述 上篇文章介绍了 if 语句相关知识。但 if 语句只能执行命令&#xff0c…

Docker 的数据管理、容器互联、镜像创建

目录 一、数据管理 1.数据卷 2. 数据卷容器 二、容器互联&#xff08;使用centos镜像&#xff09; 三、Docker 镜像的创建 1.基于现有镜像创建 1.1首先启动一个镜像&#xff0c;在容器里修改 1.2将修改后的容器提交为新的镜像&#xff0c;需使用该容器的id号创建新镜像 …

JAVA SE -- 第十天

&#xff08;全部来自“韩顺平教育”&#xff09; 一、枚举&#xff08;enumeration&#xff0c;简写enum&#xff09; 枚举是一组常量的集合 1、实现方式 a.自定义类实现枚举 b.使用enum关键字实现枚举 二、自定义类实现枚举 1、注意事项 ①不需要提供setXxx方法&#xff…

HTTP、HTTPS协议详解

文章目录 HTTP是什么报文结构请求头部响应头部 工作原理用户点击一个URL链接后&#xff0c;浏览器和web服务器会执行什么http的版本持久连接和非持久连接无状态与有状态Cookie和Sessionhttp方法&#xff1a;get和post的区别 状态码 HTTPS是什么ssl如何搞到证书nginx中的部署 加…

【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出小吉的问题 文章目录 &#x1f354;概述&#x1f354;索引结构⭐B-Tree多路平衡查找树&#x1f3f3;️‍&a…

【iOS】weak关键字的实现原理

前言 关于什么是weak关键字可以去看看我以前的一篇博客&#xff1a;【OC】 属性关键字 weak原理 1. SideTable SideTable 这个结构体&#xff0c;前辈给它总结了一个很形象的名字叫引用计数和弱引用依赖表&#xff0c;因为它主要用于管理对象的引用计数和 weak 表。在 NSOb…

(笔记)快速排序

快速排序 快速排序是一种常用的排序算法。它的时间复杂度为O(nlogn)&#xff0c;并且在实际应用中表现良好。快速排序的基本思想是通过选择一个基准数&#xff0c;将数组分成两个子数组&#xff0c;比基准数小的放在左边&#xff0c;比基准数大的放在右边&#xff0c;然后对左…

Vite + Vue3 + Ts 【免key、免账号实战本地运行GPT】

&#x1f414; 前期回顾 Vue3 Ts Vite —— 封装庆祝彩屑纷飞 示例_彩色之外的博客-CSDN博客封装 彩屑纷飞 示例https://blog.csdn.net/m0_57904695/article/details/131718019?spm1001.2014.3001.5501 目录 &#x1f30d; 公网 &#x1f6f9; 本地 &#x1fa82; 源码 &…

【前端|CSS系列第4篇】CSS布局之网格布局

前言 最近在做的一个项目前台首页有一个展示词条的功能&#xff0c;每一个词条都以一个固定大小的词条卡片进行展示&#xff0c;要将所有的词条卡片展示出来&#xff0c;大概是下面这种布局 每一行的卡片数目会随着屏幕大小自动变化&#xff0c;并且希望整个卡片区域周围不要…

20230721 Essex UK, Dongbing Gu 公开讲座--机器人前沿

个人主页&#xff1a; https://www.essex.ac.uk/people/GUDON81301/dongbing-gu 机器人领域任务的特点&#xff1a;dull, dirty, dangerous tasks in remote spaces 机器鱼&#xff1a; 实时港口环境监测 机器鱼群探索算法 化学传感器 水面声呐定位系统/SLAM/通信问题 Robotic …

C—数据的储存(下)

文章目录 前言&#x1f31f;一、练习一下&#x1f30f;1.例一&#x1f30f;2.例二&#x1f30f;3.例三&#x1f30f;4.例四 &#x1f31f;二、浮点型在内存中的储存&#x1f30f;1.浮点数&#x1f30f;2.浮点数存储&#x1f4ab;&#xff08;1&#xff09;.二进制浮点数&#x…

QDialog的两种显示方式

QDialog的两种显示方式 模态显示非模态显示 QDialog不能嵌入到其他窗口中显示&#xff08;无论继承与否&#xff09; 模态显示 d->exec(); 阻塞程序的执行 非模态显示 d->show(); 不阻塞程序

OpenCV4图像处理-图像交互式分割-GrabCut

本文将实现一个与人&#xff08;鼠标&#xff09;交互从而分割背景的程序。 GrabCut 1.理论介绍2. 鼠标交互3. GrabCut 1.理论介绍 用户指定前景的大体区域&#xff0c;剩下为背景区域&#xff0c;还可以明确指出某些地方为前景或者背景&#xff0c;GrabCut算法采用分段迭代的…

MySQL主从复制与读写分离

文章目录 一.前言二.主从复制原理1.MySQL的复制类型2.MySQL主从复制的工作过程2.1 MysQL主从复制延迟原因2.2问题解决方法2.3 MySQL 有几种同步方式2.3.1 异步复制2.3.2 同步复制2.3.3 半同步复制2.3.4 增强半同步复制&#xff08;lossless Semi-Sync Replication、无损复制&am…

消息队列 CKafka 跨洋数据同步性能优化

导语 本文主要介绍了 CKafka 在跨洋场景中遇到的一个地域间数据同步延时大的问题&#xff0c;跨地域延时问题比较典型&#xff0c;所以详细记录下来做个总结。 一. 背景 为了满足客户跨地域容灾、冷备的诉求&#xff0c;消息队列 CKafka 通过连接器功能&#xff0c;提供了跨…

进程控制学习笔记

文章目录 进程退出孤儿进程僵尸进程进程回收waitpid()函数 进程退出 子进程的退出需要父进程对其资源的释放&#xff0c;子进程只能对用户区的数据进行释放&#xff0c;无法完成对内核区的释放。 可以获取到。 两个退出的区别&#xff1a; 系统调用不会刷新缓冲区&#xff…

Kubernetes对象深入学习之四:对象属性编码实战

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习》系列的第四篇&#xff0c;前面咱们读源码和文档&#xff0c;从理论上学习了kubernetes的对象相关的知识&#xff…

python识别极验4滑块验证码实战

闲得无聊&#xff0c;趁着休息研究了一下极验4滑块验证码的安全性&#xff0c;是否有机器识别、自动化拖拽的可能性。首先看一下效果 如何识别验证码 1、下载图片 下载图片可以参考博客《采集极验4滑块验证码图片数据》 2、标记图片 3、标记滑动距离 实现代码 __author__ &…