{ "cells": [ { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "# Functions\n", "Assume we have some wind speed and direction observations we wish to incorporate into a weather model. The assimilation process requires wind observations to be supplied as the u and v Cartesian components of the wind vector. The data here are supplied as a list of [tuples](http://nbviewer.jupyter.org/github/Unidata/online-python-training/blob/master/notebooks/Basic%252520Data%252520Structures.ipynb#Tuples) representing the time of the observation, the speed in meters per second, and the degree direction where the winds are coming **from**.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "autoscroll": "json-false", "collapsed": false, "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "data = [('2016-12-28T01:22:00Z', 26.24, 290.0),\n", " ('2016-12-28T04:22:00Z', 16.46, 280.0),\n", " ('2016-12-28T05:02:00Z', 18.01, 290.0),\n", " ('2016-12-28T05:22:00Z', 16.46, 290.0),\n", " ('2016-12-28T06:02:00Z', 18.01, 290.0),\n", " ('2016-12-28T06:22:00Z', 16.98, 280.0),\n", " ('2016-12-28T07:02:00Z', 17.49, 280.0),\n", " ('2016-12-28T07:22:00Z', 17.49, 280.0),\n", " ('2016-12-28T08:02:00Z', 17.49, 280.0),\n", " ('2016-12-28T08:22:00Z', 13.38, 290.0),\n", " ('2016-12-28T08:41:00Z', 15.95, 290.0),\n", " ('2016-12-28T09:02:00Z', 14.92, 270.0),\n", " ('2016-12-28T09:22:00Z', 19.03, 280.0),\n", " ('2016-12-28T11:43:00Z', 16.98, 290.0)]" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "With some basic trigonometry and a `for` loop [we learned about in the last section](http://nbviewer.jupyter.org/github/Unidata/online-python-training/blob/master/notebooks/loops.ipynb), we can convert the data into u and v components." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "autoscroll": "json-false", "collapsed": false, "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "[('2016-12-28T01:22:00Z', 24.657534369422233, -8.974608560865555),\n", " ('2016-12-28T04:22:00Z', 16.209935614580946, -2.8582490043976674),\n", " ('2016-12-28T05:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T05:22:00Z', 15.467340538136051, -5.6296515591405125),\n", " ('2016-12-28T06:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T06:22:00Z', 16.722035646147294, -2.948546056784471),\n", " ('2016-12-28T07:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T07:22:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:22:00Z', 12.573087266115454, -4.576229517697452),\n", " ('2016-12-28T08:41:00Z', 14.988097301535237, -5.455221286044421),\n", " ('2016-12-28T09:02:00Z', 14.92, 2.7407595364917763e-15),\n", " ('2016-12-28T09:22:00Z', 18.74089153982232, -3.3045248210016775),\n", " ('2016-12-28T11:43:00Z', 15.955980700944723, -5.80750203366986)]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Import the Python math module\n", "import math\n", "\n", "# Constant for converting degrees to radians\n", "RPERD = math.pi/180\n", "\n", "# The list that will hold our u and v data\n", "uvdata = []\n", "\n", "# Loop through observations pulling apart the time, speed and direction\n", "# components of the observation.\n", "\n", "for time, speed, direction in data:\n", " u = -speed * math.sin(direction * RPERD)\n", " v = -speed * math.cos(direction * RPERD)\n", " uvdata.append((time, u, v))\n", "\n", "uvdata" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "While this code works fine it can be **refactored**, a software engineering term to describe improving the design of existing code. In particular, we can pull the section that calculates the `u` and `v` vectors into its own reusable unit of code known as a **function**. Functions in Python are defined with the `def` keyword as illustrated with this pseudocode:\n", "\n", "```\n", "def function_name( function_parameters ):\n", " \"function documentation string\"\n", " function statements usually involving the function parameters\n", " return some value or expression based on the function statements\n", "```" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "Armed with this knowledge, we can write a function called `uandv` to calculate the u and v components from wind speed and direction parameters:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "autoscroll": "json-false", "collapsed": false, "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def uandv(speed, direction):\n", " \"calculate u and v components from speed and direction\"\n", " u = -speed * math.sin(direction * RPERD)\n", " v = -speed * math.cos(direction * RPERD)\n", " # return the u and v tuple\n", " return u, v" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "The `u` and `v` vectors are returned as [tuples](http://nbviewer.jupyter.org/github/Unidata/online-python-training/blob/master/notebooks/Basic%252520Data%252520Structures.ipynb#Tuples).\n", "With this function in place, we can jump out of the `for` loop shown earlier, shift the *flow of control* to the `uandv` function, calculate the `u` and `v` wind components, and return control to where the function was initially invoked. We can now write the same `for` loop again, but this time with conciseness and clarity." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "autoscroll": "json-false", "collapsed": false, "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "[('2016-12-28T01:22:00Z', 24.657534369422233, -8.974608560865555),\n", " ('2016-12-28T04:22:00Z', 16.209935614580946, -2.8582490043976674),\n", " ('2016-12-28T05:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T05:22:00Z', 15.467340538136051, -5.6296515591405125),\n", " ('2016-12-28T06:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T06:22:00Z', 16.722035646147294, -2.948546056784471),\n", " ('2016-12-28T07:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T07:22:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:22:00Z', 12.573087266115454, -4.576229517697452),\n", " ('2016-12-28T08:41:00Z', 14.988097301535237, -5.455221286044421),\n", " ('2016-12-28T09:02:00Z', 14.92, 2.7407595364917763e-15),\n", " ('2016-12-28T09:22:00Z', 18.74089153982232, -3.3045248210016775),\n", " ('2016-12-28T11:43:00Z', 15.955980700944723, -5.80750203366986)]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "uvdata = []\n", "\n", "for time, speed, direction in data:\n", " u, v = uandv(speed, direction)\n", " uvdata.append((time, u, v))\n", "uvdata" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "Moreover, we can reuse our `uandv` function whenever we need to calculate u and v components from speed and direction.\n", "## Python, a Functional Language\n", "\n", "A chief attribute and advantage of Python is that it is a **functional language**. In functional languages, functions are treated as \"first class citizens\". Functions can be passed as arguments or they can be defined on-the-fly as with [lambda expressions](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions). A Pythonista would have a feeling of disquietude if we stopped at the code above. Indeed, we can further take advantage of the functional programming paradigm by employing **list comprehension**. A more Pythonic way of achieving the same result using list comprehension is done in this manner:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "autoscroll": "json-false", "collapsed": false, "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ "[('2016-12-28T01:22:00Z', 24.657534369422233, -8.974608560865555),\n", " ('2016-12-28T04:22:00Z', 16.209935614580946, -2.8582490043976674),\n", " ('2016-12-28T05:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T05:22:00Z', 15.467340538136051, -5.6296515591405125),\n", " ('2016-12-28T06:02:00Z', 16.92386410035421, -6.1597827812953),\n", " ('2016-12-28T06:22:00Z', 16.722035646147294, -2.948546056784471),\n", " ('2016-12-28T07:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T07:22:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:02:00Z', 17.22428760018352, -3.037106627394605),\n", " ('2016-12-28T08:22:00Z', 12.573087266115454, -4.576229517697452),\n", " ('2016-12-28T08:41:00Z', 14.988097301535237, -5.455221286044421),\n", " ('2016-12-28T09:02:00Z', 14.92, 2.7407595364917763e-15),\n", " ('2016-12-28T09:22:00Z', 18.74089153982232, -3.3045248210016775),\n", " ('2016-12-28T11:43:00Z', 15.955980700944723, -5.80750203366986)]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[(time,) + uandv(speed, direction) for time, speed, direction in data]" ] }, { "cell_type": "markdown", "metadata": { "ein.tags": [ "worksheet-0" ], "slideshow": { "slide_type": "-" } }, "source": [ "List comprehensions are enclosed by square brackets starting by a statement and followed with a `for` loop. Once again, here we are pulling apart the individual tuple *in place* into the `time`, `speed`, and `direction` components. This syntax allows us to easily pass the necessary arguments to the `uandv` function. Also, to stay consistent with the previous results, we are prepending the time to the u, v tuple with the `+` operator. In short, we can do our conversion in one clear, concise line of code. Note, we did not need an intermediate `uvdata` variable. This style of programming is the essence of functional programming where we eschew \"mutable\" variables such as `uvdata`. These \"side-effects\" can be difficult and error prone to reason about in more complex programs. Functional programming tends to be a natural fit for those with a scientific and mathematical background." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 2 }