Build a Kepler Exoplanet Classifier
Train a local model to classify Kepler objects with NASA archive data.
Introduction
30 Second Summary
Space surveys collect more possible planet signals than any person could compare one by one. A useful analysis needs to sort those signals while staying honest about what the evidence can prove.
In this project, you will retrieve current Kepler Objects of Interest from the NASA Exoplanet Archive and train a local classifier to distinguish confirmed objects from false positives. The workflow turns a deliberate missing-data failure into a reproducible machine learning pipeline with class-specific evaluation.
What You'll Build
You'll run the finished analysis cell by cell and watch live Kepler measurements become a class chart, an evaluation report, a normalized confusion matrix, and held-out predictions tied to named objects.
By the end of this project, you'll have:
- A live catalog analysis that shows the current target counts and missing measurements from selected Kepler data.
- A visible class distribution chart plus an observed raw-model failure that demonstrates why incomplete measurements need preprocessing.
- A fitted classification pipeline with precision, recall, F1 score, a normalized confusion matrix, and ten held-out prediction examples.
- Secret Mission: Compare balanced and unweighted classifiers to expose the recall trade-off between the two target classes.
Are there any prerequisites?
You need basic Python syntax, an internet connection, and permission to install packages on your Windows computer. The project runs locally in Visual Studio Code with public NASA data at no cost.
Before We Start
Before any hands-on work, commit to building a local Kepler Objects of Interest classifier to practise Python, machine learning, data science, and astrophysics. Its purpose is practice with archived labels while scientific discovery and confirmation remain outside its scope.
Prepare a Reproducible Local Workspace
Your Kepler classifier depends on several Python packages with exact versions. A project-specific virtual environment keeps those packages separate from the rest of your computer.
You will prepare Visual Studio Code for local analysis. You will also verify Python before installing the pinned packages.
In this step, get ready to:
- Prepare Python support in Visual Studio Code.
- Create an isolated workspace for the classifier.
- Complete the workspace with pinned data-science packages.
Prepare Python support
Every pinned package in this project requires Python 3.11 or newer. Checking the active runtime now prevents package compatibility problems inside the project.
- Press the Windows key to open the Windows search bar.
- Type Visual Studio Code and press Enter to open it.
- Click Terminal in the top menu bar.
- Select New Terminal to open an integrated Windows PowerShell terminal.
- Check the Python version available to Visual Studio Code by running this command:
python --version
What does this command check?
This command asks the Python executable available in the current PowerShell terminal to report its version. The result determines which setup path you need below.
✔️ I see version 3.11 or higher
You should see Python followed by version 3.11 or a higher version number. Your runtime supports every package pinned for this project.
ⓧ I see an older version
A version below 3.11 cannot run the pinned package versions used in this project.
- Open the official Python Windows installation guide in your browser.
- Choose a current Python release numbered 3.11 or newer.
- Download the Windows installer from Python.org.
- Run the downloaded installer.
- Select Add Python to PATH on the first installer screen.
- Select Install Now to install the newer runtime.
The newer runtime is now available on your computer. Visual Studio Code needs a fresh session before its terminal can find the updated path.
- Close every Visual Studio Code window.
- Press the Windows key to reopen the Windows search bar.
- Type Visual Studio Code and press Enter to reopen it.
- Open a new integrated terminal from the Terminal menu.
- Check the updated Python version by running:
python --version
What should I see?
You should now see Python followed by version 3.11 or a higher version number. The newer runtime is ready for the project.
ⓧ Command not found
PowerShell cannot locate a Python installation through your current system path. Installing Python with its path option enabled makes the command available to Visual Studio Code.
- Open the official Python Windows installation guide in your browser.
- Choose a current Python release numbered 3.11 or newer.
- Download the Windows installer from Python.org.
- Run the downloaded installer.
- Select Add Python to PATH on the first installer screen.
- Select Install Now to install Python.
Python is now installed locally. Restarting Visual Studio Code gives its terminal the updated path.
- Close every Visual Studio Code window.
- Press the Windows key to reopen the Windows search bar.
- Type Visual Studio Code and press Enter to reopen it.
- Open a new integrated terminal from the Terminal menu.
- Confirm that PowerShell can find Python by running:
python --version
What should I see?
You should see Python followed by version 3.11 or a higher version number. PowerShell can now reach the required runtime.
Visual Studio Code uses its Microsoft Python extension to discover interpreters. The Jupyter extension adds cell execution through Run Cell.
- Click the Extensions icon in the left Activity Bar.
- Enter Python in the extension search field.
- Select the Python extension published by Microsoft.
- Click Install if the extension is missing.
- Replace the search text with Jupyter.
- Select the Jupyter extension published by Microsoft.
- Click Install if the extension is missing.
Your editor can now discover Python environments. It can also run the # %% cells used later in the analysis.
Extensions will not install?
Confirm that your computer has an internet connection. Restart Visual Studio Code if an extension remains stuck during installation.
If your organization manages extension access, ask its administrator to allow the Microsoft Python and Jupyter extensions.
Help me troubleshoot a Visual Studio Code extension installation problem.
Create and isolate the workspace
The project needs one named folder for its future analysis file. Its local .venv environment keeps the classifier's dependencies isolated from other Python projects.
- Click File in the Visual Studio Code menu bar.
- Click Open Folder.
- Select Desktop in the Windows folder dialog.
- Click New Folder.
- Enter kepler-exoplanet-classifier as the folder name.
- Select the new kepler-exoplanet-classifier folder.
- Click Select Folder to open the workspace.
- Confirm that you trust the folder if Visual Studio Code asks because you created it locally.
You should see kepler-exoplanet-classifier at the top of the Explorer sidebar. The folder is empty because the classifier file belongs to the next step.
- Click Terminal in the top menu bar.
- Select New Terminal to open a terminal inside kepler-exoplanet-classifier.
- Choose PowerShell from the terminal profile menu if another shell opens.
- Create the project-specific virtual environment by running:
python -m venv .venv
What does this command create?
The command creates a complete Python environment inside the .venv folder. Packages installed after activation stay inside this project.
- Confirm that a .venv folder appears in the Explorer sidebar.
The .venv folder did not appear?
Confirm that the terminal is inside the kepler-exoplanet-classifier folder. Check that the Python version command still reports 3.11 or newer.
Help me find out why Python did not create my virtual environment.
- Activate the new environment in PowerShell by running:
.\.venv\Scripts\Activate.ps1
How do I know it is active?
Your PowerShell prompt should now include .venv near the beginning. Commands using Python now target this project's isolated environment.
PowerShell blocked the activation script?
Changing an execution policy deserves care. The command below enables locally created scripts for your current Windows user.
- Ask your administrator before changing the policy if your computer is managed by an organization.
- Allow local scripts for your Windows user by running:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
PowerShell asks you to confirm the change. Enter Y only after you have reviewed the command.
- Retry the activation command shown directly above this callout.
Help me activate my Python virtual environment safely in PowerShell.
- Press Ctrl+Shift+P to open the Visual Studio Code Command Palette.
- Enter Python: Select Interpreter.
- Select the Python interpreter located inside the .venv folder.
- Confirm that the interpreter shown in the Visual Studio Code status bar includes .venv.
Your workspace now points to its isolated Python interpreter. Package installs from the activated terminal go into that same environment.
Install and verify the packages
The project needs four pinned packages for data loading, machine learning, charting, and cell execution. Pinning each version makes the local workspace reproducible.
The first installation can take a few minutes while PowerShell downloads the packages. A stream of download messages during this wait is expected.
- Install all four pinned packages into the active .venv environment by running these commands:
python -m pip install pandas==3.1.1
python -m pip install scikit-learn==1.9.1
python -m pip install matplotlib==3.11.2
python -m pip install ipykernel==7.4.0
What did you install?
- pandas loads the live catalog into tabular data.
- scikit-learn provides the preprocessing pipeline and classification tools.
- Matplotlib displays the class distribution and confusion matrix.
- ipykernel connects the active Python environment to Visual Studio Code's cell runner.
Each installation should finish without a package error. Your PowerShell prompt should return with .venv still visible.
A package failed to install?
Confirm that your terminal prompt includes .venv. Check that your active Python version is 3.11 or newer.
A network restriction can also interrupt package downloads. Retry after checking your internet connection.
Help me troubleshoot the package installation output from my activated virtual environment.
Before you check, do you think all four imports will load from the isolated environment?
- Verify every package import by running:
python -c "import pandas, sklearn, matplotlib, ipykernel; print('Environment ready')"
What should I see?
You should see Environment ready printed in the terminal. This proves that Python can import every required package from the active environment.
That's the setup locked in. Your local workspace can now run the data analysis without borrowing packages from another project.
Do not see Environment ready?
A message about a missing module usually means the package was installed outside the active .venv. Activate the environment again before repeating the installation command for that package.
Confirm that Visual Studio Code still has the interpreter from .venv selected. The terminal prompt should also include .venv.
Help me diagnose why the environment verification command did not print Environment ready.
Your reproducible Python workspace is ready. Next, you will retrieve the live Kepler catalog and reveal its class distribution.
Reveal the Kepler Catalog
Your isolated environment is ready to run the data science stack. The next goal is to turn your empty workspace into a live view of the Kepler catalog.
The NASA Exoplanet Archive stores Kepler Objects of Interest with archive dispositions. Before modeling, you need to inspect the class distribution to see whether both target classes are represented equally.
In this step, get ready to:
- Create kepler_classifier.py with the catalog query plus ten physical features.
- Inspect a summary of the live Kepler catalog.
- Plot the disposition counts in the Interactive Window.
Connect the script to the catalog
A TAP query lets your script request selected columns from the archive. The query returns one archive label plus ten physical measurements for each object.
- Click the New File icon in the Explorer sidebar.
- Type kepler_classifier.py as the file name.
- Press Enter to create the file inside kepler-exoplanet-classifier.
- Add the analysis header plus catalog connection by pasting this code into the empty file:
# %% [markdown]
# Kepler Exoplanet Classifier
# Educational model only. Archive dispositions remain the scientific labels.
# %%
import pandas as pd
import matplotlib.pyplot as plt
DATA_URL = (
"https://exoplanetarchive.ipac.caltech.edu/TAP/sync?"
"query=select+kepoi_name%2Ckoi_disposition%2Ckoi_period%2Ckoi_duration%2C"
"koi_depth%2Ckoi_prad%2Ckoi_teq%2Ckoi_insol%2Ckoi_impact%2Ckoi_steff%2C"
"koi_slogg%2Ckoi_srad+from+cumulative+where+koi_disposition+in+%28"
"%27CONFIRMED%27%2C%27FALSE+POSITIVE%27%29&format=csv"
)
What Does This Setup Define?
- The # %% marker divides the script into cells that Visual Studio Code can run individually.
- Pandas provides the table tools needed to load plus inspect the catalog.
- Matplotlib provides the chart used to compare the target classes.
- DATA_URL requests CSV data containing the archive label plus the selected physical measurements.
- Save kepler_classifier.py.
- Confirm the file exists by locating it in the Explorer sidebar.
You should see kepler_classifier.py listed inside the open kepler-exoplanet-classifier folder.
File Missing From Explorer?
- Check that the Explorer sidebar still shows the kepler-exoplanet-classifier folder.
- Check that the file name ends with .py.
Help me find or create the Python file in the correct workspace.
The query chooses the archive columns. The feature list gives the ten physical measurements a reusable order for later analysis.
- Place your cursor below the closing parenthesis of DATA_URL.
- Add the feature list by pasting this code:
FEATURES = [
"koi_period",
"koi_duration",
"koi_depth",
"koi_prad",
"koi_teq",
"koi_insol",
"koi_impact",
"koi_steff",
"koi_slogg",
"koi_srad",
]
Why Use These Features?
FEATURES contains transit measurements plus derived planetary measurements plus stellar measurements. Direct disposition scores plus false-positive flags stay outside the list because they already contain evidence used during labeling.
- Save kepler_classifier.py.
- Click Run Cell above the second # %% marker.
The Interactive Window opens. You should see the cell complete without an exception.
Run Cell Missing or Failing?
- Check that the file is named kepler_classifier.py.
- Check that the cell marker is written exactly as # %%.
- Confirm that the selected interpreter is the existing .venv environment.
Help me run the first Python cell in Visual Studio Code.
Load and inspect the live catalog
A pandas DataFrame stores the returned catalog as labeled rows plus columns. Setting kepoi_name as the index keeps each row connected to its Kepler Object of Interest identifier.
The first inspection also measures missing values across the selected features. That evidence shows whether the raw measurements are complete enough for modeling.
- Move your cursor below the closing bracket of FEATURES.
- Add the catalog-loading cell by pasting this code:
# %%
df = pd.read_csv(DATA_URL)
df = df.set_index("kepoi_name")
print("Dataset shape:", df.shape)
print(df.head())
class_counts = df["koi_disposition"].value_counts()
print("Class counts:")
print(class_counts)
print("Missing values by feature:")
print(df[FEATURES].isna().sum())
What Does This Inspection Reveal?
- df holds the live catalog returned by the query.
- kepoi_name becomes the row index so later predictions retain their KOI identifiers.
- class_counts stores the number of CONFIRMED objects plus FALSE POSITIVE objects.
- The missing-value summary counts unavailable measurements for every feature.
- Save kepler_classifier.py.
The live download can take a moment while the archive returns the catalog.
- Click Run Cell above the new # %% marker.
You should see the dataset shape plus five preview rows plus both disposition counts. You should also see positive missing-value totals for several features.
You now have live Kepler catalog data flowing into your local analysis.
Catalog Data Not Loading?
- Confirm that your computer still has an active internet connection.
- Compare every line of DATA_URL with the earlier code block.
- Confirm that the closing parenthesis remains directly after the final URL string.
Help me debug why the NASA catalog is not loading into the DataFrame.
Plot the disposition counts
A bar chart turns the printed counts into a visible comparison. The relative bar heights make the target imbalance easy to spot before training begins.
- Place your cursor below the missing-value summary.
- Add the class-distribution chart by pasting this code:
plt.bar(
["CONFIRMED", "FALSE POSITIVE"],
[class_counts["CONFIRMED"], class_counts["FALSE POSITIVE"]],
color=["#4C78A8", "#E45756"],
)
plt.show()
How Does the Chart Work?
- The fixed label order places CONFIRMED on the left.
- class_counts supplies the height of each bar.
- The two colors make the archive dispositions visually distinct.
- plt.show() displays the finished chart in the Interactive Window.
- Save kepler_classifier.py.
Before you run this cell, do you expect the two bars to have the same height?
- Click Run Cell above the catalog-loading # %% marker.
You should see both disposition counts in the printed output. The Interactive Window should display two bars with the FALSE POSITIVE bar taller than the CONFIRMED bar.
That visible imbalance is your first evidence about the classification problem you are building.
Chart Not Appearing?
- Confirm that plt.show() remains at the end of the cell.
- Run the catalog-loading cell from its # %% marker so class_counts exists before the chart is created.
- Confirm that the .venv interpreter remains selected.
Help me debug why the class-distribution chart is missing from the Interactive Window.
✔️ Awesome, I've got everything!
Your live catalog summary plus class-distribution chart are working. Double check that kepler_classifier.py is saved.
ⓧ I'd like to double check the full code
Compare your saved kepler_classifier.py with this complete version.
# %% [markdown]
# Kepler Exoplanet Classifier
# Educational model only. Archive dispositions remain the scientific labels.
# %%
import pandas as pd
import matplotlib.pyplot as plt
DATA_URL = (
"https://exoplanetarchive.ipac.caltech.edu/TAP/sync?"
"query=select+kepoi_name%2Ckoi_disposition%2Ckoi_period%2Ckoi_duration%2C"
"koi_depth%2Ckoi_prad%2Ckoi_teq%2Ckoi_insol%2Ckoi_impact%2Ckoi_steff%2C"
"koi_slogg%2Ckoi_srad+from+cumulative+where+koi_disposition+in+%28"
"%27CONFIRMED%27%2C%27FALSE+POSITIVE%27%29&format=csv"
)
FEATURES = [
"koi_period",
"koi_duration",
"koi_depth",
"koi_prad",
"koi_teq",
"koi_insol",
"koi_impact",
"koi_steff",
"koi_slogg",
"koi_srad",
]
# %%
df = pd.read_csv(DATA_URL)
df = df.set_index("kepoi_name")
print("Dataset shape:", df.shape)
print(df.head())
class_counts = df["koi_disposition"].value_counts()
print("Class counts:")
print(class_counts)
print("Missing values by feature:")
print(df[FEATURES].isna().sum())
plt.bar(
["CONFIRMED", "FALSE POSITIVE"],
[class_counts["CONFIRMED"], class_counts["FALSE POSITIVE"]],
color=["#4C78A8", "#E45756"],
)
plt.show()
Your local analysis now reveals the catalog's labels plus missing measurements. Next, you will test whether a raw classifier can learn from those measurements as they are.
Hit the Missing-Data Wall
Your live NASA Exoplanet Archive catalog is loaded. Its bar chart shows both target classes.
Several selected physical measurements contain missing values. In this step, you will test whether a raw logistic regression model can learn from that data.
In this step, get ready to:
- Separate the ten physical measurements from the archive label.
- Create a reproducible stratified 80/20 training and test split.
- Test a raw classifier against the missing training values.
Prepare the training and test data
A feature matrix stores the ten physical measurements used as model inputs. The target series stores the archive disposition that the model attempts to predict.
A stratified train/test split holds back 20 percent of the rows for evaluation. Scikit-learn preserves the label proportions through stratify=y.
- In kepler_classifier.py, place your cursor below the existing plt.show() line.
- Add a new cell that prepares the split by pasting this code:
# %%
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X = df[FEATURES]
y = df["koi_disposition"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
print("Missing training values:")
print(X_train.isna().sum())
What does this code do?
- The imports provide the classifier and data-splitting tools used in this experiment.
- The X matrix contains only the ten physical measurements listed in FEATURES.
- The y series contains the archive labels from koi_disposition.
- The test_size=0.2 setting reserves 20 percent of the rows for testing.
- The random_state=42 setting reproduces the same split each time.
- The final two lines reveal which training features still contain missing measurements.
- Save kepler_classifier.py by pressing Ctrl+S.
- Click Run Cell above the new cell.
You will see Missing training values: followed by a count for each feature. One or more counts should be greater than zero.
That is a strong checkpoint: your reproducible split now exposes the missing training measurements.
No Missing Counts Above Zero?
- Run the earlier catalog-loading cell again if df or FEATURES is unavailable.
- Confirm the split cell includes print(X_train.isna().sum()) if no feature counts appear.
Help me inspect the missing-value counts in my training split.
Test the raw classifier
A raw LogisticRegression model receives X_train with no preprocessing. The try block keeps the experiment in view by printing a short diagnostic for a ValueError.
- In kepler_classifier.py, place your cursor below print(X_train.isna().sum()).
- Add the raw-model experiment by pasting this code:
raw_model = LogisticRegression(max_iter=1000, solver="liblinear")
try:
raw_model.fit(X_train, y_train)
print("Raw model fitted without a missing-value error.")
except ValueError as error:
print("Expected obstacle:", type(error).__name__)
What does this code do?
- The raw_model classifier uses the binary liblinear solver.
- The fit() call attempts to learn directly from the unchanged training matrix.
- The success message prints only if model fitting completes.
- The exception branch converts a ValueError into a short diagnostic.
- Save kepler_classifier.py by pressing Ctrl+S.
Before you run this cell, do you think the fit completes or stops during training?
- Click Run Cell above the raw-model cell.
You will see Expected obstacle: ValueError in the Interactive Window. This confirms that the raw classifier reaches training but cannot process the missing feature values.
You found the limitation on purpose. The model now gives you concrete evidence that the training path needs preprocessing.
Didn't See the Expected Obstacle?
- Run the split cell before the raw-model cell if a required project name is unavailable.
- Confirm X = df[FEATURES] still selects the complete feature list if Raw model fitted without a missing-value error. appears.
- Return to the catalog cell to confirm that the current data still contains missing measurements.
Help me debug why my raw Kepler classifier did not print the expected obstacle.
✔️ Awesome, I've got everything!
Your file now contains the reproducible split and the raw-model experiment. Double check that kepler_classifier.py is saved.
ⓧ I'd like to double check the full code
- Compare your kepler_classifier.py file with this complete version:
# %% [markdown]
# Kepler Exoplanet Classifier
# Educational model only. Archive dispositions remain the scientific labels.
# %%
import pandas as pd
import matplotlib.pyplot as plt
DATA_URL = (
"https://exoplanetarchive.ipac.caltech.edu/TAP/sync?"
"query=select+kepoi_name%2Ckoi_disposition%2Ckoi_period%2Ckoi_duration%2C"
"koi_depth%2Ckoi_prad%2Ckoi_teq%2Ckoi_insol%2Ckoi_impact%2Ckoi_steff%2C"
"koi_slogg%2Ckoi_srad+from+cumulative+where+koi_disposition+in+%28"
"%27CONFIRMED%27%2C%27FALSE+POSITIVE%27%29&format=csv"
)
FEATURES = [
"koi_period",
"koi_duration",
"koi_depth",
"koi_prad",
"koi_teq",
"koi_insol",
"koi_impact",
"koi_steff",
"koi_slogg",
"koi_srad",
]
# %%
df = pd.read_csv(DATA_URL)
df = df.set_index("kepoi_name")
print("Dataset shape:", df.shape)
print(df.head())
class_counts = df["koi_disposition"].value_counts()
print("Class counts:")
print(class_counts)
print("Missing values by feature:")
print(df[FEATURES].isna().sum())
plt.bar(
["CONFIRMED", "FALSE POSITIVE"],
[class_counts["CONFIRMED"], class_counts["FALSE POSITIVE"]],
color=["#4C78A8", "#E45756"],
)
plt.show()
# %%
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X = df[FEATURES]
y = df["koi_disposition"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
print("Missing training values:")
print(X_train.isna().sum())
raw_model = LogisticRegression(max_iter=1000, solver="liblinear")
try:
raw_model.fit(X_train, y_train)
print("Raw model fitted without a missing-value error.")
except ValueError as error:
print("Expected obstacle:", type(error).__name__)
You have proved that the raw model cannot train on the catalog as loaded. Next, you will build a preprocessing pipeline that handles those gaps while keeping the test data separate.
Build a Leak-Safe Classification Pipeline
Your raw model exposed the catalog's missing measurements in the previous step. That failure proves the classifier needs preprocessing before it can learn from the training split.
A scikit-learn pipeline learns preprocessing settings from the data passed to fit(). Fitting only X_train keeps held-out data from influencing those settings.
The workflow uses imputation to fill missing measurements. Feature scaling then prepares those measurements for logistic regression.
In this step, get ready to:
- Import the preprocessing tools required by the pipeline.
- Assemble median imputation with standard scaling inside a balanced classifier workflow.
- Fit the workflow on training data to produce a class-specific report.
Add the preprocessing imports
The new cell needs one class for each preprocessing stage. It also needs the reporting tools that evaluate the resulting predictions.
- At the bottom of kepler_classifier.py, place your cursor after the existing try block.
- Add a new Python cell with the required imports by pasting this code:
# %%
from sklearn.impute import SimpleImputer
from sklearn.metrics import ConfusionMatrixDisplay, classification_report
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
Why These Imports?
- SimpleImputer supplies the missing-value repair stage.
- StandardScaler supplies the feature-scaling stage.
- Pipeline runs preprocessing before model fitting through one fit() call.
- classification_report produces the class-specific evaluation metrics.
- ConfusionMatrixDisplay makes the matrix visualization API available for the completed analysis.
- Save kepler_classifier.py with Ctrl+S.
- Click Run Cell above the new cell.
The cell should complete without a traceback. This confirms the activated environment can import every pipeline component.
Imports Not Completing?
- Check that the new block starts with # %% on its own line.
- Confirm the terminal still shows the activated .venv environment.
- Check each import against the code block above for spelling differences.
Help me diagnose why the preprocessing imports fail in my activated virtual environment.
Build the leak-safe pipeline
The pipeline fixes the exact gap exposed by the raw model. Its stages run in sequence whenever the workflow is fitted.
- In kepler_classifier.py, place the following code directly below the import lines:
kepler_pipeline = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
(
"classifier",
LogisticRegression(
class_weight="balanced",
max_iter=1000,
solver="liblinear",
),
),
]
)
What Does This Pipeline Do?
- SimpleImputer(strategy="median") replaces missing feature values using the training data's median values.
- StandardScaler() standardizes the repaired features using statistics learned during fitting.
- class_weight="balanced" adjusts the classifier's weighting for the unequal class counts.
- solver="liblinear" configures logistic regression for this binary classification task.
- The stage order fills each measurement before scaling reaches it.
- Save kepler_classifier.py.
- Click Run Cell above the pipeline cell.
The cell should complete without a traceback. This confirms Python constructed the three-stage kepler_pipeline.
Pipeline Cell Not Completing?
- Check that every opening parenthesis has a matching closing parenthesis.
- Confirm steps=[ contains the imputer tuple.
- Confirm steps=[ contains the scaler tuple.
- Confirm steps=[ contains the classifier tuple.
Help me find the syntax problem in my kepler_pipeline definition.
Fit and evaluate the pipeline
Calling fit() on the complete pipeline teaches every stage from X_train. The untouched X_test split then provides the held-out measurements used for evaluation.
- In the same cell, place the following code below the closing parenthesis of kepler_pipeline:
kepler_pipeline.fit(X_train, y_train)
predictions = kepler_pipeline.predict(X_test)
print("Balanced model report:")
print(classification_report(y_test, predictions, digits=3))
How Does Evaluation Stay Leak-Safe?
- fit(X_train, y_train) teaches the complete workflow using the training split only.
- predict(X_test) applies the fitted workflow to held-out measurements.
- classification_report(y_test, predictions, digits=3) compares the held-out labels with the model's predictions.
- Save kepler_classifier.py.
Before you run the completed cell, do you expect the missing-value failure to return or the pipeline to produce metrics?
- Click Run Cell above the completed pipeline cell.
You should see Balanced model report: followed by precision, recall, F1 score, support, and accuracy. The earlier missing-value failure should be absent.
That obstacle is cleared: your classifier now handles incomplete catalog measurements through its training-only pipeline.
Report Not Printing?
- Run the earlier cells first so X_train and y_train exist in the current Interactive Window session.
- Check that kepler_pipeline.fit(X_train, y_train) appears before the prediction line.
- Confirm the prediction line uses X_test.
Help me debug why my balanced classification report does not print.
✔️ Awesome, I've got everything!
Your saved file now matches the leak-safe pipeline checkpoint.
ⓧ I'd like to double check the full code
The complete file at this checkpoint should match the reference below.
# %% [markdown]
# Kepler Exoplanet Classifier
# Educational model only. Archive dispositions remain the scientific labels.
# %%
import pandas as pd
import matplotlib.pyplot as plt
DATA_URL = (
"https://exoplanetarchive.ipac.caltech.edu/TAP/sync?"
"query=select+kepoi_name%2Ckoi_disposition%2Ckoi_period%2Ckoi_duration%2C"
"koi_depth%2Ckoi_prad%2Ckoi_teq%2Ckoi_insol%2Ckoi_impact%2Ckoi_steff%2C"
"koi_slogg%2Ckoi_srad+from+cumulative+where+koi_disposition+in+%28"
"%27CONFIRMED%27%2C%27FALSE+POSITIVE%27%29&format=csv"
)
FEATURES = [
"koi_period",
"koi_duration",
"koi_depth",
"koi_prad",
"koi_teq",
"koi_insol",
"koi_impact",
"koi_steff",
"koi_slogg",
"koi_srad",
]
# %%
df = pd.read_csv(DATA_URL)
df = df.set_index("kepoi_name")
print("Dataset shape:", df.shape)
print(df.head())
class_counts = df["koi_disposition"].value_counts()
print("Class counts:")
print(class_counts)
print("Missing values by feature:")
print(df[FEATURES].isna().sum())
plt.bar(
["CONFIRMED", "FALSE POSITIVE"],
[class_counts["CONFIRMED"], class_counts["FALSE POSITIVE"]],
color=["#4C78A8", "#E45756"],
)
plt.show()
# %%
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X = df[FEATURES]
y = df["koi_disposition"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
print("Missing training values:")
print(X_train.isna().sum())
raw_model = LogisticRegression(max_iter=1000, solver="liblinear")
try:
raw_model.fit(X_train, y_train)
print("Raw model fitted without a missing-value error.")
except ValueError as error:
print("Expected obstacle:", type(error).__name__)
# %%
from sklearn.impute import SimpleImputer
from sklearn.metrics import ConfusionMatrixDisplay, classification_report
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
kepler_pipeline = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
(
"classifier",
LogisticRegression(
class_weight="balanced",
max_iter=1000,
solver="liblinear",
),
),
]
)
kepler_pipeline.fit(X_train, y_train)
predictions = kepler_pipeline.predict(X_test)
print("Balanced model report:")
print(classification_report(y_test, predictions, digits=3))
Your classifier now repairs missing measurements without learning preprocessing settings from held-out data. Next, you will inspect where its predictions succeed or fail.
Inspect the Model's Decisions
Your fitted scikit-learn pipeline now handles missing measurements before scaling the features. It also prints metrics for each archive label.
A single classification report can hide the individual objects behind its scores. A normalized confusion matrix exposes class-specific errors.
In this step, get ready to:
- Display a normalized confusion matrix for the held-out predictions.
- Connect each held-out prediction to its KOI name.
- Run the complete analysis from top to bottom.
Display the normalized confusion matrix
The report summarizes each class with numbers. The matrix shows which actual class produced each predicted class.
- Paste the following cell below the existing classification report code in kepler_classifier.py:
# %%
ConfusionMatrixDisplay.from_predictions(
y_test,
predictions,
labels=["FALSE POSITIVE", "CONFIRMED"],
display_labels=["False positive", "Confirmed"],
normalize="true",
cmap="Blues",
)
plt.show()
What does this code do?
- The labels list fixes the class order for the matrix.
- The display_labels list gives the axes readable names.
- The normalize="true" setting converts each actual-class row into proportions.
- The cmap="Blues" setting uses darker cells for larger proportions.
- The plt.show() call displays the completed matrix.
- Save kepler_classifier.py by pressing Ctrl+S.
Before you run this cell, which class do you expect the model to confuse more often?
- Run the new cell by clicking Run Cell above it.
You should see a two-by-two blue matrix in the Interactive Window. Each row shows how one actual class is distributed across the model's predictions.
Matrix missing or empty?
- Run the pipeline cell first so y_test contains the held-out labels.
- Confirm that predictions still contains the output from kepler_pipeline.predict(X_test).
- Check that the matrix cell sits below the pipeline cell in kepler_classifier.py.
Help me fix the confusion matrix in my Kepler classifier.
Connect predictions to KOI names
A pandas DataFrame can preserve the index from the held-out labels. That index connects each result to a specific Kepler Object of Interest.
- Paste the following code below plt.show() in the same cell:
prediction_examples = pd.DataFrame(
{
"actual": y_test,
"predicted": predictions,
},
index=y_test.index,
)
print("Held-out prediction examples:")
print(prediction_examples.head(10))
How are the examples connected?
- The actual column stores the archive labels from y_test.
- The predicted column stores the classifier output.
- The index=y_test.index argument preserves the KOI names.
- The head(10) call limits the printed sample to ten held-out objects.
- Save kepler_classifier.py by pressing Ctrl+S.
Before you run this cell, do you expect its row labels to be anonymous numbers or KOI names?
- Run the updated cell by clicking Run Cell above it.
You should see ten rows indexed by KOI names in the documented KNNNNN.DD format. The actual column shows each archive label.
The predicted column shows the corresponding model decision.
KOI names missing from the table?
- Confirm that the DataFrame uses index=y_test.index.
- Check that the source data still uses kepoi_name as its index.
- Rerun the catalog cell before rerunning the prediction examples.
Help me restore the KOI names in my prediction table.
✔️ Awesome, I've got everything!
Your saved file now includes the normalized confusion matrix plus ten KOI-indexed prediction examples.
ⓧ I'd like to double check the full code
- Compare your saved kepler_classifier.py file with this complete reference:
# %% [markdown]
# Kepler Exoplanet Classifier
# Educational model only. Archive dispositions remain the scientific labels.
# %%
import pandas as pd
import matplotlib.pyplot as plt
DATA_URL = (
"https://exoplanetarchive.ipac.caltech.edu/TAP/sync?"
"query=select+kepoi_name%2Ckoi_disposition%2Ckoi_period%2Ckoi_duration%2C"
"koi_depth%2Ckoi_prad%2Ckoi_teq%2Ckoi_insol%2Ckoi_impact%2Ckoi_steff%2C"
"koi_slogg%2Ckoi_srad+from+cumulative+where+koi_disposition+in+%28"
"%27CONFIRMED%27%2C%27FALSE+POSITIVE%27%29&format=csv"
)
FEATURES = [
"koi_period",
"koi_duration",
"koi_depth",
"koi_prad",
"koi_teq",
"koi_insol",
"koi_impact",
"koi_steff",
"koi_slogg",
"koi_srad",
]
# %%
df = pd.read_csv(DATA_URL)
df = df.set_index("kepoi_name")
print("Dataset shape:", df.shape)
print(df.head())
class_counts = df["koi_disposition"].value_counts()
print("Class counts:")
print(class_counts)
print("Missing values by feature:")
print(df[FEATURES].isna().sum())
plt.bar(
["CONFIRMED", "FALSE POSITIVE"],
[class_counts["CONFIRMED"], class_counts["FALSE POSITIVE"]],
color=["#4C78A8", "#E45756"],
)
plt.show()
# %%
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X = df[FEATURES]
y = df["koi_disposition"]
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
print("Missing training values:")
print(X_train.isna().sum())
raw_model = LogisticRegression(max_iter=1000, solver="liblinear")
try:
raw_model.fit(X_train, y_train)
print("Raw model fitted without a missing-value error.")
except ValueError as error:
print("Expected obstacle:", type(error).__name__)
# %%
from sklearn.impute import SimpleImputer
from sklearn.metrics import ConfusionMatrixDisplay, classification_report
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
kepler_pipeline = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
(
"classifier",
LogisticRegression(
class_weight="balanced",
max_iter=1000,
solver="liblinear",
),
),
]
)
kepler_pipeline.fit(X_train, y_train)
predictions = kepler_pipeline.predict(X_test)
print("Balanced model report:")
print(classification_report(y_test, predictions, digits=3))
# %%
ConfusionMatrixDisplay.from_predictions(
y_test,
predictions,
labels=["FALSE POSITIVE", "CONFIRMED"],
display_labels=["False positive", "Confirmed"],
normalize="true",
cmap="Blues",
)
plt.show()
prediction_examples = pd.DataFrame(
{
"actual": y_test,
"predicted": predictions,
},
index=y_test.index,
)
print("Held-out prediction examples:")
print(prediction_examples.head(10))
What does the complete file prove?
The cells preserve one reproducible path from archive retrieval to model evaluation. The final cell turns aggregate metrics into class-level errors plus identifiable examples.
Run the complete analysis
Running every cell in order confirms that the final outputs can be reproduced from the live catalog. It also catches hidden state from cells that were previously run out of sequence.
- Save kepler_classifier.py by pressing Ctrl+S.
- Scroll to the first cell in kepler_classifier.py.
Before you rerun the analysis, which two final outputs should prove that individual model decisions are visible?
- Run every cell from top to bottom by clicking Run Cell above each cell.
You should see a normalized two-by-two Blues confusion matrix in the Interactive Window. Each actual-class row should total approximately one.
The terminal should print ten KOI names with their actual labels beside their predicted labels.
Complete run stops early?
- Confirm that the selected interpreter points to the activated .venv.
- Rerun the catalog cell if the live archive request did not finish.
- Run the cells in top-to-bottom order so each later variable already exists.
Help me debug a full run of my Kepler classifier.
You have turned the classifier's output into evidence you can inspect. The model now exposes class-level errors plus concrete KOI examples.
Secret mission
Compare Class-Weight Trade-offs
Compare your balanced classifier with a second model that follows the catalog's original class frequencies. You will inspect how removing class balancing changes recall for confirmed objects, then choose which model better suits educational screening.
Clean Up Your Resources
Clean Up Your Resources
Everything in this project runs locally with no ongoing cloud costs. Decide whether to keep your workspace available, pause your work, or delete the project entirely.
Resources you used:
- The kepler-exoplanet-classifier folder used as your Visual Studio Code workspace. It contains kepler_classifier.py, .venv, plus the packages installed inside that environment.
Keep everything running
No action is needed. Choose this option if you plan to rerun the analysis or refine your model comparison.
- Keep the kepler-exoplanet-classifier folder in its current location.
- Keep kepler_classifier.py as the reproducible record of your class-weight comparison.
- Keep .venv inside the workspace so the pinned environment remains available.
Pause - I'll come back to this later
Shut down the active workspace to free memory. Your source code plus virtual environment remain on disk.
- Close any open Matplotlib chart windows.
- Close Visual Studio Code to end the active terminal session.
- Reopen the kepler-exoplanet-classifier folder when you are ready to continue.
- Repeat the virtual environment activation from Step 1 before rerunning the analysis.
Delete - I don't want to use this again
Deleting the workspace permanently removes your analysis. The local folder is the project's only persistent resource.
- Close any open chart windows.
- Close the current Visual Studio Code workspace.
- Find the folder that contains kepler-exoplanet-classifier in the Windows file browser.
- Select the kepler-exoplanet-classifier folder.
- Delete the selected folder permanently.
- Check the containing folder. You should no longer see kepler-exoplanet-classifier listed.
Nice Work!
Nice Work!
You did it! Your local Kepler analysis now turns live archive measurements into an honest classifier with reproducible results.
Here's what you learned:
- Retrieved live Kepler data from the NASA Exoplanet Archive through a TAP query. Visualized the target distribution before training.
- Diagnosed a missing-data failure before training. Built a leak-safe pipeline for median imputation, feature scaling, and logistic regression.
- Evaluated class-specific performance using precision, recall, F1 score, and a normalized confusion matrix. Connected held-out predictions to identifiable Kepler Objects of Interest.
- Completed the optional Secret Mission by comparing balanced and unweighted classifiers. Interpreted the CONFIRMED recall trade-off to choose a model for educational screening.
Ready to quiz yourself?