Lesson 9 · Pandas Projects
Student Grades Analysis with Pandas: Stats, Trends & Outliers
A complete, standalone tutorial: build a synthetic gradebook and attendance log, then analyze performance with pandas. No prior pandas experience needed.…
- CoursePandas Projects
- Lesson9 of 10
- Video17 min
- FormatJupyter notebook · 15 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPandas for Education: Student Grades and Attendance#
- A complete, standalone tutorial: build a synthetic gradebook and attendance log, then analyze performance with pandas.
- No prior pandas experience needed. Let's jump straight in.
Before You Start#
- Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
- If pandas isn't installed yet, open a terminal in VS Code and run: pip install pandas
Part 1: Building the Dataset#
import pandas as pd
import numpy as np
print(pd.__version__)
Building the Students and Attendance Tables#
rng = np.random.default_rng(seed=111)
first_names = ['Aiko', 'Ben', 'Camila', 'Dev', 'Ella', 'Finn', 'Greta', 'Hassan', 'Ivy', 'Jamal', 'Kira', 'Leo', 'Mira', 'Noah', 'Priya', 'Quinn', 'Rosa', 'Sam', 'Tara', 'Umar']
students = pd.DataFrame({
'student_id': range(1, len(first_names) + 1),
'name': first_names,
'attendance_pct': np.clip(rng.normal(90, 8, size=len(first_names)), 55, 100).round(1)
})
students.head()
Generating Scores Across Subjects#
subjects = ['Math', 'Science', 'English', 'History']
score_rows = []
for _, student in students.iterrows():
base_ability = rng.normal(75, 10)
for subject in subjects:
attendance_bonus = (student['attendance_pct'] - 90) * 0.3
score = base_ability + attendance_bonus + rng.normal(0, 8)
score = float(np.clip(score, 30, 100).round(1))
score_rows.append([student['student_id'], subject, score])
scores = pd.DataFrame(score_rows, columns=['student_id', 'subject', 'score'])
print(scores.shape)
students.to_csv('students.csv', index=False)
scores.to_csv('student_scores.csv', index=False)
students = pd.read_csv('students.csv')
scores = pd.read_csv('student_scores.csv')
scores.head()
Part 2: First Look at the Data#
print('students:', students.shape)
print('scores:', scores.shape)
scores['subject'].value_counts()
scores['score'].describe().round(1)
Part 3: Pivoting to a Wide Gradebook#
gradebook = scores.pivot_table(values='score', index='student_id', columns='subject')
gradebook = gradebook.merge(students[['student_id', 'name']], on='student_id').set_index('name')
gradebook.round(1).head()
Part 4: Overall Averages and Letter Grades#
student_avg = scores.groupby('student_id')['score'].mean().round(1)
student_avg = student_avg.reset_index().merge(students[['student_id', 'name']], on='student_id')
student_avg = student_avg.sort_values('score', ascending=False)
student_avg.head()
def letter_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
elif score >= 60:
return 'D'
else:
return 'F'
student_avg['letter_grade'] = student_avg['score'].apply(letter_grade)
student_avg[['name', 'score', 'letter_grade']].head()
student_avg['letter_grade'].value_counts().reindex(['A', 'B', 'C', 'D', 'F'], fill_value=0)
Part 5: Pass/Fail Analysis by Subject#
scores['passed'] = scores['score'] >= 60
pass_rate_by_subject = scores.groupby('subject')['passed'].mean().mul(100).round(1).sort_values(ascending=False)
pass_rate_by_subject
subject_summary = scores.groupby('subject').agg(
avg_score=('score', 'mean'),
pass_rate_pct=('passed', lambda s: round(s.mean() * 100, 1)),
lowest_score=('score', 'min')
).round(1).sort_values('avg_score', ascending=False)
subject_summary
Part 6: Attendance and Performance#
performance = students.merge(student_avg[['student_id', 'score']], on='student_id')
performance = performance.rename(columns={'score': 'avg_score'})
correlation = performance[['attendance_pct', 'avg_score']].corr().iloc[0, 1]
print(f'Correlation between attendance and average score: {correlation:.2f}')
Part 7: Visualizing the Results#
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
subject_summary['avg_score'].plot(kind='bar', ax=ax, color='seagreen', ylim=(0, 100))
ax.set_title('Average Score by Subject')
ax.set_ylabel('Average Score')
plt.xticks(rotation=0)
plt.tight_layout()
plt.savefig('avg_score_by_subject.png', dpi=150)
plt.close(fig)
print('Saved avg_score_by_subject.png')
fig, ax = plt.subplots(figsize=(8, 5))
ax.scatter(performance['attendance_pct'], performance['avg_score'], alpha=0.7, color='indigo')
ax.set_title('Attendance vs. Average Score')
ax.set_xlabel('Attendance (%)')
ax.set_ylabel('Average Score')
plt.tight_layout()
plt.savefig('attendance_vs_score.png', dpi=150)
plt.close(fig)
print('Saved attendance_vs_score.png')
Wrap-Up: What You Learned#
- Generating a realistic synthetic gradebook and attendance log, then saving and reloading with to_csv and read_csv.
- Reshaping long-format scores into a wide, readable gradebook with pivot_table.
- Per-student averages, plus letter grades from a custom function with apply.
- Pass/fail analysis by subject, including a lambda inside agg for a custom pass-rate statistic.
- Merging tables to check a real correlation between attendance and performance.
- A bar chart and a scatter plot, both presentation-ready, with matplotlib.
- That wraps up the full 10-video pandas series, from retail sales all the way to student grades. If you've made it through the whole series, subscribing means you won't miss whatever's next thanks for following along.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



