Python Tutorial: Basic Language Tutorial and Image Processing

This tutorial uses Anaconda Python which includes useful scientific computing libraries. We run IPython which is an enhanced (interactive) Python.

Python Data Types

In [2]:
a = 5             # Python is dynamically typed; this binds a to the int value 5
In [3]:
a + 6             # An expression -- in interactive mode the value of the expression is displayed
Out[3]:
11
In [4]:
a / 2             # Division of int rounds towards zero
Out[4]:
2
In [5]:
type(a)
Out[5]:
int
In [6]:
a = 5.0           # Rebind a to the float value 5.0
In [7]:
type(a)           # Note the type has changed to float
Out[7]:
float
In [8]:
a / 2             # Float division
Out[8]:
2.5
In [9]:
(1+1j)/2          # Complex floats supported also
Out[9]:
(0.5+0.5j)
In [10]:
30**30            # Exponentiation -- note the native integer type 'int' promotes automatically to 'long' which is a big integer type
Out[10]:
205891132094649000000000000000000000000000000L
In [11]:
import math
In [12]:
math.cos(3.0)
Out[12]:
-0.9899924966004454
In [13]:
5 > 3             # Type bool is a Boolean truth value
Out[13]:
True
In [14]:
not True
Out[14]:
False
In [15]:
True and False
Out[15]:
False
In [ ]:
help(math)

Most Python modules can be Googled also for help, e.g.: "python math module" goes to http://docs.python.org/2/library/math.html.

In [16]:
int(3.5)          # Type cast to int type
Out[16]:
3
In [17]:
float(3)          # Type cast to float type
Out[17]:
3.0
In [18]:
a = 'abc' + "def" # Single or double quotes construct str data type
In [19]:
print a
abcdef
In [20]:
a[2:4]            # Substring "slicing" notation, inclusive for start index, excludes end index, indexing starts at 0
Out[20]:
'cd'
In [21]:
a[:4]             # Default start index is beginning
Out[21]:
'abcd'
In [22]:
a[2:]             # Default end index is end of str
Out[22]:
'cdef'
In [ ]:
help(str)         # Useful string methods
In [23]:
a.upper()
Out[23]:
'ABCDEF'
In [24]:
L = [1, 2, 'a']    # Python list type is constructed with square brackets and can contain heterogeneous types
In [25]:
L[0]               # List indexing
Out[25]:
1
In [26]:
L[1:]              # List slicing is the same as str slicing
Out[26]:
[2, 'a']
In [27]:
L[0] = 5           # Lists are mutable so their elements can be changed
In [28]:
L[-1]              # Negative slice indexes are relative to the end of the list, so L[-1] is the last element
Out[28]:
'a'
In [29]:
L
Out[29]:
[5, 2, 'a']
In [30]:
len(L)
Out[30]:
3
In [ ]:
help(list)         # Help on list methods
In [31]:
L + [5]           # List arithmetic performs concatenation -- use Numpy arrays for fast matrix/vector operations
Out[31]:
[5, 2, 'a', 5]
In [32]:
range(5)          # Builtin method range(n) constructs the list [0, 1, ..., n-1]
Out[32]:
[0, 1, 2, 3, 4]
In [33]:
range(3, 5)       # As with slicing notation, range(a, b) returns [a, a+1, ..., b-1]
Out[33]:
[3, 4]
In [34]:
sorted([3, 1, 2])  # Some builtin functions are useful for lists
Out[34]:
[1, 2, 3]
In [35]:
min([3, 1, 2])
Out[35]:
1
In [36]:
t = (1, 2.0)              # Tuples are created with parentheses and are immutable or const lists
In [37]:
t[0]
Out[37]:
1
In [38]:
t[0] = 5                  # One cannot modify an existing tuple
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-a2569f810c96> in <module>()
----> 1 t[0] = 5                  # One cannot modify an existing tuple

TypeError: 'tuple' object does not support item assignment
In [39]:
(x, y) = (1, 0)           # Tuples can be assigned to equal numbers of variables
In [40]:
x
Out[40]:
1
In [41]:
y
Out[41]:
0
In [42]:
(x, y) = (y, x)           # Swap two variables
In [43]:
(x, y)
Out[43]:
(0, 1)
In [44]:
d = {'a': 10, 'b': 20, 50: 100}    # The dict type, a mapping. Keys and values can be heterogeneous types
In [45]:
d['a']
Out[45]:
10
In [46]:
d.keys()
Out[46]:
['a', 50, 'b']
In [47]:
d[50] = 150

Control Flow

In [48]:
if True:          # Indentation denotes block structure in Python so there are no 'end' keywords
    print 'Hello world'
