<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <center>
<br><br>
<font size=6>
First Steps with<br><br>
Numerical Computing in Python
</font>
</b>
<br><br>
<font size=3>
Paul M. Magwene
<br>
Spring 2016
</font>
</center>
How to use IPython notebooks
This document was written in an IPython notebook. IPython notebooks allow us to
weave together explantory text, code, and figures.
Don't copy and paste!
Learning to program has similarities to learning a foreign language. You need to
practice writing your own code, not just copying and pasting it from another
document (that's why I'm providing this as a PDF rather than a notebook itself,
to make the copy-and-paste process less convenient).
Part of the practice of learning to program is making mistakes (bugs). Learning
to find and correct bugs in your own code is vital.
Code cells
Each of the grey boxes below that has In [n]
Step1:  Gee-whiz!
Let's kick off our tour of Python with some nice visualizations. In this first
section I'm not going to explain any of the code in detail, I'm simply going to
generate some figures to show of some of what Python is capable of. However,
once you work your way through this notebook you should be able to come back to
this first section and understand most of the code written here.
Step2:  Numeric data types
One of the simplest ways to use the Python interpretter is as a fancy
calculator. We'll illustrate this below and use this as an opportunity to
introduce the core numeric data types that Python supports.
Step3:  Querying objects for their type
There is a built-in Python function called type that we can use to query a
variable for it's data type.
Step4:  Booleans
Python has a data type to represent True and False values (Boolean variables)
and supports standard Boolean operators like "and", "or", and "not"
Step5:  Comparison operators
Python supports comparison operators on numeric data types. When you carry out a
comparison you get back a Boolean (True,False) value.
Step6:  Variable assignment
A value or the result of a calculation can be given a name, and then reused in a
different context by referring to that name. This is called variable assignment.
Step7:  Functions
A "function" is a named sequence of statements that performs a computation.
Functions allow us to encapsulate or abstract away the steps required to perform
a useful operation or calculation.
There are a number of Python funtions that are always available to you
Step9:  There are many other built-in functions, and we'll see more examples of these
below.  See the Python documentation on "Built-in
Functions" for more
details.
Defining functions
You can write your own functions. The general form of a function definition in Python is
Step10:  Importing Functions
Python has a mechanism to allow you to build libraries of code, which can then
be "imported" as needed. Python libraries are usually referred to as "modules".
Here’s how we would make functions and various definitions from the  math
module available for use.
Step11:  If you get tired of writing the module name, you can import all the functions
from a module by writing from math import *.  You have to be careful with this
though, as any functions or constants imported this way wil overwrite any
variables/names in your current environment that already exits.
At the beginning of this notebook I imported a library for numerical computing
called NumPy as well as a library for plotting called
Matplotlib.
Step12:  Numpy includes most of the functions defined in the math module so we didn't
really need to add the math. prefix.
Step13:  Lists
Lists are the simplest "data structure".  Data structures are computational
objects for storing, accessing, and operating on data.
List represent ordered collections of arbitrary objects. We'll begin by working
with lists of numbers.
Step14:  Indexing lists
Accessing the elements of a list is called "indexing".  In Python lists are
"zero-indexed" which means when you can access lists elements, the first element
has the index 0, the second element has the index 1, ..., and the last
element has the index len(x)-1.
Step15:  You can use negative indexing to get elements from the end of a list.
Step16:  Indexing can be used to get, set, and delete items in a list.
Step17:  You can append and delete list elements as well as concatenate two lists
Step18:  Slicing lists
Python lists support the notion of ‘slices’ - a continuous sublist of a larger
list. The following code illustrates this concept.
Step19:  List slices support a "step" specified by a third colon
Step20:  As with single indexing, the slice notation can be used to set elements of a
list.
Step21:  Finally, there are a number of useful methods associated with list objects, such
as reverse() and sort().
Step22:  NumPy arrays
NumPy is an extension package for Python that provides many facilities for numerical computing.  There is also a related package called SciPy that provides even more facilities for scientific computing. Both NumPy and SciPy can be downloaded from http
Step23:  Notice how all the arithmetic operations operate elementwise on arrays. You can also perform arithmetic operations between arrays, which also operate element wise
Step24:  The last example above shows that the lengths of the two arrays have to be the same in order to do element-wise operations.
By default,most operations on arrays work element-wise. However there are a variety of functions for doing array-wise operations such as matrix multiplication or matrix inversion. Here are a few examples of using NumPy arrays to represent matrices
Step25:  Indexing and Slicing NumPy arrays
Like the built-in lists, NumPy arrays are zero-indexed.
Step26:  Again, you can use negative indexing to get elements from the end of the vector and slicing to get subsets of the array.
Step27:  Comparison operators on arrays
NumPy arrays support the comparison operators, returning arrays of Booleans.
Step28:  Combining indexing and comparison on arrays
NumPy arrays allows us to combine the comparison operators with indexing.  This facilitates data filtering and subsetting.
Step29:  In the first example we retrieved all the elements of x that are larger than 5 (read "x where x is greater than 5"). In the second example we retrieved those elements of x that did not equal six.   The third example is slightly more complicated. We combined the logical_or function with comparison and indexing.  This allowed us to return those elements of the array x that are either less than four or greater than six. Combining indexing and comparison is a powerful concept. See the numpy documentation on logical functions for more information.
Generating Regular Sequences
Creating sequences of numbers that are separated by a specified value or that follow a particular patterns turns out to be a common task in programming. Python and NumPy have functions to simplify this task.
Step30:  You can also do some fancy tricks on lists to generate repeating patterns.
Step31:  Mathematical functions applied to arrays
Most of the standard mathematical functions can be applied to numpy arrays however you must use the functions defined in the NumPy module.
Step32:  Plots with Matplotlib
Matplotlib is a Python library for making nice 2D and
3D plots. There are a number of other plotting libraries available for Python
but matplotlib has probably the most active developer community and is capable
of producing publication quality figures.
Matplotlib plots can be generated in a variety of ways but the easiest way to
get quick plots is to use the functions defined in the matplotlib.pyplot
module.
Step33:  Commonly used functions from matplotlib.pyplot include plot, scatter, imshow, savefig among others.  We explored a decent numbers of plotting functionality at the beginning of this notebook. Here are a few more examples.
Step34:  Strings
Strings aren't numerical data, but working with strings comes up often
enough in numerical computing that it's worth mentioning them here.
Strings represent textual information, data or input.  String are an interesting
data type because they share properties with data structures like lists (data
structures will be introduced in the next handout).
<ASSISTANT_TASK:>
Python Code:
help(min)
?min # this will pop-up a documentation window in the ipython notebook
Explanation: <center>
<br><br>
<font size=6>
First Steps with<br><br>
Numerical Computing in Python
</font>
</b>
<br><br>
<font size=3>
Paul M. Magwene
<br>
Spring 2016
</font>
</center>
How to use IPython notebooks
This document was written in an IPython notebook. IPython notebooks allow us to
weave together explantory text, code, and figures.
Don't copy and paste!
Learning to program has similarities to learning a foreign language. You need to
practice writing your own code, not just copying and pasting it from another
document (that's why I'm providing this as a PDF rather than a notebook itself,
to make the copy-and-paste process less convenient).
Part of the practice of learning to program is making mistakes (bugs). Learning
to find and correct bugs in your own code is vital.
Code cells
Each of the grey boxes below that has In [n]: to the left shows a so called
"code cell". The text in the code cells is what you should type into the code
cells of your own notebook. The regions starting with Out [n]: show you the
result of the code you type in the proceeding input cell(s).
Evaluating code cells
After you type Python code into a code cell, hit Shift-Enter (hold down the
Shift key while you press the Enter (or Return) key) to evaluate the code
cell. If you type valid code you'll usually get some sort of output (at least in
these first few examples). If you make a mistake and get an error message, click
the input code cell and try and correct your mistake(s).
Try your own code
Test your understanding of the examples I've provided by writing additional code
to illustrate the same principle or concept. Don't be afraid to make mistakes.
Help and Documentation
A key skill for becoming an efficient programmer is learning to efficiently
navigate documentation resources. The Python standard library is very well
documented, and can be quickly accessed from the IPython notebook help menu or
online at the http://python.org website. Similar links to some of the more
commonly used scientific and numeric libraries are also found in the Ipython
help menu.
In addition, there are several ways to access abbreviated versions of the
documentation from the interpetter itself.
End of explanation
%matplotlib inline
from numpy import *
from scipy import stats
from matplotlib.pyplot import *
# this is a comment
x = array([1,2,3,4,5,6,7,8,9,10])
plot(x,x**2, color='red', marker='o')
xlabel("Length")
ylabel("Area")
title("Length vs Area for Squares")
pass
x = linspace(0, 10, 100) # generate 100 evenly space points
                         # between 0 and 10
