Content provided under a Creative Commons Attribution license, CC-BY 4.0; code under MIT License. (c)2014 Lorena A. Barba. Thanks: A. Ahmadia, G. Forsyth. NSF for support via CAREER award #1149784.
Version 0.3 -- February 2014

JITcode: Python

Hello! This is a quick intro to numerical programming in Python to help you hit the ground running with the JITcode set of modules.

JITcode is the short term we are using for "Just-in-Time" coding lessons. These modules are aimed at supporting the use of computing as a learning device in engineering. You learn to compute just when you need to, for supporting your engineering studies.

There are a couple of different ways to use these modules.

(1) You can download and install a Python distribution on your computer. One option is the free Anaconda Scientific Python distribution. Another is Canopy, which is free for academic use.

(2) You can run Python in the cloud using Wakari web-based data analysis, for which you need to create a free account. (No software installation required!)

Get a local copy of this notebook file, to experiment with it (later on, you may want to download the whole collection). The easiest way to do this is clicking on the download icon at the top-right, then go "Save As" from your browser window. (The notebook will look different on your computer; don't worry about that now.)

Python on your computer

If you decided to download Python to your computer (Anaconda makes it easy), then you will need to move around a little bit in the command line, which many people are not used to do. It's not hard. If you use Linux, you're probably already used to it. If you use Mac, find the Terminal under Applications/Utilities and launch it. On Windows, you probably have PowerShell installed or can use the Windows Console.

In your terminal or console, navigate to the folder where you have your JITcode lessons (the .ipynb files of this collection or your own files) using the command cd to change directory. Then, to launch the notebook server, just type:

ipython notebook

You will get a new browser window or tab with a list of the notebooks available in that folder. Click on one and start working! (You execute a cell by hitting Shift-Enter.)

Python on the cloud

This is the easiest way to work with these lessons. You will be computing in less than 3 minutes.

No need to download and install anything! No need to move around in the terminal (although later on you may want to do these anyway). Do this:

  1. Create a Wakari free account and log in. You'll get a welcome notebook that you can already start experimenting with.

  2. Now you have two options: upload your favorite notebook from the JITcode collection to work on the cloud (find the upload button, top-left) in Wakari; or, read the notebook online while typing commands into Wakari in another browser window.

Just the first 3 minutes of this video will give you a feel for computing on the cloud with Python:


In [1]:
from IPython.display import YouTubeVideo
YouTubeVideo('6mxCf8a_rMM')


Out[1]:

It really is that easy. Now, a few tips to get the hang of Python.

Python Crash Course

If you are completely new to Python, don't fret. We'll get you computing in no time! The first thing you need to know about are libraries: neat collections of functions that Pythonistas have made for you and me, to use for free.

Libraries

Python is a high-level open-source language. But the Python world is inhabited by many packages or libraries that provide useful things like array operations, plotting functions, and much more. We can import libraries of functions to expand the capabilities of Python in our programs.

OK! We'll start by importing a few libraries to help us out.

Our favorite library is NumPy. It provides a bunch of useful matrix operations (like MATLAB). We will use it a lot! Load it like this (hit shift-enter to execute the cell):


In [2]:
import numpy

Because there are so many libraries contributed by many people, when you call a function, you want to make sure that Python finds it in the right library. To do that, you prepend the library name to the function name and use a dot.

For example, the function linspace() creates an array with equally spaced numbers between a start and end. It is very useful!

Try it:


In [4]:
myarray = numpy.linspace(0, 5, 10)

What happened?! You expected to see an array with 10 numbers between 0 and 5, right? So where is it? Python has it. Let's ask to see it:


In [5]:
print myarray


[ 0.          0.55555556  1.11111111  1.66666667  2.22222222  2.77777778
  3.33333333  3.88888889  4.44444444  5.        ]

Now try it with different parameters:


In [6]:
myarray = numpy.linspace(0, 2, 11)
print myarray


[ 0.   0.2  0.4  0.6  0.8  1.   1.2  1.4  1.6  1.8  2. ]

Our next favorite library is Matplotlib. It creates 2D plots of all sorts: line plots, histograms, power spectra, bar charts, errorcharts, scatterplots, you name it!

We'll call it like this:


In [7]:
import matplotlib.pyplot as plt    # import plotting library, and call it 'plt'

What's this import...as business?

We are importing one library component from Matplotib named pyplot and calling it plt. We're just lazy and want to type less when we call our plotting functions.

You will learn to make plots with Matplotlib in JITcode 1, Lesson 1.

Pro tip:

Sometimes, you'll see people importing a whole library this way: from numpy import *, which then avoids the need to prepend the library name to function names with the dot. This saves typing but is sloppy and can get you in trouble (because some function names actually repeat in various libraries). Best to get into good habits from the beginning!

Variables

Python doesn't require explicitly declared variable types, like C and other languages do. Just assign a variable and Python understands what you want:


In [9]:
a = 5      # a is an integer 5
b = 'five' # b is a string of the word 'five'
c = 5.0    # c is a floating point number 5.0

This idea is called "duck typing": If it walks like a duck, and quacks like a duck, it must be a duck.

:-)

Meaning, you call a function on a variable, and Python will execute it—if you get a quack, it's a duck; if you get a grunt, it's a pig. Get it?

You can always ask Python to tell you what type it has assigned to a given variable name like this:


In [10]:
type(a)


Out[10]:
int

In [11]:
type(b)


Out[11]:
str

In [12]:
type(c)


Out[12]:
float

Pay special attention to assigning floating-point values to variables or you may get values you do not expect in your programs. For example,


In [13]:
14/a


Out[13]:
2

In [14]:
14/c


Out[14]:
2.8

You see, if you divide an integer by an integer, Python will return an integer. But if you wanted a floating-point answer, one of the numbers must be a float (a real number). Simply appending a decimal point will do the trick:


In [15]:
14./a


Out[15]:
2.8

Whitespace in Python

Python uses indents and whitespace to group statements together. If you were to write a short loop in the C language, you might use:

for (i = 0, i < 5, i++){
   printf("Hi! \n");
}

Python does not use curly braces like C, it uses indentation instead; so the same program as above is written in Python as follows:


In [11]:
for i in range(5):
    print "Hi \n"


Hi 

Hi 

Hi 

Hi 

Hi 

Did you notice the range() function? It is a neat built-in function of Python that gives you a list from an arithmetic progression.

If you have nested for loops, there is a further indent for the inner loop, like this:


In [12]:
for i in range(3):
    for j in range(3):
        print i, j
    
    print "This statement is within the i-loop, but not the j-loop"


0 0
0 1
0 2
This statement is within the i-loop, but not the j-loop
1 0
1 1
1 2
This statement is within the i-loop, but not the j-loop
2 0
2 1
2 2
This statement is within the i-loop, but not the j-loop

Slicing Arrays

In NumPy, you can look at portions of arrays in the same way as in MATLAB, with a few extra tricks thrown in. Let's take an array of values from 1 to 5:


In [13]:
myvals = np.array([1, 2, 3, 4, 5])
myvals


Out[13]:
array([1, 2, 3, 4, 5])

Python uses a zero-based index (like C), which is a good thing. Knowing this, let's look at the first and last element in the array we have created above,


In [14]:
myvals[0], myvals[4]


Out[14]:
(1, 5)

There are 5 elements in the array myvals, but if we try to look at myvals[5], Python will be unhappy, as myvals[5] is actually calling the non-existent 6th element of that array … You'll get an error:


In [15]:
myvals[5]


---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-6cc4d3ae83cd> in <module>()
----> 1 myvals[5]

IndexError: index 5 is out of bounds for axis 0 with size 5

Arrays can also be sliced, grabbing a range of values. Let's look at the first three elements,


In [16]:
myvals[0:3]


Out[16]:
array([1, 2, 3])

Note here, the slice is inclusive on the front end and exclusive on the back, so the above command gives us the values of myvals[0], myvals[1] and myvals[2], but not myvals[3].

Assigning array variables

One of the strange little quirks/features in Python that often confuses people comes up when assigning and comparing arrays of values. Here is a quick example. Let's start by defining a 1-D array called $a$:


In [17]:
a = np.linspace(1,5,5)

In [18]:
a


Out[18]:
array([ 1.,  2.,  3.,  4.,  5.])

OK, so we have an array $a$, with the values 1 through 5. I want to make a copy of that array, called $b$, so I'll go ahead and try the following:


In [19]:
b = a

In [20]:
b


Out[20]:
array([ 1.,  2.,  3.,  4.,  5.])

Great (we think). So $a$ has the values 1 through 5 and now so does $b$. Now that I have a backup of $a$, I can change its values without worrying about losing data, right? (No!)


In [21]:
a[2] = 17

In [22]:
a


Out[22]:
array([  1.,   2.,  17.,   4.,   5.])

Here, the 3rd element of $a$ has been changed to 17. Now let's check on $b$.


In [23]:
b


Out[23]:
array([  1.,   2.,  17.,   4.,   5.])

And that's how things go wrong! When you use a statement like a = b, rather than copying all the values of a into a new array called b, Python just creates an alias called b and tells it to route us to a. So if we change a value in a, then b will reflect that change (technically, this is called assignment by reference). If you want to make a true copy of the array, you have to tell Python to create a copy of a.


In [24]:
c = a.copy()

Now, we can try again to change a value in $a$ and see if the changes are also seen in $c$.


In [25]:
a[2] = 3

In [26]:
a


Out[26]:
array([ 1.,  2.,  3.,  4.,  5.])

In [27]:
c


Out[27]:
array([  1.,   2.,  17.,   4.,   5.])

OK, it worked! If the difference between a = b and a = b.copy() is unclear, you should read through this again. This issue will come back to haunt you otherwise.


Learn More

To learn new functions available to you, visit the NumPy Reference page. If you are a proficient MATLAB user, there is a wiki page that should prove helpful to you: NumPy for Matlab Users

There are a lot of resources online to learn more about using NumPy and other libraries. Here is a short video on YouTube on using NumPy arrays.


In [28]:
from IPython.display import YouTubeVideo
# a short video about using NumPy arrays, from Enthought
YouTubeVideo('vWkb7VahaXQ')


Out[28]:

Please ignore the code snippet below. It just loads our pretty style for this notebook.

In [29]:
from IPython.core.display import HTML
def css_styling():
    styles = open("../styles/custom.css", "r").read()
    return HTML(styles)
css_styling()


Out[29]: