#!/usr/bin/env python
# coding: utf-8
# # Introduction to programming for Geoscientists through Python
# # Lecture 5 solutions
# ## [Gerard J. Gorman (g.gorman@imperial.ac.uk)](http://www.imperial.ac.uk/people/g.gorman), [Nicolas Barral (n.barral@imperial.ac.uk)](http://www.imperial.ac.uk/people/n.barral)
# * **Fill lists with function values**
# A function with many applications in science is defined as:
# $h(x) = \frac{1}{\sqrt{2\pi}}\exp(-0.5x^2)$
# Fill lists *xlist* and *hlist* with *x* and *h(x)* values for uniformly spaced *x* coordinates in [−4, 4]. You may adapt the first example in the lecture 4 notes.
# In[1]:
from math import *
# Function h(x)
def h(x):
return (1.0/sqrt(2*pi)) * exp(-0.5 * x**2)
# Generate n points in [-4,4]
n = 9 # number of uniformly distributed points in xlist
dx = 8.0/(n-1) # x spacing
xlist = [-4.0+i*dx for i in range(n)] # Python lists
ylist = [h(x) for x in xlist]
print('xlist =', xlist)
print('ylist =', ylist)
# * **Fill arrays; loop version**
# The aim is to fill two arrays *x* and *y* with *x* and *h(x)* values, respectively, where *h(x)* is defined above. Let the *x* values be uniformly spaced in [−4, 4]. Use list comprehensions to create the *x* and *y* arrays.
# In[2]:
from math import *
from numpy import *
# Function h(x)
def h(x):
return (1.0/sqrt(2*pi)) * exp(-0.5 * x**2)
# Generate n points in [-4,4]
n = 9 # number of uniformly distributed points in x
dx = 8.0/(n-1) # x spacing
x = array([-4.0+i*dx for i in range(n)]) # list created using list comprehension and then converted to an array using the array function
y = array([h(xi) for xi in x]) #
print('x =', x) # x is an array
print('y =', y) # y is an array = h(x)
# * **Fill arrays; vectorized version**
# Vectorize the code in the previous exercise by creating the *x* values using the *linspace* function and by evaluating *h(x)* for an array argument.
# In[3]:
from math import *
from numpy import *
# Function h(x)
def h(x):
return (1.0/sqrt(2*pi)) * exp(-0.5 * x**2)
# Generate n points in [-4,4]
n = 9
x = linspace(-4.0, 4.0, n)
y = h(x)
print('x =', x) # x is an array
print('y =', y) # y is an array = h(x)
# * **Apply a function to a vector**
# Given a vector $v = (2, 3, −1)$ and a function $f(x) = x^3 + xe^x + 1$, apply $f$ to each element in $v$. Then calculate $f(v)$ as $v^3 + ve^v + 1$ using vector computing rules. Show that the two results are equal.
# In[4]:
from math import *
from numpy import *
# Function f(x)
def f(x):
return x**3 + x*exp(x) + 1.0
v = array([2.,3.,-1.]) # vector v
# applying function f(x) on each element of v
y1 = f(v[0])
y2 = f(v[1])
y3 = f(v[2])
# applying f(x) on v
y = f(v)
print('v =', v)
print('f(v) element-wise = ', y1, y2, y3)
print('f(v) = ', y)
# * **Simulate by hand a vectorized expression**
# Suppose *x* and *t* are two arrays of the same length, entering a vectorized expression:
#
# ```python
# y = cos(sin(x)) + exp(1/t)
# ```
#
# If *x* holds two elements, 0 and 2, and *t* holds the elements 1 and 1.5, calculate by hand (using a calculator) the *y* array. Thereafter, write a program that mimics the series of computations you did by hand (use explicit loops, but at the end you can use NumPy functionality to check the results).
# In[5]:
from math import *
from numpy import *
# Function f(x,t)
def f(x,t):
return cos(sin(x)) + exp(1.0/t)
# Defining x and t arrays
x=array([0.,2.])
t=array([1.,1.5])
# calculating y1 explicitly
y1=zeros(len(x))
for i in range(len(x)):
y1[i] = f(x[i],t[i])
# calculating y directly using vectorization feature of numpy
y2=f(x,t) # y2 is an array
print('y1 = ', y1)
print('y2 = ', y2)
# * **Demonstrate array slicing**
# Create an array *w* with values 0, 0.1, 0.2, ..., 3. Write out *w[:]*, *w[:-2]*, *w[::5]*, *w[2:-2:6]*. Convince yourself in each case that you understand which elements of the array are printed.
# In[6]:
from numpy import *
w=arange(0,3.1,0.1) # creates the array starting at 0, ending at (but not containing) 3.1, with a step size of 0.1
print('w[:] =', w[:])
print('w[:-2] =', w[:-2])
print('w[::5] =', w[::5])
print('w[2:-2:6] =', w[2:-2:6])
# * **Plot a formula**
# Make a plot of the function $y(t) = v_0t − 0.5gt^2$ for $v_0 = 10$, $g = 9.81$, and $t \in [0, 2v_0/g]$. The label on the *x* axis should be 'time (s)' and the label on the *y* axis should be 'height (m)'.
# In[7]:
get_ipython().run_line_magic('pylab', 'inline')
v0=10.
g=9.81
n=50 # number of points to be plotted on the graph
t=linspace(0,2*v0/g,n) # generate n points between 0 and 2*v0/g
y=v0*t - 0.5*g*t**2
plot(t,y)
xlabel('time (s)')
ylabel('height (m)')
show()
# * **Specify the x and y axes**
# Extend the program from the previous exercise such that the minimum and maximum *x* and *y* values are computed, and use the extreme values to specify the extent of the *x* and *y* axes. Add some space above the heighest curve.
# In[8]:
get_ipython().run_line_magic('pylab', 'inline')
v0=10.
g=9.81
n=50 # number of points to be plotted on the graph
t=linspace(0,2*v0/g,n) # generate n points between 0 and 2*v0/g
y=v0*t - 0.5*g*t**2
#find minimum and maximum t and y values
tmin = min(t)
tmax = max(t)
ymin = min(y)
ymax = max(y)
# plot graph
plot(t,y)
xlabel('time (s)')
ylabel('height (m)')
axis([tmin, tmax, ymin, ymax+0.1*(ymax-ymin)]) # specify the extent of the axes [tmin, tmax, ymin, ymax]
show()
# * **Plot a formula for several parameters**
# Make a program that reads a set of $v_0$ values using iPython widgets and plots the corresponding curves $y(t) = v_0t − 0.5gt^2$ in the same figure (set $g = 9.81$). Let $t \in [0, 2v_0/g$] for each curve, which implies that you need a different vector of $t$ coordinates for each curve.
# In[9]:
get_ipython().run_line_magic('pylab', 'inline')
from ipywidgets import widgets
from IPython.display import display, clear_output
g=9.81
n=50 # number of points to be plotted for each curve
# Let the user enter v0 values
def plot_functions(sender):
try:
v0_strings = sender.value.split(",")
v0_list = [float(v0_string) for v0_string in v0_strings]
except:
print("ERROR: invalid input, expects a series of numbers separated by a ','")
return 1
n_curves = len(v0_list)
v0 = array(v0_list)
t=zeros((n_curves,n))
y=zeros((n_curves,n))
for xx in range(n_curves):
t[xx]=linspace(0,2*v0[xx]/g,n) # generate n points between 0 and 2*v0/g
y[xx]=v0[xx]*t[xx] - 0.5*g*t[xx]**2
# plot graph
for i in range(n_curves):
ll='v0='+str(v0[i]) # text for curve label in legend
plt=plot(t[i],y[i],label=ll)
legend()
xlabel('time (s)')
ylabel('height (m)')
show()
widget_plot = widgets.Text()
widget_plot.on_submit(plot_functions)
display(widget_plot)
# * **Plot another formula**
# The function
# $f(x, t) = \exp(-(x - 3t)^2)\sin(3\pi(x - t))$
#
# describes, for a fixed value of *t*, a wave localized in space. Make a program that visualizes this function as a function of *x* on the interval [−4, 4] when *t* = 0.
# In[10]:
from pylab import *
def f(x,t):
return exp(-(x-3*t)**2)*sin(3*pi*(x-t))
n=500 # number of points to be plotted on the graph
x=linspace(-4.,4.,n)
y=f(x,0.0)
# plot graph
plot(x,y)
xlabel('x')
ylabel('f')
show()
# * **Implement matrix-vector multiplication**
# A matrix $\mathbf{A}$ and a vector $\mathbf{b}$, represented in Python as a 2D array and a 1D array respectively, are given by:
#
# $$
# \mathbf{A} = \left\lbrack\begin{array}{ccc}
# 0 & 12 & -1\cr
# -1 & -1 & -1\cr
# 11 & 5 & 5
# \end{array}\right\rbrack
# $$
#
# $$
# \mathbf{b} = \left\lbrack\begin{array}{c}
# -2\cr
# 1\cr
# 7
# \end{array}\right\rbrack
# $$
#
# Multiplying a matrix by a vector results in another vector $\mathbf{c}$, whose components are defined by the general rule
#
# $$\mathbf{c}_i = \sum_j\mathbf{A}_{i,j}\mathbf{b}_j$$
#
# Define $\mathbf{A}$ and $\mathbf{b}$ as NumPy arrays, and multiply them together using the above rule.
# In[11]:
from numpy import *
A = array([[0, 12, -1], [-1, -1, -1], [11, 5, 5]])
b = array([-2, 1, 7])
if A.shape[0] != A.shape[1] or A.shape[0] != b.shape[0]:
raise RunTimeError("A should be square and b a vector of the same size as A")
c = zeros(b.shape[0])
for i in range(0, b.shape[0]):
s = 0 # s holds the sum of A[i][j]*b[j] over the index j
for j in range(0, b.shape[0]):
s += A[i][j]*b[j]
c[i] = s
print("The vector c = ", c)
# * **Plot meteorological data**
# The file data/precipitation.csv contains monthly precipitation data for South-East England between 1916 and 2016 provided by the [Met Office](https://www.metoffice.gov.uk/hadobs/hadukp/data/download.html). Each row represents a year, and each column a month.
# To open the data file, use the command below:
#
# ```python
# import numpy as np
# data = np.loadtxt(fname='data/precipitation.csv', delimiter=',')
# ```
#
# - Open the data file, and print its content. Observe that it is a multi-dimensional Numpy array. Print its shape. How many years are represented ?
# In[12]:
get_ipython().run_line_magic('pylab', 'inline')
data = loadtxt(fname='data/precipitation.csv', delimiter=',')
print(data)
print("shape: ", data.shape)
# - Write a function that converts a length in mm into a length in inches. Use this function to convert the precipitation data into inches in a vectorized way.
# In[13]:
def mm_to_inches(x):
return x/25.4
data = mm_to_inches(data)
#
# - With a `for` loop, print (in a formatted way) for each month, the year where the monthly precipitation was the highest. A line like
# `The maximal precipitation for January is 181.9 mm/month and was in 2014.`
# is expected.
# Hint: you should use the [max]() and [argmax]() functions provided by Numpy.
# In[14]:
months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
for month in range(data.shape[1]-1):
data_month = data[:,month]
max_precip = amax(data_month)
year = argmax(data_month)
print("The maximal precipitation for %s is %.1f inches/month and was in %d" % (months[month], max_precip, year+1916))
# - Plot the [average](), [maximal]() and [minimal]() monthly precipitation with respect to the year. Don't forget to decorate the plot properly (add a plot title, legends and axis labels).
#
# In[15]:
avg_data = mean(data, axis=1)
max_data = amax(data, axis=1)
min_data = amin(data, axis=1)
number_years = data.shape[0]
years = linspace(1916, 2016, number_years)
plot(years, avg_data, years, max_data, years, min_data)
legend(("average", "max", "min"), loc='best')
title("Monthly precipitation between 1916 and 2016")
xlabel("year")
ylabel("precipitation (inches)")
show()
# - Plot the same quantities, but only for the last twenty years.
#
# In[16]:
plot(years[-20:], avg_data[-20:], years[-20:], max_data[-20:], years[-20:], min_data[-20:])
legend(("average", "max", "min"), loc='best')
title("Monthly precipitation for the last 20 years")
xlabel("year")
ylabel("precipitation (inches)")
show()
# - Plot the same quantities, but only for years ending in 6.
# In[17]:
plot(years[::10], avg_data[::10], years[::10], max_data[::10], years[::10], min_data[::10])
legend(("average", "max", "min"), loc='best')
title("Monthly precipitation every 10 years for the last 100 years")
xlabel("year")
ylabel("precipitation (inches)")
show()
# In[ ]: