#!/usr/bin/env python # coding: utf-8 # # Functions # 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**. # # In[1]: data = [('2016-12-28T01:22:00Z', 26.24, 290.0), ('2016-12-28T04:22:00Z', 16.46, 280.0), ('2016-12-28T05:02:00Z', 18.01, 290.0), ('2016-12-28T05:22:00Z', 16.46, 290.0), ('2016-12-28T06:02:00Z', 18.01, 290.0), ('2016-12-28T06:22:00Z', 16.98, 280.0), ('2016-12-28T07:02:00Z', 17.49, 280.0), ('2016-12-28T07:22:00Z', 17.49, 280.0), ('2016-12-28T08:02:00Z', 17.49, 280.0), ('2016-12-28T08:22:00Z', 13.38, 290.0), ('2016-12-28T08:41:00Z', 15.95, 290.0), ('2016-12-28T09:02:00Z', 14.92, 270.0), ('2016-12-28T09:22:00Z', 19.03, 280.0), ('2016-12-28T11:43:00Z', 16.98, 290.0)] # 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. # In[2]: # Import the Python math module import math # Constant for converting degrees to radians RPERD = math.pi/180 # The list that will hold our u and v data uvdata = [] # Loop through observations pulling apart the time, speed and direction # components of the observation. for time, speed, direction in data: u = -speed * math.sin(direction * RPERD) v = -speed * math.cos(direction * RPERD) uvdata.append((time, u, v)) uvdata # 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: # # ``` # def function_name( function_parameters ): # "function documentation string" # function statements usually involving the function parameters # return some value or expression based on the function statements # ``` # Armed with this knowledge, we can write a function called `uandv` to calculate the u and v components from wind speed and direction parameters: # In[3]: def uandv(speed, direction): "calculate u and v components from speed and direction" u = -speed * math.sin(direction * RPERD) v = -speed * math.cos(direction * RPERD) # return the u and v tuple return u, v # 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). # 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. # In[4]: uvdata = [] for time, speed, direction in data: u, v = uandv(speed, direction) uvdata.append((time, u, v)) uvdata # Moreover, we can reuse our `uandv` function whenever we need to calculate u and v components from speed and direction. # ## Python, a Functional Language # # 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: # In[5]: [(time,) + uandv(speed, direction) for time, speed, direction in data] # 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.