elif False and True:
    print 'Hardly likely'
else:
    print 'Goodbye world'
Hello world
In [49]:
for i in [0, 1, 2]:    # For loop -- builtin types list, tuple, dict support iteration (as do numpy arrays)
    print i
0
1
2
In [50]:
for i in range(3):     # Equivalent using range()
    print i
0
1
2
In [51]:
i = 0                  # Equivalent using while loop
while i < 3:
    print i
    i += 1             # Note C++ operator i++ does not exist in Python
0
1
2
In [52]:
i = 0                  # Keywords 'continue' and 'break' work as in C++ (continue jumps to the next iteration of the loop, break exits the loop)
while True:
    i += 1
    if i == 1:
        continue
    elif i >= 4:
        break
    print i
2
3
In [53]:
try:                   # Exception handling
    L = [1, 2]
    L[3]
except IndexError:
    print 'Caught an exception'
    raise              # Re-raise the exception
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-53-2ba81b42243d> in <module>()
      1 try:                   # Exception handling
      2     L = [1, 2]
----> 3     L[3]
      4 except IndexError:
      5     print 'Caught an exception'

IndexError: list index out of range
Caught an exception

Functions

In [54]:
def add(x, y=0):              # Declare a function -- this takes the below function code and binds it to the symbol 'add'
    z = x + y
    return z
In [55]:
add(1)
Out[55]:
1
In [56]:
add(1, 2)                     # Functions are dynamically typed so they work over various input types
Out[56]:
3
In [57]:
add(3.0, 4.5)
Out[57]:
7.5
In [58]:
add('Hello', ' world')
Out[58]:
'Hello world'
In [59]:
another_add = add             # Everything is an object in Python so we can copy this function to a different variable name
In [60]:
another_add(3, 4)
Out[60]:
7
In [61]:
def add2(a):                  # Nested functions are allowed -- variables are looked up via lexical scopes (innermost scope to outermost)
    def add1(x):
        return x + 1
    return add1(add1(a))
In [62]:
add2(1)
Out[62]:
3
In [63]:
counter = 0                   
def increment_counter():
    global counter            # Keyword global allows us to refer to the global scope inside a function
    counter += 1
    return counter
In [64]:
increment_counter()
Out[64]:
1
In [65]:
increment_counter()
Out[65]:
2
In [66]:
def read_counter():            # Keyword global is not necessary if the variable is on the right-hand side
    return counter
In [67]:
read_counter()
Out[67]:
2

Classes

Classes are often unnecessary because functions are fairly general, especially in conjunction with the pre-existing Python data types.

In [68]:
class LinkNode:                                  # Linked list element type
    def __init__(self, value=None, next=None):   # Constructor -- double underscores indicate special methods
        self.value = value
        self.next = next
        
    def __repr__(self):                          # String representation that is printed to the interactive prompt
        return 'LinkNode(' + str(self.value) + ', ' + repr(self.next) + ')'
    
def to_list(node):                               # Convert to a list
    ans = []
    while node is not None:                      # The value None is similar to C++'s type NULL. Compare with None using the 'a is b' operator.
        ans.append(node.value)
        node = node.next
    return ans
In [69]:
a = LinkNode(1)
In [70]:
a
Out[70]:
LinkNode(1, None)
In [71]:
a = LinkNode(1, LinkNode(2))
In [72]:
a
Out[72]:
LinkNode(1, LinkNode(2, None))
In [73]:
to_list(a)
Out[73]:
[1, 2]
In [74]:
a.value
Out[74]:
1
In [75]:
a.next.value
Out[75]:
2
In [76]:
a.next.next is None
Out[76]:
True

Standard Library

In [77]:
f = open('test.txt', 'wt')             # File object
print >>f, 'Hello world!', 2, [3, 4]
f.write('A fine dry day')
f.close()
In [78]:
!cat test.txt
Hello world! 2 [3, 4]
A fine dry day
In [79]:
f = open('test.txt', 'rt')
s = f.read()
print s
Hello world! 2 [3, 4]
A fine dry day
In [80]:
import os                              # Various os and path facilities are in os module
os.path.join('dir', 'subdir', 'file')
Out[80]:
'dir/subdir/file'
In [81]:
os.path.splitext('a.png')
Out[81]:
('a', '.png')
In [82]:
import sys                             # More system facilities in sys module
sys.argv                               # Argument list (relevant if .py file was run from command line using python yourprogram.py)
Out[82]:
['-c',
 '-f',
 '/Users/connelly/.ipython/profile_default/security/kernel-0978c7f7-9e4b-4425-aef8-6dfb811fd563.json',
 '--pylab',
 'inline',
 "--KernelApp.parent_appname='ipython-notebook'",
 '--parent=1']

