name = '2015-06-01-cf_units'
title = 'Yet another units module'
import os
from datetime import datetime
from IPython.core.display import HTML
with open('creative_commons.txt', 'r') as f:
html = f.read()
html = '''
<small>
<p> This post was written as an IPython notebook.
It is available for <a href='https://ocefpaf.github.com/python4oceanographers/downloads/notebooks/%s.ipynb'>download</a>
or as a static <a href='https://nbviewer.ipython.org/url/ocefpaf.github.com/python4oceanographers/downloads/notebooks/%s.ipynb'>html</a>.</p>
<p></p>
%s''' % (name, name, html)
%matplotlib inline
from matplotlib import style
style.use('ggplot')
hour = datetime.utcnow().strftime('%H:%M')
comments="true"
date = '-'.join(name.split('-')[:3])
slug = '-'.join(name.split('-')[3:])
metadata = dict(title=title,
date=date,
hour=hour,
comments=comments,
slug=slug,
name=name)
markdown = """Title: {title}
date: {date} {hour}
comments: {comments}
slug: {slug}
{{% notebook {name}.ipynb cells[1:] %}}
""".format(**metadata)
content = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, '{}.md'.format(name)))
with open('{}'.format(content), 'w') as f:
f.writelines(markdown)
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.)
from IPython.display import YouTubeVideo
YouTubeVideo("N-edLdxiM40")
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.
import udunitspy
upy = udunitspy.Unit('m/s')
print('{}: {!r}'.format(upy, upy))
m.s-1: <units of 'm.s-1'>
from cf_units import cf_units
uir = cf_units.Unit('m/s')
print('{}: {!r}'.format(uir, uir))
m/s: Unit('m/s')
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)
(True, False)
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)
(True, False)
cf_units.Unit(cf_units.Unit('m/s')) # OK.
Unit('m/s')
udunitspy.Unit(udunitspy.Unit('m/s')) # Nope. Only strings!
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-8-b752a337c5dd> in <module>() ----> 1 udunitspy.Unit(udunitspy.Unit('m/s')) # Nope. Only strings! /home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/udunitspy/udunits2.py in __init__(self, spec, system, encoding) 158 system = System(path=system) 159 self.system = system or DEFAULT_SYSTEM --> 160 self.this = ut.parse(self.system.this, spec, encoding or UT_ASCII) 161 162 if not self.this: TypeError: in method 'parse', argument 2 of type 'char const *const'
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')
(True, False)
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')
(True, False)
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')
True
def units_temporal(key):
return cf_units.Unit(key).is_time_reference()
units_temporal('seconds since 1900-01-01')
True
is_<something>
are available in cf_units¶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())
(True, False, False, False, False, False)
udunitspy.Unit(wrong) # Custom exception.
--------------------------------------------------------------------------- UdunitsError Traceback (most recent call last) <ipython-input-14-6c7e6518afc6> in <module>() ----> 1 udunitspy.Unit(wrong) # Custom exception. /home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/udunitspy/udunits2.py in __init__(self, spec, system, encoding) 161 162 if not self.this: --> 163 raise UdunitsError(Unit.__init__.__name__, ut.get_status()) 164 165 def copy(self): UdunitsError: __init__ resulted in udunits error UT_UNKNOWN: String unit representation contains unknown word
cf_units.Unit(wrong) # Regular ValueError!
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-15-57d41d544bd7> in <module>() ----> 1 cf_units.Unit(wrong) # Regular ValueError! /home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/cf_units/cf_units.py in __init__(self, unit, calendar) 928 # _ut_parse returns 0 on failure 929 if ut_unit is None: --> 930 self._raise_error('Failed to parse unit "%s"' % unit) 931 if _OP_SINCE in unit.lower(): 932 if calendar is None: /home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/cf_units/cf_units.py in _raise_error(self, msg) 963 ctypes.set_errno(0) 964 --> 965 raise ValueError('[%s] %s %s' % (status_msg, msg, error_msg)) 966 967 # NOTE: ValueError: [UT_UNKNOWN] Failed to parse unit "coconuts"
cf_units.Units
object is richer in methods and properties¶a = cf_units.Unit('km/h')
a.title(42), a.symbol, a.definition, a.origin
('42 km/h', '0.277777777777778 m.s-1', '0.277777777777778 m.s-1', 'km/h')
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))
0.277777777777778 m.s-1 0.277777777777778 m/s 0.277777777777778 m/s 0.277777777777778 m·s⁻¹ 0.277777777777778 meter-second^-1 0.277777777777778 m.s-1
a = cf_units.Unit('degree')
a.modulus
360.0
u = cf_units.Unit('hours since 1970-01-01 00:00:00',
calendar=cf_units.CALENDAR_STANDARD)
ut = u.utime()
ut.num2date(range(10))
array([datetime.datetime(1970, 1, 1, 0, 0), datetime.datetime(1970, 1, 1, 1, 0), datetime.datetime(1970, 1, 1, 2, 0, 0, 13), datetime.datetime(1970, 1, 1, 3, 0), datetime.datetime(1970, 1, 1, 4, 0), datetime.datetime(1970, 1, 1, 5, 0, 0, 13), datetime.datetime(1970, 1, 1, 6, 0), datetime.datetime(1970, 1, 1, 7, 0), datetime.datetime(1970, 1, 1, 8, 0, 0, 13), datetime.datetime(1970, 1, 1, 9, 0)], dtype=object)
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))
3600.0
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)
3600.0
I hope to write cf_units
docs soon! Stay tuned.
HTML(html)
This post was written as an IPython notebook. It is available for download or as a static html.