Predict Student Performance

Clean messy data and build a grade prediction model with NumPy and Pandas.

Introduction

30 Second Summary

Think about how a teacher might predict which students will ace an exam. They look at study habits, past grades, and attendance patterns. With the right data, a simple formula can do the same thing.

In this project, you will take a messy student performance dataset, clean it up, explore it with statistics and charts, and build a prediction function that estimates grades from study hours.

What You'll Build

Type any number of study hours into your prediction function and get back a predicted grade, backed by a regression line plotted over real student data in a Google Colab notebook.

By the end of this project, you'll have:

  • A cleaned dataset transformed from messy raw data using Pandas, with missing values and duplicates removed so every calculation produces reliable results.
  • A scatter plot and histogram built with Matplotlib that reveal the relationship between study habits and grades at a glance.
  • A working prediction function powered by NumPy linear regression that estimates a student's grade based on how many hours they study.
  • Secret Mission: Add a pass/fail classifier and a grouped performance analysis by attendance level.

Are there any prerequisites?

You should be comfortable with basic Python (variables, functions, and loops). Experience with the Build a Mini ML Framework project or equivalent object-oriented programming helps but is not strictly required.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters.

Set Up Your Colab Notebook

Every data analysis project starts with loading your tools. Before you can clean data or build predictions, you need a working environment with the right libraries available and verified.

Google Colab gives you a free Python notebook in your browser with NumPy, Pandas, and Matplotlib already installed. In this step, you'll create your notebook and load the dataset you'll analyze throughout this project.

In this step, get ready to:
  • Create a new Colab notebook.
  • Import NumPy, Pandas, and Matplotlib and verify they work.
  • Load the raw student performance dataset into a DataFrame.
Create a new Colab notebook

Google Colab is a free, browser-based notebook environment that lets you write and run Python code in individual cells. You don't need to install anything on your computer.

  • Open your browser and go to colab.research.google.com.
  • Sign in with your Google account if prompted.
  • Click File in the top menu bar.
  • Select New notebook. A fresh notebook opens in a new tab.
  • Click the notebook title in the top-left corner (it says "Untitled" by default).
  • Rename it to Student Performance Analysis and press Enter.

What is Google Colab?

Google Colab is a hosted Jupyter notebook service. Each notebook is made up of cells that you can run independently. Code cells execute Python and display output directly below them. Your notebooks save automatically to Google Drive.

Import libraries and verify versions

Colab comes with the major data science libraries pre-installed. You still need to import them into your notebook so Python knows you want to use them.

  • In the first code cell of your notebook, paste the following code to import your three libraries and print their versions:
# Cell 1: Setup - Import libraries and verify
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

print(f"NumPy version: {np.__version__}")
print(f"Pandas version: {pd.__version__}")
print(f"Matplotlib version: {plt.matplotlib.__version__}")
print("All libraries loaded successfully!")

What does this code do?

  • import numpy as np loads the NumPy library and gives it the short alias np so you can type less.
  • import pandas as pd loads Pandas, the library for working with tabular data.
  • import matplotlib.pyplot as plt loads the plotting module from Matplotlib.
  • The __version__ attributes confirm each library loaded correctly by printing its version number.

Before you run this, what do you expect to see printed below the cell?

  • Run the cell by clicking the play button on its left side, or pressing Shift+Enter.

You should see version numbers for all three libraries followed by "All libraries loaded successfully!" with no error messages.

Seeing an import error?

This is rare in Colab since the libraries are pre-installed. If you see a ModuleNotFoundError, try restarting the runtime by clicking Runtime then Restart runtime, and run the cell again.

Why can't I import NumPy or Pandas in my Colab notebook?

Create the raw student dataset

Now you need data to analyze. You'll create a dataset of 24 students with their study hours, grades, absences, and previous grades. This dataset has intentional data quality issues baked in that you'll discover and fix in later steps.

  • Click the + Code button at the top of the notebook to add a new cell below Cell 1.
  • Paste the following code into the new cell to create your dataset as a Pandas DataFrame:
# Cell 2: Create the raw dataset with intentional data quality issues
data = {
    'Name': ['Dan', 'Joann', 'Pedro', 'Rosie', 'Ethan', 'Vicky', 'Frederic',
             'Jimmie', 'Rhonda', 'Giovanni', 'Francesca', 'Rajab', 'Naiyana',
             'Kian', 'Jenny', 'Jakeem', 'Helena', 'Ismat', 'Anila', 'Skye',
             'Daniel', 'Aisha', 'Ethan', 'Vicky'],
    'StudyHours': [10.0, 11.5, 9.0, 16.0, 9.25, 1.0, 11.5, 9.0, 8.5, 14.5,
                   15.5, 13.75, 9.0, 8.0, 15.5, 8.0, 9.0, 6.0, 10.0, 12.0,
                   12.5, 12.0, 9.25, 1.0],
    'Grade': [50.0, 50.0, 47.0, 97.0, 49.0, 3.0, 53.0, 42.0, 26.0, 74.0,
              82.0, 62.0, 37.0, 15.0, 70.0, 27.0, 36.0, 35.0, 48.0, 64.0,
              55.0, 64.0, 49.0, 3.0],
    'Absences': [2, 0, 3, 1, 4, 15, 1, 3, 5, 0,
                 1, 2, None, 6, 1, 7, 4, None, 3, 2,
                 1, 0, 4, 15],
    'PreviousGrade': [45.0, 55.0, 40.0, 92.0, 44.0, None, 50.0, 38.0, 30.0, 70.0,
                      78.0, 60.0, 42.0, None, 65.0, 30.0, 38.0, 32.0, 45.0, 58.0,
                      52.0, 60.0, 44.0, None]
}

df = pd.DataFrame(data)
print(f"Dataset shape: {df.shape}")
df.head(10)

What does this code do?

  • data is a Python dictionary where each key becomes a column name and each list becomes the column's values.
  • pd.DataFrame(data) converts the dictionary into a structured table that Pandas can manipulate.
  • Some entries are None (missing values) and some rows are exact duplicates. These are intentional problems you'll fix in Step 3.
  • df.head(10) displays the first 10 rows as a formatted table so you can visually inspect the data.
  • Run this cell by pressing Shift+Enter.

You should see Dataset shape: (24, 5) printed above a formatted table showing the first 10 students with columns for Name, StudyHours, Grade, Absences, and PreviousGrade.

Seeing a NameError for pd?

This means Cell 1 hasn't been run yet. Colab cells must be run in order. Click on Cell 1 and press Shift+Enter to run it first, then run Cell 2 again.

Help me fix a NameError in my Colab notebook.

✔️ Awesome, I've got everything!

Great. Make sure both cells are saved. Colab auto-saves to Google Drive, so you're all set.

ⓧ I'd like to double check the full code

Scroll up through your notebook and confirm you have these two cells in order:

  • Cell 1: Imports numpy, pandas, and matplotlib. Prints version numbers and a success message.
  • Cell 2: Creates the data dictionary, converts it to a DataFrame called df, prints the shape, and displays the first 10 rows.

Both cells should show output below them (no red error messages). The shape should read (24, 5).

Your environment is ready and your raw data is loaded. Next up, you'll explore this dataset to understand its structure and spot the data quality issues hiding inside it.

Explore Your Data

You have a dataset loaded, but you don't yet know what's inside it. Before running any analysis, you need to understand the data's shape, types, and completeness.

In this step, you'll explore the DataFrame to map its structure and test whether it's ready for calculations. You'll discover that it has hidden problems that silently corrupt results.

In this step, get ready to:
  • Inspect the DataFrame's structure, types, and completeness.
  • Compute summary statistics to quantify the data.
  • Test a calculation that reveals hidden data quality problems.
Inspect the DataFrame structure

Cell 2 already printed the DataFrame's shape and first 10 rows. Look at that output now.

  • Find the Dataset shape: (24, 5) line in Cell 2's output. This confirms 24 rows and 5 columns.
  • Look at the table below it. You can see the column names (Name, StudyHours, Grade, Absences, PreviousGrade) and some values that say None.

The .info() method goes deeper. It shows the data type of each column and how many non-null values each one has.

  • Create a new cell below Cell 2 and add the following code to inspect the data types and get summary statistics:
# Cell 3: Explore the data structure
print(df.info())
print("\n")
print(df.describe())
  • Run the cell by pressing Shift+Enter.

What does this code do?

These two methods give you different views of the same data.

  • .info() prints each column's name, how many non-null entries it has, and its data type. This is how you spot missing values at a glance.
  • .describe() computes count, mean, standard deviation, min, max, and quartiles for every numeric column. The count row tells you how many valid (non-missing) values each column has.

Your output from .info() reveals that Absences and PreviousGrade have fewer non-null values than the other columns. That means some rows are missing data in those columns.

Seeing an error instead of output?

  • Make sure you ran Cell 2 first. Cell 3 depends on the df variable created there.
  • Check for typos in df.info() and df.describe(). Both use parentheses with nothing inside.
  • Why isn't my df.info() printing anything?
Review the summary statistics for clues

Look at the .describe() output from Cell 3. Focus on the count row at the top of the table.

  • Notice that StudyHours and Grade both show a count of 24. That means all 24 rows have values for those columns.
  • Now look at Absences and PreviousGrade. Their counts are lower than 24. This confirms those columns have missing data.

Why do missing values matter?

Missing values don't just leave gaps. They propagate through calculations. Any math involving a missing value produces NaN (Not a Number), which can silently corrupt your entire analysis.

You're about to see this happen firsthand.

Try computing an Improvement column

A useful feature for your analysis would be how much each student improved from their previous grade. The calculation is simple subtraction.

  • Create a new cell below Cell 3 and add this code to calculate each student's improvement:
# Cell 4: Try to compute Improvement (hits the obstacle)
df['Improvement'] = df['Grade'] - df['PreviousGrade']
print(df[['Name', 'Grade', 'PreviousGrade', 'Improvement']])

Before you run this, what do you think will happen for rows where PreviousGrade is missing?

  • Run the cell by pressing Shift+Enter.

Look at the rows for Vicky, Kian, and Ismat. Their Improvement column shows NaN instead of a number. The missing PreviousGrade values made the subtraction produce garbage.

What does this tell you?

  • df['Grade'] - df['PreviousGrade'] subtracts each student's previous grade from their current grade.
  • For rows where PreviousGrade is None (missing), Pandas can't do arithmetic. It fills the result with NaN.
  • This makes the entire Improvement column unreliable. You can't trust averages, charts, or predictions built on a column full of silent gaps.

This is the problem you'll fix next

Missing values and duplicate rows make your data untrustworthy. Any analysis built on this dirty data will produce misleading results. Step 3 introduces the cleaning techniques that solve this.

✔️ Awesome, I've got everything!

Great. Make sure you've saved your notebook.

ⓧ I'd like to double check the full code

Confirm you have four cells in order. Scroll up through your notebook and check:

  • Cell 1: imports and version prints.
  • Cell 2: the data dictionary and DataFrame creation.
  • Cell 3: print(df.info()), a blank line print, and print(df.describe()).
  • Cell 4: df['Improvement'] = df['Grade'] - df['PreviousGrade'] followed by a print of the Name, Grade, PreviousGrade, and Improvement columns.

You've confirmed that the data has structural problems. Missing values silently break calculations, and you noticed duplicate rows earlier too. Next, you'll clean the data so every calculation you run from here on is trustworthy.

Clean the Dirty Data

In the previous step, you tried computing an Improvement column and hit NaN values. Those blank cells corrupted your calculation completely.

Missing values and duplicate rows are two of the most common data quality problems. You cannot trust any analysis built on dirty data. In this step, you'll diagnose exactly where the problems are and clean them out so every calculation works on solid ground.

In this step, get ready to:
  • Diagnose missing values and duplicate rows in the dataset.
  • Remove bad data to create a clean DataFrame.
  • Recalculate the Improvement column with valid results in every row.
Diagnose the data problems

Before you can fix dirty data, you need to know exactly how dirty it is. You saw NaN values in the Improvement column, but you haven't yet counted how many rows are affected or where the problems live.

  • Create a new code cell in your notebook and count the missing values and duplicates by running this code:
# Cell 5: Diagnose data problems
print(f"Missing values per column:")
print(df.isnull().sum())
print(f"\nDuplicate rows: {df.duplicated().sum()}")

What does this code do?

Two diagnostic tools are at work here.

  • .isnull().sum() checks every cell in the DataFrame for missing values, then totals them per column. This tells you which columns have gaps.
  • .duplicated().sum() checks for rows that are exact copies of earlier rows. Duplicates inflate your statistics and skew your results.
  • Run the cell. You should see that Absences has 2 missing values, PreviousGrade has 3 missing values, and there are 2 duplicate rows.

Seeing NameError: name 'df' is not defined?

This means Cell 2 (where you created the DataFrame) hasn't been run in this session. Go back to Cell 2 and run it first, then re-run Cell 5.

help me fix a NameError in my Colab notebook

Remove duplicates and missing values

Now you know the exact scope of the damage. Time to fix it. You'll create a brand new clean DataFrame so the original data stays untouched for reference.

  • Create a new code cell and clean the data by running this code:
# Cell 6: Clean the data
df_clean = df.drop_duplicates()
df_clean = df_clean.dropna(subset=['Absences', 'PreviousGrade'])
df_clean = df_clean.reset_index(drop=True)
print(f"Clean dataset shape: {df_clean.shape}")

What does this code do?

Three cleaning operations happen in sequence, each building on the last.

  • .drop_duplicates() removes rows that are exact copies of an earlier row. The first occurrence stays.
  • .dropna(subset=['Absences', 'PreviousGrade']) removes rows where either of those columns is missing. This targets the exact columns that broke your Improvement calculation.
  • .reset_index(drop=True) renumbers the rows from 0 so you don't have gaps in the index after removing rows.
  • Run the cell. You should see Clean dataset shape: (19, 6) confirming duplicates were removed and rows with missing values were dropped.

Seeing a different number of rows?

Make sure you ran Cell 4 before this cell. The Improvement column from Cell 4 must exist in df for the shape to include 6 columns. If you skipped Cell 4, re-run it and then re-run Cell 6.

help me debug my data cleaning output

Recalculate the Improvement column

The original Improvement column was calculated on dirty data, so it carried NaN values. Now that the clean DataFrame has no missing PreviousGrade values, the same subtraction will produce a valid number for every row.

Before you run the next cell, what do you think will happen when you subtract PreviousGrade from Grade on the clean data? Will there still be any NaN values?

  • Create a new code cell and recalculate Improvement on the clean data by running this code:
# Cell 7: Recalculate Improvement on clean data
df_clean['Improvement'] = df_clean['Grade'] - df_clean['PreviousGrade']
print(df_clean[['Name', 'Grade', 'PreviousGrade', 'Improvement']])

What does this code do?

This is the same subtraction you tried in Cell 4. The difference is the data underneath. Since df_clean has no missing PreviousGrade values, every row produces a valid number instead of NaN.

  • Run the cell. Every row in the Improvement column now shows a valid number. No more NaN values.

Still seeing NaN in the Improvement column?

Make sure you're printing from df_clean, not df. The original df still has dirty data. Also confirm that Cell 6 ran successfully before this cell.

help me fix NaN values in my cleaned DataFrame

✔️ Awesome, I've got everything!

Great. Double check you've saved your notebook (Colab auto-saves, but you can press Ctrl+S to be sure).

ⓧ I'd like to double check the full code

Scroll through your notebook and confirm these cells are present, in order:

  • Cell 1: Imports and version prints.
  • Cell 2: Raw dataset creation with 24 rows.
  • Cell 3: df.info() and df.describe() exploration.
  • Cell 4: Improvement column attempt (shows NaN).
  • Cell 5: Diagnose missing values and duplicates.
  • Cell 6: Clean the data (df_clean created with shape (19, 6)).
  • Cell 7: Recalculate Improvement on clean data (no NaN).

Your data is clean and your Improvement column works perfectly. Next up, you'll use NumPy to compute statistics and Matplotlib to visualize the relationship between study hours and grades.

Analyze with NumPy and Visualize

Your data is clean and trustworthy. But a tidy table of 19 rows does not tell you whether study hours actually predict grades.

In this step, you will use NumPy to compute statistics that quantify the relationship between study hours and grades. Then you will use Matplotlib to visualize the pattern so you can see it with your own eyes.

In this step, get ready to:
  • Compute descriptive statistics and a correlation coefficient with NumPy.
  • Create a scatter plot and histogram to visualize the data patterns.
Compute statistics and correlation

Descriptive statistics like mean, median, and standard deviation summarize what is typical in your data. The correlation coefficient goes further. It measures how strongly two variables move together.

  • Create a new code cell below your Cell 7 (Recalculate Improvement) by clicking the + Code button.
  • Paste the following code into the new cell to convert your columns to NumPy arrays and compute statistics:
# Cell 8: NumPy statistical analysis
grades = df_clean['Grade'].to_numpy()
study_hours = df_clean['StudyHours'].to_numpy()

print("Grade Statistics:")
print(f"  Mean: {np.mean(grades):.1f}")
print(f"  Median: {np.median(grades):.1f}")
print(f"  Std Dev: {np.std(grades):.1f}")
print(f"  Min: {np.min(grades):.1f}")
print(f"  Max: {np.max(grades):.1f}")

print(f"\nStudy Hours Statistics:")
print(f"  Mean: {np.mean(study_hours):.1f}")
print(f"  Median: {np.median(study_hours):.1f}")
print(f"  Std Dev: {np.std(study_hours):.1f}")

correlation = np.corrcoef(study_hours, grades)[0, 1]
print(f"\nCorrelation (StudyHours vs Grade): {correlation:.3f}")

What does this code do?

.to_numpy() extracts the column values from the Pandas DataFrame into a raw NumPy array. This lets you use NumPy's fast math functions directly on the data.

np.mean(), np.median(), and np.std() compute the average, middle value, and spread of your data.

np.corrcoef() returns a correlation matrix. The value at [0, 1] is the correlation between the two inputs. A value close to 1.0 means the variables move strongly together.

  • Run the cell by pressing Shift+Enter.

You should see printed statistics for both arrays, followed by a correlation value above 0.9. That is a very strong positive correlation. It means study hours and grades move closely together.

Seeing a NameError for df_clean?

This means the cells above were not run in order. Click Runtime > Run all to execute every cell from the top, then try Cell 8 again.

Help me fix my NumPy statistics cell

Visualize the relationship between study hours and grades

A correlation number tells you the relationship is strong. A chart lets you actually see the shape of that relationship and spot any outliers the number alone would hide.

Before you run the next cell, think about this: given a correlation above 0.9, what do you expect the scatter plot to look like? A random cloud of dots, or a clear upward trend?

  • Create a new code cell below Cell 8 by clicking + Code.
  • Paste the following code to create a figure with two side-by-side plots:
# Cell 9: Visualization
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

axes[0].scatter(study_hours, grades, color='blue', alpha=0.7)
axes[0].set_xlabel('Study Hours')
axes[0].set_ylabel('Grade')
axes[0].set_title('Study Hours vs Grade')

axes[1].hist(grades, bins=8, color='green', edgecolor='black', alpha=0.7)
axes[1].set_xlabel('Grade')
axes[1].set_ylabel('Frequency')
axes[1].set_title('Grade Distribution')

plt.tight_layout()
plt.show()

What does this code do?

plt.subplots(1, 2) creates a figure with 1 row and 2 columns of plot areas. The axes variable holds both plot areas so you can draw on each one separately.

The left plot (axes[0]) is a scatter plot showing each student as a dot positioned by their study hours (x-axis) and grade (y-axis). The right plot (axes[1]) is a histogram showing how grades are distributed across 8 bins.

plt.tight_layout() prevents the plots from overlapping, and plt.show() renders the figure in the notebook output.

  • Run the cell by pressing Shift+Enter.

You should see two charts appear below the cell. The scatter plot on the left shows a clear upward trend from bottom-left to top-right. More study hours equals higher grades. The histogram on the right shows most grades cluster in the middle range.

Why two plots side by side?

The scatter plot answers "do study hours and grades move together?" The histogram answers "what does the grade distribution look like?" Together, they give you a complete picture that no single statistic can provide.

Plots not showing up?

Make sure Cell 8 was run before Cell 9. Cell 9 uses the study_hours and grades arrays created in Cell 8.

If you see a blank output, check that plt.show() is included as the last line of the cell.

Help me get my Matplotlib plots to display

✔️ Awesome, I've got everything!

Your notebook now has 9 cells running from data creation through visualization. Make sure all cells have been executed in order.

ⓧ I'd like to double check the full code

