Skip to content

Instantly share code, notes, and snippets.

@ahmedmoustafa
Created February 21, 2026 16:28
Show Gist options
  • Select an option

  • Save ahmedmoustafa/1c591f1327c86da4855497714e4b13bb to your computer and use it in GitHub Desktop.

Select an option

Save ahmedmoustafa/1c591f1327c86da4855497714e4b13bb to your computer and use it in GitHub Desktop.
dsci2410_spring2026_assignment01.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/ahmedmoustafa/1c591f1327c86da4855497714e4b13bb/dsci2410_spring2026_assignment01.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"id": "fb9d47e9",
"metadata": {
"id": "fb9d47e9"
},
"source": [
"# Assignment 1: Python Foundations\n",
"### DSCI 2410 — Fundamentals of Data Science II | Spring 2026\n",
"**The American University in Cairo**\n",
"\n",
"---\n",
"\n",
"> **Submission**\n",
">\n",
"> Work directly in this notebook. When finished, ensure all cells are executed and all outputs are visible, then download via **File → Download → Download .ipynb**.\n",
"> Name your file **`DSCI2410_STUDENTID_HW1.ipynb`** (e.g., `DSCI2410_900123456_HW1.ipynb`) and upload via the course portal.\n",
">\n",
"> **Due: March 3, 2026**\n",
"\n",
"> **Predict-before-run rule:** For problems that ask you to predict output, type your prediction into the designated Markdown cell **immediately above** the corresponding code cell, before executing it. Predictions entered after running receive **zero credit** for the prediction component, regardless of correctness.\n",
"\n",
"> **On written explanations:** All explanations must be in your own words. An answer that merely describes the observed result without naming the underlying Python mechanism receives no credit. Answers reproduced from external sources receive zero credit regardless of accuracy.\n",
"\n",
"> **Reference links** — for syntax reference only; consult *after* writing your predictions, not before:\n",
"> - Markdown syntax: https://www.markdownguide.org/cheat-sheet\n",
"> - Python built-in types: https://docs.python.org/3/library/stdtypes.html\n"
]
},
{
"cell_type": "markdown",
"id": "0547cb91",
"metadata": {
"id": "0547cb91"
},
"source": [
"---\n",
"## Part 1 — Python Basics, Types, and Operators *(20 points)*"
]
},
{
"cell_type": "markdown",
"id": "1451e98e",
"metadata": {
"id": "1451e98e"
},
"source": [
"### Problem 1.1 — Dynamic Typing: Predict the Output *(5 points)*\n",
"\n",
"Python is an **interpreted, dynamically typed language** ([Python Language Reference](https://docs.python.org/3/reference/)). A variable carries no fixed type annotation; its type is determined at runtime and can change as new values are assigned to it. This is a deliberate design choice that prioritises flexibility, but it introduces a type of bug that does not exist in statically typed languages — the interpreter cannot catch type mismatches until the problematic line actually executes.\n",
"\n",
"Study the code block below carefully. It runs as a **single, continuous unit**: variable values carry over from one statement to the next. In particular, `x` is reassigned between Lines A and B — it is an `int` at Line A and a `str` at Line B. Before running anything, record your predicted output for each labelled `print` statement in the Markdown cell below.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd16d3c3",
"metadata": {
"id": "bd16d3c3"
},
"outputs": [],
"source": [
"x = 10\n",
"y = \"3\"\n",
"z = x + int(y)\n",
"print(z, type(z)) # Line A\n",
"\n",
"x = str(x)\n",
"result = x * 2\n",
"print(result, type(result)) # Line B\n",
"\n",
"a = True\n",
"b = False\n",
"print(a + 1, b + 10) # Line C\n",
"\n",
"city = \"Cairo\"\n",
"print(city * 2 == \"CairoCairo\", type(city * 2)) # Line D\n"
]
},
{
"cell_type": "markdown",
"id": "7d4fba70",
"metadata": {
"id": "7d4fba70"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"| Line | Your Prediction |\n",
"|------|----------------|\n",
"| A — `print(z, type(z))` | |\n",
"| B — `print(result, type(result))` | |\n",
"| C — `print(a + 1, b + 10)` | |\n",
"| D — `print(city * 2 == \"CairoCairo\", type(city * 2))` | |\n",
"\n",
"**Explanations — fill in after running:**\n",
"\n",
"For each line: if your prediction was **incorrect**, explain what you misunderstood (1–2 sentences). If it was **correct**, explain *why* the output is what it is — name the specific Python rule.\n",
"\n",
"- **Line A:**\n",
"- **Line B:**\n",
"- **Line C:**\n",
"- **Line D:**\n",
"\n",
"> **Grading note:** $4 \\times 0.5$ points for committed predictions $+\\ 4 \\times 0.75$ points for explanations $= 5$ points total. A prediction is credited whether correct or not, provided it is written before execution.\n"
]
},
{
"cell_type": "markdown",
"id": "448216af",
"metadata": {
"id": "448216af"
},
"source": [
"---\n",
"### Problem 1.2 — Debugging: Four Common Errors *(5 points)*\n",
"\n",
"Your classmate Karim wrote a program to manage event registrations. His code contains **four independent bugs**, each violating a different rule from Lecture 1. The bugs are labelled so that you can treat each one in isolation.\n",
"\n",
"> **Cell isolation required.** Place each bug in its **own separate code cell** before testing. Running all four in a single cell halts execution at the first error, preventing you from observing the remaining three.\n",
"\n",
"For **each bug**, answer all three parts in the Markdown cell that follows its code cell:\n",
"\n",
"**(a)** State the **exact error type** produced (e.g., `TypeError`, `IndexError`, `NameError`).\n",
"\n",
"**(b)** Explain *why* this error occurs. What is Python doing here, and which rule does this code break?\n",
"\n",
"**(c)** Write the corrected version of the specific line(s) only — do not rewrite the entire block.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab533f3e",
"metadata": {
"id": "ab533f3e"
},
"outputs": [],
"source": [
"# Bug 1\n",
"registered = \"120\"\n",
"new_arrivals = 35\n",
"total = registered + new_arrivals\n",
"print(\"Total attendees:\", total)"
]
},
{
"cell_type": "markdown",
"id": "8c016d53",
"metadata": {
"id": "8c016d53"
},
"source": [
"**Bug 1 answers:**\n",
"- (a) Error type:\n",
"- (b) Why it occurs:\n",
"- (c) Fix:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2fe0b63c",
"metadata": {
"id": "2fe0b63c"
},
"outputs": [],
"source": [
"# Bug 2\n",
"event_name = \"TechSummit2026\"\n",
"event_name[0] = \"t\"\n",
"print(\"Updated name:\", event_name)"
]
},
{
"cell_type": "markdown",
"id": "b48b1a91",
"metadata": {
"id": "b48b1a91"
},
"source": [
"**Bug 2 answers:**\n",
"- (a) Error type:\n",
"- (b) Why it occurs:\n",
"- (c) Fix:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fe932925",
"metadata": {
"id": "fe932925"
},
"outputs": [],
"source": [
"# Bug 3\n",
"int = 42\n",
"print(int(\"99\"))"
]
},
{
"cell_type": "markdown",
"id": "24d2e101",
"metadata": {
"id": "24d2e101"
},
"source": [
"**Bug 3 answers:**\n",
"- (a) Error type:\n",
"- (b) Why it occurs:\n",
"- (c) Fix:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "606b2160",
"metadata": {
"id": "606b2160"
},
"outputs": [],
"source": [
"# Bug 4\n",
"scores = [88, 74, 91, 65]\n",
"last_score = scores[4]\n",
"print(\"Last score:\", last_score)"
]
},
{
"cell_type": "markdown",
"id": "4e98b1c0",
"metadata": {
"id": "4e98b1c0"
},
"source": [
"**Bug 4 answers:**\n",
"- (a) Error type:\n",
"- (b) Why it occurs:\n",
"- (c) Fix:"
]
},
{
"cell_type": "markdown",
"id": "b3374036",
"metadata": {
"id": "b3374036"
},
"source": [
"---\n",
"### Problem 1.3 — Arithmetic Operators: Variable Tracing *(4 points)*\n",
"\n",
"Python provides a set of arithmetic operators whose return types are not always immediately obvious. The critical distinction is between **true division**, which always produces a `float`, and **floor division**, which discards the fractional part and returns an `int` when both operands are integers ([Python built-in types](https://docs.python.org/3/library/stdtypes.html)).\n",
"\n",
"For two integers $a$ and $b$, the operators used in this problem are defined as follows:\n",
"\n",
"| Operator | Mathematical Form | Python Syntax | Description |\n",
"|----------|-----------------|---------------|-------------|\n",
"| True division | $a \\div b$ | `a / b` | Returns $\\frac{a}{b}$ as a `float` |\n",
"| Floor division | $\\lfloor a \\div b \\rfloor$ | `a // b` | Returns the largest integer $n$ such that $n \\leq \\frac{a}{b}$ |\n",
"| Modulo | $a \\bmod b$ | `a % b` | Returns the remainder $r$ where $a = b \\cdot q + r,\\ 0 \\leq r < b$ |\n",
"| Exponentiation | $a^{b}$ | `a ** b` | Returns $a$ raised to the power $b$ |\n",
"\n",
"**Your tracing table — fill in before running:**\n",
"\n",
"| Line | Variable | Value | Type |\n",
"|------|----------|-------|------|\n",
"| 1 | `c` | | |\n",
"| 2 | `d` | | |\n",
"| 3 | `e` | | |\n",
"| 4 | `f` | | |\n",
"| 5 | `g` | | |\n",
"| 6 | `h` | | |\n",
"\n",
"After completing the table, run the code cell below to verify. For any entry that was incorrect, add a brief comment identifying which rule you misunderstood.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "272b0045",
"metadata": {
"id": "272b0045"
},
"outputs": [],
"source": [
"a = 5\n",
"b = 2\n",
"c = a / b # Line 1\n",
"d = a // b # Line 2\n",
"e = a % b # Line 3\n",
"f = a ** b # Line 4\n",
"g = str(a) + str(b) # Line 5\n",
"h = (a > 3) and (b < 5) # Line 6\n",
"\n",
"print(f\"c = {c}, type: {type(c)}\")\n",
"print(f\"d = {d}, type: {type(d)}\")\n",
"print(f\"e = {e}, type: {type(e)}\")\n",
"print(f\"f = {f}, type: {type(f)}\")\n",
"print(f\"g = {g}, type: {type(g)}\")\n",
"print(f\"h = {h}, type: {type(h)}\")\n"
]
},
{
"cell_type": "markdown",
"id": "71255fe8",
"metadata": {
"id": "71255fe8"
},
"source": [
"**Corrections (if any):**"
]
},
{
"cell_type": "markdown",
"id": "d880bd9e",
"metadata": {
"id": "d880bd9e"
},
"source": [
"---\n",
"### Problem 1.4 — Logical Operators: Predict Before Running *(3 points)*\n",
"\n",
"For each expression below, determine whether it evaluates to `True` or `False` **without running it**. Record your answer and a one-sentence justification in the Markdown cell below, then run the code to confirm.\n",
"\n",
"> **Truthiness in Python.** Python evaluates any object in a Boolean context using its **truthiness** ([Python docs](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)). The following values are *falsy*: `False`, `0`, `0.0`, `\"\"`, `None`, and empty containers (`[]`, `{}`, `()`). All other values are *truthy*. This rule governs the behaviour of `not x` in Expression B below.\n",
"\n",
"> **Grading note:** 1 point per expression — prediction and explanation together.\n"
]
},
{
"cell_type": "markdown",
"id": "852fc53f",
"metadata": {
"id": "852fc53f"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"- **Expression A** — `print(not True or False)`: *(True / False)* — Explanation:\n",
"- **Expression B** — `print(not x and 5 > 3)` where `x = 0`: *(True / False)* — Explanation:\n",
"- **Expression C** — `print(\"Cairo\" in \"Visit Cairo in Summer\")`: *(True / False)* — Explanation:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b11fba1a",
"metadata": {
"id": "b11fba1a"
},
"outputs": [],
"source": [
"# Expression A\n",
"print(not True or False)\n",
"\n",
"# Expression B\n",
"x = 0\n",
"print(not x and 5 > 3)\n",
"\n",
"# Expression C\n",
"print(\"Cairo\" in \"Visit Cairo in Summer\")\n"
]
},
{
"cell_type": "markdown",
"id": "c15a2f28",
"metadata": {
"id": "c15a2f28"
},
"source": [
"---\n",
"### Problem 1.5 — String Methods: Predict and Apply *(3 points)*\n",
"\n",
"Fill in your predictions in the Markdown cell **before** running the code cell. For any prediction that was incorrect after running, add a comment identifying the specific rule you missed.\n"
]
},
{
"cell_type": "markdown",
"id": "9e9635f6",
"metadata": {
"id": "9e9635f6"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"| Expression | Your Prediction |\n",
"|------------|----------------|\n",
"| `s.strip()` | |\n",
"| `s.strip().title()` | |\n",
"| `s.strip().upper().replace(\"AND\", \"&\")` | |\n",
"| `len(s.strip())` | |\n",
"| `s.strip().split()` | |\n",
"\n",
"**Corrections (if any):**\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7f23d471",
"metadata": {
"id": "7f23d471"
},
"outputs": [],
"source": [
"s = \" world history and philosophy \"\n",
"\n",
"print(s.strip())\n",
"print(s.strip().title())\n",
"print(s.strip().upper().replace(\"AND\", \"&\"))\n",
"print(len(s.strip()))\n",
"print(s.strip().split())\n"
]
},
{
"cell_type": "markdown",
"id": "80497bea",
"metadata": {
"id": "80497bea"
},
"source": [
"---\n",
"## Part 2 — Lists and Copying *(30 points)*"
]
},
{
"cell_type": "markdown",
"id": "2c9bb161",
"metadata": {
"id": "2c9bb161"
},
"source": [
"### Problem 2.1 — Slicing: Predict and Reverse-Engineer *(8 points)*\n",
"\n",
"Python's slice notation provides a concise mechanism for extracting subsequences from any sequence type. For a list `data`, the general slice expression $\\text{data}[i\\, :\\, j\\, :\\, k]$ extracts every $k$-th element from index $i$ up to, but not including, index $j$ ([Python docs](https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range)). Negative indices count from the end of the sequence, and a negative step reverses the direction of traversal.\n"
]
},
{
"cell_type": "markdown",
"id": "fb0d709d",
"metadata": {
"id": "fb0d709d"
},
"source": [
"**Part A — Predict before running:**\n",
"\n",
"Fill the \"Your Prediction\" column *before* running, then complete \"Actual Output\" after.\n",
"\n",
"| Expression | Your Prediction | Actual Output |\n",
"|------------|----------------|---------------|\n",
"| `data[2:6]` | | |\n",
"| `data[-3:]` | | |\n",
"| `data[::3]` | | |\n",
"| `data[1:8:2]` | | |\n",
"| `data[::-1]` | | |\n",
"| `data[7:2:-1]` | | |\n",
"| `data[10:20]` | | |\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "487ee7ea",
"metadata": {
"id": "487ee7ea"
},
"outputs": [],
"source": [
"data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n",
"\n",
"print(data[2:6])\n",
"print(data[-3:])\n",
"print(data[::3])\n",
"print(data[1:8:2])\n",
"print(data[::-1])\n",
"print(data[7:2:-1])\n",
"print(data[10:20])\n"
]
},
{
"cell_type": "markdown",
"id": "1be805d3",
"metadata": {
"id": "1be805d3"
},
"source": [
"**Part B — Reverse engineering:**\n",
"\n",
"For each target output, write **one** slice expression on `data` that produces it exactly. No loops, no individual index operations.\n",
"\n",
"| Target Output | Your Slice Expression |\n",
"|---------------|-----------------------|\n",
"| `[100, 90, 80, 70, 60]` | |\n",
"| `[90, 70, 50, 30]` | |\n",
"| `[30]` | |\n",
"| `[10]` | |\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af5b4227",
"metadata": {
"id": "af5b4227"
},
"outputs": [],
"source": [
"data = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n",
"\n",
"# [100, 90, 80, 70, 60]\n",
"print()\n",
"\n",
"# [90, 70, 50, 30]\n",
"print()\n",
"\n",
"# [30]\n",
"print()\n",
"\n",
"# [10]\n",
"print()\n"
]
},
{
"cell_type": "markdown",
"id": "1f74d822",
"metadata": {
"id": "1f74d822"
},
"source": [
"**Part C — Conceptual explanation (3–4 sentences):**\n",
"\n",
"What happens when the stop index in a slice exceeds the list length — for example, `data[7:20]`? Contrast this with what happens when a single index exceeds the list length — for example, `data[20]`. Why does Python treat these two cases differently?\n",
"\n",
"*Your answer:*\n"
]
},
{
"cell_type": "markdown",
"id": "e72ef505",
"metadata": {
"id": "e72ef505"
},
"source": [
"---\n",
"### Problem 2.2 — `append()` vs. `extend()`: A Critical Difference *(4 points)*\n",
"\n",
"Two students both intend to merge two lists of city names into a single flat list.\n"
]
},
{
"cell_type": "markdown",
"id": "e7e9f385",
"metadata": {
"id": "e7e9f385"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"- **Dina's output:**\n",
"- **Yousef's output:**\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "86016a5e",
"metadata": {
"id": "86016a5e"
},
"outputs": [],
"source": [
"# Dina's code\n",
"cities_a = [\"Cairo\", \"Alexandria\", \"Giza\"]\n",
"cities_b = [\"Luxor\", \"Aswan\"]\n",
"cities_a.append(cities_b)\n",
"print(cities_a)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "51ab11ea",
"metadata": {
"id": "51ab11ea"
},
"outputs": [],
"source": [
"# Yousef's code\n",
"cities_a = [\"Cairo\", \"Alexandria\", \"Giza\"]\n",
"cities_b = [\"Luxor\", \"Aswan\"]\n",
"cities_a.extend(cities_b)\n",
"print(cities_a)\n"
]
},
{
"cell_type": "markdown",
"id": "b2bb1ffc",
"metadata": {
"id": "b2bb1ffc"
},
"source": [
"**(c)** Dina's result is not what she intended. Explain why `append()` produces a different structure than `extend()`. Focus on what each method does with its argument.\n",
"\n",
"*Your explanation:*\n",
"\n",
"**(d)** Write a third version that produces the correct flat list using **neither** `append()` nor `extend()`:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a797ba7",
"metadata": {
"id": "6a797ba7"
},
"outputs": [],
"source": [
"# (d) Your solution\n",
"cities_a = [\"Cairo\", \"Alexandria\", \"Giza\"]\n",
"cities_b = [\"Luxor\", \"Aswan\"]\n"
]
},
{
"cell_type": "markdown",
"id": "7687613b",
"metadata": {
"id": "7687613b"
},
"source": [
"---\n",
"### Problem 2.3 — Shallow Copy: The Silent Bug *(10 points)*\n",
"\n",
"When a list contains mutable objects — such as other lists — copying the outer container does not automatically copy the inner objects. The result is a **shallow copy**: a new outer list whose elements are references to the same objects as the original ([`copy` module docs](https://docs.python.org/3/library/copy.html)). Modifying a nested object through either the original or the copy affects both, because they share the same reference.\n",
"\n",
"The diagram below shows the memory model after `backup = original.copy()`. Both outer list objects are independent; the inner sublists are shared.\n"
]
},
{
"cell_type": "markdown",
"id": "141153c9",
"metadata": {
"id": "141153c9"
},
"source": [
"Because both outer lists reference the same inner objects, modifying a nested element through `backup` also changes what `original` sees. Appending a new element to `backup`, however, only updates `backup`'s own reference table — `original` is unaffected.\n",
"\n",
"**Part A — Predict the behavior:**\n"
]
},
{
"cell_type": "markdown",
"id": "32c0a18e",
"metadata": {
"id": "32c0a18e"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"- Value of `original` after `backup[0][1] = 999`:\n",
"- Value of `original` after `backup.append([\"Ziad\", 65])`:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f4d97466",
"metadata": {
"id": "f4d97466"
},
"outputs": [],
"source": [
"original = [[\"Sara\", 88], [\"Tarek\", 92], [\"Mona\", 75]]\n",
"backup = original.copy()\n",
"\n",
"backup[0][1] = 999\n",
"print(\"After backup[0][1] = 999:\")\n",
"print(\" original:\", original)\n",
"print(\" backup: \", backup)\n",
"\n",
"backup.append([\"Ziad\", 65])\n",
"print(\"\\nAfter backup.append(['Ziad', 65]):\")\n",
"print(\" original:\", original)\n",
"print(\" backup: \", backup)\n"
]
},
{
"cell_type": "markdown",
"id": "d6ab2e70",
"metadata": {
"id": "d6ab2e70"
},
"source": [
"**Part B — Explain the memory model (3–4 sentences):**\n",
"\n",
"Explain *why* modifying `backup[0][1]` affects `original`, but `backup.append(...)` does **not**. Make sure to use the terms **shared references** and **outer container** in your answer.\n",
"\n",
"*Your explanation:*\n",
"\n",
"---\n",
"\n",
"**Part C — Diagnose and fix a real bug:**\n",
"\n",
"A developer wrote the code below to create an independent backup of a product catalog before applying a sale update.\n"
]
},
{
"cell_type": "markdown",
"id": "4d18331d",
"metadata": {
"id": "4d18331d"
},
"source": [
"**Part C(i) — Predict both print outputs before running:**\n",
"\n",
"- `Original colours for Laptop`:\n",
"- `Sale colours for Laptop`:\n",
"\n",
"**Part C(ii)** — Is this the intended behavior? Explain in 2 sentences:\n",
"\n",
"**Part C(iii)** — Fix (write corrected code in the cell below):\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b4b59558",
"metadata": {
"id": "b4b59558"
},
"outputs": [],
"source": [
"import copy\n",
"\n",
"catalog = [\n",
" [\"Laptop\", 1200, [\"black\", \"silver\"]],\n",
" [\"Phone\", 800, [\"black\", \"white\", \"blue\"]],\n",
"]\n",
"\n",
"sale_copy = catalog.copy()\n",
"sale_copy[0][2].append(\"red\") # add a new colour option for the sale\n",
"\n",
"print(\"Original colours for Laptop:\", catalog[0][2])\n",
"print(\"Sale colours for Laptop: \", sale_copy[0][2])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5b6c3b78",
"metadata": {
"id": "5b6c3b78"
},
"outputs": [],
"source": [
"# (iii) Fixed version\n",
"import copy\n",
"\n",
"catalog = [\n",
" [\"Laptop\", 1200, [\"black\", \"silver\"]],\n",
" [\"Phone\", 800, [\"black\", \"white\", \"blue\"]],\n",
"]\n",
"\n",
"# Your fix here\n"
]
},
{
"cell_type": "markdown",
"id": "9dc3ff8e",
"metadata": {
"id": "9dc3ff8e"
},
"source": [
"---\n",
"### Problem 2.4 — Nested Lists: Access and Modification *(8 points)*\n",
"\n",
"Each row in `gradebook` has the structure `[student_name, [quiz_scores], final_exam_score]`.\n",
"\n",
"> **Sequential dependency.** Complete parts **(a)–(f) in order**. Parts (c), (e), and (f) each depend on the state of `gradebook` left by the preceding parts. Running them out of order will produce incorrect results.\n",
"\n",
"Write code for each task in the cells provided and include a brief inline comment explaining your approach.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "deddb6bc",
"metadata": {
"id": "deddb6bc"
},
"outputs": [],
"source": [
"gradebook = [\n",
" [\"Amira\", [85, 90, 78], 88],\n",
" [\"Bassem\", [70, 65, 80], 73],\n",
" [\"Salma\", [92, 88, 95], 91],\n",
" [\"Dawoud\", [60, 72, 68], 65],\n",
"]\n",
"\n",
"# (a) Print Salma's final exam score — index access only, no loops\n",
"\n",
"# (b) Print Bassem's second quiz score — index access only\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "13746680",
"metadata": {
"id": "13746680"
},
"outputs": [],
"source": [
"# (c) Amira sat a makeup quiz and scored 95.\n",
"# Add this score to her quiz list IN PLACE — do not create a new list."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "142c0e4a",
"metadata": {
"id": "142c0e4a"
},
"outputs": [],
"source": [
"# (d) Calculate Dawoud's quiz average.\n",
"# Store in dawoud_avg, print rounded to 2 decimal places.\n",
"# Use sum() and len() — no loops."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2ad3c30e",
"metadata": {
"id": "2ad3c30e"
},
"outputs": [],
"source": [
"# (e) Add the new student [\"Eman\", [88, 91, 84], 89] to the gradebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c0b27d7",
"metadata": {
"id": "1c0b27d7"
},
"outputs": [],
"source": [
"# (f) Print the total number of students in the gradebook\n",
"# and the number of quiz scores Amira now has."
]
},
{
"cell_type": "markdown",
"id": "60372eb8",
"metadata": {
"id": "60372eb8"
},
"source": [
"---\n",
"## Part 3 — Dictionaries, Tuples, and Sets *(30 points)*"
]
},
{
"cell_type": "markdown",
"id": "f664f091",
"metadata": {
"id": "f664f091"
},
"source": [
"### Problem 3.1 — Dictionary Traps *(8 points)*\n",
"\n",
"**Part A — `KeyError` vs. `get()`:**\n",
"\n",
"Predict the output of each line before running. Treat each line as **independent** — assume the dictionary is intact and that previous lines completed successfully.\n"
]
},
{
"cell_type": "markdown",
"id": "baadef99",
"metadata": {
"id": "baadef99"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"| Line | What happens? *(produces output / raises error / prints None)* |\n",
"|------|----------------------------------------------------------------|\n",
"| Line 1 — `print(student[\"name\"])` | |\n",
"| Line 2 — `print(student[\"major\"])` | |\n",
"| Line 3 — `print(student.get(\"major\"))` | |\n",
"| Line 4 — `print(student.get(\"major\", \"Undeclared\"))` | |\n",
"\n",
"For Line 2 — exact error type and why it is raised:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "709186eb",
"metadata": {
"id": "709186eb"
},
"outputs": [],
"source": [
"student = {\"name\": \"Lina\", \"gpa\": 3.6, \"year\": 2}\n",
"\n",
"print(student[\"name\"]) # Line 1\n",
"# print(student[\"major\"]) # Line 2 — uncomment to test in isolation\n",
"print(student.get(\"major\")) # Line 3\n",
"print(student.get(\"major\", \"Undeclared\")) # Line 4\n"
]
},
{
"cell_type": "markdown",
"id": "6b1044a1",
"metadata": {
"id": "6b1044a1"
},
"source": [
"---\n",
"**Part B — A subtle mutation bug:**"
]
},
{
"cell_type": "markdown",
"id": "ea959938",
"metadata": {
"id": "ea959938"
},
"source": [
"**Your prediction — fill in before running:**\n",
"\n",
"- Value of `team_a` after the code runs:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f651b70c",
"metadata": {
"id": "f651b70c"
},
"outputs": [],
"source": [
"team_a = {\"keyboard\": 15, \"mouse\": 30, \"monitor\": 8}\n",
"team_b = {\"monitor\": 5, \"webcam\": 12, \"headset\": 20}\n",
"\n",
"merged = team_a\n",
"merged.update(team_b)\n",
"\n",
"print(\"team_a:\", team_a)\n",
"print(\"merged:\", merged)\n"
]
},
{
"cell_type": "markdown",
"id": "ac0203d3",
"metadata": {
"id": "ac0203d3"
},
"source": [
"**(ii)** Is this the intended behavior if the goal was to keep `team_a` unchanged? Explain in 2 sentences.\n",
"\n",
"*Your answer:*"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab61140a",
"metadata": {
"id": "ab61140a"
},
"outputs": [],
"source": [
"# (iii) Rewrite the merge so that team_a remains unchanged.\n",
"# merged should contain the combined inventory with team_b values taking priority.\n",
"\n",
"team_a = {\"keyboard\": 15, \"mouse\": 30, \"monitor\": 8}\n",
"team_b = {\"monitor\": 5, \"webcam\": 12, \"headset\": 20}\n",
"\n",
"# Your fix here\n",
"\n",
"print(\"team_a:\", team_a)\n",
"print(\"merged:\", merged)\n"
]
},
{
"cell_type": "markdown",
"id": "7ddd9cdf",
"metadata": {
"id": "7ddd9cdf"
},
"source": [
"---\n",
"\n",
"**Part C — Understanding `setdefault()` behavior:**\n",
"\n",
"The `setdefault(key, default)` method inserts a key with a default value only if that key is not already present. Its behaviour when a key *already exists* is the critical point this task tests.\n",
"\n",
"Study the code below carefully. **Without running it**, predict the value of `inventory` after each step.\n"
]
},
{
"cell_type": "markdown",
"id": "4346cb9d",
"metadata": {
"id": "4346cb9d"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"| After | `inventory` |\n",
"|-------|-------------|\n",
"| Call 1 | |\n",
"| Call 2 | |\n",
"| after `inventory[\"keyboard\"] = 15` | |\n",
"| Call 3 | |\n",
"| Call 4 | |\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "595390bb",
"metadata": {
"id": "595390bb"
},
"outputs": [],
"source": [
"inventory = {}\n",
"\n",
"inventory.setdefault(\"keyboard\", 0) # Call 1\n",
"print(\"After Call 1:\", inventory)\n",
"\n",
"inventory.setdefault(\"mouse\", 0) # Call 2\n",
"print(\"After Call 2:\", inventory)\n",
"\n",
"inventory[\"keyboard\"] = 15 # direct assignment\n",
"print(\"After direct assignment:\", inventory)\n",
"\n",
"inventory.setdefault(\"keyboard\", 0) # Call 3\n",
"print(\"After Call 3:\", inventory)\n",
"\n",
"inventory.setdefault(\"monitor\", 8) # Call 4\n",
"print(\"After Call 4:\", inventory)\n"
]
},
{
"cell_type": "markdown",
"id": "7b0ddd79",
"metadata": {
"id": "7b0ddd79"
},
"source": [
"What does `setdefault()` do when the key is already present, and why is this property useful when building frequency counts or grouped dictionaries? (2 sentences)\n",
"\n",
"*Your answer:*\n"
]
},
{
"cell_type": "markdown",
"id": "56a58cd9",
"metadata": {
"id": "56a58cd9"
},
"source": [
"---\n",
"### Problem 3.2 — Tuples: Immutability and Purpose *(7 points)*\n",
"\n",
"**Part A — The single-element trap:**\n"
]
},
{
"cell_type": "markdown",
"id": "8c343080",
"metadata": {
"id": "8c343080"
},
"source": [
"**Your predictions — fill in before running:**\n",
"\n",
"| Variable | Value | Type |\n",
"|----------|-------|------|\n",
"| `t1` | | |\n",
"| `t2` | | |\n",
"| `t3` | | |\n",
"| `t4` | | |\n",
"| `t5` | | |\n",
"\n",
"Why is `t1` not a tuple? (1–2 sentences):\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7c5300d7",
"metadata": {
"id": "7c5300d7"
},
"outputs": [],
"source": [
"t1 = (42)\n",
"t2 = (42,)\n",
"t3 = (\"Cairo\",)\n",
"t4 = ()\n",
"t5 = (1, 2, 3)\n",
"\n",
"print(t1, type(t1))\n",
"print(t2, type(t2))\n",
"print(t3, type(t3))\n",
"print(t4, type(t4))\n",
"print(t5, type(t5))\n"
]
},
{
"cell_type": "markdown",
"id": "0022ead2",
"metadata": {
"id": "0022ead2"
},
"source": [
"---\n",
"**Part B — Tuples as dictionary keys:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3418bab4",
"metadata": {
"id": "3418bab4"
},
"outputs": [],
"source": [
"population = {}\n",
"# population[[\"Egypt\", \"Cairo\"]] = 21_323_000 # Attempt 1 — uncomment to test\n",
"population[(\"Egypt\", \"Cairo\")] = 21_323_000 # Attempt 2\n",
"\n",
"print(population)\n"
]
},
{
"cell_type": "markdown",
"id": "69a66053",
"metadata": {
"id": "69a66053"
},
"source": [
"**(i)** Which attempt raises a `TypeError` and why?\n",
"\n",
"*Your answer:*\n",
"\n",
"**(ii)** What does **hashability** mean and why is it required for dictionary keys and set elements? (2–3 sentences, [Python docs](https://docs.python.org/3/library/stdtypes.html#hashing-of-numeric-types))\n",
"\n",
"*Your answer:*\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0c853f7b",
"metadata": {
"id": "0c853f7b"
},
"outputs": [],
"source": [
"# (iii) Can a tuple containing a list be used as a dictionary key?\n",
"# Test your hypothesis and explain the result.\n",
"\n",
"d = {}\n",
"# Your test here"
]
},
{
"cell_type": "markdown",
"id": "09dc9ae4",
"metadata": {
"id": "09dc9ae4"
},
"source": [
"**Part C — When to choose a tuple:**\n",
"\n",
"Describe **two concrete, everyday scenarios** where you would deliberately choose a tuple over a list. In each case, name the specific property that makes it the right choice — do not simply state \"it is immutable.\"\n",
"\n",
"- **Scenario 1:**\n",
"- **Scenario 2:**\n"
]
},
{
"cell_type": "markdown",
"id": "300977fa",
"metadata": {
"id": "300977fa"
},
"source": [
"---\n",
"### Problem 3.3 — Sets: Order, Operations, and Hashability *(8 points)*\n",
"\n",
"**Part A — The ordering misconception:**\n"
]
},
{
"cell_type": "markdown",
"id": "904a8feb",
"metadata": {
"id": "904a8feb"
},
"source": [
"**Before running — answer both questions:**\n",
"\n",
"**(i)** How many elements will the set contain? Explain why:\n",
"\n",
"**(ii)** Can you predict the exact printed order? Explain why or why not, and name one type of task where this property makes sets the *wrong* choice:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6b422ecb",
"metadata": {
"id": "6b422ecb"
},
"outputs": [],
"source": [
"s = {3, 1, 4, 1, 5, 9, 2, 6, 5}\n",
"print(s)\n",
"print(len(s))\n"
]
},
{
"cell_type": "markdown",
"id": "8ae2ff9d",
"metadata": {
"id": "8ae2ff9d"
},
"source": [
"---\n",
"**Part B — Set operations:**\n",
"\n",
"Three bookshops report which genres they currently stock. Write **one Python expression** (no loops) for each query and print the result with a descriptive label."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a7156594",
"metadata": {
"id": "a7156594"
},
"outputs": [],
"source": [
"shop_cairo = {\"Fiction\", \"History\", \"Philosophy\", \"Poetry\", \"Biography\"}\n",
"shop_alex = {\"Fiction\", \"History\", \"Science\", \"Poetry\", \"Self-Help\"}\n",
"shop_luxor = {\"Fiction\", \"Travel\", \"Philosophy\", \"Science\", \"History\"}\n",
"\n",
"# (a) Genres stocked by all three shops\n",
"print(\"(a) All three:\", )\n",
"\n",
"# (b) Genres stocked by at least one shop\n",
"print(\"(b) At least one:\", )\n",
"\n",
"# (c) Genres carried by Cairo but NOT by either of the other two\n",
"print(\"(c) Cairo only:\", )\n",
"\n",
"# (d) Genres carried by exactly one of Cairo or Alexandria, but not both\n",
"print(\"(d) Cairo XOR Alexandria:\", )\n",
"\n",
"# (e) Count of genres unique to Luxor (not stocked by Cairo or Alexandria)\n",
"print(\"(e) Luxor unique count:\", )\n"
]
},
{
"cell_type": "markdown",
"id": "4fc1f322",
"metadata": {
"id": "4fc1f322"
},
"source": [
"---\n",
"**Part C — Hashability:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2ecceee3",
"metadata": {
"id": "2ecceee3"
},
"outputs": [],
"source": [
"# (i) Run this to observe the TypeError, then explain below.\n",
"# waypoints = {[30.06, 31.24], [29.95, 31.13], [31.20, 29.92]}"
]
},
{
"cell_type": "markdown",
"id": "f34e013c",
"metadata": {
"id": "f34e013c"
},
"source": [
"**(i)** Explanation (1–2 sentences):\n",
"\n",
"*Your answer:*"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ea48baae",
"metadata": {
"id": "ea48baae"
},
"outputs": [],
"source": [
"# (ii) Fixed version\n"
]
},
{
"cell_type": "markdown",
"id": "bd716e68",
"metadata": {
"id": "bd716e68"
},
"source": [
"---\n",
"### Problem 3.4 — Choosing the Right Data Structure *(7 points)*\n",
"\n",
"Selecting the right data structure is a basic design decision. Each of Python's four core containers has its own set of properties and trade-offs: order, mutability, uniqueness, and key-based access. The decision guide below summarises the selection logic (Ramalho, *Fluent Python*, 2022):\n"
]
},
{
"cell_type": "markdown",
"id": "3f66b983",
"metadata": {
"id": "3f66b983"
},
"source": [
"For each scenario below, choose the **single most appropriate** structure (`list`, `dict`, `tuple`, or `set`) and:\n",
"\n",
"- Justify your choice in **2–3 sentences**, referencing the specific property that makes it the right fit.\n",
"- Write a **5–8 line code snippet** that uses the structure to perform a meaningful operation on the data — not merely to store it."
]
},
{
"cell_type": "markdown",
"id": "5447a8fa",
"metadata": {
"id": "5447a8fa"
},
"source": [
"**(a)** A streaming service tracks which user IDs have already been sent a promotional email today, to avoid duplicates. The collection is checked millions of times per day.\n",
"\n",
"- **Choice:**\n",
"- **Justification:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eb39c58e",
"metadata": {
"id": "eb39c58e"
},
"outputs": [],
"source": [
"# (a) Code snippet\n"
]
},
{
"cell_type": "markdown",
"id": "b8800ea4",
"metadata": {
"id": "b8800ea4"
},
"source": [
"**(b)** A weather application stores daily temperature readings in the order they arrive from a sensor. The same value can appear multiple times, and the order must be preserved for trend analysis.\n",
"\n",
"- **Choice:**\n",
"- **Justification:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9ab9bf96",
"metadata": {
"id": "9ab9bf96"
},
"outputs": [],
"source": [
"# (b) Code snippet\n"
]
},
{
"cell_type": "markdown",
"id": "9d5102f4",
"metadata": {
"id": "9d5102f4"
},
"source": [
"**(c)** A language-learning app stores each English word mapped to its Arabic translation. Users look up translations by English word.\n",
"\n",
"- **Choice:**\n",
"- **Justification:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "051fab92",
"metadata": {
"id": "051fab92"
},
"outputs": [],
"source": [
"# (c) Code snippet\n"
]
},
{
"cell_type": "markdown",
"id": "2b6d4b92",
"metadata": {
"id": "2b6d4b92"
},
"source": [
"**(d)** A configuration file holds three fixed values for a database connection — hostname, port number, and database name — that must not change during program execution, and and are passed together as one argument to connection functions.\n",
"\n",
"- **Choice:**\n",
"- **Justification:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a0c4193a",
"metadata": {
"id": "a0c4193a"
},
"outputs": [],
"source": [
"# (d) Code snippet\n"
]
},
{
"cell_type": "markdown",
"id": "b170980e",
"metadata": {
"id": "b170980e"
},
"source": [
"---\n",
"## Part 4 — Integration: Online Course Platform *(20 points)*\n",
"\n",
"The dataset below represents five students enrolled on an online learning platform. Each record contains a list of completed courses, a home city, and a set of interests. Two design decisions in this dataset are worth noting before you begin: courses are stored as **tuples** and interests as **sets**. Task 4.1 asks you to justify both.\n",
"\n",
"Because loops have not yet been covered, the derived lookup structures — `course_index` and `interest_courses` — are provided as pre-built data in the second code cell below. Tasks 4.2, 4.3, and 4.4 work directly with these structures using only the dictionary and set operations from Lectures 1 and 2.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2171e9ba",
"metadata": {
"id": "2171e9ba"
},
"outputs": [],
"source": [
"students = [\n",
" {\n",
" \"id\": \"STU-001\",\n",
" \"name\": \"Nour Hassan\",\n",
" \"city\": \"Cairo\",\n",
" \"courses\": [(\"HIS101\", \"World History\"),\n",
" (\"PHI301\", \"Critical Thinking & Philosophy\"),\n",
" (\"LIT201\", \"Introduction to Literature\")],\n",
" \"interests\": {\"history\", \"philosophy\"},\n",
" },\n",
" {\n",
" \"id\": \"STU-002\",\n",
" \"name\": \"Omar Farouk\",\n",
" \"city\": \"Alexandria\",\n",
" \"courses\": [(\"LIT201\", \"Introduction to Literature\"),\n",
" (\"COM101\", \"Public Speaking & Communication\"),\n",
" (\"SOC201\", \"Introduction to Sociology\")],\n",
" \"interests\": {\"literature\", \"history\"},\n",
" },\n",
" {\n",
" \"id\": \"STU-003\",\n",
" \"name\": \"Layla Naguib\",\n",
" \"city\": \"Mansoura\",\n",
" \"courses\": [(\"PHI301\", \"Critical Thinking & Philosophy\"),\n",
" (\"PSY301\", \"Introduction to Psychology\"),\n",
" (\"HIS101\", \"World History\")],\n",
" \"interests\": {\"philosophy\", \"psychology\"},\n",
" },\n",
" {\n",
" \"id\": \"STU-004\",\n",
" \"name\": \"Khaled Mostafa\",\n",
" \"city\": \"Cairo\",\n",
" \"courses\": [(\"ECO201\", \"Principles of Economics\"),\n",
" (\"PHI301\", \"Critical Thinking & Philosophy\"),\n",
" (\"SOC201\", \"Introduction to Sociology\")],\n",
" \"interests\": {\"economics\", \"philosophy\"},\n",
" },\n",
" {\n",
" \"id\": \"STU-005\",\n",
" \"name\": \"Hana El-Gamal\",\n",
" \"city\": \"Luxor\",\n",
" \"courses\": [(\"PSY301\", \"Introduction to Psychology\"),\n",
" (\"HIS101\", \"World History\"),\n",
" (\"LIT201\", \"Introduction to Literature\")],\n",
" \"interests\": {\"psychology\", \"history\"},\n",
" },\n",
"]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d32b99da",
"metadata": {
"id": "d32b99da"
},
"outputs": [],
"source": [
"# Pre-built lookup structures — run this cell before starting the tasks\n",
"\n",
"# Maps each course code to the set of student IDs enrolled in it\n",
"course_index = {\n",
" \"HIS101\": {\"STU-001\", \"STU-003\", \"STU-005\"},\n",
" \"PHI301\": {\"STU-001\", \"STU-003\", \"STU-004\"},\n",
" \"LIT201\": {\"STU-001\", \"STU-002\", \"STU-005\"},\n",
" \"COM101\": {\"STU-002\"},\n",
" \"SOC201\": {\"STU-002\", \"STU-004\"},\n",
" \"PSY301\": {\"STU-003\", \"STU-005\"},\n",
" \"ECO201\": {\"STU-004\"},\n",
"}\n",
"\n",
"# Course codes for each student, extracted from their tuples\n",
"courses_001 = {\"HIS101\", \"PHI301\", \"LIT201\"}\n",
"courses_002 = {\"LIT201\", \"COM101\", \"SOC201\"}\n",
"courses_003 = {\"PHI301\", \"PSY301\", \"HIS101\"}\n",
"courses_004 = {\"ECO201\", \"PHI301\", \"SOC201\"}\n",
"courses_005 = {\"PSY301\", \"HIS101\", \"LIT201\"}\n",
"\n",
"# Maps each interest to the set of course codes completed by students holding it\n",
"interest_courses = {\n",
" \"history\": {\"HIS101\", \"PHI301\", \"LIT201\", \"COM101\", \"SOC201\", \"PSY301\"},\n",
" \"philosophy\": {\"HIS101\", \"PHI301\", \"LIT201\", \"PSY301\", \"ECO201\", \"SOC201\"},\n",
" \"literature\": {\"LIT201\", \"COM101\", \"SOC201\"},\n",
" \"psychology\": {\"PHI301\", \"PSY301\", \"HIS101\", \"LIT201\"},\n",
" \"economics\": {\"ECO201\", \"PHI301\", \"SOC201\"},\n",
"}\n"
]
},
{
"cell_type": "markdown",
"id": "cadae4bb",
"metadata": {
"id": "cadae4bb"
},
"source": [
"---\n",
"**Task 4.1 — Design Rationale *(3 pts)*:**\n",
"\n",
"**(a)** Each student's courses are stored as a list of **tuples** rather than a list of lists. Explain in 2–3 sentences why tuples are the more appropriate choice for a course entry. What specific property makes them the right fit?\n",
"\n",
"*Your answer:*\n",
"\n",
"**(b)** Each student's interests are stored as a **set** rather than a list. What problem does this solve? Then write a **one-line Python expression** in the cell below demonstrating a query that becomes more natural because interests is a set.\n",
"\n",
"*Your explanation:*\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a81a25cd",
"metadata": {
"id": "a81a25cd"
},
"outputs": [],
"source": [
"# (b) One-line expression demonstrating the advantage of interests being a set\n"
]
},
{
"cell_type": "markdown",
"id": "0222558a",
"metadata": {
"id": "0222558a"
},
"source": [
"---\n",
"**Task 4.2 — Working with the Enrollment Index *(4 pts)*:**\n",
"\n",
"**(a)** Look up and print the set of student IDs enrolled in `\"PHI301\"`.\n",
"\n",
"**(b)** A new student, STU-006, just enrolled in `\"HIS101\"`. Update `course_index` to reflect this using `setdefault()`. Print the updated entry for `\"HIS101\"` to confirm.\n",
"\n",
"**(c)** Print the **number of courses** that have exactly one student enrolled.\n",
"\n",
"**(d)** STU-006 also enrolled in a brand new course `\"ART101\"` not yet in the index. Add this entry correctly using `setdefault()` and print the updated `course_index`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4ee9632d",
"metadata": {
"id": "4ee9632d"
},
"outputs": [],
"source": [
"# (a) Students enrolled in PHI301\n",
"\n",
"# (b) Add STU-006 to HIS101 using setdefault()\n",
"\n",
"# (c) Number of courses with exactly one student enrolled\n",
"\n",
"# (d) Add ART101 for STU-006 using setdefault()\n"
]
},
{
"cell_type": "markdown",
"id": "a10729ab",
"metadata": {
"id": "a10729ab"
},
"source": [
"---\n",
"**Task 4.3 — Set Operations on Course Data *(4 pts)*:**\n",
"\n",
"**(a)** Find the course codes taken by STU-001 but **not** by STU-002. Use a single set operation. Print the result.\n",
"\n",
"**(b)** Find the course codes taken by **both** STU-001 and STU-003. Print the result.\n",
"\n",
"**(c)** Find the course codes taken by **at least one** of STU-002, STU-004, or STU-005. Print the result.\n",
"\n",
"**(d)** Find course codes that appear in `course_index` with **3 or more** enrolled students. Use dictionary access and `len()` — no loops. Store the result as `popular_courses` and print it.\n",
"\n",
"> **Hint for (d):** There are only seven courses in `course_index`. You can check each one directly without a loop.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab417263",
"metadata": {
"id": "ab417263"
},
"outputs": [],
"source": [
"# (a) Courses taken by STU-001 but not STU-002\n",
"\n",
"# (b) Courses taken by both STU-001 and STU-003\n",
"\n",
"# (c) Courses taken by at least one of STU-002, STU-004, STU-005\n",
"\n",
"# (d) Courses with 3 or more enrolled students (no loops)\n",
"popular_courses = set()\n",
"# Your code here\n",
"print(\"Popular courses:\", popular_courses)\n"
]
},
{
"cell_type": "markdown",
"id": "7e5418e5",
"metadata": {
"id": "7e5418e5"
},
"source": [
"---\n",
"**Task 4.4 — Interest-Based Course Associations *(5 pts)*:**\n",
"\n",
"**(a)** Find the course codes associated with **both** `\"history\"` and `\"philosophy\"`. Use a set operation and print the result.\n",
"\n",
"**(b)** Find the course codes associated with `\"history\"` but **not** `\"philosophy\"`. Print the result.\n",
"\n",
"**(c)** Find the course codes associated with **at least one** of `\"psychology\"` or `\"economics\"`. Print the result.\n",
"\n",
"**(d)** How many distinct course codes appear across **all** interests combined? Compute this using set union and `len()` — no loops. Print the result.\n",
"\n",
"**(e)** Which interests share **at least one** course code with `\"literature\"`? For each interest other than `\"literature\"` itself, check whether its course set intersects with `interest_courses[\"literature\"]`. Write one expression per interest — no loops.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a1ad44e9",
"metadata": {
"id": "a1ad44e9"
},
"outputs": [],
"source": [
"# (a) history AND philosophy\n",
"\n",
"# (b) history but NOT philosophy\n",
"\n",
"# (c) psychology OR economics\n",
"\n",
"# (d) Total distinct course codes across all interests\n",
"\n",
"# (e) Which interests overlap with \"literature\"?\n",
"print(\"history overlaps:\", )\n",
"print(\"philosophy overlaps:\", )\n",
"print(\"psychology overlaps:\", )\n",
"print(\"economics overlaps:\", )\n"
]
},
{
"cell_type": "markdown",
"id": "d29331f0",
"metadata": {
"id": "d29331f0"
},
"source": [
"---\n",
"**Task 4.5 — Safe Copy Before Modification *(4 pts)*:**\n",
"\n",
"**(a)** Nada proposes `backup = students.copy()`. In 3–4 sentences, explain whether this is sufficient and what could go wrong if you subsequently modify `backup[0][\"courses\"]` or `backup[0][\"interests\"]` ([`copy` module docs](https://docs.python.org/3/library/copy.html)).\n",
"\n",
"*Your answer:*\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b293bc2a",
"metadata": {
"id": "b293bc2a"
},
"outputs": [],
"source": [
"# (b) Create a fully independent backup of students.\n",
"# Add a new interest to backup[0][\"interests\"] and verify\n",
"# that students[0][\"interests\"] is unchanged.\n",
"\n",
"import copy\n",
"\n",
"# Your fix here\n",
"\n",
"print(\"backup interests: \", backup[0][\"interests\"])\n",
"print(\"original interests:\", students[0][\"interests\"])\n"
]
},
{
"cell_type": "markdown",
"id": "a8efb6fe",
"metadata": {
"id": "a8efb6fe"
},
"source": [
"---\n",
"## Grading Rubric\n",
"\n",
"| Part | Problems | Points |\n",
"|------|----------|--------|\n",
"| 1 — Python Basics, Types, and Operators | P1.1 (5) + P1.2 (5) + P1.3 (4) + P1.4 (3) + P1.5 (3) | 20 |\n",
"| 2 — Lists and Copying | P2.1 (8) + P2.2 (4) + P2.3 (10) + P2.4 (8) | 30 |\n",
"| 3 — Dictionaries, Tuples, and Sets | P3.1 (8) + P3.2 (7) + P3.3 (8) + P3.4 (7) | 30 |\n",
"| 4 — Integration: Online Course Platform | T4.1 (3) + T4.2 (4) + T4.3 (4) + T4.4 (5) + T4.5 (4) | 20 |\n",
"| **Total** | | **100** |\n",
"\n",
"Written explanations are graded on the precision of the mechanism named, not on writing quality alone. Partial credit is awarded for answers that demonstrate correct reasoning but explain it only partially.\n",
"\n",
"---\n",
"*DSCI 2410 — Fundamentals of Data Science II | The American University in Cairo | Spring 2026*\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.10.0"
},
"colab": {
"provenance": [],
"toc_visible": true,
"include_colab_link": true
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment