physipy: 使 Python 具有单位感知
原文:towardsdatascience.com/physipy-make-python-unit-aware-846162522889你是否曾经用 Python 进行过工程/科学计算,最终发现自己迷失或困惑于变量的单位,比如“那是米还是毫米的值”?或者你意识到在某个时刻你添加了一个电流和一个电阻——这是不可能的?就像每个物理老师都说过的一样:你不能把胡萝卜和西红柿加在一起。
好吧,**physipy**正是为了解决这类问题而存在的。
由Artturi Jalli在Unsplash上的照片
目录:
· 什么是 physipy? · 一次一个示例理解 physipy ∘使用 physipy 计算身体质量指数 BMI ∘ 使用 NumPy 数组牛顿运动定律 ∘ 使用 NumPy 函数的欧姆定律 ∘ 使用 favunit 计算常见粒子的爱因斯坦质能等价 ∘ 使用内置 favunit 进行自由落体 ∘ 使用 Matplotlib 绘制物体位置和速度 · 总结
所有图片均为作者所有。
什么是 physipy?
***physipy***是一个轻量级的包,它允许你非常容易地定义和声明物理单位,并跟踪所有变量的单位。换句话说,你永远不需要在变量后加上相应的单位(例如my_height_cm将变成my_height),如果你试图把胡萝卜和西红柿加在一起,将会抛出一个异常。
physipy为科学/工程工作提供了很好的功能:它与 NumPy 完美集成,提供了 Pandas 扩展,完整的文档,与 Matplotlib 无缝工作,并为 jupyter 提供了(ipy)小部件。
导入米和秒,并开始使用具有单位感知的变量。
为了完全透明,我是 physipy 的创建者。另外请注意,physipy 在MIT 许可证下。
一次一个示例理解 physipy
在这篇第一篇文章中,我们将看到如何使用physipy,特别是它如何与 NumPy 和 Matplotlib 很好地集成,通过一些基本的示例。
这些示例的目的是按顺序阅读,因为在阅读过程中会逐渐添加概念和工具。另一方面,它们都旨在非常简单且注释良好,所以即使直接跳到最后一个也能理解。
您也可以直接访问 Physipy 的文档或 GitHub 上的项目仓库。
physipy : 使 python 具有单位感知功能
GitHub – mocquin/physipy: 一个透明的处理物理量(如 2…
要安装 physipy,您可以使用 pypi 通过简单的命令行:
pip install physipy 或者如果您更喜欢从 GitHub 下载最新版本,您可以在本地下载并解压缩包,或者使用以下命令克隆 git 仓库:
git clone https://github.com/mocquin/physipy 使用 physipy 计算身体质量指数 BMI
如您所知,一个常见的健康指标是身体质量指数。正如维基百科所述(As wikipedia states):
体质指数(BMI)是从一个人的质量(体重)和身高推导出的一个值。BMI 定义为身体质量除以身体高度的平方,并以 kg/m2 的单位表示,由千克(kg)的质量和米(m)的高度组成。
让我们看看没有 physipy 的代码会是什么样子:
#### BMI example, without physipy# no physipy → we need to keep track of the unit everywhere my_weight_kg =75 my_height_m =1.89defbmi_calculator(weight_kg, height_m):"Compute Body-Mass-Index from weight and height."return weight_kg / height_m**2print(bmi_calculator(my_weight_kg, my_height_m))# notice that the output is pure, unit-less number, while it is actually kg-per-meter-squared# 20.99605274208449现在让我们看看使用 physipy 的代码是什么样的:
from physipy import kg, m # define physical quantities that are unit aware my_weight =75* kg my_height =1.89* m defbmi_calculator(weight, height):"Compute Body-Mass-Index from weight and height."return weight / height**2print(bmi_calculator(my_weight, my_height))# 20.99605274208449 kg/m**2注意代码变化有多小:我们可以在变量名称中去除任何单位后缀,而不会失去对它们的跟踪。
另一方面,我们可以看到变量的值是明确定义的,使用了命名单位。另一个很好的特性是返回的输出仍然附有适当的单位,在这种情况下是 kg/m**2。
好处在于我们可以使用任何单位,而不用担心任何转换:所有转换都由 Physipy 在后端完成。
例如,假设你记录了身高(厘米)和体重(克)。
# retrieve the unitsfrom physipy import units # units is just a dict cm = units['cm'] g = units['g']print(bmi_calculator(75000*g,189*cm))# 20.99605274208449 kg/m**2因此,我们得到了相同的输出值,同时我们能够指定变量在其他单位中。
使用 numpy 数组进行牛顿运动定律
我们在这里演示如何仅使用常规乘法运算符(类似于浮点数和整数)将单位附加到常规 NumPy 数组上:
import numpy as np from physipy import m, kg, s m = np.array([10,20,30])* kg # mass a =2* m / s**2# acceleration# Calculate force using Newton's Second Law : force = mass * acceleration F = m * a print(F)# [ 200\. 800\. 1800.] kg**2/s**2 # 1 kg**s/s**2 is equivalent to 1 Newton欧姆定律与 NumPy 函数
使用 NumPy 和 Physipy 几乎可以与所有 NumPy 的函数一起工作。在这里,我们可以使用具有单位感知值的 NumPy 函数,并且返回的数组仍然具有正确的单位。
import numpy as np from physipy import units V = units['V'] ohm = units['ohm'] V =12* V # voltage R = np.arange(1*ohm,5*ohm)# array with unit ohm# Calculate current using Ohm's Law I = V / R print(I)# [12\. 6\. 4\. 3.] A# If for some reason you forgot the unit of I, physipy knows and attaches# it to I for you - you get an output in Amps for free !对于常见粒子,使用 favunit 的爱因斯坦质量-能量等价
对于常见粒子,使用 Favunit 在这个例子中,我们介绍了任何单位感知对象的 .favunit 属性,这意味着这个变量的“首选单位”。这个单位将用于打印变量。在国际单位制中,能量的标准单位是焦耳,等于 1 kg*m2/s2。
import numpy as np from physipy import constants, kg, units # constants is just a dict of physical constants c = constants['c'] J = units['J'] masses = np.array([9.1093837E-31,# mass of electron1.673E-27,# mass of proton1.675E-27,# mass of neutron])* kg # Calculate energy using Einstein's equation E = masses * c**2# indexing works just as usual, with the added output energy unitprint(E[0])# enery for proton : 8.187105775475753e-14 kg*m**2/s**2print(E)# [8.18710578e-14 1.50361741e-10 1.50541492e-10] kg*m**2/s**2# changing the display unit to Joule E.favunit = J print(E[0])# 8.187105775475753e-14 Jprint(E)# [8.18710578e-14 1.50361741e-10 1.50541492e-10] J如您所见,相同的变量 favunit 可以随意修改以更改其字符串表示形式。Physipy 的酷之处在于,这个 .favunit 属性仅用于打印目的;实际的“真实”值和物理单位(kg*m2/s2)存储在后端。
使用内置的 favunit 进行自由落体
在这个例子中,我们正在模拟一个物体从 100 米的初始高度自由落体。我们使用自由落体的运动方程,h=h0−1/2gt²,其中 h 是物体在时间 t 的高度,h0 是初始高度,g 是重力加速度,t 是时间。
我们使用这个例子来展示如何设置 favunit —— 变量用于打印的单位,可以是函数中的“手动”设置,也可以使用 set_favunit 装饰器。
让我们先看看如何手动设置首选单位:
import numpy as np from physipy import m, s, units cm = units['cm']# we are using cm as the favunit# Constants initial_height =100* m # Initial height of the object gravity =9.81* m / s**2# Acceleration due to gravity# Time samples time_samples = np.linspace(0,3,50)* s # Time samples from 0 to 5 secondsdefcalculate_height(time_samples): heigth = initial_height -0.5* gravity * time_samples**2 heigth.favunit = cm return heigth # Calculate heights for each time sample heights = calculate_height(time_samples)# Print time samples and corresponding heightsfor t, h inzip(time_samples, heights):print(f"Time: {t}, Height: {h}")# Time: 0.0 s, Height: 10000.0 cm# ...# Time: 3.0 s, Height: 5585.5 cm虽然设置高度 favunit 的方法很简单,但它与实际的数学计算无关。另一种方法是使用 set_favunit 装饰器。所以以下两个片段是等效的:
手动设置 favunit:
defcalculate_height(time_samples): heigth = initial_height -0.5* gravity * time_samples**2 heigth.favunit = cm return heigth 在这里使用 set_favunit 装饰器
from physipy import set_favunit @set_favunit(cm)defcalculate_height(time_samples): heigth = initial_height -0.5* gravity * time_samples**2return heigth 使用 set_favunit 装饰器可能更受欢迎,因为它将计算逻辑和单位打印分离开来。
使用 Matplotlib 绘制物体的位置和速度
在这个例子中,我们正在可视化物体在重力影响下的运动。具体来说,我们正在绘制其运动的两个重要方面:位移和速度,随时间的变化。
重要的语句是调用 setup_matplotlib(),这样 Matplotlib 就会意识到 Physipy,并且默认情况下会在绘制单位感知对象时设置相应的单位。
换句话说,你可以去掉 set_xlabel('s') 或 set_ylabel('km') 来指定单位;physipy 会自动完成这项工作。
import numpy as np import matplotlib.pyplot as plt from physipy import units, s, setup_matplotlib, constants cm = units['cm']# centimeter ms = units["ms"]# millisecond g = constants['g']# this makes matplotlib aware of physipy setup_matplotlib() time = np.linspace(0,10,100)* s # Time values time.favunit = ms velocity = g * time # Velocity as a function of time (assuming constant acceleration) displacement =0.5* g * time**2# displace from origin position displacement.favunit = cm # Plotting fig, ax = plt.subplots(figsize=(10,4)) ax.plot(time, displacement, label="Displacement", color="blue") ax2 = ax.twinx() ax2.plot(time, velocity, label="Velocity", linestyle=" - ", color="red")注意,轴标签上添加了一些单位,但没有显式调用 set_xlabel/set_ylabel。
总结
希望这些例子能帮助你理解如何使用 physipy 的基础知识。你应该记住以下事项:
- 使用 physipy 最简单的方法是导入一些单位/常量,这些单位/常量存储在经典的 Python 字典中,使用
from physipy import units, constants。注意,国际单位制(Système International units)可以直接使用from physipy import m, s, kg。 - 要定义单位感知变量,只需将值乘以单位即可,例如
heights = np.linspace(1, 50)*km。 - 几乎所有的 numpy 函数都适用于单位感知数组,例如,你可以简单地使用
np.max(heights)找到高度数组中的最大值,返回的值将是50000 m。 - 默认情况下,单位感知值将使用 SI 单位打印,例如,焦耳将打印为
kg*m**2/s**2。如果你想使用特定的单位,只需设置favunit属性:heights.favunit = mm - 你可以使用
from physipy import setup_matplotlib和setup_matplotlib()来使 matplotlib 识别 physipy 并处理单位。从那时起,任何图表都会将单位添加到轴标签中。
physipy 的优点是它轻量级(简单的示例,源代码简单),它与 numpy 和 matplotlib 高度集成。
我们稍后会看到,它也是一个处理 Python 中单位的快速包,归功于其轻量级结构(因此开销小)。它还提供了pandas 扩展和ipywidgets for jupyter/notebooks。
在下一篇文章中,我们将稍微深入探讨 physipy,介绍 physipy 的 2 个(也是唯一 2 个)重要类,即Dimension和Quantity。
你可以在项目的文档中找到更多示例和大量信息:
physipy : 使 Python 具有单位感知
你可能喜欢我之前的一些帖子,我写关于 Python、机器学习/数据科学、信号处理、数值技术的内容:
Sklearn 教程
科学/数值 Python
数据科学和机器学习
时间序列的傅里叶变换