Scroll through your notebook and confirm you have these cells in order, each with output visible below it:

  • Cell 1: Import libraries and print versions.
  • Cell 2: Create the raw dataset dictionary and DataFrame.
  • Cell 3: Explore with .info() and .describe().
  • Cell 4: Attempt Improvement column (shows NaN values).
  • Cell 5: Diagnose missing values and duplicates.
  • Cell 6: Clean the data into df_clean.
  • Cell 7: Recalculate Improvement on clean data.
  • Cell 8: NumPy statistical analysis with correlation.
  • Cell 9: Scatter plot and histogram visualization.

You now have hard numbers proving study hours strongly predict grades, and two charts that make that pattern impossible to miss. Next up, you will turn that pattern into a prediction function that estimates grades for any number of study hours.

Build a Prediction Function

Your scatter plot and correlation value confirmed a strong relationship between study hours and grades. But knowing a pattern exists is different from using that pattern to make predictions.

In this step, you will fit a straight line through your data using linear regression. That line becomes a prediction function: type in any number of study hours, get back an estimated grade. This is the bridge between data analysis and machine learning.

In this step, get ready to:
  • Fit a regression line to the data using NumPy.
  • Visualize the regression line overlaid on the scatter plot.
  • Write a prediction function that estimates grades from study hours.
Fit a regression line with np.polyfit

A regression line is the straight line that best fits through your data points. It follows the equation y = slope * x + intercept. NumPy can calculate both the slope and intercept for you in one function call.

  • Create a new cell below Cell 9 and add the following code to fit a line and print the equation:
# Cell 10: Linear regression with NumPy
coefficients = np.polyfit(study_hours, grades, 1)
slope = coefficients[0]
intercept = coefficients[1]

print(f"Linear Regression Equation:")
print(f"  Grade = {slope:.2f} * StudyHours + {intercept:.2f}")
print(f"\nInterpretation:")
print(f"  Each extra hour of study adds ~{slope:.1f} grade points")

What does this code do?

np.polyfit(study_hours, grades, 1) fits a polynomial of degree 1 (a straight line) to your data. It returns the coefficients in highest-power-first order, so index 0 is the slope and index 1 is the intercept.

The slope tells you how many extra grade points one additional hour of studying is worth. The intercept is the predicted grade at zero study hours.

  • Run Cell 10. You should see the equation printed with a slope around 5-6 and an interpretation line showing how much each extra hour is worth.

Seeing a NameError?

If you see NameError: name 'study_hours' is not defined, make sure you ran Cell 8 earlier in this session. Colab cells must be run in order for variables to persist.

Help me debug my np.polyfit NameError

Why np.polyfit instead of scikit-learn?

For a degree-1 polynomial (a straight line), np.polyfit keeps the entire project within the NumPy ecosystem. No extra imports needed. You can see that ML prediction boils down to math you already understand: y = mx + b.

Visualize the regression line

Numbers alone do not show whether the line actually fits the data well. Plotting the line on top of the scatter plot lets you visually confirm the fit.

  • Create a new cell below Cell 10 and add this code to plot the regression line over the data points:
# Cell 11: Visualize the regression line
plt.figure(figsize=(8, 5))
plt.scatter(study_hours, grades, color='blue', alpha=0.7, label='Actual Data')

x_line = np.linspace(study_hours.min(), study_hours.max(), 100)
y_line = slope * x_line + intercept
plt.plot(x_line, y_line, color='red', linewidth=2, label='Prediction Line')

plt.xlabel('Study Hours')
plt.ylabel('Grade')
plt.title('Student Grade Prediction')
plt.legend()
plt.show()

What does this code do?

np.linspace(study_hours.min(), study_hours.max(), 100) generates 100 evenly spaced x-values across the range of your data. This creates a smooth line rather than a jagged one connecting just a few points.

y_line = slope * x_line + intercept computes the corresponding y-value for each x-value using your regression equation. The result is the red prediction line.

  • Run Cell 11. You should see a scatter plot of blue data points with a red line cutting through them from lower-left to upper-right.

Red line looks off or flat?

Make sure you ran Cell 10 first so slope and intercept have the correct values. If you re-ran Cell 10 with different data by mistake, the line will not match the blue points.

