Homework 2¶
Due Thursday, September 26th 2019 at 11:59 PM.¶
Be sure to push the final version of your code to your GitHub repo. Follow the instructions on the course website.¶
Topics¶
Part 0: Course Workflow¶
Part 1: git
Remotes¶
- Problem 1. Understanding remotes
Part 2: Python Basics¶
- Problem 2. Count and plot
- Problem 3. Average area of the circles (part 1)
- Problem 4. DNA complements
Part 3: Closure and Decorators¶
- Problem 5. Simple bank account withdraw system
- Problem 6. Average area of the circles (part 2)
- Problem 7. Positivity
Part 0 [10 pts]: Course Workflow¶
Once you receive HW1 feedback (no later than Friday Sep 20) you will need to merge your HW1-dev
branch into master
. This counts for points so be sure to do this.
For HW2 and homework going forward you will be rewarded direct points for following all stages of the git
workflow which involves:
- 5pts for completing homework on
HWn-dev
(HW2-dev
for this assignment) - 2pts for making a PR on
HWn-dev
to merge into master (HW2-dev
in this assignment) - 3pts for merging your previous assignment ($\text{HW}_{n-1}$) into
master
(HW1 from the previous assignment) AFTER approval by the teaching staff.
Part 1 [15 pts]: git
Remotes¶
Problem 1 [15 pts]: Understanding Remotes¶
Remotes are an important concept in git
. Being a decentralized version control system, git
allows each user to have the entire history of the project locally. This means that you can develop anywhere you want (e.g. an airplane over the ocean!). It also means that you have to be comfortable working with repos from other people; not just a central repository.
The key idea here is that of remote repositories. You have probably noticed by now that git
has names such as origin
and master
. The name origin
refers to the name of the remote repository while the name master
refers to the name of the branch
. Repositories can have many branches. You can create references to other repositories (called remotes) within your current repository and customize the names.
You will complete this problem in your course repo in the HW2/
directory on the HW2-dev
branch.
Complete the following steps:
- Type
git remote -v
and take a screenshot. Save it asP1_1.png
, put it in yourHW2-final/
directory, commit the changes to your local repo, and push the changes toorigin
. - Add your
playground
repository as a remote. Use the commandgit remote add remote_name remote_url
whereremote_name
should be replaced with the alias of the remote repo andremote_url
should be replaced with the url to the remote repo. You can name the remote whatever you want. In the rest of this exercise, we will refer to the playground remote name asmy_name
. - Type
git remote -v
and take a screenshot. Save it asP1_3.png
and put it in yourHW2-final/
directory, commit the changes to your local repo, and push the changes toorigin
. - Now create a remote to the course playground (
dsondak/playground
). No need to take a screenshot this time. - Next
fetch
the changes from the remote course playground. - Try out the
git remote show my_name
command. How useful! This is a good thing to check before blindly merging. - Now
merge
the changes into your local copy (this should go smoothly because you shouldn't have any of the files yet so there's really nothing to merge).- Note that steps 5 and 7 could have been combined with a
git pull remote_name branch_name
command, wherebranch_name
should be replaced with the branch that you want to pull from. It's good to get in the habit of first fetching and then merging; this helps enforce good practices. - Hint: Because your
playground
and course repos are not related, they have differentgit
histories.git
automatically rejects merges of repos with different histories. However, for this problem we will override that behavior. Try using the option--allow-unrelated-histories
. - Hint: You may need to resolve a merge conflict. In case you do, it's probably best to retain whatever was in your course repo (instead of what was in the playground).
- Note that steps 5 and 7 could have been combined with a
- Show the status of your repo with the appropriate
git
command. Take a screen shot, save it asP1_7.png
, and put it in yourHW2-final/
directory. - Commit the changes to your local repo.
- Push the changes to your remote course repo (probably named
origin
). - Optional Rename the remote with the command
git remote rename my_name new_name
, wherenew_name
should be replaced by the new alias that you want to use. Remove the remote withgit remote remove my_name
.
Final Deliverables¶
- The
P1_1.png
,P1_3.png
andP1_7.png
Part 2 [30 pts]: Python Basics¶
Problem 2 [10 pts]: Count and Plot¶
In this problem, you will make a bar plot of the computer languages that students who take CS207 know. The file languages.txt
contains all the languages that students listed as their primary language in the course survey from a previous iteration of the course.
Do the following:
- Write a function called
languages
that accepts a string representing the languages filename. - Load the language strings from the file into a list (you may assume the file is in the current directory).
- Use the
Counter
function from thecollections
library to count the number of occurrences of each element of the list. Print out your answer to the screen. - The
languages
function should return the frequency and label of each language (this can be extracted from the dictionary returned by theCounter
function). - In the same file where you defined the function (
P2.py
), call the function withlanguages.txt
and get the labels and frequency. - Create a bar plot to display the frequency of each language. Be sure to label the x-axis!
- Be sure to import matplotlib:
import matplotlib.pyplot as plt
. - To generate the bar plot write
plt.bar(x_coords, freqs)
. You need to definex_coords
andfreqs
. - Hint: You may want to use the
numpy
arange
function to createx_coords
. Remember,x_coords
is the x-axis and it should have points for each distinct language. - Hint: To get
freqs
, you may want to use thevalues()
method on your result from step 2. That is,freqs = result_from_2.values()
. - Hint: To label the x-axis you should use
plt.xticks(x_coords, labels)
where labels can be accessed through thekeys()
method on your result from step 2.
- Be sure to import matplotlib:
Final Deliverables¶
- The
P2.py
file containing your code - The bar plot (in
.png
or.pdf
format) - Please include the
languages.txt
file in yourHW2-final/
directory.
Problem 3 [10 pts]: Average Area of the Circles (Part 1)¶
The file circles.txt
contains measurements of circle radii. Your task is to write a script that reports the average area of the circles. You will not use the numpy
mean
function. Here are the requirements:
- Open
circles.txt
(your function will assume the file is in the current directory) , read in the data, and convert the data to floats. Store in a list. - Write a function that computes the area of a circle. The argument should be a single
float
. - Write a function, called
my_ave
, that computes the average of the areas of the circles.- At the very least,
my_ave
should accept the list of radii as one argument and the circle function that you wrote in step 2 as another argument. - Note: There are other ways of doing this task, but I want you to do it this way.
- At the very least,
- Print out the result. You should format the result
Myavg : YOUR_RESULT
. Hence if you're average area was $10$, the result should look likeMyavg : 10
Final Deliverables¶
P3.py
- Please include the
circles.txt
file in yourHW2-final/
directory.
Problem 4 [10 pts]: DNA complement¶
Much of biology and bioinformatics depends on understanding and manipulating long sequences of DNA. A common thing to look at would be the complement of a given sequence.
Recall that DNA has 4 bases: A
,T
,G
,C
. DNA sequences are described by a concatenation of these letters. For example, a DNA sequence might have the form AATGGC
. The complement of the base A
is the base T
and the complement of the base G
is the base C
. The DNA complement of AATGGC
is therefore TTACCG
.
You must write a Python function that accepts a string of arbitrary length representing a DNA sequence and returns the corresponding DNA complement, also as a string. Please put this function in a file called P4.py
.
API Design Considerations and Requirements¶
- Name your function
dna_complement
and have it take in a DNA sequence (string) as input. - You must handle the situation in which the input string does not contain characters corresponding to the real DNA bases. If this situation arises, please have your function return
None
. - Your function should be case-agnostic in the sense that the input string to the function can be any case. However, your function must return the DNA complement in upper case.
- Demo your function in the same Python file. Print out an example input string, call the function, and print out the output string. Assume the original DNA sequence definition already exists in the file. That is, there is no need to have the user input the string on the command line. You should also demo the case where the input is an invalid DNA string (containing characters other than ATGC).
Final deliverables¶
- The
P4.py
file containing your function.
Part 3 [45 pts]: Closures and Decorators¶
Problem 5 [15 pts]: Simple Bank Account Withdraw System¶
The goal of this problem is to write a simple bank account withdraw system. The problem is based off of a problem in Structure and Interpretation of Computer Programs.
Instructions: Do each part in a different file (naming convention supplied below) and clearly label each with comments and useful naming.
a).
Write a closure to make withdrawals from a bank account. The outer function should accept the initial balance as an argument (we'll refer to this argument as balance
in this problem statement, but you can call it whatever you want). The inner function should accept the withdrawal amount as an argument and return the new balance.
NOTE 1: For this part, do not try to reassign balance
in the inner function. Just see what happens when you return a new balance. You can store the new balance in a new name (call it new_bal
if you want) or just return the new balance directly.
NOTE 2: You should check for basic exceptions (e.g. attempting to withdraw more than the current balance).
Once you write your functions, demo them as follows:
wd = make_withdrawal(init_balance)
wd(withdrawal_amount)
wd(new_withdrawal_amount)
This demo should be submitted as P5a.py
.
You should observe that this does not behave correctly. Explain why not.
Note: The explanation can be entered as a Python
string in your demo script and printed out to the screen.
Note: Feel free to put the explanation above the demo. Otherwise the Python
exception handler will kill the script before your explanation gets printed out. For example, your string could start with "The following code does not work because...". When code is run, the warning string will print out followed by the Python
exception.
b).
You can fix things up by updating balance
within the inner function. But this won't work. Try it out and explain why it doesn't work. Try to use the language that we used in lecture.
- Hint: Python Execution Model.
Submit this solution in the file P5b.py
. Again, provide an explanation as a string and print to the screen. As before, you can print this string before calling the demo.
c).
Now make just one small change to your code from Part b. Declare balance
as a nonlocal variable using the nonlocal
keyword. That is, after you get to the inner function, say nonlocal balance
. Here's some information on the nonlocal
statement: nonlocal
.
Now test things out like you did in Part a. It should be behaving correctly now.
Submit as P5c.py
.
d).
Finally, visualize your code from Part 5c with Python Tutor. Take a screen shot and name it P5d.png
Summary of Deliverables¶
P5a.py
P5b.py
P5c.py
P5d.png
Problem 6 [15pts]: Average Area of the Circles (part 2)¶
Let's return to the data from Problem 3.
Write two functions:
- The first function should return the average circle area (you can re-use the
my_ave
function you already wrote if you'd like, but you might need to update it slightly for this problem). - The second function
np_ave
should just usenumpy
to compute the average.
Write a decorator to time the evaluation of each function. You can use the timing decorator from lecture.
Notes and Hints¶
- Include the functions in a file called
P6.py
. - Be fair!
- To be as fair as possible, do the following:
- Create an
areas
list/array outside of your averaging functions. This means that you should do a loop over the radii you read in fromcircles.txt
, compute the area from each point, and store that area in an array. Do you know why this is more fair? If not, think about it make sure you understand. Also, do not useappend
. Instead, preallocate space for yourarea
list/array. - Your
my_ave
function should accept your areas data as a list. Remember, to allocate a list you should do[0.0]*N
: if you use such a construct your list will be filled in with $N$ zeros. - Your
np_ave
function should accept your areas data as anumpy
array. To allocate anumpy
array doareas_np = np.zeros(len(radii))
. - Now both functions are using the best data types possible for their tasks.
- Create an
Final deliverables¶
P6.py
containing your functions and speed test.
Problem 7 [15 pts]: Positivity¶
Write a decorator to check if a quantity returned from a function is positive. An exception should be raised if the quantity is not positive.
Write three functions and decorate them with your decorator:
- A function that returns the discriminant $\displaystyle d = b^{2} - 4ac$
- Note: This function should accept
a
,b
, andc
as inputs and return the discriminant. - Note: You may assume that all three coefficients are real numbers.
- Note: This function should accept
- A function that computes the absolute value.
- Note: You must write this yourself. Do not use built-ins functions.
- Note: This function should accept a scalar
x
as an input and return the absolute value ofx
. - Note: You may assume that
x
is a real number.
- A function of your own choosing.
Put these functions in a file called P7.py
.
Show that your decorator behaves correctly. For each function, show two cases (where applicable):
- A case where positivity is not violated
- A case where positivity is violated
You should demo your functions in the same P7.py
file in which they are defined. For example, your demo might look something like (but not exactly the same):
print("Trying to compute the discriminant of {0:4.2f}x^2 + {1:4.2f}x + {2:4.2f}\n".format(a, b, c))
print(disc(a, b, c)) # Positivity not violated
print("Trying to calculate the absolute value of {0:8.6f}".format(x))
print(new_abs(x)) # Positivity not violated
and the output might look like:
Trying to compute the discriminant of 2x^2 + 0x + -1
8.0
Trying to compute the discriminant of absolute value of -1
1.0
We also want to make sure we test that exceptions occur when we want them to occur. Provided below is an example of how you might want to test whether your exceptions are working.
def dummy():
'''Simple function that raises a ValueError'''
raise ValueError("Test ValueError")
def dummy2():
'''Simple function that doesn't return an error'''
return "hello"
def test(f):
'''Checks to make sure ValueErrors are being thrown correctly'''
try:
f()
'''If you reached this part this means you didn't get an exception!'''
print("Didn't catch an error - this is not expected!")
except ValueError:
'''Caught the exception type you want. Let the user know.'''
print("Caught ValueError as expected!")
except Exception:
'''Some other exception happened. Let the user know something went wack.'''
print("Caught some Unexpected Error")
test(dummy) #This one raises ValueError
test(dummy2) # This one doesn't
If you're not used to testing, this might seem strange to you. Why do we expect an error? The reason is that we're making sure we get the correct behavior under certain situations. We expect an error when the user inputs something illegal (however we define that).
Note that the example tests above aren't exactly complete. You may need to tweak them a little bit.
Final deliverables¶
P7.py
- Including the functions, the function demos, and their tests.