Skip to main content

Homework 1: Computing and Plotting in Python

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

Overview

This homework reviews the Python tools and programming practices we will use throughout the course. You will practice core Python, NumPy, Matplotlib, classes, objects, and dictionaries.

By the end of the homework, you should be able to:

Credit: Adapted from materials by Sara Mathieson and Allison Gong. Some NumPy examples are based on the NumPy quickstart tutorial.

Getting started

Download the Homework 1 starter files from Piazza. Your working folder should contain:

The lab computers have Python and the required libraries installed. If you work on your own computer, install Python 3 and the required packages:

python3 -m pip install numpy matplotlib

Run each program from a terminal. For example:

python3 python_intro.py

Your programs may initially produce no output, but they should run without errors.

Part 1: Introduction to Python

Complete the exercises in python_intro.py. Test every function in a main() function.

1. Movie-ticket price

Write a function named movie_ticket_cost() that prompts the user for their age and prints the price of one ticket. Its signature should be:

def movie_ticket_cost() -> None:

Use Python’s input() function and an appropriate conditional statement.

2. Shuffling lists

Write two functions that shuffle a given list. Use a type variable so the functions work with lists containing any one type:

from typing import TypeVar

T = TypeVar("T")

def shuffle_in_place(items: list[T]) -> None:
    ...

def shuffled_copy(items: list[T]) -> list[T]:
    ...
  1. An in-place version that changes the original list and returns nothing.
  2. An out-of-place version that leaves the original list unchanged and returns a new shuffled list.

Do not use a built-in list-shuffling function or a built-in operation that swaps elements. You may use the random library, except for random.shuffle().

Document the algorithm used by each function. In your tests, print the original list after calling each function so that the difference between in-place and out-of-place behavior is clear. The Python list documentation may be helpful.

3. Fibonacci numbers

Write a recursive function that returns the nth Fibonacci number for a non-negative integer n:

def fib(n: int) -> int:

Use these base cases:

Implement two binary-search functions. Each function takes a query and a sorted list lst, then returns the index of the query or -1 if it is absent. Use type annotations like these:

def binary_search_iterative(query: int, values: list[int]) -> int:
    ...

def binary_search_recursive(query: int, values: list[int]) -> int:
    ...

Iterative version

Use a loop and maintain low and high indices:

  1. Compute the middle index.
  2. Compare the middle element with the query.
  3. If the query is smaller, move the high index down.
  4. If the query is larger, move the low index up.
  5. Repeat until you find the query or exhaust the search interval.

Recursive version

Use recursion and list slicing:

  1. Compare the query with the middle element.
  2. Return the middle element’s index when they match.
  3. Search the first half when the query is smaller.
  4. Search the second half when the query is larger.
  5. Include an appropriate base case for an unsuccessful search.

You may write a helper function. Recall that list slices use the following form:

lst[start_index:end_index:step_size]

For example:

words = ["hello", "welcome", "to", "data", "science"]
words[:2]   # ["hello", "welcome"]
words[2:]   # ["to", "data", "science"]
words[2:4]  # ["to", "data"]

In README.md, state the Big-O running time of each binary-search implementation and explain your answers in terms of the list length, n.

Part 2: Introduction to NumPy

NumPy provides arrays, matrices, and efficient mathematical operations. Complete the three typed functions in numpy_tutorial.py, then run main() to see their results alongside the provided demonstrations.

1. Array initialization

def create_random_3d_array() -> np.ndarray:

Return a NumPy array with shape (4, 6, 5). Fill every entry with a random integer in [0, 301).

2. Array slicing

