In [ ]:
instructors = ['Dave', 'Jim', 'Dorkus the Clown']
if 'Dorkus the Clown' in instructors:
print('#fakeinstructor')
There is a special do nothing word: pass
that skips over some arm of a conditional, e.g.
In [ ]:
if 'Jim' in instructors:
print("Congratulations! Jim is teaching, your class won't stink!")
else:
pass
In [ ]:
for instructor in instructors:
print(instructor)
You can combine loops and conditionals:
In [ ]:
for instructor in instructors:
if instructor.endswith('Clown'):
print(instructor + " doesn't sound like a real instructor name!")
else:
print(instructor + " is so smart... all those gooey brains!")
Since for operates over lists, it is common to want to do something like:
NOTE: C-like
for (i = 0; i < 3; ++i) {
print(i);
}
The Python equivalent is:
for i in [0, 1, 2]:
do something with i
What happens when the range you want to sample is big, e.g.
NOTE: C-like
for (i = 0; i < 1000000000; ++i) {
print(i);
}
That would be a real pain in the rear to have to write out the entire list from 1 to 1000000000.
Enter, the range()
function. E.g.
range(3) is [0, 1, 2]
In [1]:
sum = 0
for i in range(10):
sum += i
print(sum)
For loops let you repeat some code for every item in a list. Functions are similar in that they run the same lines of code for new values of some variable. They are different in that functions are not limited to looping over items.
Functions are a critical part of writing easy to read, reusable code.
Create a function like:
def function_name (parameters):
"""
docstring
"""
function expressions
return [variable]
Note: Sometimes I use the word argument in place of parameter.
Here is a simple example. It prints a string that was passed in and returns nothing.
In [20]:
def print_string(str):
"""This prints out a string passed as the parameter."""
print(str)
for c in str:
print(c)
if c == 'r':
break
print("done")
return
In [21]:
print_string("string")
To call the function, use:
print_string("Dave is awesome!")
Note: The function has to be defined before you can call it!
In [ ]:
print_string("Dave is awesome!")
If you don't provide an argument or too many, you get an error.
In [7]:
#print_string()
Parameters (or arguments) in Python are all passed by reference. This means that if you modify the parameters in the function, they are modified outside of the function.
See the following example:
def change_list(my_list):
"""This changes a passed list into this function"""
my_list.append('four');
print('list inside the function: ', my_list)
return
my_list = [1, 2, 3];
print('list before the function: ', my_list)
change_list(my_list);
print('list after the function: ', my_list)
In [23]:
def change_list(my_list):
"""This changes a passed list into this function"""
my_list.append('four');
print('list inside the function: ', my_list)
return
my_list = [1, 2, 3];
print('list before the function: ', my_list)
change_list(my_list);
print('list after the function: ', my_list)
Variables have scope: global
and local
In a function, new variables that you create are not saved when the function returns - these are local
variables. Variables defined outside of the function can be accessed but not changed - these are global
variables, Note there is a way to do this with the global
keyword. Generally, the use of global
variables is not encouraged, instead use parameters.
my_global_1 = 'bad idea'
my_global_2 = 'another bad one'
my_global_3 = 'better idea'
def my_function():
print(my_global_1)
my_global_2 = 'broke your global, man!'
global my_global_3
my_global_3 = 'still a better idea'
return
my_function()
print(my_global_2)
print(my_global_3)
In [25]:
my_global_1 = 'bad idea'
my_global_2 = 'another bad one'
my_global_3 = 'better idea'
def my_function():
print(my_global_1)
my_global_2 = 'broke your global, man!'
print(my_global_2)
global my_global_3
my_global_3 = 'still a better idea'
return
my_function()
print(my_global_2)
print(my_global_3)
In general, you want to use parameters to provide data to a function and return a result with the return
. E.g.
def sum(x, y):
my_sum = x + y
return my_sum
If you are going to return multiple objects, what data structure that we talked about can be used? Give and example below.
In [30]:
def a_function(parameter):
return None
In [31]:
foo = a_function('bar')
print(foo)
type | behavior |
---|---|
required | positional, must be present or error, e.g. my_func(first_name, last_name) |
keyword | position independent, e.g. my_func(first_name, last_name) can be called my_func(first_name='Dave', last_name='Beck') or my_func(last_name='Beck', first_name='Dave') |
default | keyword params that default to a value if not provided |
In [32]:
def print_name(first, last='the Clown'):
print('Your name is %s %s' % (first, last))
return
Take a minute and play around with the above function. Which are required? Keyword? Default?
In [34]:
def massive_correlation_analysis(data, method='pearson'):
pass
return
Functions can contain any code that you put anywhere else including:
In [39]:
def print_name_age(first, last, age):
print_name(first, last)
print('Your age is %d' % (age))
print('Your age is ' + str(age))
if age > 35:
print('You are really old.')
return
In [40]:
print_name_age(age=40, last='Beck', first='Dave')
Once you have some code that is functionalized and not going to change, you can move it to a file that ends in .py
, check it into version control, import it into your notebook and use it!
Let's do this now for the above two functions.
...
See you after the break!
Import the function...
In [ ]:
Call them!
In [ ]:
Notes from last class:
os
package has tools for checking if a file exists: os.path.exists
import os
filename = 'HCEPDB_moldata.zip'
if os.path.exists(filename):
print("wahoo!")
requests
package to get the file given a url (got this from the requests docs)
import requests
url = 'http://faculty.washington.edu/dacb/HCEPDB_moldata.zip'
req = requests.get(url)
assert req.status_code == 200 # if the download failed, this line will generate an error
with open(filename, 'wb') as f:
f.write(req.content)
zipfile
package to decompress the file while reading it into pandas
import pandas as pd
import zipfile
csv_filename = 'HCEPDB_moldata.csv'
zf = zipfile.ZipFile(filename)
data = pd.read_csv(zf.open(csv_filename))
Here was my solution
import os
import requests
import pandas as pd
import zipfile
filename = 'HCEPDB_moldata.zip'
url = 'http://faculty.washington.edu/dacb/HCEPDB_moldata.zip'
csv_filename = 'HCEPDB_moldata.csv'
if os.path.exists(filename):
pass
else:
req = requests.get(url)
assert req.status_code == 200 # if the download failed, this line will generate an error
with open(filename, 'wb') as f:
f.write(req.content)
zf = zipfile.ZipFile(filename)
data = pd.read_csv(zf.open(csv_filename))
My solution:
In [4]:
def download_if_not_exists(url, filename):
if os.path.exists(filename):
pass
else:
req = requests.get(url)
assert req.status_code == 200 # if the download failed, this line will generate an error
with open(filename, 'wb') as f:
f.write(req.content)
In [5]:
def load_HCEPDB_data(url, zip_filename, csv_filename):
download_if_not_exists(url, zip_filename)
zf = zipfile.ZipFile(zip_filename)
data = pd.read_csv(zf.open(csv_filename))
return data
In [6]:
import os
import requests
import pandas as pd
import zipfile
load_HCEPDB_data('http://faculty.washington.edu/dacb/HCEPDB_moldata_set1.zip', 'HCEPDB_moldata_set1.zip', 'HCEPDB_moldata_set1.csv')
Out[6]:
How many functions did you use?
Why did you choose to use functions for these pieces?
Let's say we have three molecules (A, B, C) with three measurements each (v1, v2, v3). So for each molecule we have a vector of measurements:
$$X=\begin{bmatrix} X_{v_{1}} \\ X_{v_{2}} \\ X_{v_{3}} \\ \end{bmatrix} $$Where X is a molecule and the components are the values for each of the measurements. These make up the rows in our matrix.
Often, we want to compare molecules to determine how similar or different they are. One measure is the Pearson correlation.
Pearson correlation:
Expressed graphically, when you plot the paired measurements for two samples (in this case molecules) against each other you can see positively correlated, no correlation, and negatively correlated. Eg.
Simple input dataframe (note when you are writing code it is always a good idea to have a simple test case where you can readily compute by hand or know the output):
index | v1 | v2 | v3 |
---|---|---|---|
A | -1 | 0 | 1 |
B | 1 | 0 | -1 |
C | .5 | 0 | .5 |
For our test case, what will the output be?
A | B | C | |
---|---|---|---|
A | 1 | -1 | 0 |
B | -1 | 1 | 0 |
C | 0 | 0 | 1 |
In [ ]:
for
and possibly if
..py
file in the directory with the Jupyter notebook, import and run!To create the sample dataframe:
df = pd.DataFrame([[-1, 0, 1], [1, 0, -1], [.5, 0, .5]])
To loop over rows in a dataframe, check out (Google is your friend):
DataFrame.iterrows
In [11]:
In [ ]:
In [ ]:
In [ ]:
In [ ]:
Our three row example is a useful tool for checking that our code is working. We can write some tests that compare the output of our functions to our expectations.
E.g. The diagonals should be 1, and corr(A, B) = -1, ...
assert
and raise
We've already briefly been exposed to assert in this code:
if os.path.exists(filename):
pass
else:
req = requests.get(url)
# if the download failed, next line will raise an error
assert req.status_code == 200
with open(filename, 'wb') as f:
f.write(req.content)
What is the assert doing there?
Let's play with assert
. What should the following asserts do?
assert True == False, "You assert wrongly, sir!"
assert 'Dave' in instructors
assert function_that_returns_True_or_False(parameters)
In [ ]:
So when an assert statement is true, the code keeps executing and when it is false, it raises
an exception (also known as an error).
We've all probably seen lots of exception. E.g.
def some_function(parameter):
return
some_function()
some_dict = { }
print(some_dict['invalid key'])
'fourty' + 2
Like C++ and other languages, Python let's you raise
your own exception. You can do it with raise
(surprise!). Exceptions are special objects and you can create your own type of exceptions. For now, we are going to look at the simplest Exception
.
We create an Exception
object by calling the generator:
Exception()
This isn't very helpful. We really want to supply a description. The Exception object takes any number of strings. One good form if you are using the generic exception object is:
Exception('Short description', 'Long description')
In [ ]:
Creating an exception object isn't useful alone, however. We need to send it down the software stack to the Python interpreter so that it can handle the exception condition. We do this with raise
.
raise Exception("An error has occurred.")
Now you can create your own error messages like a pro!
In [ ]:
There are lots of types of exceptions beyond the generic class Exception
. You can use them in your own code if they make sense. E.g.
import math
my_variable = math.inf
if my_variable == math.inf:
raise ValueError('my_variable cannot be infinity')
List of Standard Exceptions −
EXCEPTION NAME | DESCRIPTION |
---|---|
Exception | Base class for all exceptions |
StopIteration | Raised when the next() method of an iterator does not point to any object. |
SystemExit | Raised by the sys.exit() function. |
StandardError | Base class for all built-in exceptions except StopIteration and SystemExit. |
ArithmeticError | Base class for all errors that occur for numeric calculation. |
OverflowError | Raised when a calculation exceeds maximum limit for a numeric type. |
FloatingPointError | Raised when a floating point calculation fails. |
ZeroDivisonError | Raised when division or modulo by zero takes place for all numeric types. |
AssertionError | Raised in case of failure of the Assert statement. |
AttributeError | Raised in case of failure of attribute reference or assignment. |
EOFError | Raised when there is no input from either the raw_input() or input() function and the end of file is reached. |
ImportError | Raised when an import statement fails. |
KeyboardInterrupt | Raised when the user interrupts program execution, usually by pressing Ctrl+c. |
LookupError | Base class for all lookup errors. |
IndexError KeyError |
Raised when an index is not found in a sequence. Raised when the specified key is not found in the dictionary. |
NameError | Raised when an identifier is not found in the local or global namespace. |
UnboundLocalError EnvironmentError |
Raised when trying to access a local variable in a function or method but no value has been assigned to it. Base class for all exceptions that occur outside the Python environment. |
IOError IOError |
Raised when an input/ output operation fails, such as the print statement or the open() function when trying to open a file that does not exist. Raised for operating system-related errors. |
SyntaxError IndentationError |
Raised when there is an error in Python syntax. Raised when indentation is not specified properly. |
SystemError | Raised when the interpreter finds an internal problem, but when this error is encountered the Python interpreter does not exit. |
SystemExit | Raised when Python interpreter is quit by using the sys.exit() function. If not handled in the code, causes the interpreter to exit. |
Raised when Python interpreter is quit by using the sys.exit() function. If not handled in the code, causes the interpreter to exit. | Raised when an operation or function is attempted that is invalid for the specified data type. |
ValueError | Raised when the built-in function for a data type has the valid type of arguments, but the arguments have invalid values specified. |
RuntimeError | Raised when a generated error does not fall into any category. |
NotImplementedError | Raised when an abstract method that needs to be implemented in an inherited class is not actually implemented. |
In [ ]:
assert
and raise
Breaking assert down, it is really just an if test followed by a raise. So the code below:
assert <some_test>, <message>
is equivalent to a short hand for:
if not <some_test>:
raise AssertionError(<message>)
Prove it? OK.
instructors = ['Dorkus the Clown', 'Jim']
assert 'Dave' in instructors, "Dave isn't in the instructor list!"
instructors = ['Dorkus the Clown', 'Jim']
assert 'Dave' in instructors, "Dave isn't in the instructor list!"
if not 'Dave' in instructors:
raise AssertionError("Dave isn't in the instructor list!")
Can we write some quick tests that make sure our code is doing what we think it is? Something of the form:
corr_matrix = pairwise_row_correlations(my_sample_dataframe)
assert corr_matrix looks like what we expect, "The function is broken!"
What are the smallest units of code that we can test?
What asserts can we make for these pieces of code?
Put the following in an empty cell:
.99999999999999999999
How can we test for two floating point numbers being (almost) equal? Pro tip: Google!
In [ ]:
Here we created some functions from just a short description of our needs.
assert
to verify our code against the simple test example.Next: errors, part 2; unit tests; debugging;
In [ ]: