aNet.ai
Data Science/Lab work
0day streak0XP · Lv 1
← Lab 6 overview and setup
Lab 6 · Section 1 of 7

Count the right thing

Task 1 · registrations, students and a safe join

First · connect to the lecture

The Week 5 lesson on counts and denominators becomes code: the same four registrations give a 75% registration pass rate and a 50% student pass rate.

Lab 6 PDF p. 1 · Task 1 (12 minutes)

Your task

Compute the registration pass fraction and the share of students who passed at least one course. Merge student information with validate="many_to_one", then check row counts, unmatched rows, ID uniqueness and duplicate registrations.

Evidence to include

Both fractions with their denominators, 4 rows before and after the join, 0 unmatched rows, and one complete question naming population, period and unit.

Worked example and code

Try the task in the PDF first, then compare your working below. Run the code in your own Python notebook.

Open Lab 6 instructions (PDF)

Task 1 · L6 p. 1. Setup used by every later task:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statistics import mean, median

1. Registrations versus students

students = pd.DataFrame({"id": ["A", "B"],
                         "mode": ["Full time", "Part time"]})
enrolments = pd.DataFrame([
    ["A", "C1", "T1", 60], ["A", "C2", "T1", 70],
    ["A", "C3", "T1", 80], ["B", "C1", "T1", 40]
], columns=["id", "course", "term", "grade"])
enrolments["passed"] = enrolments["grade"] >= 50

passes = enrolments["passed"].sum()
n_reg = len(enrolments)
print("Registration pass fraction:", passes, "/", n_reg, "=", passes / n_reg)
print("Registration pass percentage:", 100 * passes / n_reg)

students_passed = enrolments.groupby("id")["passed"].any().sum()
n_students = enrolments["id"].nunique()
print("Students passing at least one course:", students_passed, "/", n_students,
      "=", 100 * students_passed / n_students, "%")

Output: 3 / 4 = 0.75, 75.0, and 1 / 2 = 50.0 %.

Why both are correct. They answer different questions with different units. 75% is the share of registrations that passed (denominator: 4 course registrations). 50% is the share of students who passed at least one course (denominator: 2 students). Student A contributes three passing registrations but only one passing student.

2. Attach student information without multiplying rows

joined = enrolments.merge(students, on="id", how="left",
                          validate="many_to_one", indicator=True)
print("Rows before and after:", len(enrolments), len(joined))
print("Unmatched:", joined["_merge"].ne("both").sum())
print("students id unique:", students["id"].is_unique)
print("Duplicate registrations:",
      enrolments.duplicated(["id", "course", "term"]).sum())

Output: 4 4, Unmatched: 0, True, Duplicate registrations: 0.

Explanation. In enrolments one row is one course registration in one term, so A legitimately appears three times; the natural key is (id, course, term), and it has no duplicates. In students one row is one student, so id must be unique. If A appeared twice in students, every A registration would match two student rows and the join would grow from 4 to 7 rows, silently inflating counts. validate="many_to_one" would raise an error instead.

A complete question for the TA: "Among the two synthetic students registered in term T1, what share of course registrations had a final grade of at least 50, and what share of students passed at least one course?" It names the population (these students), period (T1) and unit (registration versus student). Student IDs are labels; their digits are not quantities to average.

Saved only in this browser. This note doesn’t fill in the downloaded notebook; “reviewed” is a personal checklist, not a grade.

aNet.ai · Data Science Fundamentals · Weeks 1–6 · practice material, not an official examProgress saved in this browser