I have been working in splitting iris units module into its own module for a while and recently we had our first release, and our first external use: the IOOS compliance-checker.
(Warning! Before reading the post please watch the video below.)
In [2]:
from IPython.display import YouTubeVideo
YouTubeVideo("N-edLdxiM40")
Out[2]:
cf_units goal is to be a CF-compliant and UDUNITS-compatible units module.
Until now the next best was udunitspy. Sadly udunitspy is no longer being developed, nor it works on Windows.
In this post I will make a comparison with udunitspy to make the case for replacing with cf_units and, hopefully, present a quick introduction on how to use cf_units.
The Unit object
In [3]:
import udunitspy
upy = udunitspy.Unit('m/s')
print('{}: {!r}'.format(upy, upy))
In [4]:
from cf_units import cf_units
uir = cf_units.Unit('m/s')
print('{}: {!r}'.format(uir, uir))
Units known/unknown
In [5]:
right = 'm/s'
wrong = 'coconuts'
def units_known(key):
try:
udunitspy.Unit(str(key))
except udunitspy.UdunitsError:
return False
return True
units_known(right), units_known(wrong)
Out[5]:
In [6]:
def units_known(key):
try:
cf_units.Unit(key)
except ValueError: # I prefer standard exceptions.
return False
return True
units_known(right), units_known(wrong)
Out[6]:
Can it quack like a duck?
In [7]:
cf_units.Unit(cf_units.Unit('m/s')) # OK.
Out[7]:
In [8]:
udunitspy.Unit(udunitspy.Unit('m/s')) # Nope. Only strings!
Units convertible?
In [9]:
def units_convertible(units1, units2, reftimeistime=True):
"""Return True if a Unit representing the string units1 can be converted
to a Unit representing the string units2, else False."""
try:
udunitspy.Converter(str(units1), str(units2))
except udunitspy.UdunitsError:
return False
u1 = udunitspy.Unit(str(units1))
u2 = udunitspy.Unit(str(units2))
return u1.are_convertible(u2)
units_convertible('km/h', 'm/s'), units_convertible('km/h', 's')
Out[9]:
In [10]:
def units_convertible(units1, units2):
"""No need for a try/exception clause."""
u1 = cf_units.Unit(units1)
return u1.is_convertible(units2)
units_convertible('km/h', 'm/s'), units_convertible('km/h', 's')
Out[10]:
Temporal units
In [11]:
def units_temporal(units):
r = False
try:
u = udunitspy.Unit('seconds since 1900-01-01')
r = u.are_convertible(str(units))
except udunitspy.UdunitsError:
return False
return r
units_temporal('seconds since 1900-01-01')
Out[11]:
In [12]:
def units_temporal(key):
return cf_units.Unit(key).is_time_reference()
units_temporal('seconds since 1900-01-01')
Out[12]:
... many more is_<something>
are available in cf_units
In [13]:
t = cf_units.Unit('seconds since 1900-01-01')
(t.is_udunits(), t.is_dimensionless(), t.is_no_unit(),
t.is_unknown(), t.is_vertical(), t.is_time())
Out[13]:
The exceptions
In [14]:
udunitspy.Unit(wrong) # Custom exception.
In [15]:
cf_units.Unit(wrong) # Regular ValueError!
The cf_units.Units
object is richer in methods and properties
In [16]:
a = cf_units.Unit('km/h')
a.title(42), a.symbol, a.definition, a.origin
Out[16]:
In [17]:
formats = (cf_units.UT_ASCII, cf_units.UT_ISO_8859_1, cf_units.UT_LATIN1,
cf_units.UT_UTF8, cf_units.UT_NAMES, cf_units.UT_DEFINITION)
for fmt in formats:
print(a.format(option=fmt))
Some are units specific properties
In [18]:
a = cf_units.Unit('degree')
a.modulus
Out[18]:
How about creating time-ranges?
In [19]:
u = cf_units.Unit('hours since 1970-01-01 00:00:00',
calendar=cf_units.CALENDAR_STANDARD)
ut = u.utime()
ut.num2date(range(10))
Out[19]:
And time conversions?
In [20]:
import numpy as np
var = [1, 2, 3, 4]
udunitspy.Unit('hours since 1970-01-01 00:00:00').get_converter("seconds since 1970-01-01").evaluate(min(var))
Out[20]:
In [21]:
calendar = cf_units.CALENDAR_GREGORIAN
origin = cf_units.Unit('hours since 1970-01-01 00:00:00', calendar=calendar)
target = cf_units.Unit('seconds since 1970-01-01', calendar=calendar)
origin.convert(min(var), target)
Out[21]:
I hope to write cf_units
docs soon! Stay tuned.
In [22]:
HTML(html)
Out[22]: