python boot camp
This is a gentle introduction to people who have some experience with programming to get started with python. We used this boot camp for EE 16A at Berkeley. Source files: ee16a_python_bootcamp.zip
EE 16A Python Bootcamp
EECS 16A: Designing Information Devices and Systems I, Spring 2017
Table of Contents
Overview
This minilab serves as an introduction to IPython and a couple important packages we will be using throughout the semester. The lab aims to teach you proper usage of certain commands and can serve as a reference doc in the future. Even if you are a python wizard already, we recommend that you at least look through the lab to get reacquainted with the functions we will be using a lot.
This lab is separated into two main parts: Guide and Questions. The Guide portion walks you through frequently used Python and IPython code, functions, and techniques. The Guide is supplemented with numerous blocks of example code to showcase concepts. The Questions portion of the lab is a collection of 10 problems meant to test your understanding of this guide. If you can answer these questions, then you have the knowledge to complete any and all programming tasks in EE 16A.
Python
Both the labs and the homeworks in this course will require you to write some Python code. If you’re new to programming, have no fear, as the assignments don’t require more than just the fundamentals; this is not round 2 of 61A.
Control Flow in Python
Programming languages usually contain statements that can be used to direct or “control the flow” of execution. This includes (but is not limited to) conditional statements such as if
, else
, and elif
, and loopcontrol statements such as while
, for
, break
, and continue
.
Conditional Statements: (if, else, elif)
# Example 1: Simple if/else
x = 16
if x > 20: # Asking the question, "Is x greater than 20?"
print('if condition is True!')
else:
print('if condition is False!')
# Example 2: Introducing elif
x = 16
if x > 20: # Asking the question, "Is x greater than 20?"
print('first if condition is True!')
elif x > 10 and x < 20: # Asking the question, "Is x greater than 10 AND less than 20?"
print('first if condition is False and second if condition is True!')
else:
print('Neither if condition was True!')
LoopControl Statements: (while, for)
# Example 3: while
i = 0
while i < 5: # Check if i < 5 every iteration. Stop looping if the condition is false, i.e. if i >= 5.
print('i:',i)
i += 1 # increment i by 1
Unlike while loops, which can theoretically run “forever” given the right condition, for loops serve a different purpose – iterating a fixed number of times. For loops in Python expect an iterable object – something similar to a list – to control the number of iterations. The example below is “equivalent” to the while loop in the previous example.
# Example 4: for (pun intended)
for i in range(0,5): # read about range() here: http://pythoncentral.io/pythonsrangefunctionexplained/ .
print('i:',i)
# Notice no i += 1 statement!
# Example 5: Iterating through lists
char_list = [1, 6, 'a']
word = ''
for element in char_list:
word += str(element)
print('word:',word)
All of the loop examples so far have terminated with some sort of stopping condition (i < 5, i in range(0,5), element in char_list). But what if we wanted to exit a loop early? Or, what if we wanted to immediately go to the next loop iteration? These two changes can be applied using the break and continue statements, respectively.
# Example 6: The break statement
candies = ['Skittles', 'Snickers', '3 Musketeers', 'Twizzlers', 'KitKat', 'Twix', 'Almond Joy']
print('Loop without break statement.')
for candy in candies:
print(candy)
print('\nLoop with break statement.')
for candy in candies:
print(candy)
if candy == 'KitKat':
break
# Example 7: The continue statement
candies = ['Skittles', 'Snickers', '3 Musketeers', 'Twizzlers', 'KitKat', 'Twix', 'Almond Joy']
print('Same Loop as above but with continue instead of break statement.')
for candy in candies:
print(candy)
if candy == 'KitKat':
continue
print('\nLoop that skips over everyother candy.')
for i in range(len(candies)):
if i % 2 == 1: # if i is odd. The "%" symbol is the modulo operator: https://en.wikipedia.org/wiki/Modulo_operation
continue
print(candies[i])
Notice how the continue statement enabled us to skip everyother candy.
List Comprehension
There are multiple ways of creating lists in Python. A list is a mutable array of data. They can be created using square brackets [ ]. Elements in the list are separated by commas. Elements can be of any type (int, string, float, etc.).
Important Python List functions:

'+'
joins lists and creates a new list. 
len(x)
to get the length of listx
Next, we will explore the idea of a list comprehension, which is a compact way of creating a list from a forloop in a single line. Please keep in mind that list comprehension is just a style suggestion; any list comprehension can be expanded to a fully fleshed out controlloop block. However, the advantage of list comprehensions is their compact yet expressive syntax.
For the example below, our goal is to create a list of the squares of each even integer in the range from 0 to 10 (inclusive).
# Example 8a: forloop list construction
# Expected output: [0*0, 2*2, 4*4, 6*6, 8*8, 10*10], which equals: [0, 4, 16, 36, 64, 100]
lst = []
for i in range(11): # iterate over numbers 0 through 10 (inclusive)
if i % 2 == 0: # see example 7 for explanation of "%" symbol.
lst += [i**2] # '**' is Python syntax for raising the power. Alternatively: lst.append(i**2)
print(lst)
# Example 8b: List Comprehension
lst = [i**2 for i in range(11) if i % 2 == 0] # oneliner magic
print(lst)
The syntax for a list comprehension is as follows:
list = ** **[function(ITEM) for ITEM in ITERABLE_OBJECT if condition(ITEM)]
In example 8b above:
ITEM = i
ITERABLE_OBJECT = range(11)
function() = raise ITEM to the second power
condition() = is ITEM even?
A couple notes: list comprehensions DO NOT require a condition, list comprehensions can have nested forloops.
NumPy
From the NumPy website, “NumPy is the fundamental package for scientific computing with Python. It contains among other things: a powerful Ndimensional array object.” For the purposes of this course, we primarily use NumPy for its fast and fancy matrix functions. In general, Python list operations are slow; NumPy functions exploit the NumPy array object to “vectorize” the code, which usually improves the runtime of matrix calculations. As a general rule of thumb, if a task involves vectors or matrices, you should resort to NumPy. In addition to speeding up basic operations, NumPy contains an enormous library of matrix functions, so if you ever need to manipulate a vector or matrix, NumPy most likely already has a function implemented to suit your needs.
Quintessential NumPy Documentation: http://docs.scipy.org/doc/numpy/reference/index.html
Run the cell below to import the packages needed to complete this lab.
import numpy as np # from now on, we can access numpy functions by referencing "np" instead of numpy
from numpy import linalg # import the linalg package, which includes useful matrix operations
Creating a NumPy array object
NumPy is centered around the numpy.array()
class. This array object is extremely useful, however, it is often confused with builtin Python lists, particularly when trying to represent vectors. NumPy arrays and Python Lists are NOT synonymous; you cannot simply apply functions to NumPy arrays as if they were Python Lists.
# Example 9: Going from Python list to NumPy array
py_lst = [1,2,3,4]
np_arr = np.array(py_lst)
print('Python list:',py_lst)
print('NumPy array:',np_arr)
# Example 10: Populating an empty NumPy array
np_arr = np.empty([4,4], dtype=np.int) # An empty 4x4 numpy array
for i in range(4):
for j in range(4):
np_arr[i,j] = i+j
print(np_arr)
# Example 11: Creating a NumPy array of zeros and the Identity matrix
np_zeros = np.zeros([5,5]) # 5x5 NumPy array of all zeros
np_id = np.eye(5) # 5x5 Identity array
print('np_zeros:\n',np_zeros)
print('\nnp_id:\n',np_id)
# Example 12: Creating a NumPy array that spans a certain set/list of numbers
"""numpy.linspace() is useful when you know the number of divisions over a certain range you want,
i.e., you want to divide the range [09] into 10 equal divisions.
"""
np_arr1 = np.linspace(0, 9, 10) # args for linspace(): (start, stop, num_divisions)
print('np_arr1:',np_arr1)
"""numpy.arange() is useful when you know how far away each division is from one another, a.k.a. the step size.
You want to start at 0 and get every number that is 1 away from the previous number until you get to 9.
"""
np_arr2 = np.arange(0, 10, 1) # args for arange(): (start, stop, step)
print('np_arr2:',np_arr2)
NumPy array vs. Python List
Most arithmetic operations apply to NumPy arrays in an elementwise fashion. This is in contrast with arithmetic operations for Python lists, which apply via concatenation.
# Example 13: NumPy array vs. Python list
lst = [1,2,3]
arr = np.eye(3)
lst2 = lst + lst
arr2 = arr + arr
print('lst:',lst)
print('lst + lst =',lst2)
print('\narr:\n',arr)
print('arr + arr =\n',arr2)
NumPy array slicing
Array slicing is a technique in Python (and other languages) that programmers use to extract specific indexbased information from an array. Array slicing answers queries such as, “What are the first/last n elements in this array?”, “What are the elements in the first r rows and first c columns of this matrix?”, “What is every nth element in this array?”
# Example 14: Basic vector/list slicing
simple_arr = np.arange(0,100,1)
print('\nFirst ten elements of simple_arr:',simple_arr[:10])
print('\nLast ten elements of simple_arr:',simple_arr[10:]) # you should be aware that in Python,
# requesting a negative index (n) from list a is the same as requesting is equivalent to requesting a[len(a)n].
print('\nElements 1626 of simple_arr:',simple_arr[16:26]) # Notice slicing includes the first index and excludes that last index.
Slicing includes the start index and excludes the end index, i.e. simple_arr[16:26]
means to extract the values in simple_arr
at indexes in the range [16,26)
which is the same as [16,25]
since indexes can only be integers.
# Example 15: Some fancy vector/list slicing
simple_arr = np.arange(0,20,1)
print('\nEveryother element of simple_arr, starting from 0:',simple_arr[::2])
print('\nEverythird element of simple_arr, starting from 0:',simple_arr[::3])
print('\nEveryother element of simple_arr, starting from 1016:',simple_arr[10:16:2])
# Example 16: Slicing NumPy arrays
i = np.array(range(25), dtype=np.int).reshape([5,5]) # numpy.reshape() will be introduced in the next cell
print('i:\n',i)
print('\nFirst row of i:',i[0])
print('\nFirst column of i:',i[:,0])
print('\nRows 13 of i:\n',i[1:4])
print('\nColumns 13 of i:\n',i[:,1:4])
print('\nTop left 3x3 of i:\n',i[:3,:3])
print('\nEveryother column of i:\n',i[:,::2])
NumPy array reshaping
Reshaping is useful when you want to do something such as turn a vector into a matrix or viceversa. We want to be able to do this because it is often easier to construct the desired array as a vector then reshape the vector into a matrix.
# Example 17: Determining the shape of a NumPy array
test_arr = np.zeros([15,189])
print('Shape of test_arr:',test_arr.shape) # Notice .shape is a NumPy array property NOT a function, i.e. no parenthesis.
print('Number of elements in test_arr:',test_arr.size)
# Example 18: Using reshape()
test_arr = np.array(range(16), dtype=np.int)
print('\ntest_arr:',test_arr)
print('Shape of test_arr:',test_arr.shape)
test_arr_4x4 = test_arr.reshape([4,4]) # Notice reshape() is called on the array object, i.e. array.reshape(dimensions) NOT np.reshape(arr, dimensions)!
print('\nReshaped test_arr:\n',test_arr_4x4)
print('Shape of test_arr_4x4:',test_arr_4x4.shape)
test_arr_vec = test_arr_4x4.reshape(test_arr_4x4.size) # Use array.flatten() instead. This is just to show array.reshape works in both directions.
print('\ntest_arr back as a vector:',test_arr_vec)
print('Shape of test_arr_vec:',test_arr_vec.shape)
Useful NumPy functions: (transpose(), linalg.inv(), dot(), concatenate(), vstack(), hstack(), max(), argmax())
Quintessential NumPy Documentation: http://docs.scipy.org/doc/numpy/reference/index.html
# Example 19: numpy.transpose()
norm = np.array(range(16), dtype=np.int).reshape([4,4])
print('\nnorm:\n',norm)
norm_transpose = np.transpose(norm)
print('\nnorm_transpose:\n',norm_transpose)
print('\nnorm easy transpose:\n',norm.T) # numpy.transpose(arr) == arr.T
# Example 20: numpy.linalg.inv (finds the inverse of a matrix)
i = np.eye(4)
print('\ni:\n',i)
i_inv = np.linalg.inv(i) # Notice .inv() is a function in the linalg library of NumPy.
print('\ni_inv:\n',i_inv)
print('\nAs expected, i == inv(i).')
# Example 21a: numpy.dot() (how to do matrix multiplication in NumPy!)
a = np.array([[2,3],[4,5]])
print('\na:\n',a)
b = np.array([[1,2],[0,2]])
print('\nb:\n',b)
print('\nMatrix multiplication.')
c = np.dot(a,b)
print('a*b:\n',c)
print('\nOrder matters in numpy.dot()!')
d = np.dot(b,a)
print('b*a:\n',d)
print('Notice a*b != b*a.')
e = np.array([2,2])
print('\ne:',e)
print('\nnumpy.dot() can be used to multiply an array and vector too.')
f = np.dot(a,e)
print('a*e:',f)
Instead of using numpy.dot()
to perform matrix multiplication, NumPy provides an alternative using the *
operator. Up until now, we’ve been exclusively dealing with NumPy arrays
; but there is another NumPy class called matrix
. A NumPy matrix
is just a 2dimensional NumPy array
, except it has a few additional features. In particular, we can use the *
operator to perform multiplication of two NumPy matrices
(we CANNOT use *
when multiplying NumPy arrays
).
# Example 21b: NumPy matrix multiplication
a = np.matrix([[2,3],[4,5]])
print('\na:\n',a)
b = np.matrix([[1,2],[0,2]])
print('\nb:\n',b)
print('\nMatrix multiplication using * operator.')
c = a*b
print('a*b:\n',c)
# Example 22: numpy.concatenate() (how to append/attach multiple arrays.)
a = np.array([[2,3],[4,5]])
print('\na:\n',a)
b = np.array([[1,2],[0,2]])
print('\nb:\n',b)
c = np.concatenate([a,b], axis=0) # axis controls how to concatenate the arrays. axis=0 attach vertically, axis=1 attach horizontally.
print('\nAppend b to the "bottom" of a:\n',c)
d = np.concatenate([a,b], axis=1)
print('\nAppend b to the "right" of a:\n',d)
# Example 23: numpy.vstack() and numpy.hstack()
a = np.array([[2,3],[4,5]])
print('\na:\n',a)
b = np.array([[1,2],[0,2]])
print('\nb:\n',b)
c = np.vstack([a,b])
print('\nvstack a and b:\n',c)
print('Notice this is equivalent to concatenate with axis=0.')
d = np.hstack([a,b])
print('\nhstack a and b:\n',d)
print('Notice this is equivalent to concatenate with axis=1.')
Miscellaneous Functions
# Example 24: np.floor(), np.ceil()
a = 16.5
print('a:',a)
print('floor of a:',np.floor(a))
print('ceiling of a:',np.ceil(a))
# Example 25: np.max(), np.min(), np.argmax(), np.argmin()
a = np.array([0,1,2,3,16,3,2,1,0])
print('a:',a)
print('max of a =',np.max(a))
print('min of a =',np.min(a))
print('index of max value of a =',np.argmax(a))
print('index of min value of a =',np.argmin(a))
Questions
These questions are in no particular order (except for question 0, do that one first). The questions range in difficulty; some are oneliners, others require a lot more thinking. Don’t be discouraged if you hit a roadblock. Talk to your neighbors and ask for help from the lab staff.
Question 0
In order to test your code, please run the cell below to load the autograder. There is a cell after each question that you can run in order to check your answer. The autograder is purposefully not very verbose.
%run autograder.py
Question 1
Search the NumPy documentation and/or the web for a NumPy function that can solve a system of linear equations of the form Ax=b
. Once you’ve found the package and function, insert those names into the package
and function
placeholders in the cell below.
# find the missing package and function
func = np.your.function
# Do not modify the code below
def q1(A,b):
return func(A,b)
test_q1(q1)
Question 2
Given NumPy array A, return an array that consists of every entry of A that is in an even row and in an odd column.
def q2(A):
"""
Input:
A  MxN NumPy array
Output:
Returns a NumPy array that consists of every entry of A that has an even row index and has an odd column index.
Example:
A = np.array([[ 1 2 3 4 5]
[ 6 7 8 9 10]
[11 12 13 14 15]
[16 17 18 19 20]
[21 22 23 24 25]])
Output = np.array([[ 2 4]
[12 14]
[22 24]])
"""
# YOUR CODE HERE
test_q2(q2)
Question 3
Given an MxN NumPy array, first find the indices of the maximum value in each row of the array, then return the largest of the indices.
def q3(A):
"""
Input:
A  MxN NumPy array
Output:
Return the maximum index of maximum row values of A.
Example:
A = np.array([[0 1 0 0]
[1 0 0 0]
[0 0 0 0]
[0 0 1 0]])
Output = 2
"""
# YOUR CODE HERE
return
test_q3(q3)
Question 4
Given two MxN NumPy arrays, copy everyother column of array A to the right side of array B.
def q4(A, B):
"""
Inputs:
A  MxN NumPy array
B  MxP NumPy array
Output:
Returns an Mx(P+(N/2)) NumPy array where everyother column of A is added to the right side of B in order,
starting from index 0.
Example:
A = np.array([[1,0,0,2]
[0,1,0,2]
[0,0,1,2]])
B = np.array([[1,2,3]
[4,5,6]
[7,8,9]])
Output = np.array([[1,2,3,1,0]
[4,5,6,0,0]
[7,8,9,0,1]])
"""
# YOUR CODE HERE
return
test_q4(q4)
Question 5
Given vectors u = [1,2,3,…,N] and v = [2017,2018,2019,…,2017+N1], create a vector that contains the following sequence: [1*
2017, 2*
2018, 3*
2019,…,N*
(2017+N1)].
def q5(N):
"""
Input:
N  the number of elements in u and v.
Output:
Returns the sequence: np.array([1*2017,2*2018,...,N*(2017+N1)])
Example:
N = 5
Output = np.array([ 2017 4036 6057 8080 10105])
"""
u = # YOUR CODE HERE
v = # YOUR CODE HERE
# YOUR CODE HERE
return
test_q5(q5)
Question 6
Given a NumPy vector v, shift all of the elements in v by n steps to the right; values that “fall off” the right end of v get inserted at the beginning of v, thus the length of v is preserved. You can either attempt to implement this on your own, or, (hint hint) try searching for a related NumPy function that does some/all of the work for you…
def q6(v, N=10):
"""
Input:
v = NumPy vector
N = number of steps to shift v to the right
Output:
Returns v shifted to the right by N steps.
Example:
v = np.array([0,1,2,3,4,5])
N = 3
Output = np.array([3,4,5,0,1,2])
"""
# YOUR CODE HERE
return
test_q6(q6)
Question 7
Given an MxM identity matrix, convert this to an (MN)x(MN) identity matrix WITHOUT using numpy.eye().
def q7(I=np.eye(10), N=4):
"""
Input:
I  MxM NumPy array representing the identity matrix
N  number of rows and columns to cut from I
Output:
Returns an (MN)x(MN) NumPy identity array.
Example:
I = np.eye(10)
N = 8
Output = np.array([[1,0]
[0,1]])
"""
# YOUR CODE HERE; REMEMBER, YOU CANNOT USE np.eye()!
return
test_q7(q7)
Question 8
Given a square NxN NumPy array A, return a Python list of the values along the diagonal of A, sorted in descending order.
def q8(A):
"""
Input:
A  NxN NumPy array
Output:
Returns a Python list containing the diagonal of A sorted in descending order.
Example:
A = np.array([[1,2,3]
[4,5,6]
[7,8,9]])
Output = [9,5,1]
"""
# YOUR CODE HERE
return
test_q8(q8)
Question 9
Given two differently sized matrices, “pad” the matrices with the smaller dimensions with rows/columns of zeros until they are the same size as one another. Add the padding to the bottom (if adding rows) and to the right (if adding columns). Hint: there might be a NumPy function that does something similar/exactly to this, but it’s good practice to try this yourself.
def q9(A,B):
"""
Input:
A  MxN NumPy array
B  YxZ NumPy array
Output:
Returns the zeropadded versions of each array such that they are of equivalent dimensions.
Padding is added to the bottom and right.
Example:
A = np.array([[1,2,3]
[4,5,6]])
B = np.array([[1,1]
[1,1]
[1,1]])
Output = np.array([[1,2,3]
[4,5,6]
[0,0,0]]),
np.array([[1,1,0]
[1,1,0]
[1,1,0]])
"""
# YOUR CODE HERE
return A, B
test_q9(q9)
Question 10
Given an MxN matrix, A, and an NxM matrix, B, concatenate (sidebyside) the first p rows of A with the transpose of the last p columns of B.
def q10(A, B, p):
"""
Input:
A  MxN NumPy array
B  NxM NumPy array
p  the number of rows from A to concatenate with the number of columns from B
Output:
Returns the sidebyside concatenation of the first p rows of A with the transpose of the last p columns of B.
Example:
A = np.array([[1,1,1]
[1,1,1]
[1,1,1]
[1,1,1]])
B = np.array([[1,2,3,4]
[5,6,7,8]
[9,10,11,12]])
p = 2
Output = np.array([[1,1,1,3,7,11]
[1,1,1,4,8,12]])
"""
# YOUR CODE HERE
return
test_q10(q10)
test_all(q1,q2,q3,q4,q5,q6,q7,q8,q9,q10)