Due: Tuesday, September 15 at 11:59 PM EDT
In this homework you will use NumPy and Matplotlib with larger datasets, compare several models for the same data, and examine the tradeoff between model fit and model complexity.
By the end of the homework, you should be able to:
Credit: Adapted from materials by Sara Mathieson and Allison Gong. The climate dataset and plotting activity are based on materials from Gary Witt; the regression dataset is from Jessica Wu.
Download the Homework 2 starter files from Piazza. Your working folder should contain:
model_analysis.py
README.md
data/
regression_test.csv
regression_train.csv
sea_ice_1979-2012.csv
sea_ice_2013-2020.csv
Install the required packages if they are not already available:
python3 -m pip install numpy matplotlib
Run the complete analysis with:
python3 model_analysis.py
The program must create figures/ when needed and regenerate every required figure. Keep all calculations in functions and call them from main(). This lets you test each calculation separately and allows the Gradescope autograder to import your file without running the plotting workflow.
Do not rename these functions or change their parameters or return types:
from pathlib import Path
from typing import Literal, Sequence, Union
import numpy as np
from matplotlib.axes import Axes
Number = Union[int, float]
XValue = Union[int, float]
def read_in_data(
file_path: Union[str, Path],
x_type: Literal["int", "float"],
) -> tuple[list[XValue], list[float]]:
...
def calculate_y(
x: Union[Number, np.ndarray],
coefficients: Sequence[float],
) -> Union[float, np.ndarray]:
...
def calculate_residuals(
x_values: Sequence[Number],
y_values: Sequence[Number],
coefficients: Sequence[float],
) -> list[float]:
...
def residual_sum_of_squares(residuals: Sequence[Number]) -> float:
...
def plot_scatter(
axes: Axes,
x_values: Sequence[Number],
y_values: Sequence[Number],
*,
label: str,
color: str,
) -> None:
...
def plot_polynomial(
axes: Axes,
x_start: float,
x_end: float,
coefficients: Sequence[float],
*,
label: str,
color: str,
) -> None:
...
def plot_elbow(axes: Axes, rss_values: Sequence[Number]) -> None:
...
Type annotations are required. You may add helper functions, but the functions above must remain importable at module scope. Do not use numpy.polyval, numpy.polyfit, or another built-in polynomial evaluator or fitter.
Open data/sea_ice_1979-2012.csv. Its first column contains the year; its second contains September sea-ice extent in millions of square kilometers.
Implement read_in_data() and use it to load the file. Plot the data as a scatter plot with a descriptive title and axis labels. The y-axis must begin at zero. Save the result as:
figures/part1.pdf
Every figure in this homework must include a title, labeled axes, and a legend.
Compare these models:
DEGREE_1_COEFFICIENTS = [190.5038227644984, -0.09224446142042844]
DEGREE_2_COEFFICIENTS = [
-15150.155305067638,
15.283380627913214,
-0.0038525745647042583,
]
Coefficients are ordered from the constant term upward. Thus [a, b, c] represents a + bx + cx².
Implement calculate_y() from scratch so it works for a scalar or NumPy array and for a polynomial of any degree. Plot each model on top of the original data and save:
figures/part2_deg1.pdf
figures/part2_deg2.pdf
Next implement calculate_residuals(), where each residual is:
observed y - predicted y
Plot residuals against the original years and save:
figures/part2_residuals1.pdf
figures/part2_residuals2.pdf
In README.md, describe the patterns in both residual plots and explain which model appears to fit better.
The file data/sea_ice_2013-2020.csv contains observations that were not available when the earlier dataset ended. Treat these observations as held-out data.
For each model, make one plot containing the 1979–2012 observations, the 2013–2020 observations in a different color, and the model. Save:
figures/part3_pred1.pdf
figures/part3_pred2.pdf
In README.md, discuss what the held-out observations reveal, which model you now prefer, and what caution you would give a government using these models for policy decisions.
Use data/regression_train.csv and the eleven sets of coefficients supplied in README.md. With a loop, plot the training data with each polynomial model. Because the data are not sorted by x-value, make sure model lines are drawn using ordered x-values. Save at least three representative plots as:
figures/part4_degN.pdf
Replace N with the corresponding polynomial degree. In README.md, describe how model behavior changes with degree and state which model you would initially choose.
Implement residual_sum_of_squares() using:
RSS = sum(residual² for each residual)
Compute one RSS value per model and use plot_elbow() to plot polynomial degree against RSS. Save:
figures/part4_elbow.pdf
In README.md, identify where the RSS begins to level off and explain which degree you would choose.
data/regression_test.csv to evaluate the candidate models on held-out data. Explain why this may give a more reliable model choice.Submit one .zip file to the HW02 assignment on Gradescope. The archive must preserve this structure:
model_analysis.py
README.md
figures/
part1.pdf
part2_deg1.pdf
part2_deg2.pdf
part2_residuals1.pdf
part2_residuals2.pdf
part3_pred1.pdf
part3_pred2.pdf
part4_degN.pdf
part4_elbow.pdf
You do not need to upload the data/ directory. Gradescope supplies the four datasets at data/ while grading. Include all required figures and fully answer every required prompt in README.md.
Before submitting, run:
python3 model_analysis.py
Then confirm that all required figures were recreated and that importing model_analysis does not start the program.