sinx = sin(x)
sinsqrx = sinx * sinx
plot(x, sinx, color='red', label='sin(x)')
plot(x, sinsqrx, color='blue', label='sin^2(x)')
legend(loc='best')  # add optional legend to plot
pass
# draw 1000 random samples from a normal distribution
# with mean = 1000, sd = 15
mean = 1000
sd = 15
samples = random.normal(mean, sd, size=1000)
# draw a histogram
# normed means to make the total area under the
# histogram sum to 1 (i.e. a density histogram)
hist(samples, bins=50, normed=True, color='steelblue')
# draw probability density function for a normal 
# distribution with the same parameters
x = linspace(940,1080,250)
y = stats.norm.pdf(x, loc=mean, scale=sd)
plot(x, y, color='firebrick', linestyle='dashed', linewidth=3)
# label axes
xlabel("x")
ylabel("density")
pass
# the function of 2 variables we want to plot
def f(x,y):
    return cos(radians(x)) * sin(radians(y))
# generate a grid of x,y points at 10 step
# intervals from 0 to 360
x,y = meshgrid(arange(0, 361, 10), arange(0, 361, 10))
# calculate a function over the grid
z = f(x,y)
# draw a contour plot representing the function f(x,y)
contourf(x, y, z, cmap='inferno')
title("A contour plot\nof z = cos(x)*sin(x)")
pass
# function from previous plot, now represented in 3D
from mpl_toolkits.mplot3d import Axes3D
fig = figure()
ax = Axes3D(fig)
ax.plot_surface(x, y, z, rstride=2, cstride=2, cmap='inferno')
# setup axis labels
ax.set_xlabel("x (degrees)")
ax.set_ylabel("y (degrees)")
ax.set_zlabel("z")
# set elevation and azimuth for viewing
ax.view_init(68, -11)
title("A 3D representation\nof z = cos(x)*sin(x)")
pass
Explanation: Gee-whiz!
Let's kick off our tour of Python with some nice visualizations. In this first
section I'm not going to explain any of the code in detail, I'm simply going to
generate some figures to show of some of what Python is capable of. However,
once you work your way through this notebook you should be able to come back to
this first section and understand most of the code written here.
End of explanation
# this is a comment, the interpretter ignores it
# you can use comments to add short notes or explanation
2 + 10 # add two integers (whole numbers)
2.0 + 10.0 # add two floating point numbers (real (decimal) numbers)
2 + 10.0 # operations that mix integers and floats return floats
2 * 10 # multiplication of integers
2.0 * 10.0 # multiplication of floats
1.0/5.0 # division
2/5 # in Python 2 this used to default to integer division
    # in Python 3 division always returns a float
10 % 3 # The % (modulo) operator yields the remainder after division
2**10  # exponentiation -- 2 raised to the power 10
2**0.5  # exponentiation with fractional powers
        # **0.5 = square root, **(1/3.) = cube root
(10+2)/(4-5)  # numerical operators differ in their precedence
              # contrast the output of this line with the line below
(10+2)/4-5   # it is a good habit to use parentheses to disambiguate 
             # potentially confusing calculations
(1 + 1j)  # complex numbers; we won't use these in the course
          # but you might occasionally find the need for them 
          # in biological research
(1 + 1j) + (3 + 2j) # adding complex numbers
(0 + 1j) * (1j)  # complex multiplication
Explanation: Numeric data types
One of the simplest ways to use the Python interpretter is as a fancy
calculator. We'll illustrate this below and use this as an opportunity to
introduce the core numeric data types that Python supports.
End of explanation
type(2)
type(2.0)
type(2 + 10.0)  # when adding variables of two numeric types, the outcome
                # is always the more general type
Explanation: Querying objects for their type
There is a built-in Python function called type that we can use to query a
variable for it's data type.
End of explanation
x = True
y = False
x
not x
y
not y # if True return False, if False return True
x and y # if both arguments are True return true, else return False
x and (not y)
x or y  # if either argument is True, return True, else return False
Explanation: Booleans
Python has a data type to represent True and False values (Boolean variables)
and supports standard Boolean operators like "and", "or", and "not"
End of explanation
4 < 5 # less than
4 > 5 # greater than
5 <= 5.0 # less than or equal to
5 == 5 # tests equality
5 == (5**0.5)**2  # the results of this comparison might surprise you
(5**0.5)**2 # the problem is that sqrt(5) can not be represented 
            # exactly with floating point numbers. This is not a 
            # limitation of only Python but is generally true
            # for all programming languages
# here's one way to test approximate equality when you suspect
# a floating point calculation might be imprecise
epsilon = 0.0000001
(5 - epsilon) <= ((5**0.5)**2) <= (5 + epsilon)
Explanation: Comparison operators
Python supports comparison operators on numeric data types. When you carry out a
comparison you get back a Boolean (True,False) value.
End of explanation
pi = 3.141592654
radius = 4.0
area_circ = pi * radius**2
# notice that you don't get any output from this code cell
# however, once you evaluate this code cell you will see
# the results of your calculation
area_circ 
Explanation: Variable assignment
A value or the result of a calculation can be given a name, and then reused in a
different context by referring to that name. This is called variable assignment.
End of explanation
min(1,2) # find the minimum of its input
max(10, 9, 11) # find maximum of inputs
abs(-99)  # return absolute value of numerical input
Explanation: Functions
A "function" is a named sequence of statements that performs a computation.
Functions allow us to encapsulate or abstract away the steps required to perform
a useful operation or calculation.
There are a number of Python funtions that are always available to you:
End of explanation
# a function that carries out a simple mathematical calculation
def area_of_circle(radius):
    radius of circle --> area of circle 
    return 3.141592654 * radius**2
area_of_circle(1)
area_of_circle(8)
Explanation: There are many other built-in functions, and we'll see more examples of these
below.  See the Python documentation on "Built-in
Functions" for more
details.
Defining functions
You can write your own functions. The general form of a function definition in Python is:
def func_name(arg1, arg2, ...):
    body of function
    return result
Note:
 * Python is white space sensitive, body of a function must be indented
(idiomatic style is to indent by 4 spaces NOT tabs)
 * Use a Python aware editor/environment to help get indenting correct. Jupyter
will help you get the indentation correct
End of explanation
import math
math.cos(2 * 3.141592654)  # cosine
math.pi  # a constant defined in math
pi = math.pi
math.cos(2 * pi)
Explanation: Importing Functions
Python has a mechanism to allow you to build libraries of code, which can then
be "imported" as needed. Python libraries are usually referred to as "modules".
Here’s how we would make functions and various definitions from the  math
module available for use.
End of explanation
from numpy import *
from matplotlib.pyplot import *
Explanation: If you get tired of writing the module name, you can import all the functions
from a module by writing from math import *.  You have to be careful with this
though, as any functions or constants imported this way wil overwrite any
variables/names in your current environment that already exits.
At the beginning of this notebook I imported a library for numerical computing
called NumPy as well as a library for plotting called
Matplotlib.
End of explanation
exp(1) # e^1
log(e) # natural logarithm of e
log10(100) # log base 10 of 100
Explanation: Numpy includes most of the functions defined in the math module so we didn't
really need to add the math. prefix.
End of explanation
x = [1,2,3,4,5] # a list with the numbers 1..5
x
# a list with floats and ints and complex numbers
y = [2.0, 4, 6, 8, 10.0, 11, (1+1j), 3.14159] 
y
# lists of a length. We can use the `len` function to get this
len(x)
len(y)
Explanation: Lists
Lists are the simplest "data structure".  Data structures are computational
objects for storing, accessing, and operating on data.
List represent ordered collections of arbitrary objects. We'll begin by working
with lists of numbers.
End of explanation
z = [2, 4, 6, 8, 10]
z[0] # first element
z[3]  # fourth element
len(z)
z[5]  ## this generates an error -- why?
z[4] # last element of z
Explanation: Indexing lists
Accessing the elements of a list is called "indexing".  In Python lists are
"zero-indexed" which means when you can access lists elements, the first element
has the index 0, the second element has the index 1, ..., and the last
element has the index len(x)-1.
End of explanation
z[-1] # last element
z[-2] # second to last element
Explanation: You can use negative indexing to get elements from the end of a list.
End of explanation
m = [1, 2, 4, 6, 8, "hike"]
m[-1] = "learning python is so great!"  # set the last element
m
del m[0]
m
Explanation: Indexing can be used to get, set, and delete items in a list.
End of explanation
x = [1,2,3]
y = ['a', 'b', 'c', 'd']
x.append(4)
x
x + y
Explanation: You can append and delete list elements as well as concatenate two lists
End of explanation
c = ['a','b','c','d','e','f']
c[0:3]  # get the elements of from index 0 up to 
        # but not including the element at index 3
c[:3]  # same as above, first index implied
c[2:5]  # from element 2 up to 5
c[3:]  # from index three to end (last index implied)
c[-1:0]  # how come this returned an empty list?
Explanation: Slicing lists
Python lists support the notion of ‘slices’ - a continuous sublist of a larger
list. The following code illustrates this concept.
End of explanation
c[0:5:2]  # c from 0 to 5, step by 2
# you can you a negative step to walk backward over a list
# note where the output stops (why didn't we get 'a'?)
c[-1:0:-1]
Explanation: List slices support a "step" specified by a third colon
End of explanation
c[2:4] = ['C', 'D']
c
Explanation: As with single indexing, the slice notation can be used to set elements of a
list.
End of explanation
d = [1, 5, 3, 4, 1, 11, 3]
d.sort() # sort in place
d
d.reverse() # reverse in place
d
Explanation: Finally, there are a number of useful methods associated with list objects, such
as reverse() and sort().
End of explanation
from numpy import *
x = array([2,4,6,8,10]) 
x
type(x)
-x
x**2
x * pi
Explanation: NumPy arrays
NumPy is an extension package for Python that provides many facilities for numerical computing.  There is also a related package called SciPy that provides even more facilities for scientific computing. Both NumPy and SciPy can be downloaded from http://www.scipy.org/. NumPy does not come with the standard Python distribution, but it does come as an included package if you use the Anaconda Python distribution.  The NumPy package comes with documentation and a tutorial.  You can access the documentation here: http://docs.scipy.org/doc/.
The basic data structure in NumPy is the array, which you've already seen in several examples above.  As opposed to lists, all the elements in a NumPy array must be of the same type (but this type can differ between different arrays).  Arrays are commonly used to represent matrices (2D-arrays) but can be used to represent arrays of arbitrary dimension ($n$-dimensional arrays). 
Arithmetic operations on NumPy arrays
End of explanation
 y = array([0, 1, 3, 5, 9])
x + y
x * y
z = array([1, 4, 7, 11])
x + z
Explanation: Notice how all the arithmetic operations operate elementwise on arrays. You can also perform arithmetic operations between arrays, which also operate element wise
End of explanation
m = np.array([[1,2],
              [3,4]])