def required_slices(
    array: np.ndarray,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:

Return these four slices, in this order:

  1. array[2]
  2. array[:, 1]
  3. array[:3, :2]
  4. array[2:4, :]

Before running the code, write the expected value of each slice in README.md.

3. Concatenation

def concatenate_arrays(
    array: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]:

The input will have shape (5, 4). Return:

  1. A (6, 4) array formed by appending the row [2, 4, 6, 8] along axis 0.
  2. A (5, 5) array formed by appending the column [0, 1, 2, 3, 4] along axis 1.

Study the provided invalid-concatenation example and explain in README.md why its shapes are incompatible. Also review the provided basic array operations and make sure their output is clear to you.

Part 3: Introduction to Matplotlib

Complete matplotlib_tutorial.py. You will create two figures: one from real data and one from mathematical functions.

1. Facebook users: scatter plot and linear model

The file data/facebook_users.csv contains two columns: year and the number of monthly active Facebook users worldwide during the fourth quarter, measured in millions. The data were obtained from Statista.

Keep the filename in your code exactly as data/facebook_users.csv. The Gradescope autograder includes its own copy of this dataset and places it at that relative path before running your program. Do not upload facebook_users.csv to Gradescope.

First, read the data one line at a time:

years: list[int] = []
users: list[int] = []

fb_file = open("data/facebook_users.csv", "r")
for line in fb_file:
    tokens = line.split(",")
    years.append(int(tokens[0]))
    users.append(int(tokens[1]))

print(years)
print(users)

Then read the same file as a NumPy array:

user_data: np.ndarray = np.loadtxt("data/facebook_users.csv", delimiter=",")
print(user_data)

Use NumPy slicing to separate the x- and y-values when using the second approach.

Create a scatter plot with plt.scatter():

plt.scatter(x_values, y_values, color="black")

Every submitted graph must include meaningful axis labels and a title:

plt.xlabel("x-axis label")
plt.ylabel("y-axis label")
plt.title("figure title")

Next, overlay predictions from this linear model:

y = -432342.27 + 215.39x

For each year, compute the predicted number of users and draw the predictions with plt.plot():

plt.plot(x_values, predicted_values, color="blue")

Create a figures folder if it does not already exist, then save the finished plot exactly as follows:

plt.savefig("figures/facebook_users.pdf", format="pdf")
plt.clf()

Remove or comment out plt.show() before saving; otherwise, you may save an empty figure.

2. Plotting two polynomials

Use these coefficient lists:

quad_coefs: list[int] = [0, 4, -1]       # y = 4x - x^2
cubic_coefs: list[int] = [2, 0, -2, 1]   # y = 2 - 2x^2 + x^3

Create a sequence of x-values with np.linspace(), then compute the corresponding y-values for both polynomials:

np.linspace(0, 5, 10)

Plot both curves on one graph. Adjust the number of x-values and observe how it affects the apparent smoothness of the curves. Record your observation in README.md.

Give each curve a label and add a legend:

plt.plot(x_values, y_values, label="curve label")
plt.legend()

Add axis labels and a title. You may use plt.xlim() or plt.ylim() to improve the visible range. Save the final figure exactly as follows:

plt.savefig("figures/quad_cubic.pdf", format="pdf")

Part 4: Classes, objects, and dictionaries

This portion is an independent review. Consult the linked Python documentation and ask questions during lab as needed.

The Student class

In Student.py, implement a Student class with these attributes:

Define a constructor:

def __init__(self, name: str, graduation_year: int, college: str) -> None:

All instance methods must include self as their first parameter.

Next, implement these methods with the exact signatures shown:

def add_course(self, course: str) -> None:
    ...

def drop_course(self, course: str) -> None:
    ...

def __str__(self) -> str:
    ...

Getters for the attributes are optional. Comment the class and test every method in main().

Dictionaries

Review the Python dictionary documentation. Using the student information supplied in the starter code, construct a dictionary whose keys are integer student ID numbers and whose values are Student objects.

Then complete these experiments:

  1. Add a student using a key that is already present. Print the dictionary and explain what happened in README.md.
  2. Add an entry with a new key but a Student value already stored under another key. Print the dictionary and explain what happened.
  3. Retrieve a student using a specific key.
  4. Remove a student using a specific key.

Python style requirements

Style is part of the grade for every lab. Follow PEP 8 and these course conventions:

  1. Begin every Python file with a triple-quoted header containing the author, date, and a short program description.
  2. Add concise comments before major blocks of code.
  3. Use meaningful names. Variables, functions, and methods use snake_case; classes use PascalCase.
  4. Add type annotations to every function parameter and return value. Add variable annotations when they clarify the intended type of an empty or otherwise ambiguous collection.
  5. Give every function a docstring describing its purpose, parameters, and return value. Do not repeat type information already expressed by annotations.
  6. Keep lines at or below 100 characters.
  7. Put executable code inside functions or classes, apart from imports and rare justified global constants.
  8. Organize imports below the file header. Alphabetize standard and third-party imports, keep imports from your own files separate, and avoid wildcard imports such as from module import *.

Example function documentation:

def fib(n: int) -> int:
    """Return the nth Fibonacci number.

    Args:
        n: A non-negative integer.

    Returns:
        The nth Fibonacci number.
    """

Submission checklist

Before submitting:

Submit one .zip file to the HW01 assignment on Gradescope. The archive must preserve this structure:

python_intro.py
numpy_tutorial.py
matplotlib_tutorial.py
Student.py
README.md
figures/facebook_users.pdf
figures/quad_cubic.pdf

Do not include data/facebook_users.csv in the submitted archive. Although the file is part of the starter materials for local testing, the Gradescope autograder supplies it automatically at data/facebook_users.csv when your code runs.

Name the archive LastName_FirstName_HW01.zip. After uploading, verify that Gradescope displays every required file.