Scientific Computing using Numpy

In [84]:
import numpy
In [85]:
u = numpy.array([1.0, 2.5])                 # Construct two vectors (1D arrays)
v = numpy.array([2.0, 3.0])
In [86]:
u + v
Out[86]:
array([ 3. ,  5.5])
In [87]:
u * v                                       # Multiplication is elementwise by default
Out[87]:
array([ 2. ,  7.5])
In [88]:
A = numpy.array([[1.0, 2.0], [3.0, 4.0]])   # Construct a matrix (2D array)
In [89]:
numpy.dot(A, u)                             # Inner product (matrix multiply)
Out[89]:
array([  6.,  13.])
In [90]:
numpy.dot(A, A)
Out[90]:
array([[  7.,  10.],
       [ 15.,  22.]])
In [91]:
numpy.linalg.solve(A, u)                    # Invert the linear system (same as MATLAB's \)
Out[91]:
array([ 0.5 ,  0.25])
In [92]:
numpy.dot(numpy.linalg.inv(A), u)           # Alternative way to invert. Note that numpy.linalg.pinv() (pseudoinverse) is more stable than inv()
Out[92]:
array([ 0.5 ,  0.25])
In [93]:
numpy.cos(u)                                # Math routines are available in numpy
Out[93]:
array([ 0.54030231, -0.80114362])
In [94]:
A[0]                                        # Numpy arrays slice to give smaller numpy arrays or scalars
Out[94]:
array([ 1.,  2.])
In [95]:
A[0,0] = 5                                  # Array assignment
A
Out[95]:
array([[ 5.,  2.],
       [ 3.,  4.]])
In [96]:
numpy.concatenate((A,A),0)                  # Concatenate along 1st dimension (dimension 0)
Out[96]:
array([[ 5.,  2.],
       [ 3.,  4.],
       [ 5.,  2.],
       [ 3.,  4.]])
In [97]:
numpy.vstack((A,A))                         # Vertical concatenate
Out[97]:
array([[ 5.,  2.],
       [ 3.,  4.],
       [ 5.,  2.],
       [ 3.,  4.]])
In [98]:
numpy.hstack((A,A))                         # Horizontal concatenate
Out[98]:
array([[ 5.,  2.,  5.,  2.],
       [ 3.,  4.,  3.,  4.]])
In [99]:
numpy.dstack((A,A))                         # Stack along 'depth' dimension (3rd dimension) -- useful for images
Out[99]:
array([[[ 5.,  5.],
        [ 2.,  2.]],

       [[ 3.,  3.],
        [ 4.,  4.]]])
In [100]:
A = numpy.zeros((3,4),'uint8')              # Most numpy constructors take an optional datatype which can be 'uint8', 'uint16', 'uint32', 'int8', 'float', 'double'
In [101]:
A
Out[101]:
array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]], dtype=uint8)
In [102]:
A+numpy.uint8(256)                          # Overflow wraps
Out[102]:
array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]], dtype=uint8)
In [103]:
A = numpy.eye(3,3)
In [104]:
A
Out[104]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
In [105]:
A[1:,1:3]                                   # Slice a subarray
Out[105]:
array([[ 1.,  0.],
       [ 0.,  1.]])

Visualization using Matplotlib (pylab) and Scikit-Image (skimage)

In [106]:
import pylab
import skimage
import skimage.io
import skimage.transform
In [107]:
I = skimage.img_as_float(skimage.io.imread('/Users/connelly/Downloads/rgb.png'))
In [108]:
I[5,3,0]                                   # Image arrays are numpy arrays, indexed by y, x, channel
Out[108]:
0.019607843137254902
In [109]:
I.shape                                    # Dimensions: h, w, channels
Out[109]:
(2560, 1536, 3)
In [110]:
pylab.imshow(I)
Out[110]:
<matplotlib.image.AxesImage at 0x109b16590>
In [ ]:
pylab.show()                              # The show() function may be necessary if the image is not displayed inline
In [111]:
pylab.imshow(2*I)
Out[111]:
<matplotlib.image.AxesImage at 0x10ad89bd0>
In [112]:
Ip = numpy.clip(2*I, 0, 1)               # Change contrast. Use clip() to prevent under-flow or over-flow. clip(I, a, b) = minimum(maximum(I, a), b)
In [113]:
pylab.imshow(Ip)
Out[113]:
<matplotlib.image.AxesImage at 0x10aaa92d0>
In [114]:
pylab.imshow(numpy.fliplr(I))           # Flip horizontally
Out[114]:
<matplotlib.image.AxesImage at 0x10aad2fd0>