Skip to main content

Homework 2: Modeling Climate Change

Due: Tuesday, September 15 at 11:59 PM EDT

Overview

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.

Getting started

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.

Required interfaces

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.

Part 1: Explore the sea-ice data

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.

Part 2: Models and residuals

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.

Part 3: Prediction on later data

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.

Part 4: Choosing polynomial degree

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.

Optional extensions

  1. Use data/regression_test.csv to evaluate the candidate models on held-out data. Explain why this may give a more reliable model choice.
  2. Plot residuals for several Part 4 models and discuss whether this changes your selection.

Submission

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.