Steam Game Popularity Predictor

Build a local app that estimates a Steam game's popularity tier.

Introduction

30 Second Summary

A high score can make a prediction look trustworthy even when it hides serious mistakes. Without a simple point of comparison or a closer look at the results, those mistakes stay invisible.

In this project, you will build a local Census Income Model Auditor with Streamlit. The dashboard turns public census data into a baseline comparison, holdout metrics, and a ranked feature-importance chart.

What You'll Build

Your finished dashboard shows real census rows beside model metrics and a ranked chart of the original columns that influence performance most.

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

  • A local dashboard that previews ten Adult Census rows. Its responsible-use note sets the ethical boundary before any model result.
  • An honest baseline comparison that places accuracy beside balanced accuracy. You can see why predicting only the majority class misses one class completely.
  • A trained model panel with accuracy, balanced accuracy, and ROC AUC. A sorted permutation-importance chart shows which original columns matter most.
  • Secret Mission: Compare balanced accuracy across sex and race groups without using either protected column for training.

Are there any prerequisites?

You need basic Python syntax plus a Windows computer with Visual Studio Code. You also need a GitHub account, internet access for the first installation, and Python 3.11 or newer.

Before We Start

This checkpoint commits you to building an educational Census Income Model Auditor that predicts the less frequent Adult Census income label. Comparing it with a majority-class baseline reveals whether the model learns beyond class imbalance.

Prepare the Windows ML Workspace

A reproducible model auditor starts with a supported interpreter. A reviewer should be able to rebuild the same environment from your project files.

An isolated virtual environment keeps this project's packages separate from other projects. Exact dependency pins give every later result a consistent foundation.

In this step, get ready to:
  • Verify Python 3.11 or newer inside Visual Studio Code.
  • Build .venv with the pinned project dependencies.
  • Launch the Streamlit example in your browser.
Verify Python in your project workspace

Visual Studio Code keeps your files beside an integrated PowerShell terminal. Checking Python there confirms that the project can use the installed interpreter.

  • Press the Windows key to open Windows search.
  • Type Visual Studio Code.
  • Press Enter to open Visual Studio Code.

The project folder gives your files one named workspace. Creating it on your Desktop also makes it easy to find outside the editor.

Visual Studio Code may show a Workspace Trust dialog after the folder opens. You created this empty folder yourself.

  • Click File in the menu bar.
  • Select Open Folder....
  • Select Desktop in the folder dialog.
  • Select New Folder.
  • Enter census-income-model-auditor as the folder name.
  • Select Select Folder.
  • Review the folder contents if the Workspace Trust dialog appears.
  • Select Yes, I trust the authors.

You should see census-income-model-auditor at the top of the Explorer sidebar. Visual Studio Code now treats this folder as your project workspace.

  • Click Terminal in the menu bar.
  • Select New Terminal.
  • Check that the terminal tab is labeled PowerShell.
  • Select the terminal dropdown if another shell is active.
  • Select PowerShell.
  • Check the installed Python version by running this command:
python --version

What Does This Command Do?

The command prints the Python version available to this PowerShell terminal. The project requires Python 3.11 or newer because every pinned dependency supports that range.

✔️ I see version 3.11 or higher

The supported interpreter is ready for this project. Keep this Visual Studio Code workspace open.

ⓧ I see an older version

The installed interpreter is below the project's minimum version. Install a current stable Windows release before creating the environment.

  • Visit the official Python Windows downloads page.
  • Download a current stable Windows installer for Python 3.11 or newer.
  • Run the downloaded installer.
  • Follow the official Windows installation instructions on the download page.
  • Close the existing Visual Studio Code terminal.
  • Click Terminal in the menu bar.
  • Select New Terminal.
  • Recheck the installed version by running this command:
python --version

What Does This Check Confirm?

A fresh PowerShell terminal picks up the newly installed interpreter. The output should now report Python 3.11 or newer.

ⓧ Command not found

PowerShell cannot currently locate a Python installation. The official Windows installer provides the interpreter required by this project.

  • Visit the official Python Windows downloads page.
  • Download a current stable Windows installer for Python 3.11 or newer.
  • Run the downloaded installer.
  • Follow the official Windows installation instructions on the download page.
  • Close the existing Visual Studio Code terminal.
  • Click Terminal in the menu bar.
  • Select New Terminal.
  • Confirm the installation by running this command:
python --version

What Does This Check Confirm?

The command confirms that PowerShell can locate the new interpreter. The output should report Python 3.11 or newer.

Create the isolated environment

The dependency file records the exact package versions behind your results. Anyone rebuilding the project can install the same versions from this one file.

  • Select the file icon with a plus sign beside census-income-model-auditor in the Explorer sidebar.
  • Enter requirements.txt as the file name.
  • Press Enter to create the file.
  • Add the exact dependency pins to requirements.txt by pasting:
pandas==3.0.6
scikit-learn==1.9.1
streamlit==1.66.0

What Do These Pins Control?

  • pandas 3.0.6 provides the tables used for dataset previews and importance results.
  • scikit-learn 1.9.1 provides the dataset loader and machine-learning workflow.
  • Streamlit 1.66.0 provides the local dashboard interface.
  • Save requirements.txt.
  • Confirm that the Explorer sidebar lists requirements.txt inside census-income-model-auditor.

Dependency File Looks Different?

Check that each dependency appears on its own line. Remove any spaces around the double equals signs.

Ask for targeted help with checking the dependency pins.

✔️ Awesome, I've got everything!

Your dependency file matches the tested project versions.

ⓧ I'd like to double check the full code

pandas==3.0.6
scikit-learn==1.9.1
streamlit==1.66.0

The next commands place those dependencies inside .venv. The integrated terminal already starts inside the open census-income-model-auditor folder.

  • Create .venv inside the project folder by running this command:
python -m venv .venv

What Does This Command Do?

Python creates an isolated interpreter inside .venv. Packages installed there stay separate from your global Python installation.

  • Confirm that .venv appears in the Explorer sidebar.

The Environment Was Not Created?

Confirm that the terminal path ends with census-income-model-auditor. Check that the earlier version command reports Python 3.11 or newer.

Ask for help with creating the Windows virtual environment.

  • Activate .venv in PowerShell by running this command:
.\.venv\Scripts\Activate.ps1

What Does Activation Change?

Activation points terminal commands at the interpreter inside .venv. The PowerShell prompt gains an environment prefix as a visible confirmation.

You should see (.venv) at the beginning of the PowerShell prompt. This confirms that future package commands target the project environment.

PowerShell Blocked Activation?

PowerShell can block local activation scripts under its current execution policy. The documented fallback changes that policy for your Windows user account.

  • Allow locally created activation scripts by running this command:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

The command applies the execution policy to your current Windows user. It allows the virtual environment activation script to run.

  • Close the existing terminal.
  • Click Terminal in the menu bar.
  • Select New Terminal.
  • Rerun the activation command shown directly above.

Ask for help with PowerShell activation.

Package installation may take a few minutes while PowerShell downloads the pinned releases. Keep the terminal open until the command finishes.

  • Install the pinned dependencies from requirements.txt by running this command:
py -m pip install -r requirements.txt

What Does This Command Install?

The package installer reads each requirement from requirements.txt. It installs the three pinned releases into the active environment.

The installation output should finish without a package installation error. Your prompt should still begin with (.venv).

Dependency Installation Failed?

Confirm that the PowerShell prompt begins with (.venv). Check your internet connection before retrying the installation.

Ask for help with diagnosing the pinned package installation.

Launch the Streamlit example

Streamlit includes an example app for testing an installation. The command starts a local server and opens the example in your default browser.

The terminal stays attached to the server until you press Ctrl+C.

Before you run the final check, do you think the activated environment can start its bundled browser app?

  • Start the Streamlit example by running this command:
streamlit hello

What Does This Command Prove?

The command runs the example app included with Streamlit. A working browser page confirms that the package and local server can run from your environment.

You should see the built-in Streamlit example in a new browser tab. The page contains interactive demonstrations from the installed package.

That is your workspace foundation working from end to end. Python can now run an installed dashboard package from the isolated project environment.

  • Return to Visual Studio Code.
  • Stop the local server by pressing Ctrl+C in the PowerShell terminal.

Your reproducible Windows workspace is ready. Next, you will turn it into a visible app that loads real Census data and states its responsible-use boundary.

Turn Census Data into a Visible App

Your Windows workspace can now run Streamlit. The project has a working local foundation.

This step connects that foundation to the Adult Census dataset. You will make the selected positive class plus the responsible-use boundary visible before any model result can hide them.

In this step, get ready to:
  • Create the app.py entry point with its imports plus page configuration.
  • Load Adult Census version 2 through a cached data function.
  • Display the responsible-use boundary plus a ten-row dataset preview.
Create the Streamlit entry point

The entry point defines the app's dependencies plus its browser-page settings. It also records which columns must stay outside model training.

  • Use the new-file control in Visual Studio Code's left sidebar to create app.py inside the census-income-model-auditor folder.

You should now see app.py in the project file list. The new editor tab is ready for your app code.

  • Add the imports plus page configuration by pasting this code into app.py:
import pandas as pd
import streamlit as st
from sklearn.datasets import fetch_openml

RANDOM_STATE = 42
PROTECTED_COLUMNS = ["sex", "race"]

st.set_page_config(page_title="Census Income Model Auditor", layout="wide")

What Does This Code Set Up?

  • The pd alias provides access to pandas data structures.
  • The st alias provides the functions that render your Streamlit page.
  • The fetch_openml import provides access to datasets from OpenML.
  • The RANDOM_STATE value gives later data splits plus model steps one reproducible seed.
  • The PROTECTED_COLUMNS list records the attributes reserved for auditing.
  • The st.set_page_config call sets the browser title plus a wide page layout.
  • Save app.py.

The next command starts a local Streamlit server. It keeps the PowerShell terminal occupied until you press Ctrl+C.

  • Start the app shell from the activated PowerShell terminal by running this command:
streamlit run app.py

Your browser should open a wide app shell. The browser tab should read Census Income Model Auditor.

The App Shell Did Not Open?

Confirm that the terminal prompt still shows the activated .venv environment. Check that the file is named app.py inside census-income-model-auditor.

Ask for targeted help with troubleshooting the Streamlit app shell.

Load and identify the Census data

The Adult Census dataset contains numeric plus categorical columns. A scikit-learn loader returns those features with the original target labels.

A cache keeps the downloaded data available across app reruns. The less frequent target label becomes the positive class for this binary classification project.

  • Return to the app.py editor tab from earlier.
  • Add the cached loader plus its first visible result directly below the st.set_page_config line by pasting this code:
@st.cache_data
def load_data():
    X, y = fetch_openml(
        "adult",
        version=2,
        as_frame=True,
        return_X_y=True,
    )
    positive_label = y.value_counts().idxmin()
    y_binary = (y == positive_label).astype(int)
    return X, y_binary, str(positive_label)


X, y, positive_label = load_data()

st.title("Census Income Model Auditor")
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

How Does the Loader Work?

  • The @st.cache_data decorator stores the returned data for later app reruns.
  • The fetch_openml call retrieves Adult Census version 2 as feature plus target objects.
  • The positive_label expression selects the target label with the smallest count.
  • The y_binary expression converts that label to 1.
  • The returned X value contains the original feature columns used by the preview.
  • Save app.py.

The first save downloads the public dataset through scikit-learn. Your browser should then show the app title plus the selected positive-label message.

That is the first data-backed result working. Your page now proves that the app can retrieve plus interpret the real target labels.

The Dataset Did Not Load?

Confirm that your internet connection is available for the first dataset download. Check the indentation inside load_data against the code above.

Ask for targeted help with diagnosing the Census data loader.

Render the responsible-use preview

A visible dataset preview makes the app's evidence inspectable. The boundary statement also rules out high-impact uses before the project displays any model score.

  • Find this current output block at the bottom of app.py:
st.title("Census Income Model Auditor")
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

What Does the Current Output Show?

This minimal output proves that the dataset loaded plus the positive label was selected. The next version adds the evidence plus usage boundary that make it an auditor.

  • Replace that current output block with the complete visible preview below:
st.title("Census Income Model Auditor")
st.write(
    "Educational model audit only. Do not use this app to make employment, "
    "credit, benefits, or other high-impact decisions."
)
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

st.write("## Dataset preview")
st.dataframe(X.head(10))
st.write(
    "The protected columns `sex` and `race` remain visible for auditing but "
    "are excluded from model training."
)

What Does the Preview Add?

  • The first st.write call places the responsible-use boundary near the top of the page.
  • The X.head(10) expression selects the first ten feature rows.
  • The st.dataframe call renders those rows as a scrollable table.
  • The final note keeps sex plus race visible for later auditing.
  • Save app.py.

Your running app should update with the responsible-use note plus a ten-row table. A note below the table should state that sex plus race are excluded from training.

The Preview Is Missing?

Confirm that the preview block comes after X, y, positive_label = load_data(). Check that the table uses X.head(10).

Ask for targeted help with comparing the visible output block.

✔️ Awesome, I've got everything!

Your app.py now matches the complete visible-data slice.

ⓧ I'd like to double check the full code

import pandas as pd
import streamlit as st
from sklearn.datasets import fetch_openml

RANDOM_STATE = 42
PROTECTED_COLUMNS = ["sex", "race"]

st.set_page_config(page_title="Census Income Model Auditor", layout="wide")


@st.cache_data
def load_data():
    X, y = fetch_openml(
        "adult",
        version=2,
        as_frame=True,
        return_X_y=True,
    )
    positive_label = y.value_counts().idxmin()
    y_binary = (y == positive_label).astype(int)
    return X, y_binary, str(positive_label)


X, y, positive_label = load_data()

st.title("Census Income Model Auditor")
st.write(
    "Educational model audit only. Do not use this app to make employment, "
    "credit, benefits, or other high-impact decisions."
)
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

st.write("## Dataset preview")
st.dataframe(X.head(10))
st.write(
    "The protected columns `sex` and `race` remain visible for auditing but "
    "are excluded from model training."
)
  • Switch back to the activated PowerShell terminal from earlier.
  • Press Ctrl+C to stop the current Streamlit server.

Before you restart the app, which title, warning, label, plus dataset details do you expect to confirm?

  • Run the completed app from the activated PowerShell terminal with this command:
streamlit run app.py

Your browser should show the Census Income Model Auditor title plus the responsible-use note. You should also see the selected positive label, ten rows of Census data, plus the note about excluding sex plus race from training.

The Final App Does Not Match?

Confirm that app.py is saved inside census-income-model-auditor. Compare the file with the full-code tab above.

Ask for targeted help with diagnosing the final app output.

Your auditor now puts real data plus its ethical boundary on screen. Next, you will build a simple baseline that exposes why one familiar metric can hide a complete class-level failure.

Build an Honest Baseline

Your Streamlit dashboard now shows the Adult Census data plus its responsible-use boundary. A trustworthy model auditor also needs a baseline for every later score.

Class imbalance can make raw accuracy tell an incomplete story. You will test a majority-class predictor with balanced accuracy to reveal how it performs across both target classes.

In this step, get ready to:
  • Create a stratified holdout split from training features that exclude the protected columns.
  • Fit a majority-class baseline on the training split.
  • Compare holdout accuracy with balanced accuracy.
Add the baseline evaluation tools

Four scikit-learn tools handle the split, constant predictor, and two evaluation scores. Keeping these imports together makes the evaluation dependencies easy to find.

  • Switch back to app.py in the Visual Studio Code editor.
  • Locate from sklearn.datasets import fetch_openml near the top of the file.
  • Add the four evaluation imports directly below it by pasting this code:
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import balanced_accuracy_score
from sklearn.model_selection import train_test_split

What Do These Imports Add?

  • DummyClassifier provides the deliberately simple reference predictor.
  • accuracy_score measures the proportion of correct predictions.
  • balanced_accuracy_score averages recall across the target classes.
  • train_test_split reserves unseen rows for the evaluation.
  • Save app.py.
  • Return to the browser tab served by the running app.

The page should reload with the title, responsible-use note, positive label, and ten-row preview intact. This confirms every evaluation import loads successfully.

The App Stopped Reloading?

Check that each new import starts with from sklearn. Confirm that the imported names match the code block exactly.

Ask for targeted help with diagnosing the baseline imports.

Create a protected holdout split

A holdout split keeps part of the dataset unseen during training. Stratification preserves the target-class proportions across the training and test sets.

  • Locate X, y, positive_label = load_data() below the load_data() function.
  • Create the model inputs plus the stratified split directly below it by pasting this code:
X_model = X.drop(columns=PROTECTED_COLUMNS)

X_train, X_test, y_train, y_test = train_test_split(
    X_model,
    y,
    test_size=0.2,
    random_state=RANDOM_STATE,
    stratify=y,
)

How Does the Split Protect the Audit?

  • X_model removes the columns named in PROTECTED_COLUMNS before model training.
  • test_size=0.2 reserves 20 percent of the rows for evaluation.
  • random_state=RANDOM_STATE makes the split reproducible with the project value of 42.
  • stratify=y preserves the class proportions in both splits.
  • Save app.py.
  • Switch back to the running app in your browser.

The app should reload successfully with the same ten-row dataset preview. This proves the protected-column removal and stratified split execute before the page renders.

The Split Raised an Error?

Confirm that X_model uses X.drop(columns=PROTECTED_COLUMNS). Check that the four split variables appear on the left side of the assignment.

Ask for help with debugging the stratified split.

Measure the majority-class baseline

A majority-class predictor ignores every input feature. Its value comes from setting the minimum performance that a learned model should beat.

  • Locate the closing parenthesis of the train_test_split() call.
  • Fit the constant predictor plus its metrics directly below the split by pasting this code:
baseline = DummyClassifier(strategy="most_frequent")
baseline.fit(X_train, y_train)
baseline_predictions = baseline.predict(X_test)
baseline_accuracy = accuracy_score(y_test, baseline_predictions)
baseline_balanced_accuracy = balanced_accuracy_score(y_test, baseline_predictions)

What Does the Baseline Measure?

  • DummyClassifier(strategy="most_frequent") always predicts the most common training label.
  • baseline.fit() identifies that label from y_train.
  • baseline.predict() produces one constant prediction for every held-out row.
  • baseline_accuracy records the overall correct-prediction rate.
  • baseline_balanced_accuracy records the average recall across both classes.
  • Save app.py.
  • Return to the browser tab from earlier.

The title and dataset preview should render without a traceback. This confirms the constant predictor fitted against the training split.

The Baseline Did Not Fit?

Confirm that the baseline uses X_train with y_train. Check that both metric functions compare y_test with baseline_predictions.

Ask for help with debugging the baseline calculation.

Metrics become useful when you can compare them in one place. The dashboard will now expose both views of the same constant predictions.

  • Locate the final dataset-preview note at the bottom of app.py.
  • Render the baseline section directly below that note by pasting this code:
st.write("## Designed obstacle: majority-class baseline")
st.metric("Baseline accuracy", f"{baseline_accuracy:.3f}")
st.metric(
    "Baseline balanced accuracy",
    f"{baseline_balanced_accuracy:.3f}",
)
st.write(
    "This baseline ignores every feature and always predicts the most "
    "frequent training label."
)

What Does This Section Show?

  • The heading identifies the baseline as the designed obstacle.
  • The two st.metric() calls display each score to three decimal places.
  • The final explanation states the exact behavior behind those scores.

Before you save this change, predict which metric will reveal more about the constant predictor.

  • Save app.py.
  • Return to the browser tab served by the earlier Streamlit process.

You will see a new majority-class baseline section below the dataset preview. Baseline balanced accuracy reads 0.500.

Good work. Your dashboard now exposes that the constant predictor earns credit for one class while completely missing the other.

The Baseline Section Is Missing?

Confirm that the new display code sits after the dataset-preview note. Check the running PowerShell terminal for a filename or indentation error.

Ask for help with restoring the baseline display.

✔️ Awesome, I've got everything!

Your app.py now matches the honest-baseline checkpoint.

ⓧ I'd like to double check the full code

import pandas as pd
import streamlit as st
from sklearn.datasets import fetch_openml
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score
from sklearn.metrics import balanced_accuracy_score
from sklearn.model_selection import train_test_split

RANDOM_STATE = 42
PROTECTED_COLUMNS = ["sex", "race"]

st.set_page_config(page_title="Census Income Model Auditor", layout="wide")


@st.cache_data
def load_data():
    X, y = fetch_openml(
        "adult",
        version=2,
        as_frame=True,
        return_X_y=True,
    )
    positive_label = y.value_counts().idxmin()
    y_binary = (y == positive_label).astype(int)
    return X, y_binary, str(positive_label)


X, y, positive_label = load_data()
X_model = X.drop(columns=PROTECTED_COLUMNS)

X_train, X_test, y_train, y_test = train_test_split(
    X_model,
    y,
    test_size=0.2,
    random_state=RANDOM_STATE,
    stratify=y,
)

baseline = DummyClassifier(strategy="most_frequent")
baseline.fit(X_train, y_train)
baseline_predictions = baseline.predict(X_test)
baseline_accuracy = accuracy_score(y_test, baseline_predictions)
baseline_balanced_accuracy = balanced_accuracy_score(y_test, baseline_predictions)

st.title("Census Income Model Auditor")
st.write(
    "Educational model audit only. Do not use this app to make employment, "
    "credit, benefits, or other high-impact decisions."
)
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

st.write("## Dataset preview")
st.dataframe(X.head(10))
st.write(
    "The protected columns `sex` and `race` remain visible for auditing but "
    "are excluded from model training."
)

st.write("## Designed obstacle: majority-class baseline")
st.metric("Baseline accuracy", f"{baseline_accuracy:.3f}")
st.metric(
    "Baseline balanced accuracy",
    f"{baseline_balanced_accuracy:.3f}",
)
st.write(
    "This baseline ignores every feature and always predicts the most "
    "frequent training label."
)

Your auditor now has an honest reference point for model performance. Next, you will build a mixed-type pipeline that can learn from the training data.

Train a Reproducible Pipeline

Your Streamlit app now proves that a majority-class baseline misses one class. That honest failure gives the trained model a meaningful result to beat.

The next goal is a reproducible scikit-learn Pipeline that learns from numeric columns plus categorical columns. Keeping preprocessing inside the pipeline protects the held-out test data from preprocessing leakage.

In this step, get ready to:
  • Build numeric preprocessing with missing-value imputation plus feature scaling.
  • Build categorical preprocessing with one-hot encoding.
  • Train logistic regression on the training split before evaluating accuracy, balanced accuracy, plus ROC AUC.
Add the pipeline imports

The next sections need transformers for mixed data types. They also need a classifier plus an extra evaluation metric.

  • In app.py, replace the existing import block with this complete import block:
import pandas as pd
import streamlit as st
from sklearn.compose import ColumnTransformer
from sklearn.compose import make_column_selector
from sklearn.datasets import fetch_openml
from sklearn.dummy import DummyClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.metrics import balanced_accuracy_score
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

What Do These Imports Provide?

  • The transformer imports prepare numeric columns plus categorical columns through separate paths.
  • The pipeline import connects preprocessing to the classifier in one fitted object.
  • The metric import measures how well predicted probabilities rank the two classes.
  • Save app.py.

The running app should reload with the dataset preview plus both baseline metrics still visible. The terminal should finish the rerun without a traceback.

Seeing an Import Problem?

  • Check that every import matches the block above.
  • Confirm that the activated terminal prompt still shows .venv.

Ask for help with diagnosing the pipeline imports.

Build the preprocessing paths

Numeric columns can contain missing values. Median imputation fills those gaps before scaling puts numeric features on comparable ranges.

  • In app.py, find baseline_balanced_accuracy = balanced_accuracy_score(y_test, baseline_predictions).
  • Add this numeric transformer directly below that line:
numeric_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler()),
    ]
)

How Does the Numeric Path Work?

  • The imputer learns replacement medians from the training data supplied during fitting.
  • The scaler learns the training distribution used to transform each numeric feature.
  • The pipeline preserves the order of those two operations.
  • Save app.py.

Your baseline section should reload without a traceback. The numeric preprocessing path is now valid Python that the model can fit.

Numeric Transformer Not Loading?

  • Check that the two step tuples remain inside the steps list.
  • Confirm that the transformer appears before st.title("Census Income Model Auditor").

Ask for help with fixing the numeric transformer.

Categorical values need a different path. Column selectors let the preprocessor route each column according to its data type.

  • Locate the closing parenthesis of numeric_transformer in app.py.
  • Add this column transformer directly below it:
preprocessor = ColumnTransformer(
    transformers=[
        (
            "numeric",
            numeric_transformer,
            make_column_selector(dtype_exclude="category"),
        ),
        (
            "categorical",
            OneHotEncoder(handle_unknown="ignore"),
            make_column_selector(dtype_include="category"),
        ),
    ]
)

How Are Columns Routed?

  • The numeric selector sends non-category columns through the numeric transformer.
  • The categorical selector sends category columns through the encoder.
  • Unknown-category handling keeps a new category from breaking prediction.
  • Save app.py.

The app should reload with its preview plus baseline section intact. This successful rerun confirms that both data-type paths assemble into one preprocessor.

Preprocessor Raising an Error?

  • Check that each transformer entry contains a name, a transformer, plus a selector.
  • Confirm that both selector values spell category exactly.
  • Check that handle_unknown="ignore" remains inside OneHotEncoder.

Ask for help with repairing the column transformer.

Train and evaluate the model

The classifier belongs after the preprocessor in the same pipeline. Fitting that combined object on the training split keeps every learned preprocessing value away from the held-out rows.

  • Locate the closing parenthesis of preprocessor in app.py.
  • Add this model pipeline directly below it:
model = Pipeline(
    steps=[
        ("preprocessor", preprocessor),
        (
            "classifier",
            LogisticRegression(max_iter=1000, random_state=RANDOM_STATE),
        ),
    ]
)

Why Keep Everything in One Pipeline?

The pipeline fits the preprocessor before fitting the classifier. Prediction reuses the same fitted transformations for the held-out data.

This structure makes the complete training process reproducible from one object.

  • Save app.py.

The app should complete another rerun with the baseline still visible. This confirms that the preprocessing stage connects to logistic regression successfully.

Model Pipeline Not Assembling?

  • Check that the preprocessor step appears before the classifier step.
  • Confirm that RANDOM_STATE matches the constant near the top of app.py.

Ask for help with fixing the model pipeline.

The model now has a complete training recipe. Predictions support class-label metrics while probabilities support ROC AUC.

  • Locate the closing parenthesis of model in app.py.
  • Add the training plus evaluation code directly below it:
model.fit(X_train, y_train)
model_predictions = model.predict(X_test)
model_probabilities = model.predict_proba(X_test)[:, 1]

model_accuracy = accuracy_score(y_test, model_predictions)
model_balanced_accuracy = balanced_accuracy_score(y_test, model_predictions)
model_roc_auc = roc_auc_score(y_test, model_probabilities)

What Does This Evaluation Measure?

  • Accuracy measures the overall share of correct held-out predictions.
  • Balanced accuracy gives each class equal weight through average recall.
  • ROC AUC evaluates the ranking produced by positive-class probabilities.

The rerun can take a little longer while logistic regression fits. A short pause is expected.

  • Save app.py.

The terminal should finish training without a traceback. The browser should retain the dataset preview plus baseline section.

Training Did Not Finish?

  • Confirm that model.fit receives X_train plus y_train.
  • Check that predictions use X_test.
  • Confirm that the probability expression ends with [:, 1].

Ask for help with diagnosing model training.

The calculated values become useful once the dashboard places them beside the weak baseline. That comparison shows whether learning from features improves performance across both classes.

  • Scroll to the baseline explanation at the bottom of app.py.
  • Add this trained-model section directly below it:
st.write("## Trained pipeline")
st.metric("Model accuracy", f"{model_accuracy:.3f}")
st.metric("Model balanced accuracy", f"{model_balanced_accuracy:.3f}")
st.metric("Model ROC AUC", f"{model_roc_auc:.3f}")

What Will the Dashboard Show?

The three metrics come from predictions on the held-out test split. Their placement beneath the baseline makes the comparison visible in one browser view.

Before you save this final display chunk, which metric do you expect to reveal the clearest improvement over the constant baseline?

  • Save app.py.

The browser should show a trained pipeline section beneath the baseline. You should see model accuracy, model balanced accuracy, plus model ROC AUC.

That is the core model working: your app now learns from mixed-type training data before measuring performance on untouched held-out rows.

Trained Metrics Missing?

  • Check that the four display lines appear after the baseline explanation.
  • Confirm that each metric variable matches the evaluation code above.
  • Review the terminal for the first traceback shown during the rerun.

Ask for help with restoring the trained metrics.

✔️ Awesome, I've got everything!

Your app.py now matches the complete trained-pipeline version for this step.

ⓧ I'd like to double check the full code

  • Compare your app.py with this complete file:
import pandas as pd
import streamlit as st
from sklearn.compose import ColumnTransformer
from sklearn.compose import make_column_selector
from sklearn.datasets import fetch_openml
from sklearn.dummy import DummyClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.metrics import balanced_accuracy_score
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

RANDOM_STATE = 42
PROTECTED_COLUMNS = ["sex", "race"]

st.set_page_config(page_title="Census Income Model Auditor", layout="wide")


@st.cache_data
def load_data():
    X, y = fetch_openml(
        "adult",
        version=2,
        as_frame=True,
        return_X_y=True,
    )
    positive_label = y.value_counts().idxmin()
    y_binary = (y == positive_label).astype(int)
    return X, y_binary, str(positive_label)


X, y, positive_label = load_data()
X_model = X.drop(columns=PROTECTED_COLUMNS)

X_train, X_test, y_train, y_test = train_test_split(
    X_model,
    y,
    test_size=0.2,
    random_state=RANDOM_STATE,
    stratify=y,
)

baseline = DummyClassifier(strategy="most_frequent")
baseline.fit(X_train, y_train)
baseline_predictions = baseline.predict(X_test)
baseline_accuracy = accuracy_score(y_test, baseline_predictions)
baseline_balanced_accuracy = balanced_accuracy_score(y_test, baseline_predictions)

numeric_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler()),
    ]
)

preprocessor = ColumnTransformer(
    transformers=[
        (
            "numeric",
            numeric_transformer,
            make_column_selector(dtype_exclude="category"),
        ),
        (
            "categorical",
            OneHotEncoder(handle_unknown="ignore"),
            make_column_selector(dtype_include="category"),
        ),
    ]
)

