Homework 5
Objectives
- Read data from a CSV file, skipping its header
- Compute summary statistics from a list of numbers without changing the list
- Group data into a dictionary of lists
- Write results to a JSON file
- Interpret what the numbers say about real data
Background
In this assignment you will analyze ten years of daily weather from Cornwall, Vermont, just southwest of Middlebury. weather.csv has one line for each day from 2016 through 2025: the date, the high and low temperatures (°F), and the snowfall (inches). A few days with missing readings were left out, so there are 3,631 days in all. The first lines of the file are:
weather.csv
date,high,low,snow
2016-01-01,38,32,0.0
2016-01-02,38,27,0.0
2016-01-03,33,30,0.0The homework builds on Lecture 14 (reading CSV files and writing JSON files), Lecture 12 (building a dictionary in a loop, like letter_counts), Lecture 9 (sorted), and Lecture 13 (references).
Read the whole writeup before you start. Then write one function at a time, and test each one before moving on.
Part 0: Setup
Click “Download Starter Code” above to download hw5.zip. Move it to your cs145/homework folder, then extract it (Mac: double-click it; Windows: right-click it and choose “Extract All…”) to create a hw5 folder with four files:
hw5.pyis where you write your code. At the bottom, insideif __name__ == "__main__":, it has a commented-out test for each function, with the expected output. Uncomment each test once you’ve written that function, and keep the tests inside that block.weather.csvis the full data set.tiny.csvhas 7 days fromweather.csv, so you can check your results by hand.empty.csvhas only the header line.
Keep all four files in the same folder. Your hw5.py file is nearly empty: you will write each function and its docstring yourself.
tiny.csv
date,high,low,snow
2025-01-30,38,2,0.0
2025-01-31,27,1,0.0
2025-02-01,35,12,3.0
2025-02-02,13,-11,0.0
2025-02-03,23,-10,2.0
2025-03-20,72,37,0.0
2025-03-21,69,30,0.5Part 1: read_column
read_column(filename, column) returns the values in one column of a CSV file as a list of floats, in order. column is the column’s header, such as "high". If the file has only a header line, it returns an empty list.
>>> read_column("tiny.csv", "high")
[38.0, 27.0, 35.0, 13.0, 23.0, 72.0, 69.0]
>>> read_column("tiny.csv", "snow")
[0.0, 0.0, 3.0, 0.0, 2.0, 0.0, 0.5]
>>> read_column("empty.csv", "high")
[]- Read the rows with
csv.reader, as in Lecture 14. The first row is the header, a list such as['date', 'high', 'low', 'snow']. - Don’t assume which column
columnis. The list methodindexfinds it:["date", "high", "low", "snow"].index("low")is2. - Each value in a row is a string. Convert it with
float.
Part 2: median
median(nums) returns the median of a list of numbers: the middle value once the numbers are in order. If there is an even number of values, the median is the average of the two middle values. If nums is empty, it returns None.
>>> nums = [38.0, 27.0, 35.0]
>>> median(nums)
35.0
>>> nums
[38.0, 27.0, 35.0]
>>> median([38.0, 27.0])
32.5
>>> print(median([]))
Nonemedian must not change nums. As we saw in Lecture 9, nums.sort() sorts nums itself, while sorted(nums) returns a new sorted list and leaves nums alone. Since nums refers to the caller’s list (Lecture 13), nums.sort() would change the caller’s list too.
Part 3: summarize
summarize(nums) returns a dictionary with five statistics about a list of numbers:
| Key | Value |
|---|---|
"count" |
how many numbers there are |
"min" |
the smallest number |
"max" |
the largest number |
"mean" |
the average, rounded to 2 decimal places |
"median" |
the median, from your median function |
If nums is empty, it returns {"count": 0}, since the other statistics don’t exist.
>>> summarize([38.0, 27.0, 35.0, 13.0, 23.0, 72.0, 69.0])
{'count': 7, 'min': 13.0, 'max': 72.0, 'mean': 39.57, 'median': 35.0}
>>> summarize([])
{'count': 0}You may use len, min, max, sum, and round here. round(39.5714, 2) is 39.57.
Part 4: by_month
by_month(filename, column) reads a weather CSV file and returns a dictionary that groups one column’s values by month. Each key is a month number (an int from 1 to 12), and its value is the list of that column’s values on days in that month, in the order they appear in the file. Only months that appear in the file are keys. If the file has only a header line, it returns an empty dictionary.
>>> by_month("tiny.csv", "high")
{1: [38.0, 27.0], 2: [35.0, 13.0, 23.0], 3: [72.0, 69.0]}
>>> by_month("tiny.csv", "snow")
{1: [0.0, 0.0], 2: [3.0, 0.0, 2.0], 3: [0.0, 0.5]}
>>> by_month("empty.csv", "high")
{}- Read the file and find the column as in Part 1. The date is always in the first column. You can’t use
read_columnfor the dates, since they aren’t numbers, so it’s fine to repeat the lines that read the file. - The month is the middle part of a date such as
"2025-02-03".splitit, and convert the month withint. - Build the dictionary the way
letter_countsdid in Lecture 12: the first time you see a month, it isn’t a key yet, so give it an empty list. Then append the value to that month’s list.
Part 5: save_monthly_summary
save_monthly_summary(filename, column, out_filename) groups a column by month and writes a JSON file named out_filename. The JSON file holds a dictionary that maps each month to the summarize dictionary for that month’s values. It doesn’t return anything.
Use by_month and summarize, and write the file with json.dump, as in Lecture 14. Pass indent=2 to json.dump so that the file is easy to read. After running save_monthly_summary("tiny.csv", "high", "tiny_summary.json"), open tiny_summary.json in Thonny. It should start like this:
tiny_summary.json
{
"1": {
"count": 2,
"min": 27.0,
"max": 38.0,
"mean": 32.5,
"median": 32.5
},
"2": {
"count": 3,JSON keys are always strings, so json.dump writes the month 1 as "1". That’s expected.
Part 6: Questions about the data
Run the last two tests in hw5.py, which write high_summary.json and snow_summary.json for the full data set. Use them to answer these questions in the docstring at the top of hw5.py. A short answer is enough for each part.
- Spread of the daily highs
- Which two months have the biggest spread between their warmest high and their coldest high (
maxminusmin)? What are those spreads? - Which two months have the smallest spread? What are those spreads?
- Which two months have the biggest spread between their warmest high and their coldest high (
- Snowfall
- What is the median snowfall for each month?
- Why is the median the same for every month?
- Which statistic,
meanormedian, shows better how snowy a month is? - Using that statistic, about how many inches of snow fall in a typical February?
Specification
At a minimum, your hw5.py should have:
- A function named
read_columnwith two parameters, a file name and a column header, that returns that column’s values as a list of floats. - A function named
medianwith one parameter, a list of numbers, that returns their median (orNonefor an empty list) without changing the list. - A function named
summarizewith one parameter, a list of numbers, that returns the dictionary described in Part 3. - A function named
by_monthwith two parameters, a file name and a column header, that returns a dictionary mapping each month number to a list of that column’s values. - A function named
save_monthly_summarywith three parameters, a file name, a column header, and an output file name, that writes the JSON file described in Part 5. - Answers to the two questions in Part 6.
Style and Design
- You must add your code to the provided starter file.
- At the top of the file, include your name (
Author:), the resources you used, and your answers to Part 6 (included in the starter file). - Each function needs a docstring with its purpose, parameters, and return value. Spell out the “otherwise” cases, such as what
medianreturns for an empty list. - Use the functions you’ve already written instead of repeating their code:
summarizecallsmedian, andsave_monthly_summarycallsby_monthandsummarize. - Follow the Guidelines for Code Quality.
Common issues
FileNotFoundError: [Errno 2] No such file or directory: 'tiny.csv'? The data files must be in the same folder as hw5.py. Keep all four files in your hw5 folder.
ValueError: could not convert string to float: 'high'? You’re converting the header line. Skip the first row.
ValueError: 'hgih' is not in list? The column header you passed isn’t in the file. Check its spelling.
nums is sorted after you call median? Use sorted(nums) instead of nums.sort() (see Part 2).
KeyError in by_month? You appended to a month’s list before that month was a key. Check whether the month is in the dictionary first.
Submitting your work
When you are done, submit hw5.py to Gradescope. You don’t need to submit the data files or the JSON files.
Ensure that the upload succeeds and your submission passes all visible tests. Passing all of the visible tests does not guarantee that your submission correctly satisfies all of the requirements of the assignment. Some tests are hidden until after the grace period; they use other data files. We encourage you to submit early and often so you have time to resolve any issues.