m
m.transpose()
linalg.inv(m)
Explanation: The last example above shows that the lengths of the two arrays have to be the same in order to do element-wise operations.
By default,most operations on arrays work element-wise. However there are a variety of functions for doing array-wise operations such as matrix multiplication or matrix inversion. Here are a few examples of using NumPy arrays to represent matrices:
End of explanation
x
x[0]
x[1]
x[4]
x[5]
Explanation: Indexing and Slicing NumPy arrays
Like the built-in lists, NumPy arrays are zero-indexed.
End of explanation
x[-1]
x[-2]
x[2:]
x[::3] # every third element of x
Explanation: Again, you can use negative indexing to get elements from the end of the vector and slicing to get subsets of the array.
End of explanation
x
x < 5
x >= 6
Explanation: Comparison operators on arrays
NumPy arrays support the comparison operators, returning arrays of Booleans.
End of explanation
x = array([2, 4, 6, 10, 8, 7, 9, 2, 11])
x[x > 5]
x[x != 2]
x[logical_or(x <4, x > 8)]
Explanation: Combining indexing and comparison on arrays
NumPy arrays allows us to combine the comparison operators with indexing.  This facilitates data filtering and subsetting.
End of explanation
arange(10)
# generate numbers from 3 to 12 (non-inclusive) stepping by 4
arange(3, 12, 4)  
arange(1,10,0.5)
Explanation: In the first example we retrieved all the elements of x that are larger than 5 (read "x where x is greater than 5"). In the second example we retrieved those elements of x that did not equal six.   The third example is slightly more complicated. We combined the logical_or function with comparison and indexing.  This allowed us to return those elements of the array x that are either less than four or greater than six. Combining indexing and comparison is a powerful concept. See the numpy documentation on logical functions for more information.
Generating Regular Sequences
Creating sequences of numbers that are separated by a specified value or that follow a particular patterns turns out to be a common task in programming. Python and NumPy have functions to simplify this task.
End of explanation
[True,True,False]*3
Explanation: You can also do some fancy tricks on lists to generate repeating patterns.
End of explanation
x = array([2, 4, 6, 8])
cos(x)
sin(x)
log(x)
Explanation: Mathematical functions applied to arrays
Most of the standard mathematical functions can be applied to numpy arrays however you must use the functions defined in the NumPy module.
End of explanation
# this tells Jupyter to draw plots in the notebook itself
%matplotlib inline 
# import all the plotting functions from matplotlib.pyplot
from matplotlib.pyplot import *
Explanation: Plots with Matplotlib
Matplotlib is a Python library for making nice 2D and
3D plots. There are a number of other plotting libraries available for Python
but matplotlib has probably the most active developer community and is capable
of producing publication quality figures.
Matplotlib plots can be generated in a variety of ways but the easiest way to
get quick plots is to use the functions defined in the matplotlib.pyplot
module.
End of explanation
x = arange(1, 10, 0.25)
y = x + random.normal(size=len(x))
scatter(x,y,color='black')
pass
# see http://matplotlib.org/users/image_tutorial.html
    
import matplotlib.image as mpimg # required for loading images
img = mpimg.imread("http://matplotlib.org/_images/stinkbug.png")
imshow(img)
pass
# demonstrating subplots
fig, (ax1, ax2, ax3) = subplots(nrows=1, ncols=3)
fig.set_size_inches(15,5)
x = linspace(1, 100, 200)
y = log(x**2) - sqrt(x) + sin(x)
ax1.plot(x, y, color='blue')
ax1.set_xlabel("x")
ax1.set_ylabel("y")
z = sqrt(x) * sin(x) - exp(1/x**2)
ax2.plot(x, z, color='orange')
ax2.set_xlabel("x")
ax2.set_ylabel("z")
ax3.plot(x, y*z,color='purple')
ax3.set_xlabel("x")
ax3.set_ylabel("y * z")
pass
Explanation: Commonly used functions from matplotlib.pyplot include plot, scatter, imshow, savefig among others.  We explored a decent numbers of plotting functionality at the beginning of this notebook. Here are a few more examples.
End of explanation
# strings can be enclosed in double quotes
s1 = "Beware the Jabberwock, my son!"  
print(s1)
type(s1)  # what type are you, s1?
# OR in single quotes
s2 = 'The jaws that bite, the claws that catch!' 
print(s2)
# If the string you want to write has a quote character
# you need to wrap it in the other type of quote
# note the single quote at the beginning of 'Twas
s3 = "'Twas brillig, and the slithy toves"  
print(s3)
# Concatenating (adding) string
s4 = "abc"
s5 = "def"
print(s4 + s5)
Explanation: Strings
Strings aren't numerical data, but working with strings comes up often
enough in numerical computing that it's worth mentioning them here.
Strings represent textual information, data or input.  String are an interesting
data type because they share properties with data structures like lists (data
structures will be introduced in the next handout).
End of explanation
<END_TASK>