Help me fix my regression line visualization

Write the prediction function

The regression line already tells you the predicted grade for any x-value. Wrapping that equation in a function makes it reusable. Type in study hours, get back an estimated grade.

Before you run this next cell, think about what grade you expect for a student who studies 13 hours per week. Look at your scatter plot and make a guess.

  • Create a new cell below Cell 11 and add the prediction function with a test table:
# Cell 12: Prediction function
def predict_grade(hours):
    predicted = slope * hours + intercept
    return round(predicted, 1)

test_hours = [5, 10, 13, 15, 20]
print("Grade Predictions:")
print("-" * 35)
print(f"{'Study Hours':<15}{'Predicted Grade':<15}")
print("-" * 35)
for hours in test_hours:
    print(f"{hours:<15}{predict_grade(hours):<15}")

What does this code do?

predict_grade(hours) takes any number of study hours and applies the regression equation to produce an estimated grade, rounded to one decimal place.

The loop at the bottom tests it with five different inputs so you can see how the predictions scale. This is conceptually the same as calling a trained model's predict() method in a full ML pipeline.

  • Run Cell 12. You should see a formatted table with predictions for 5, 10, 13, 15, and 20 study hours.
  • Compare the prediction for 13 hours to your earlier guess. Was it close to what you expected from the scatter plot?

Seeing unexpected predictions?

If all predictions show the same value or very strange numbers, check that slope and intercept are correct by printing them. The slope should be around 5-6 (each extra hour adds about 5-6 grade points).

Help me debug my predict_grade function

✔️ Awesome, I've got everything!

Great. Double check that you saved all three new cells (10, 11, and 12) and that each ran without errors.

ⓧ I'd like to double check the full code

Scroll through your notebook and confirm these cells are present in order:

  • Cell 10: Linear regression with NumPy (defines coefficients, slope, and intercept).
  • Cell 11: Visualize the regression line (scatter plot with red line using np.linspace).
  • Cell 12: Prediction function (predict_grade(hours) tested with [5, 10, 13, 15, 20]).

You just built a working prediction model from scratch. You can now type in any number of study hours and get back a data-backed grade estimate. Ready for a challenge? The Secret Mission adds pass/fail classification and grouped attendance analysis to your toolkit.

Secret mission

Pass/Fail Classifier and Attendance Analysis

Your prediction function tells you a grade, but it doesn't tell you whether that grade means success or failure. And what about students who skip class? Can attendance alone predict who passes? Build a classifier and grouped analysis to find out.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely in Google Colab, so there are no ongoing costs.

Resources you used:

  • Google Colab notebook Student Performance Analysis

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your notebook or extending the analysis.

  • Your notebook stays in Google Drive and is accessible from colab.research.google.com any time.
  • Colab disconnects idle runtimes after about 90 minutes, so any variables in memory will be lost. Re-run the cells to restore them.

Pause - I'll come back to this later

Disconnect the runtime to free up Colab's resources while keeping your notebook saved in Google Drive.

  • In your Colab notebook, click Runtime in the top menu bar.
  • Click Disconnect and delete runtime.
  • Your notebook code and outputs remain saved. When you return, re-run all cells from the top to restore variables and plots.

Delete - I don't want to use this again

Remove the notebook from your Google Drive entirely.

  • In your Colab notebook, click File in the top menu bar.
  • Click Move to trash.
  • To permanently delete, open Google Drive, navigate to Trash in the left sidebar, right-click the notebook file, and select Delete forever.

Nice Work!

Nice Work!

You just built a complete data analysis notebook that takes messy student data, cleans it, visualizes patterns, and predicts grades with a linear regression model. That is the exact data preparation workflow that feeds into real machine learning systems.

You've learned how to:

  • Clean a messy dataset by removing duplicates and handling missing values with Pandas, transforming unreliable raw data into a trustworthy foundation for analysis.
  • Compute descriptive statistics and correlation with NumPy, then visualize relationships with scatter plots and histograms using Matplotlib.
  • Build a linear regression prediction function using np.polyfit that estimates grades from study hours, bridging data analysis and machine learning.
  • Secret Mission: Added pass/fail classification with np.where and attendance-based grouped analysis with np.select and groupby for multi-feature insights.

Ready to quiz yourself?