This tutorial uses Anaconda Python which includes useful scientific computing libraries. We run IPython which is an enhanced (interactive) Python.
a = 5 # Python is dynamically typed; this binds a to the int value 5
a + 6 # An expression -- in interactive mode the value of the expression is displayed
11
a / 2 # Division of int rounds towards zero
2
type(a)
int
a = 5.0 # Rebind a to the float value 5.0
type(a) # Note the type has changed to float
float
a / 2 # Float division
2.5
(1+1j)/2 # Complex floats supported also
(0.5+0.5j)
30**30 # Exponentiation -- note the native integer type 'int' promotes automatically to 'long' which is a big integer type
205891132094649000000000000000000000000000000L
import math
math.cos(3.0)
-0.9899924966004454
5 > 3 # Type bool is a Boolean truth value
True
not True
False
True and False
False
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.
int(3.5) # Type cast to int type
3
float(3) # Type cast to float type
3.0
a = 'abc' + "def" # Single or double quotes construct str data type
print a
abcdef
a[2:4] # Substring "slicing" notation, inclusive for start index, excludes end index, indexing starts at 0
'cd'
a[:4] # Default start index is beginning
'abcd'
a[2:] # Default end index is end of str
'cdef'
help(str) # Useful string methods
a.upper()
'ABCDEF'
L = [1, 2, 'a'] # Python list type is constructed with square brackets and can contain heterogeneous types
L[0] # List indexing
1
L[1:] # List slicing is the same as str slicing
[2, 'a']
L[0] = 5 # Lists are mutable so their elements can be changed
L[-1] # Negative slice indexes are relative to the end of the list, so L[-1] is the last element
'a'
L
[5, 2, 'a']
len(L)
3
help(list) # Help on list methods
L + [5] # List arithmetic performs concatenation -- use Numpy arrays for fast matrix/vector operations
[5, 2, 'a', 5]
range(5) # Builtin method range(n) constructs the list [0, 1, ..., n-1]
[0, 1, 2, 3, 4]
range(3, 5) # As with slicing notation, range(a, b) returns [a, a+1, ..., b-1]
[3, 4]
sorted([3, 1, 2]) # Some builtin functions are useful for lists
[1, 2, 3]
min([3, 1, 2])
1
t = (1, 2.0) # Tuples are created with parentheses and are immutable or const lists
t[0]
1
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
(x, y) = (1, 0) # Tuples can be assigned to equal numbers of variables
x
1
y
0
(x, y) = (y, x) # Swap two variables
(x, y)
(0, 1)
d = {'a': 10, 'b': 20, 50: 100} # The dict type, a mapping. Keys and values can be heterogeneous types
d['a']
10
d.keys()
['a', 50, 'b']
d[50] = 150
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
for i in [0, 1, 2]: # For loop -- builtin types list, tuple, dict support iteration (as do numpy arrays)
print i
0 1 2
for i in range(3): # Equivalent using range()
print i
0 1 2
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
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
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
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
add(1)
1
add(1, 2) # Functions are dynamically typed so they work over various input types
3
add(3.0, 4.5)
7.5
add('Hello', ' world')
'Hello world'
another_add = add # Everything is an object in Python so we can copy this function to a different variable name
another_add(3, 4)
7
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))
add2(1)
3
counter = 0
def increment_counter():
global counter # Keyword global allows us to refer to the global scope inside a function
counter += 1
return counter
increment_counter()
1
increment_counter()
2
def read_counter(): # Keyword global is not necessary if the variable is on the right-hand side
return counter
read_counter()
2
Classes are often unnecessary because functions are fairly general, especially in conjunction with the pre-existing Python data types.
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
a = LinkNode(1)
a
LinkNode(1, None)
a = LinkNode(1, LinkNode(2))
a
LinkNode(1, LinkNode(2, None))
to_list(a)
[1, 2]
a.value
1
a.next.value
2
a.next.next is None
True
f = open('test.txt', 'wt') # File object
print >>f, 'Hello world!', 2, [3, 4]
f.write('A fine dry day')
f.close()
!cat test.txt
Hello world! 2 [3, 4] A fine dry day
f = open('test.txt', 'rt')
s = f.read()
print s
Hello world! 2 [3, 4] A fine dry day
import os # Various os and path facilities are in os module
os.path.join('dir', 'subdir', 'file')
'dir/subdir/file'
os.path.splitext('a.png')
('a', '.png')
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)
['-c', '-f', '/Users/connelly/.ipython/profile_default/security/kernel-0978c7f7-9e4b-4425-aef8-6dfb811fd563.json', '--pylab', 'inline', "--KernelApp.parent_appname='ipython-notebook'", '--parent=1']
import numpy
u = numpy.array([1.0, 2.5]) # Construct two vectors (1D arrays)
v = numpy.array([2.0, 3.0])
u + v
array([ 3. , 5.5])
u * v # Multiplication is elementwise by default
array([ 2. , 7.5])
A = numpy.array([[1.0, 2.0], [3.0, 4.0]]) # Construct a matrix (2D array)
numpy.dot(A, u) # Inner product (matrix multiply)
array([ 6., 13.])
numpy.dot(A, A)
array([[ 7., 10.], [ 15., 22.]])
numpy.linalg.solve(A, u) # Invert the linear system (same as MATLAB's \)
array([ 0.5 , 0.25])
numpy.dot(numpy.linalg.inv(A), u) # Alternative way to invert. Note that numpy.linalg.pinv() (pseudoinverse) is more stable than inv()
array([ 0.5 , 0.25])
numpy.cos(u) # Math routines are available in numpy
array([ 0.54030231, -0.80114362])
A[0] # Numpy arrays slice to give smaller numpy arrays or scalars
array([ 1., 2.])
A[0,0] = 5 # Array assignment
A
array([[ 5., 2.], [ 3., 4.]])
numpy.concatenate((A,A),0) # Concatenate along 1st dimension (dimension 0)
array([[ 5., 2.], [ 3., 4.], [ 5., 2.], [ 3., 4.]])
numpy.vstack((A,A)) # Vertical concatenate
array([[ 5., 2.], [ 3., 4.], [ 5., 2.], [ 3., 4.]])
numpy.hstack((A,A)) # Horizontal concatenate
array([[ 5., 2., 5., 2.], [ 3., 4., 3., 4.]])
numpy.dstack((A,A)) # Stack along 'depth' dimension (3rd dimension) -- useful for images
array([[[ 5., 5.], [ 2., 2.]], [[ 3., 3.], [ 4., 4.]]])
A = numpy.zeros((3,4),'uint8') # Most numpy constructors take an optional datatype which can be 'uint8', 'uint16', 'uint32', 'int8', 'float', 'double'
A
array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], dtype=uint8)
A+numpy.uint8(256) # Overflow wraps
array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], dtype=uint8)
A = numpy.eye(3,3)
A
array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]])
A[1:,1:3] # Slice a subarray
array([[ 1., 0.], [ 0., 1.]])
import pylab
import skimage
import skimage.io
import skimage.transform
I = skimage.img_as_float(skimage.io.imread('/Users/connelly/Downloads/rgb.png'))
I[5,3,0] # Image arrays are numpy arrays, indexed by y, x, channel
0.019607843137254902
I.shape # Dimensions: h, w, channels
(2560, 1536, 3)
pylab.imshow(I)
<matplotlib.image.AxesImage at 0x109b16590>
pylab.show() # The show() function may be necessary if the image is not displayed inline
pylab.imshow(2*I)
<matplotlib.image.AxesImage at 0x10ad89bd0>
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)
pylab.imshow(Ip)
<matplotlib.image.AxesImage at 0x10aaa92d0>
pylab.imshow(numpy.fliplr(I)) # Flip horizontally
<matplotlib.image.AxesImage at 0x10aad2fd0>
pylab.imshow(I**2) # Gamma correct using exponentiation
<matplotlib.image.AxesImage at 0x10add4b90>
x = numpy.arange(10) # Numpy's arange() is an array variant of the builtin range()
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
y = numpy.random.random(10) # Uniform random values in interval [0, 1), of the given shape
y
array([ 0.7180393 , 0.7314042 , 0.79039345, 0.73046915, 0.13549318, 0.05911389, 0.33409875, 0.40429189, 0.35688352, 0.09963837])
pylab.plot(x, y)
[<matplotlib.lines.Line2D at 0x10af927d0>]
pylab.scatter(x, y)
<matplotlib.collections.PathCollection at 0x10afc3d50>
pylab.bar(x, y)
<Container object of 10 artists>
Many more plot types can be seen in the Matplotlib gallery.
Ip = skimage.transform.rotate(I, 45) # Rotate image
pylab.imshow(Ip)
<matplotlib.image.AxesImage at 0x140f1fe90>
T = skimage.transform.AffineTransform(scale=[0.5, 0.5]) # More general transformations
Ip = skimage.transform.warp(I, T)
pylab.imshow(Ip)
<matplotlib.image.AxesImage at 0x14ab82b10>
Additional scientific computing such as ODEs, PDEs (ordinary and partial differential equations), statistics, and interpolation are available in module scipy.
Ordinarily one should attempt to write array notation code using Numpy for efficiency, rather than looping over indices with for loops. However in some cases that is not possible. You can use the Numba JIT to help speed up such computations.
def blur(I): # Naive blur
ans = numpy.empty(I.shape)
(h, w, channels) = I.shape
for y in range(h):
for x in range(w):
y1 = max(y-1,0)
x1 = max(x-1,0)
y3 = min(y+1,h-1)
x3 = min(x+1,w-1)
for c in range(channels):
ans[y,x,c] = (I[y1,x1,c]+I[y1,x,c]+I[y1,x3,c]+
I[y,x1,c]+I[y,x,c]+I[y,x3,c]+
I[y3,x1,c]+I[y3,x,c]+I[y3,x3,c])/9
return ans
Ismall = skimage.transform.rescale(I, 1000.0/I.shape[0])
Ismall.shape
(1000, 600, 3)
%time Ip = blur(Ismall)
CPU times: user 10.77 s, sys: 0.49 s, total: 11.25 s Wall time: 10.91 s
I_stack = numpy.hstack((Ismall, Ip))
pylab.imshow(I_stack)
<matplotlib.image.AxesImage at 0x14abafa10>
skimage.io.imsave('birds.png', I_stack)
!open birds.png
import numba
blur_jit = numba.autojit(blur) # Attempt to accelerate with JIT (autojit takes a function and returns a new JITed function).
%time Ip = blur_jit(Ismall)
CPU times: user 12.11 s, sys: 0.15 s, total: 12.26 s Wall time: 12.24 s
# The JIT program is no faster. Culprit: min(), max() have not yet been accelerated by Numba. Solution: rewrite those functions in blur (or use Numba >= 0.9.1)
def blur(I):
ans = numpy.empty(I.shape)
(h, w, channels) = I.shape
for y in range(h):
for x in range(w):
y1 = y-1
if y1 < 0: y1 = 0
y3 = y+1
if y3 >= h: y3 = h-1
x1 = x-1
if x1 < 0: x1 = 0
x3 = x+1
if x3 >= w: x3 = w-1
for c in range(channels):
ans[y,x,c] = (I[y1,x1,c]+I[y1,x,c]+I[y1,x3,c]+
I[y,x1,c]+I[y,x,c]+I[y,x3,c]+
I[y3,x1,c]+I[y3,x,c]+I[y3,x3,c])/9
return ans
blur_jit = numba.autojit(blur)
%time Ip = blur_jit(Ismall) # First run includes JIT compilation overhead
CPU times: user 0.73 s, sys: 0.06 s, total: 0.79 s Wall time: 0.76 s
%time Ip = blur_jit(Ismall) # Second run is fast
CPU times: user 0.44 s, sys: 0.00 s, total: 0.45 s Wall time: 0.45 s
I recommend Spyder and PyScripter. Show use of Spyder.
Scientific and Imaging Libraries:
GUI Libraries:
Make IPython inline figures by running the Notebook with ipython notebook --pylab inline
pylab.rcParams['figure.figsize']=(8,7) # Change figure size