model = Pipeline(
    steps=[
        ("preprocessor", preprocessor),
        (
            "classifier",
            LogisticRegression(max_iter=1000, random_state=RANDOM_STATE),
        ),
    ]
)

model.fit(X_train, y_train)
model_predictions = model.predict(X_test)
model_probabilities = model.predict_proba(X_test)[:, 1]

model_accuracy = accuracy_score(y_test, model_predictions)
model_balanced_accuracy = balanced_accuracy_score(y_test, model_predictions)
model_roc_auc = roc_auc_score(y_test, model_probabilities)

st.title("Census Income Model Auditor")
st.write(
    "Educational model audit only. Do not use this app to make employment, "
    "credit, benefits, or other high-impact decisions."
)
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

st.write("## Dataset preview")
st.dataframe(X.head(10))
st.write(
    "The protected columns `sex` and `race` remain visible for auditing but "
    "are excluded from model training."
)

st.write("## Designed obstacle: majority-class baseline")
st.metric("Baseline accuracy", f"{baseline_accuracy:.3f}")
st.metric(
    "Baseline balanced accuracy",
    f"{baseline_balanced_accuracy:.3f}",
)
st.write(
    "This baseline ignores every feature and always predicts the most "
    "frequent training label."
)

st.write("## Trained pipeline")
st.metric("Model accuracy", f"{model_accuracy:.3f}")
st.metric("Model balanced accuracy", f"{model_balanced_accuracy:.3f}")
st.metric("Model ROC AUC", f"{model_roc_auc:.3f}")

This reference includes every import, preprocessing stage, model operation, metric, plus dashboard element introduced through this step.

Your reproducible classifier now beats the baseline with evidence from held-out data. Next, you will use permutation importance to explain which original features influence its performance.

Explain and Package the Model

The trained Streamlit dashboard now reports holdout performance. A reviewer still needs evidence about which original features influenced that performance.

Permutation importance measures how much the holdout score changes when one feature is shuffled. This step turns that explanation into a reviewer-ready dashboard plus a reproducible README.md.

In this step, get ready to:
  • Measure each original feature's influence on held-out balanced accuracy.
  • Display a sorted importance table with a bar chart plus clear limitations.
  • Package the project purpose with reproducible setup instructions in README.md.
Calculate permutation importance

Permutation importance tests a fitted model against held-out data. It reveals which original columns matter most to balanced accuracy without changing the trained pipeline.

  • In app.py, locate the SimpleImputer import.
  • Insert the permutation-importance import between the existing imputer import and logistic-regression import:
from sklearn.impute import SimpleImputer
from sklearn.inspection import permutation_importance
from sklearn.linear_model import LogisticRegression

What Does This Import Add?

The permutation_importance function measures how model performance changes after feature values are shuffled. The shuffled score reveals predictive dependence on each feature.

  • In app.py, locate the line that calculates model_roc_auc.
  • Insert the held-out importance calculation directly below that line:
importance_X = X_test.iloc[:1000]
importance_y = y_test.iloc[:1000]
importance_result = permutation_importance(
    model,
    importance_X,
    importance_y,
    scoring="balanced_accuracy",
    n_repeats=3,
    random_state=RANDOM_STATE,
)
importance_table = pd.DataFrame(
    {
        "feature": X_model.columns,
        "importance": importance_result.importances_mean,
    }
).sort_values(by="importance", ascending=False)

What Does This Calculation Do?

  • The importance_X sample contains the first 1,000 held-out feature rows.
  • The importance_y sample preserves the matching held-out labels.
  • The function scores each shuffle with balanced accuracy across 3 repeats.
  • The pandas table maps the mean results back to the original columns.
  • The descending sort places the strongest measured influence first.

Before you refresh, do you expect the existing dashboard to complete its rerun after adding the new calculation?

  • Save app.py.
  • Switch back to the browser showing your running app.
  • Refresh the browser page.

You should see the existing dataset preview plus both metric sections render again. This confirms the new importance calculation completes before the dashboard is displayed.

Dashboard Does Not Return?

Check that the new import sits between the existing imputer and logistic-regression imports. Confirm that the importance calculation appears after model_roc_auc.

Ask for help with debugging the permutation-importance calculation.

Display the explanation and limitations

A calculation becomes useful to a reviewer when its result is visible. The table provides exact values while the chart makes the ranking easy to scan.

  • In app.py, locate the final st.metric line in the trained-pipeline section.
  • Insert the permutation-importance display directly below that line:
st.write("## Permutation importance on held-out data")
st.write(
    "Higher values mean shuffling that original feature reduced balanced "
    "accuracy more strongly. Negative values can occur when a feature adds noise."
)
st.dataframe(importance_table)
st.bar_chart(importance_table.set_index("feature"))

How Does the Display Explain the Model?

  • The explanatory note defines what higher importance values mean.
  • The table preserves the sorted feature names with their measured values.
  • The chart uses each feature as its index to visualize the same ranking.
  • Save app.py.
  • Switch back to the running dashboard.
  • Refresh the browser page.

You should see a sorted permutation-importance table below the trained-model metrics. A bar chart should show the same original features in ranked form.

Importance Results Are Missing?

Confirm that the display code appears after the model metrics. Check that importance_table uses the same spelling in the calculation and display sections.

Ask for help with fixing the missing importance table or chart.

Feature importance can show predictive dependence. A responsible audit also states what the analysis cannot establish.

  • In app.py, locate the st.bar_chart line in the permutation-importance section.
  • Insert the limitations section directly below that line:
st.write("## Limitations")
st.write(
    "This project uses historical census data, a single train-test split, and "
    "a simple linear classifier. Predictive association is not causation, and "
    "good aggregate metrics do not establish fairness or suitability for real decisions."
)

Why State These Limits?

The limitations identify the dataset context plus the evaluation scope. They prevent predictive association from being presented as causation or proof of fairness.

  • Save app.py.
  • Switch back to the running dashboard.
  • Refresh the browser page.

You should see a Limitations section below the importance chart. Its text should name historical data plus the single train-test split plus the simple linear classifier.

Limitations Section Is Missing?

Confirm that the limitations code sits below the importance chart. Check that both st.write calls have matching parentheses.

Ask for help with repairing the limitations section.

✔️ Awesome, I've got everything!

Your app.py now contains the complete core dashboard with model metrics plus permutation importance plus limitations.

ⓧ I'd like to double check the full code

import pandas as pd
import streamlit as st
from sklearn.compose import ColumnTransformer
from sklearn.compose import make_column_selector
from sklearn.datasets import fetch_openml
from sklearn.dummy import DummyClassifier
from sklearn.impute import SimpleImputer
from sklearn.inspection import permutation_importance
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.metrics import balanced_accuracy_score
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

RANDOM_STATE = 42
PROTECTED_COLUMNS = ["sex", "race"]

st.set_page_config(page_title="Census Income Model Auditor", layout="wide")


@st.cache_data
def load_data():
    X, y = fetch_openml(
        "adult",
        version=2,
        as_frame=True,
        return_X_y=True,
    )
    positive_label = y.value_counts().idxmin()
    y_binary = (y == positive_label).astype(int)
    return X, y_binary, str(positive_label)


X, y, positive_label = load_data()
X_model = X.drop(columns=PROTECTED_COLUMNS)

X_train, X_test, y_train, y_test = train_test_split(
    X_model,
    y,
    test_size=0.2,
    random_state=RANDOM_STATE,
    stratify=y,
)

baseline = DummyClassifier(strategy="most_frequent")
baseline.fit(X_train, y_train)
baseline_predictions = baseline.predict(X_test)
baseline_accuracy = accuracy_score(y_test, baseline_predictions)
baseline_balanced_accuracy = balanced_accuracy_score(y_test, baseline_predictions)

numeric_transformer = Pipeline(
    steps=[
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler()),
    ]
)

preprocessor = ColumnTransformer(
    transformers=[
        (
            "numeric",
            numeric_transformer,
            make_column_selector(dtype_exclude="category"),
        ),
        (
            "categorical",
            OneHotEncoder(handle_unknown="ignore"),
            make_column_selector(dtype_include="category"),
        ),
    ]
)

model = Pipeline(
    steps=[
        ("preprocessor", preprocessor),
        (
            "classifier",
            LogisticRegression(max_iter=1000, random_state=RANDOM_STATE),
        ),
    ]
)

model.fit(X_train, y_train)
model_predictions = model.predict(X_test)
model_probabilities = model.predict_proba(X_test)[:, 1]

model_accuracy = accuracy_score(y_test, model_predictions)
model_balanced_accuracy = balanced_accuracy_score(y_test, model_predictions)
model_roc_auc = roc_auc_score(y_test, model_probabilities)

importance_X = X_test.iloc[:1000]
importance_y = y_test.iloc[:1000]
importance_result = permutation_importance(
    model,
    importance_X,
    importance_y,
    scoring="balanced_accuracy",
    n_repeats=3,
    random_state=RANDOM_STATE,
)
importance_table = pd.DataFrame(
    {
        "feature": X_model.columns,
        "importance": importance_result.importances_mean,
    }
).sort_values(by="importance", ascending=False)

st.title("Census Income Model Auditor")
st.write(
    "Educational model audit only. Do not use this app to make employment, "
    "credit, benefits, or other high-impact decisions."
)
st.write(f"Positive class selected as the less frequent target label: {positive_label}")

st.write("## Dataset preview")
st.dataframe(X.head(10))
st.write(
    "The protected columns `sex` and `race` remain visible for auditing but "
    "are excluded from model training."
)

st.write("## Designed obstacle: majority-class baseline")
st.metric("Baseline accuracy", f"{baseline_accuracy:.3f}")
st.metric(
    "Baseline balanced accuracy",
    f"{baseline_balanced_accuracy:.3f}",
)
st.write(
    "This baseline ignores every feature and always predicts the most "
    "frequent training label."
)

st.write("## Trained pipeline")
st.metric("Model accuracy", f"{model_accuracy:.3f}")
st.metric("Model balanced accuracy", f"{model_balanced_accuracy:.3f}")
st.metric("Model ROC AUC", f"{model_roc_auc:.3f}")

st.write("## Permutation importance on held-out data")
st.write(
    "Higher values mean shuffling that original feature reduced balanced "
    "accuracy more strongly. Negative values can occur when a feature adds noise."
)
st.dataframe(importance_table)
st.bar_chart(importance_table.set_index("feature"))

st.write("## Limitations")
st.write(
    "This project uses historical census data, a single train-test split, and "
    "a simple linear classifier. Predictive association is not causation, and "
    "good aggregate metrics do not establish fairness or suitability for real decisions."
)
Package the project story

The dashboard demonstrates the analysis while README.md makes the work reproducible. It also gives a reviewer the purpose plus the technical highlights plus the responsible-use boundary in one place.

  • In the Visual Studio Code file sidebar, use the new-file control to create README.md inside census-income-model-auditor.
  • Add the project purpose plus the technical highlights by pasting this first section:
# Census Income Model Auditor

A free local machine-learning dashboard that compares a majority-class baseline with a reproducible logistic regression pipeline on the Adult Census dataset.

## What this project demonstrates

- Mixed numeric and categorical preprocessing
- Missing-value imputation and one-hot encoding
- A stratified holdout evaluation
- Accuracy, balanced accuracy, and ROC AUC
- Model-agnostic permutation importance
- Responsible-use and limitation statements

What Does This Section Add?

The opening identifies the dashboard's purpose. The list turns implementation details into capabilities a reviewer can scan.

  • Save README.md.
  • Confirm that README.md appears beside app.py in the file sidebar.

README File Is Missing?

Confirm that you created README.md inside census-income-model-auditor. Check that the filename does not have an extra text-file extension.

Ask for help with finding the README file.

  • Append the reproducible Windows commands plus the responsible-use boundary to README.md by pasting this section:
## Run on Windows PowerShell

```powershell
python --version
python -m venv .venv
.\.venv\Scripts\Activate.ps1
py -m pip install -r requirements.txt
streamlit run app.py
```

The first app run downloads the dataset through scikit-learn and requires internet access.

## Responsible-use boundary

This project is educational. It must not be used for employment, credit, benefits, or other high-impact decisions. The protected columns `sex` and `race` are excluded from model training and reserved for optional auditing.

Why Include These Details?

