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-devfor this assignment) - 2pts for making a PR on
HWn-devto merge into master (HW2-devin 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 -vand 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
playgroundrepository as a remote. Use the commandgit remote add remote_name remote_urlwhereremote_nameshould be replaced with the alias of the remote repo andremote_urlshould 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 -vand take a screenshot. Save it asP1_3.pngand 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
fetchthe changes from the remote course playground. - Try out the
git remote show my_namecommand. How useful! This is a good thing to check before blindly merging. - Now
mergethe 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_namecommand, wherebranch_nameshould 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
playgroundand course repos are not related, they have differentgithistories.gitautomatically 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
gitcommand. 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_nameshould 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.pngandP1_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
languagesthat 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
Counterfunction from thecollectionslibrary to count the number of occurrences of each element of the list. Print out your answer to the screen. - The
languagesfunction should return the frequency and label of each language (this can be extracted from the dictionary returned by theCounterfunction). - In the same file where you defined the function (
P2.py), call the function withlanguages.txtand 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_coordsandfreqs. - Hint: You may want to use the
numpyarangefunction to createx_coords. Remember,x_coordsis 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.pyfile containing your code - The bar plot (in
.pngor.pdfformat) - Please include the
languages.txtfile 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_aveshould 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.txtfile 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_complementand 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.pyfile 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.pyP5b.pyP5c.pyP5d.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_avefunction you already wrote if you'd like, but you might need to update it slightly for this problem). - The second function
np_aveshould just usenumpyto 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
areaslist/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 yourarealist/array. - Your
my_avefunction 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_avefunction should accept your areas data as anumpyarray. To allocate anumpyarray doareas_np = np.zeros(len(radii)). - Now both functions are using the best data types possible for their tasks.
- Create an
Final deliverables¶
P6.pycontaining 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, andcas 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
xas an input and return the absolute value ofx. - Note: You may assume that
xis 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.