{ "metadata": { "name": "", "signature": "sha256:31e47feffb00b301259bd8f83327926a71927c86e6f9064984a2c8047858445b" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "name = '2015-06-01-cf_units'\n", "title = 'Yet another units module'\n", "\n", "import os\n", "from datetime import datetime\n", "\n", "from IPython.core.display import HTML\n", "\n", "with open('creative_commons.txt', 'r') as f:\n", " html = f.read()\n", "\n", "html = '''\n", "\n", "

This post was written as an IPython notebook.\n", " It is available for download\n", " or as a static html.

\n", "

\n", "%s''' % (name, name, html)\n", "\n", "%matplotlib inline\n", "from matplotlib import style\n", "style.use('ggplot')\n", "\n", "hour = datetime.utcnow().strftime('%H:%M')\n", "comments=\"true\"\n", "\n", "date = '-'.join(name.split('-')[:3])\n", "slug = '-'.join(name.split('-')[3:])\n", "\n", "metadata = dict(title=title,\n", " date=date,\n", " hour=hour,\n", " comments=comments,\n", " slug=slug,\n", " name=name)\n", "\n", "markdown = \"\"\"Title: {title}\n", "date: {date} {hour}\n", "comments: {comments}\n", "slug: {slug}\n", "\n", "{{% notebook {name}.ipynb cells[1:] %}}\n", "\"\"\".format(**metadata)\n", "\n", "content = os.path.abspath(os.path.join(os.getcwd(), os.pardir, os.pardir, '{}.md'.format(name)))\n", "with open('{}'.format(content), 'w') as f:\n", " f.writelines(markdown)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "I have been working in splitting\n", "[iris units](https://github.com/SciTools/cf_units)\n", "module into its own module for a while and recently we had our\n", "[first release](https://pypi.python.org/pypi/cf_units),\n", "and our first external use: the IOOS\n", "[compliance-checker](https://github.com/ioos/compliance-checker/pull/114).\n", "\n", "(Warning! Before reading the post please watch the video below.)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from IPython.display import YouTubeVideo\n", "\n", "YouTubeVideo(\"N-edLdxiM40\")" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", " \n", " " ], "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ "" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "cf_units goal is to be a\n", "[CF-compliant](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/build/ch03.html#units)\n", "and\n", "[UDUNITS-compatible](http://www.unidata.ucar.edu/software/udunits/)\n", "units module.\n", "\n", "Until now the next best was\n", "[udunitspy](https://github.com/blazetopher/udunitspy).\n", "Sadly udunitspy is no longer being developed, nor it works on Windows.\n", "\n", "In this post I will make a comparison with udunitspy to make the case for\n", "replacing with cf_units and, hopefully, present a quick introduction on how to use cf_units." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Unit object" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import udunitspy\n", "\n", "upy = udunitspy.Unit('m/s')\n", "print('{}: {!r}'.format(upy, upy))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "m.s-1: \n" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "from cf_units import cf_units\n", "\n", "uir = cf_units.Unit('m/s')\n", "print('{}: {!r}'.format(uir, uir))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "m/s: Unit('m/s')\n" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Units known/unknown" ] }, { "cell_type": "code", "collapsed": false, "input": [ "right = 'm/s'\n", "wrong = 'coconuts'\n", "\n", "\n", "def units_known(key):\n", " try:\n", " udunitspy.Unit(str(key))\n", " except udunitspy.UdunitsError:\n", " return False\n", " return True\n", "\n", "units_known(right), units_known(wrong)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "(True, False)" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "def units_known(key):\n", " try:\n", " cf_units.Unit(key)\n", " except ValueError: # I prefer standard exceptions.\n", " return False\n", " return True\n", "\n", "units_known(right), units_known(wrong)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "(True, False)" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Can it quack like a duck?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "cf_units.Unit(cf_units.Unit('m/s')) # OK." ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 7, "text": [ "Unit('m/s')" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "udunitspy.Unit(udunitspy.Unit('m/s')) # Nope. Only strings!" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "in method 'parse', argument 2 of type 'char const *const'", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mudunitspy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mUnit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mudunitspy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mUnit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'm/s'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Nope. Only strings!\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/udunitspy/udunits2.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, spec, system, encoding)\u001b[0m\n\u001b[0;32m 158\u001b[0m \u001b[0msystem\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mSystem\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0msystem\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 159\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msystem\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msystem\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mDEFAULT_SYSTEM\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 160\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mthis\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mut\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mparse\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msystem\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mthis\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mspec\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mencoding\u001b[0m \u001b[1;32mor\u001b[0m \u001b[0mUT_ASCII\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 161\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 162\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mthis\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mTypeError\u001b[0m: in method 'parse', argument 2 of type 'char const *const'" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Units convertible?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def units_convertible(units1, units2, reftimeistime=True):\n", " \"\"\"Return True if a Unit representing the string units1 can be converted\n", " to a Unit representing the string units2, else False.\"\"\"\n", " try:\n", " udunitspy.Converter(str(units1), str(units2))\n", " except udunitspy.UdunitsError:\n", " return False\n", "\n", " u1 = udunitspy.Unit(str(units1))\n", " u2 = udunitspy.Unit(str(units2))\n", " return u1.are_convertible(u2)\n", "\n", "units_convertible('km/h', 'm/s'), units_convertible('km/h', 's')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 9, "text": [ "(True, False)" ] } ], "prompt_number": 9 }, { "cell_type": "code", "collapsed": false, "input": [ "def units_convertible(units1, units2):\n", " \"\"\"No need for a try/exception clause.\"\"\"\n", " u1 = cf_units.Unit(units1)\n", " return u1.is_convertible(units2)\n", "\n", "\n", "units_convertible('km/h', 'm/s'), units_convertible('km/h', 's')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 10, "text": [ "(True, False)" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Temporal units" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def units_temporal(units):\n", " r = False\n", " try:\n", " u = udunitspy.Unit('seconds since 1900-01-01')\n", " r = u.are_convertible(str(units))\n", " except udunitspy.UdunitsError:\n", " return False\n", " return r\n", "\n", "units_temporal('seconds since 1900-01-01')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 11, "text": [ "True" ] } ], "prompt_number": 11 }, { "cell_type": "code", "collapsed": false, "input": [ "def units_temporal(key):\n", " return cf_units.Unit(key).is_time_reference()\n", "\n", "units_temporal('seconds since 1900-01-01')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 12, "text": [ "True" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ... many more `is_` are available in cf_units" ] }, { "cell_type": "code", "collapsed": false, "input": [ "t = cf_units.Unit('seconds since 1900-01-01')\n", "\n", "(t.is_udunits(), t.is_dimensionless(), t.is_no_unit(),\n", " t.is_unknown(), t.is_vertical(), t.is_time())" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 13, "text": [ "(True, False, False, False, False, False)" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The exceptions" ] }, { "cell_type": "code", "collapsed": false, "input": [ "udunitspy.Unit(wrong) # Custom exception." ], "language": "python", "metadata": {}, "outputs": [ { "ename": "UdunitsError", "evalue": "__init__ resulted in udunits error UT_UNKNOWN: String unit representation contains unknown word", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mUdunitsError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mudunitspy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mUnit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwrong\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Custom exception.\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/udunitspy/udunits2.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, spec, system, encoding)\u001b[0m\n\u001b[0;32m 161\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 162\u001b[0m \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mthis\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 163\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mUdunitsError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mUnit\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mut\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_status\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 164\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 165\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mcopy\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mUdunitsError\u001b[0m: __init__ resulted in udunits error UT_UNKNOWN: String unit representation contains unknown word" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "cf_units.Unit(wrong) # Regular ValueError!" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "[UT_UNKNOWN] Failed to parse unit \"coconuts\" ", "output_type": "pyerr", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mcf_units\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mUnit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwrong\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# Regular ValueError!\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[1;32m/home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/cf_units/cf_units.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, unit, calendar)\u001b[0m\n\u001b[0;32m 928\u001b[0m \u001b[1;31m# _ut_parse returns 0 on failure\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 929\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mut_unit\u001b[0m \u001b[1;32mis\u001b[0m \u001b[0mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 930\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_raise_error\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Failed to parse unit \"%s\"'\u001b[0m \u001b[1;33m%\u001b[0m \u001b[0munit\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 931\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0m_OP_SINCE\u001b[0m \u001b[1;32min\u001b[0m \u001b[0munit\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 932\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mcalendar\u001b[0m \u001b[1;32mis\u001b[0m \u001b[0mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;32m/home/filipe/.virtualenvs/blog/lib/python2.7/site-packages/cf_units/cf_units.py\u001b[0m in \u001b[0;36m_raise_error\u001b[1;34m(self, msg)\u001b[0m\n\u001b[0;32m 963\u001b[0m \u001b[0mctypes\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mset_errno\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 964\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 965\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'[%s] %s %s'\u001b[0m \u001b[1;33m%\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mstatus_msg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0merror_msg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 966\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 967\u001b[0m \u001b[1;31m# NOTE:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mValueError\u001b[0m: [UT_UNKNOWN] Failed to parse unit \"coconuts\" " ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The `cf_units.Units` object is richer in methods and properties" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = cf_units.Unit('km/h')\n", "\n", "a.title(42), a.symbol, a.definition, a.origin" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 16, "text": [ "('42 km/h', '0.277777777777778 m.s-1', '0.277777777777778 m.s-1', 'km/h')" ] } ], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "formats = (cf_units.UT_ASCII, cf_units.UT_ISO_8859_1, cf_units.UT_LATIN1,\n", " cf_units.UT_UTF8, cf_units.UT_NAMES, cf_units.UT_DEFINITION)\n", "\n", "for fmt in formats:\n", " print(a.format(option=fmt))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0.277777777777778 m.s-1\n", "0.277777777777778 m/s\n", "0.277777777777778 m/s\n", "0.277777777777778 m\u00b7s\u207b\u00b9\n", "0.277777777777778 meter-second^-1\n", "0.277777777777778 m.s-1\n" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Some are units specific properties" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = cf_units.Unit('degree')\n", "\n", "a.modulus" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 18, "text": [ "360.0" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How about creating time-ranges?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "u = cf_units.Unit('hours since 1970-01-01 00:00:00',\n", " calendar=cf_units.CALENDAR_STANDARD)\n", "ut = u.utime()\n", "ut.num2date(range(10))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "array([datetime.datetime(1970, 1, 1, 0, 0),\n", " datetime.datetime(1970, 1, 1, 1, 0),\n", " datetime.datetime(1970, 1, 1, 2, 0, 0, 13),\n", " datetime.datetime(1970, 1, 1, 3, 0),\n", " datetime.datetime(1970, 1, 1, 4, 0),\n", " datetime.datetime(1970, 1, 1, 5, 0, 0, 13),\n", " datetime.datetime(1970, 1, 1, 6, 0),\n", " datetime.datetime(1970, 1, 1, 7, 0),\n", " datetime.datetime(1970, 1, 1, 8, 0, 0, 13),\n", " datetime.datetime(1970, 1, 1, 9, 0)], dtype=object)" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## And time conversions?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np\n", "\n", "var = [1, 2, 3, 4]\n", "\n", "udunitspy.Unit('hours since 1970-01-01 00:00:00').get_converter(\"seconds since 1970-01-01\").evaluate(min(var))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 20, "text": [ "3600.0" ] } ], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": [ "calendar = cf_units.CALENDAR_GREGORIAN\n", "\n", "origin = cf_units.Unit('hours since 1970-01-01 00:00:00', calendar=calendar)\n", "target = cf_units.Unit('seconds since 1970-01-01', calendar=calendar)\n", "\n", "origin.convert(min(var), target)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 21, "text": [ "3600.0" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "I hope to write `cf_units` docs soon! Stay tuned." ] }, { "cell_type": "code", "collapsed": false, "input": [ "HTML(html)" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "\n", "

This post was written as an IPython notebook.\n", " It is available for download\n", " or as a static html.

\n", "

\n", "
python4oceanographers by Filipe Fernandes is\n", "licensed under a Creative Commons\n", "Attribution-ShareAlike 4.0 International License.
Based on a work at https://ocefpaf.github.io/.\n" ], "metadata": {}, "output_type": "pyout", "prompt_number": 22, "text": [ "" ] } ], "prompt_number": 22 } ], "metadata": {} } ] }