The command sequence gives another Windows learner a reproducible path from environment creation to the running app. The boundary states where this educational model must not be used.

  • Save README.md.
  • Confirm that the editor shows five PowerShell commands under the Windows heading.

Commands Do Not Render as Code?

Check that the PowerShell commands remain between the opening and closing Markdown code fences. Confirm that the responsible-use heading begins after the closing fence.

Ask for help with fixing the README code block.

  • Complete README.md by appending the limitations plus the resume bullet:
## Limitations

- The dataset is historical and may encode social inequities.
- A single holdout split does not measure every source of uncertainty.
- Permutation importance describes predictive dependence, not causation.
- Aggregate performance does not prove fairness across groups.

## Resume bullet

Built an explainable binary-classification dashboard with a leakage-resistant scikit-learn pipeline, held-out evaluation, a majority-class baseline, permutation importance, and responsible-AI limitations.

How Does This Finish the Story?

The limitations keep the project claims proportionate to the evidence. The resume bullet condenses the implementation plus the evaluation approach into one shareable result.

  • Save README.md.
  • Confirm that the final section ends with the resume bullet.

README Sections Are Out of Order?

Place the limitations after the responsible-use boundary. Keep the resume bullet as the final section in README.md.

Ask for help with checking the README structure.

✔️ Awesome, I've got everything!

Your README.md now packages the project purpose plus reproducible setup plus responsible-use limits plus resume-ready wording.

ⓧ I'd like to double check the full code

# Census Income Model Auditor

A free local machine-learning dashboard that compares a majority-class baseline with a reproducible logistic regression pipeline on the Adult Census dataset.

## What this project demonstrates

- Mixed numeric and categorical preprocessing
- Missing-value imputation and one-hot encoding
- A stratified holdout evaluation
- Accuracy, balanced accuracy, and ROC AUC
- Model-agnostic permutation importance
- Responsible-use and limitation statements

## Run on Windows PowerShell

```powershell
python --version
python -m venv .venv
.\.venv\Scripts\Activate.ps1
py -m pip install -r requirements.txt
streamlit run app.py
```

The first app run downloads the dataset through scikit-learn and requires internet access.

## Responsible-use boundary

This project is educational. It must not be used for employment, credit, benefits, or other high-impact decisions. The protected columns `sex` and `race` are excluded from model training and reserved for optional auditing.

## Limitations

- The dataset is historical and may encode social inequities.
- A single holdout split does not measure every source of uncertainty.
- Permutation importance describes predictive dependence, not causation.
- Aggregate performance does not prove fairness across groups.

## Resume bullet

Built an explainable binary-classification dashboard with a leakage-resistant scikit-learn pipeline, held-out evaluation, a majority-class baseline, permutation importance, and responsible-AI limitations.
  • Return to the Visual Studio Code terminal running Streamlit.
  • Stop the current Streamlit server by pressing Ctrl+C.

Before you run the finished app, which new evidence do you expect to see below the trained-model metrics?

  • Start the complete dashboard from the activated environment by running this command:
streamlit run app.py

What Does This Final Check Prove?

The command starts the complete app.py from the activated project environment. A successful render proves the model plus the explanation plus the limitations execute together.

  • Switch to the browser page opened by Streamlit.

You should see the full dashboard with the dataset preview plus baseline metrics plus trained-model metrics. Below them, you should see a sorted permutation-importance table plus a bar chart plus the limitations section.

  • Return to Visual Studio Code.
  • Select README.md in the file sidebar.

You should see the project purpose plus Windows setup commands plus technical highlights plus limitations plus the resume bullet. That completes the reviewer-ready story for your model auditor.

Complete Dashboard Does Not Render?

Confirm that the terminal still shows the activated .venv environment. Compare app.py with the full-code reference above if the terminal reports a Python syntax problem.

Ask for help with debugging the complete model auditor.

Secret mission

Audit Performance Across Groups

Compare held-out model performance across sex and race. Keep both protected columns outside training. Explain why group differences are signals for further investigation.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely on your Windows computer. Keeping the project folder creates no ongoing charges.

Resources you used:

  • The running Streamlit dashboard process.
  • The local census-income-model-auditor folder.
  • The repository-local .venv environment inside census-income-model-auditor.

Keep everything running

No action is needed while you are actively reviewing the model auditor.

  • Keep the running Streamlit dashboard available for demonstrations.
  • Keep the census-income-model-auditor folder on your computer.
  • Keep the .venv environment for reproducible local runs.

Your dashboard stays available while its local process runs. No billing continues in the background.

Pause - I'll come back to this later

Pausing ends the local dashboard process while preserving every project file.

  • Return to Visual Studio Code from earlier.
  • Close the PowerShell terminal that is running the dashboard.
  • Refresh the dashboard's browser tab.
  • Close Visual Studio Code.
  • Keep the census-income-model-auditor folder on disk.
  • Keep the .venv environment inside the project folder.

The browser can no longer reconnect to the local dashboard. Your code remains ready for another session.

Delete - I don't want to use this again

Deleting is the only irreversible choice here. Your dashboard has no cloud resources that can create surprise charges.

  • Return to Visual Studio Code from earlier.
  • Close the PowerShell terminal that is running the dashboard.
  • Close Visual Studio Code.

The local dashboard process has stopped. Your files remain until you delete the project folder.

  • Use Windows search to open the system file browser.
  • Navigate to the parent directory containing census-income-model-auditor.
  • Select the census-income-model-auditor folder.
  • Delete the selected folder.
  • Empty the recycle bin to complete the removal.
  • Confirm that census-income-model-auditor no longer appears in its parent directory.

The census-income-model-auditor folder is gone. Its .venv environment is gone with it.

Nice Work!

Nice Work!

You made it! Your local Streamlit model auditor now presents model evidence within clear responsible-use boundaries.

You've learned how to:

  • Expose a majority-class baseline by measuring its balanced accuracy on held-out Census data.
  • Train a leakage-resistant pipeline with scikit-learn on mixed-type data with missing values.
  • Create an explainable project story with ranked permutation importance plus documented limitations in README.md.
  • Secret Mission: Audit balanced accuracy across sex groups plus race groups while keeping both protected attributes outside model training.

Ready to quiz yourself? See how much of the model audit workflow you can recall.