我想将3cm/µs² + 4e-4 sqmiles/km/h**2
之类的字符串拆分为SI unit(在本例中为m/s**2
)及其大小(以该单位的倍数)。
由于sympy同时提供parsing module和many physical units and SI prefixes,我猜使用sympy会是一个好主意。但实现这一目标的好方法是什么?我会写一个类似下面的算法,但我想避免重新发明方形轮:
4e-4
之类的语法除外)和空格(除非它在显式运算符旁边)之间的转换视为乘法,然后标记化Magnitude * some SI units
(在不一致的单位上提供有意义的错误消息,例如Cannot add m**2 to s
)这可以通过现有手段轻松实现吗?或者如何最好地实施?
答案 0 :(得分:3)
解决方案是从SymPy units
模块收集所有单元并使用它们替换sympify
>>> import sympy.physics.units as u
... subs = {}
... for k, v in u.__dict__.items():
... if isinstance(v, Expr) and v.has(u.Unit):
... subs[Symbol(k)] = v # Map the `Symbol` for a unit to the unit
>>> # sympify returns `Symbol`s, `subs` maps them to `Unit`s
>>> print sympify('yard*millimeter/ly').subs(subs)
127*m/1313990343414000000000
如果符号不在units
中,则只会将其打印为未知符号(例如barn
)
>>> print sympify('barn/meter**2').subs(subs)
barn/m**2
但您可以随时在subs
字典中添加内容。
>>> subs[Symbol('almost_meter')] = 0.9*u.meter
... sympify('almost_meter').subs(subs)
0.9*m
SI前缀不能完全像您想要的那样工作。您需要添加一个乘法符号(或者希望它是一个明确实现的km
之类的公共单位)。此外,由于它们不是Unit
个实例,而是Integer
个实例,因此您必须将它们添加到subs
:
>>> import sympy.physics.units as u
... subs = {}
... for k, v in u.__dict__.items():
... if (isinstance(v, Expr) and v.has(u.Unit)) or isinstance(v, Integer):
... subs[Symbol(k)] = v
>>> print sympify('mega*m').subs(subs)
1000000*m
对于unicode,您可能需要一些预处理。我不认为SymPy对unicode支持作出任何承诺。
如果您实施新的Unit
,请考虑在github上向他们发送拉取请求。要编辑的文件应为sympy/physics/units.py
。
在SymPy的开发版本中,您可以找到用于假定隐式乘法的代码,其中写入了适当的空格:
>>> from sympy.parsing.sympy_parser import (parse_expr,
... standard_transformations, implicit_multiplication_application)
>>> parse_expr("10sin**2 x**2 + 3xyz + tan theta",
... transformations=(standard_transformations +
... (implicit_multiplication_application,)))
3*x*y*z + 10*sin(x**2)**2 + tan(theta)
sympify
使用eval
,如果您打算将其用于面向网络的应用程序,则可以利用它!
答案 1 :(得分:1)
我发现astropy有一个好的单位模块。经过一些准备,你可以做到
import astropy.units as u
from functools import reduce
u.Unit('MeV/fm').si #160.218 N
eval('1*MeV/fm+3*N',u.__dict__).si #163.21765649999998 N
from astropy.units import imperial
u.__dict__.update(imperial.__dict__)
u.sqmiles = u.mile**2
eval('3*cm/Ys**2 + 4e-4*sqmiles/km/h**2',u.__dict__).si #7.993790464000001e-08 m / s2
以下函数将scipy
CODATA常量作为astropy
单位的数量添加
def units_and_constants():
"""
>>> u = units_and_constants()
>>> u.hartree_joule_relationship
<Quantity 4.35974434e-18 J>
>>> eval('1*MeV/fm+3*N',u.__dict__).si
<Quantity 163.21765649999998 N>
"""
import astropy.units as u
from astropy.units import imperial
u.__dict__.update(imperial.__dict__)
from scipy.constants import physical_constants, value, unit
import string
def qntty(x):
un = unit(x)
va = value(x)
if un:
return va*eval(un.strip().replace(' ','*').replace('^','**'),u.__dict__)
else:
return va
u.sr = u.radian**2
u.E_h = qntty('hartree-joule relationship')
u.c = qntty('speed of light in vacuum')
u.C_90 = (1+4.6e-8)*u.C
codata = {}
for n, t in physical_constants.items():
v = qntty(n)
for x in string.punctuation+' ':
n = n.replace(x,'_')
codata[n] = v
u.__dict__.update(codata)
return u
yt也tackles a problem similar to yours。 查看Test file以查看其使用方式。