How to use this companion
Open the Module 2 notebook and this companion at the same time. Work through the companion from top to bottom and run the matching notebook cells as you go. The notebook is the authoritative source for the lab sequence, code and required work. This companion explains what each step means, what to notice in the output and why the step matters.
Read aloud: controls and directions
Before you start: choose a reading mode, speed, and voice below. Select Read Aloud from Beginning to hear the full companion, or use Read Aloud from This Section beside any major section to continue from that point. While audio is playing, a floating control bar remains visible at the bottom of the page. Use Pause to pause temporarily, Resume to continue, and Stop to end the current reading.
Study mode reads explanations, results, tables, and notes while skipping exact Python code. Full mode also reads Python code and verbalizes common programming symbols.
Ready to read.
The read-aloud feature uses your browser's speech engine. A screen reader can access the page independently of these controls.
What is in the notebook, and what this companion adds
- Notebook: the exact Python code, the Your Turn tasks, the final practice, the offline assignment and the written reflections.
- Companion: plain-language explanations, the results you should expect (taken from a real run of the notebook), sample solutions for the Your Turn tasks (folded, so you try first), common mistakes and how to fix them.
- If anything ever seems inconsistent: follow the current notebook.
This companion is designed for keyboard and screen-reader access, but it is not a separate version for students who need accessibility features. It is the same learning guide for everyone. You can read it visually, use a screen reader, or use the built-in read-aloud controls while you work in the notebook.
Use either course environment
- GitHub Codespaces (main path): open your own repository in Codespaces, open the Module 2 notebook and use the default Python kernel.
- Local VS Code (optional, for experienced students): clone your repository, use the
baseconda environment, installrequirements.txt, open the repository folder in Visual Studio Code and open the notebook. - In either environment: the notebook and this companion are the same. Keep the repository structure unchanged so the notebook paths and output folders work.
Module 2 Learning Goals
Last week you asked where data lives. This week you ask what condition the data is in. You load a small employee table with Python and Pandas and profile it: you inspect its structure, content and quality before anyone fixes anything. By the end of the lab you should be able to:
- Run basic Python code in a notebook and read the result.
- Explain why Pandas is useful for tables of rows and columns.
- Load a CSV file with
pd.read_csv(). - Inspect a table with
head(),tail(),shape,columns,info()anddtypes. - Count missing values with
isna().sum(), summarize numbers withdescribe()and categories withvalue_counts(). - Find possible duplicate IDs, and write short observations based on evidence.
The main idea: profile first, clean later. This week you describe problems; you do not fix them.
Before You Start: Repository and Setup
Module 2 uses the same repository you created in Module 1 (the Weeks 1–2 template). Do not start a new one, and do not work in the instructor template.
- Open your own repository in GitHub Codespaces. A new Codespace usually needs 4–5 minutes the first time; reopening it is much faster.
- Open
notebooks/Week2_lab1_python_pandas_guided_lab.ipynb. Check the file name: the folder also holdsWeek1_introduction_to_codespaces.ipynbandWeek2_assignment.ipynb. - If VS Code asks for a kernel, choose the Python environment it suggests. Then run the cells from the top. If you restart the kernel, run from the top again.
Optional local path: in GitHub Desktop, select your repository and choose Open in Visual Studio Code. Trust the folder when VS Code asks, and choose the base conda environment as the kernel. The screen looks almost the same as in Codespaces, because Codespaces also uses VS Code.
Part 1: Python Basics You Need for This Course
This is not a full Python course. You need to run cells, read code carefully, change small pieces of example code and explain the output.
1.1 Python as a calculator
# If you run a 10 kilometer race in 43 minutes and 30 seconds,
# what is your average seconds per mile?
# 1 mile = 1.61 kilometers
seconds = 43 * 60 + 30
miles = 10 / 1.61
seconds_per_mile = seconds / miles
seconds_per_mile
1.2 Store values in variables
name = "Alice"
age = 28
salary = 95000.0
is_manager = False
print(name)
print(age)
print(salary)
print(is_manager)
print(type(name), type(age), type(salary), type(is_manager))
1.3 Comments
# This is a comment. It explains what the code does.
message1 = "It's not a bug; it's a feature."
print(message1)
1.4 Lists
departments = ["Engineering", "Marketing", "Sales"]
print(departments)
print(departments[0]) # first element
print(departments[1]) # second element
Change a list element
vector1 = [1, 2, 3]
vector1[0] = 100
print(vector1)
Multiplying a list repeats it
vector1 = [100, 2, 3]
print(vector1 * 2)
Double each element
# Loop version
new_vector = []
for x in vector1:
new_vector.append(x * 2)
print(new_vector)
# Optional: list comprehension version
[x * 2 for x in vector1]
1.5 Dictionaries
employee = {
"name": "Bob",
"department": "Marketing",
"salary": 78000
}
print(employee["name"])
print(employee["department"])
print(employee["salary"])
1.6 Errors and help()
help(print)
Part 2: What Is Pandas?
Pandas is the main Python tool for tables. You will use it to load, inspect, filter, summarize, group, clean and transform data. Modeling comes later, with scikit-learn.
2.1 Import Pandas
import pandas as pd
print("Pandas imported successfully!")
2.2 A DataFrame and a Series
sample = pd.DataFrame({
"employee_id": [1, 2, 3],
"department": ["Sales", "Marketing", "Engineering"],
"salary": [60000, 75000, 90000]
})
sample
Select one column
sample["department"]
2.3 Methods and attributes
print("DataFrame shape attribute:")
print(sample.shape)
print("DataFrame head method:")
sample.head()
Part 3: Data Import
The file is data/employee_data.csv. A CSV file stores a table as plain text, with commas between values.
3.1 Reading a CSV without Pandas
import csv
csv_rows = []
with open("../data/employee_data.csv", "r") as file:
csv_reader = csv.reader(file)
for row in csv_reader:
csv_rows.append(row)
# Show only the first 3 rows so the output is not too long
csv_rows[:3]
Reading the same file with Pandas
df = pd.read_csv("../data/employee_data.csv")
df.head()
3.2 Load the employee dataset
df = pd.read_csv("../data/employee_data.csv")
print("Data loaded successfully!")
Part 4: First Look at the Data
4.1 First rows
df.head()
| employee_id | first_name | last_name | hire_date | department | salary | age | full_time | |
|---|---|---|---|---|---|---|---|---|
| 0 | 101 | John | Smith | 4/15/22 | Marketing | 85000 | 34.0 | True |
| 1 | 102 | Jane | Doe | 8/20/21 | Sales | 92000 | 45.0 | True |
| 2 | 103 | Peter | Jones | 1/2/22 | HR | 71000 | 29.0 | True |
| 3 | 104 | Mary | Johnson | 11/10/23 | Engineering | 115000 | 38.0 | True |
| 4 | 105 | Mike | Brown | 5/30/20 | sales | 89000 | NaN | False |
4.2 Last rows
df.tail()
| employee_id | first_name | last_name | hire_date | department | salary | age | full_time | |
|---|---|---|---|---|---|---|---|---|
| 8 | 102 | Emily | White | 1/15/24 | Sales | 91000 | 31.0 | True |
| 9 | 110 | David | Miller | 7/22/19 | HR | 68000 | 58.0 | True |
| 10 | 111 | Sarah | Moore | 3/12/23 | Marketing | $91,000 | 28.0 | True |
| 11 | error | Kevin | Clark | 2/5/24 | Sales | 88000 | 35.0 | True |
| 12 | 113 | Brian | Hall | 6/18/22 | Engineering | 5900000 | 999.0 | True |
4.3 Dataset size
df.shape
A readable size message
num_rows, num_columns = df.shape
print(f"This dataset has {num_rows} rows and {num_columns} columns.")
4.4 Column names
df.columns
4.5 Data types and non-missing counts
df.info()
4.6 Data types only
df.dtypes
Part 5: Data Profiling Checklist
Profiling asks six questions: How many rows and columns? What types? Any missing values? What range of numbers? Are categories consistent? Any duplicate IDs or rows? You are still diagnosing, not fixing.
5.1 Missing values
df.isna().sum()
Missing counts as percentages
missing_summary = pd.DataFrame({
"missing_count": df.isna().sum(),
"missing_percent": df.isna().mean() * 100
})
missing_summary
| column | missing_count | missing_percent |
|---|---|---|
| employee_id | 0 | 0.000000 |
| first_name | 0 | 0.000000 |
| last_name | 0 | 0.000000 |
| hire_date | 1 | 7.692308 |
| department | 0 | 0.000000 |
| salary | 1 | 7.692308 |
| age | 1 | 7.692308 |
| full_time | 1 | 7.692308 |
5.2 Numerical summary with describe()
df.describe()
5.3 One statistic at a time
# Example: summary statistics for age
print("Count:", df["age"].count())
print("Mean:", df["age"].mean())
print("Median:", df["age"].median())
print("Minimum:", df["age"].min())
print("Maximum:", df["age"].max())
print("Standard deviation:", df["age"].std())
5.4 Categorical summary with value_counts()
df["department"].value_counts()
Include missing values in the count
df["department"].value_counts(dropna=False)
5.5 Unique values
df["department"].nunique()
5.6 Full duplicate rows
# Count fully duplicated rows
full_duplicate_rows = df.duplicated().sum()
print(f"Number of fully duplicated rows: {full_duplicate_rows}")
Duplicate employee IDs
# Count duplicate employee IDs
employee_id_duplicates = df["employee_id"].duplicated().sum()
print(f"Number of duplicated employee_id values after the first occurrence: {employee_id_duplicates}")
Which ID repeats?
df["employee_id"].value_counts().head(10)
Part 6: Writing Data Quality Observations
Profiling is not finished until you write down what you found. A good observation is specific (it names the column and the number) and based on evidence (an output you can point to). Clear notes matter when you hand work to a teammate, a manager or a client.
Complete independently: Your Turn
Write 3–5 observations in the notebook cell Your Turn: Write 3–5 Observations. This is graded, so the companion does not write them for you.
What to check: each observation names a column, gives a number from an output above (a count, a percentage, a minimum or maximum), and says why it might matter later. Describe the problem; do not propose a fix yet. Do not copy the notebook's examples, which describe a different, 500-row dataset. You may also run one more check of your own, for example value_counts() on another column, and write about it.
Part 7: Optional Challenge
These cells are optional and not graded. Run them if you have time.
Challenge 1: another categorical column
# Optional challenge 1: Choose another categorical column and run value_counts().
# Replace "department" with another column name if appropriate.
df["department"].value_counts(dropna=False)
Try first
Change "department" to another categorical column. Which column has a missing value that dropna=False will show?
- Count the values of
full_time, including missing values.
Show sample solution for Challenge 1 (optional)
Use this only after you have made your own attempt. Compare the structure and logic, not just the final line of code. This sample solution is written by the companion; it is not part of the notebook.
df["full_time"].value_counts(dropna=False)What it prints:
full_time
True 11
False 1
NaN 1
Name: count, dtype: int64Without dropna=False, the NaN row disappears and the counts add up to 12, not 13.
Challenge 2: rows with any missing value
# Optional challenge 2: Show rows with at least one missing value.
df[df.isna().any(axis=1)].head()
| employee_id | first_name | hire_date | salary | age | full_time | |
|---|---|---|---|---|---|---|
| 4 | 105 | Mike | 5/30/20 | 89000 | NaN | False |
| 5 | 106 | Susan | NaN | 95000 | 41.0 | True |
| 6 | 107 | Chris | 2023-FEB-21 | NaN | 33.0 | True |
| 7 | 108 | Laura | 9/1/21 | 120000 | 52.0 | NaN |
Challenge 3: sort by salary
# Optional challenge 3: Sort by a numerical column.
# If salary is stored as text in this dataset, this may not sort the way you expect.
# That is okay! We will learn how to fix data types in Week 3.
df.sort_values("salary", ascending=False).head()
Try first
Optional extra: show both rows that share a duplicated ID, so you can compare them side by side.
- Use
duplicated(keep=False), which marks every copy, not just the later ones.
Show sample solution for the optional extra
Use this only after you have made your own attempt. Compare the structure and logic, not just the final line of code. This sample solution is written by the companion; it is not part of the notebook.
df[df["employee_id"].duplicated(keep=False)]What it prints:
employee_id first_name last_name hire_date department salary age full_time
1 102 Jane Doe 8/20/21 Sales 92000 45.0 True
8 102 Emily White 1/15/24 Sales 91000 31.0 TrueIn a large table, sorting the result by ID with .sort_values("employee_id") puts each pair next to each other.
Week 2 Assignment Notebook
Part 8 of the guided lab looks ahead to Week 3: correcting data types, handling duplicates and missing values, and documenting each decision. Before that, there is a second notebook to complete.
Complete independently: Week2_assignment.ipynb
This notebook is graded and intentionally not solved in the companion. It uses the same employee_data.csv, so the guided lab is your reference: find the matching function there and apply it yourself.
- Task 0: run the cell that imports Pandas and loads the data.
- Task 1: show the shape, the column names, the first five rows and the last five rows.
- Task 2: inspect types and non-missing counts with
info()anddtypes. - Task 3: count missing values with
isna().sum()(percentages optional). - Task 4: run
describe(), then at least two single statistics for one numeric column. - Task 5:
value_counts()fordepartmentand one more categorical column. - Task 6: check full duplicate rows, duplicate
employee_idvalues, and the most common IDs. - Task 7: write 3–5 overall observations. Do not clean the data.
What to check: each # Your code here cell is filled and runs without errors, and its output is visible. Each written response uses numbers from your own outputs. A cell can show only its last result automatically, so use print() or separate cells when one task asks for several outputs.
Common Problems
If a cell fails, read the last line of the error message first. Then find it below.
Still stuck? Post the error message and the cell number in the course Q&A discussion. Do not post your full answers.
Save and Submit
- Complete the guided lab, including your observations, and the assignment notebook.
- Run each notebook from top to bottom and confirm there are no errors. Save both notebooks.
- Commit and push. In Codespaces: open Source Control, write a short message such as
Complete Module 2 data profiling lab, select Commit, then Sync Changes. Or in the terminal:git add .,git commit -m "Complete Module 2 data profiling lab",git push. On the local path, use GitHub Desktop: Commit to main, then Push origin. - Open your repository on GitHub in the browser, refresh it, and confirm your latest commit is there. The instructor grades the online repository, not the copy on your computer.
- Submit your repository link in Canvas. If the repository is private, invite
zzz1990771first.
Lab Wrap-Up
You now have a minimum profiling toolkit:
head(),tail(),shape,columns: see the structure.info(),dtypes: check types and non-missing counts.isna().sum(): count missing values.describe()and single statistics: check numeric ranges and impossible values.value_counts(),nunique(): check category labels.duplicated(): check repeated rows and repeated IDs.
These are the essentials, not every function you will ever need. When you need something new, check the Pandas documentation, use help(), or ask. In Week 3 you start fixing what you found today: data types, duplicates and missing values, with each decision documented.