Fix a Wine Classifier with Scaling

Use feature scaling to improve and evaluate a Wine dataset KNN classifier.

Introduction

30 Second Summary

Two measurements can describe the same thing while using completely different number ranges. The larger-looking numbers can drown out the smaller ones even when both matter.

In this project, you will build a shareable Google Colab notebook that tests whether feature scaling improves a K-nearest neighbors wine classifier. The finished notebook shows the model's accuracy before scaling. It also reveals the result after scaling through a confusion matrix.

What You'll Build

Your finished notebook reveals the wine classes on a scatter plot before showing how scaling changes the classifier's held-out accuracy.

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

  • A labeled scatter plot of proline versus hue that reveals the three wine classes across mismatched number ranges.
  • A repeatable raw KNN baseline that reports its held-out accuracy using the same stratified split every time.
  • A scaled KNN pipeline with a side-by-side accuracy result. A three-class confusion matrix shows the remaining mistakes. A short recommendation explains which workflow to keep.
  • Secret Mission: Compare 3, 5, 7, and 9 neighbors to select the best tested scaled model.

Do I need any experience before starting?

You'll need a Windows computer with internet access. You'll also need a Google Account. Previous Python or notebook experience is optional because the project covers account creation and Colab setup from scratch.

Before We Start

This first checkpoint connects your notebook experiment to the practical question behind it. You are building a Wine-class classifier to test whether feature scaling improves a KNN workflow.

Set Up a Reproducible Colab Runtime

Your wine classifier needs the same library behavior every time you reopen it. A changing cloud runtime can shift package versions beneath the notebook.

Google Colab gives you a browser-based notebook without local setup. You'll pin runtime 2026.07 before testing the project's imports.

In this step, get ready to:
  • Sign in to Google Colab with a Google Account.
  • Create the Wine Scaling Lab notebook on runtime 2026.07.
  • Run the import cell to verify that the pinned libraries load.
Sign in to Google Colab

Colab notebooks are stored in Google Drive. Signing in gives this experiment a saved home you can reopen.

This step touches your Google Account credentials. Google's sign-in page handles the entire flow.

  • Choose the tab that matches your current Google Account access.

✔️ I Have a Google Account

  • Visit https://colab.research.google.com/ in your Windows browser.
  • Select Sign in.
  • Enter your Google Account details.
  • Complete any security prompts until you reach the Colab start screen.

ⓧ I Need a Google Account

  • Visit https://colab.research.google.com/ in your Windows browser.
  • Select Sign in.
  • Select Create account.
  • Choose the account type that fits you.
  • Enter the requested profile details.
  • Select Next to move through each account-creation screen.
  • Return to https://colab.research.google.com/ after your account is ready.
  • Sign in with your new Google Account.

That account hurdle is handled: your Colab workspace is ready to hold the experiment.

Create the pinned notebook

A Colab runtime controls the Python version and installed packages behind your notebook. Pinning the runtime keeps those dependencies consistent when you return later.

  • Create a blank notebook from the Colab start screen.
  • Select the notebook title in the upper-left corner.
  • Enter Wine Scaling Lab as the notebook name.
  • Select Runtime from the notebook's top menu.
  • Select Change runtime type.
  • Open the Runtime Version dropdown.
  • Select 2026.07.
  • Select the confirmation button at the bottom of the dialog.

You should see 2026.07 selected as the notebook's runtime version. Your package environment is now fixed to the project's tested snapshot.

Why Pin the Runtime?

Cloud runtimes update their package snapshots over time. Pinning this runtime keeps the experiment tied to one known set.

  • Python 3.12.13 runs the notebook code.
  • The scikit-learn 1.6.1 package provides the dataset and machine learning tools.
  • Matplotlib 3.10.0 provides the plotting tools.
Run the import check

The notebook now has a stable environment. One import cell checks that every library needed by the later experiment is available together.

  • Select the first code cell in Wine Scaling Lab.
  • Paste this import check into the selected code cell:
import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

What Does This Code Check?

  • The matplotlib.pyplot import provides plotting tools through plt.
  • The load_wine import provides the built-in Wine recognition dataset.
  • The ConfusionMatrixDisplay import prepares the final evaluation chart.
  • The train_test_split import prepares a repeatable training and test split.
  • The KNeighborsClassifier import provides the distance-based classifier.
  • The make_pipeline import provides the reusable preprocessing workflow.
  • The StandardScaler import provides feature standardization.
  • The final print statement creates a visible readiness signal.

Before you run this cell, do you expect every import to finish without an error on the pinned runtime?

  • Run the cell by selecting the play button on its left.

You'll see Libraries imported successfully. below the cell. That's the setup hurdle cleared: all seven imports load together on the pinned runtime.

Imports Not Loading?

  • Confirm that Runtime Version still displays 2026.07.
  • Run the import cell again after the runtime reconnects.

Still stuck? Help me troubleshoot why my Colab import cell is failing.

✔️ Awesome, I've got everything!

Your import cell matches the complete setup code.

ⓧ I'd like to double check the full code

import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

Your notebook now opens with a pinned runtime and a verified library set. Next up, you'll load the Wine dataset and turn two of its measurements into a labeled scatter plot.

Reveal the Dataset in a Scatter Plot

Your Google Colab notebook is ready on the pinned runtime. The import cell confirms that the required libraries load.

A scatter plot shows how wine classes sit across two measurements with very different ranges. In this step, you'll create that first visual result from the Wine dataset.

In this step, get ready to:
  • Load the Wine dataset and inspect its dimensions.
  • Locate the proline and hue measurements.
  • Display a labeled scatter plot for all three wine classes.
Load and inspect the Wine dataset

The Wine recognition dataset is built into scikit-learn. Each row pairs measurements for one sample with a class label.

  • Create a new code cell beneath the import cell using the notebook's add-cell control.
  • Add the dataset-loading code plus its summary output by copying this into the new cell:
wine = load_wine()
X = wine.data
y = wine.target

print(f"Samples: {X.shape[0]}")
print(f"Features: {X.shape[1]}")
print(f"Classes: {list(wine.target_names)}")

What does this code do?

  • The load_wine() function loads the dataset into wine.
  • The X variable stores the measurement columns used as model features.
  • The y variable stores the class assigned to each sample.
  • The print statements expose the dataset dimensions.
  • The final print statement lists the available class names.
  • Run the new cell using its play control.

The output reports 178 samples. It also reports 13 features.

The final line lists three class names.

Good progress. Your notebook now holds the measurements and labels needed for the experiment.

Dataset cell not running?

  • Confirm that the import cell ran before this cell.
  • Rerun the import cell if the Colab runtime restarted.
  • Check that load_wine uses the same spelling as the import cell.

Still stuck? Help me troubleshoot why my Wine dataset cell does not run in Google Colab.

Locate the columns and plot each class

The feature names provide a reliable way to find each measurement's column position. The resulting indexes let the plot read proline values from one column and hue values from another.

  • Create another code cell beneath the dataset cell using the notebook's add-cell control.
  • Add the column lookup plus the scatter plot by copying this code into the new cell:
proline_index = wine.feature_names.index("proline")
hue_index = wine.feature_names.index("hue")

for class_index, class_name in enumerate(wine.target_names):
    class_rows = y == class_index
    plt.scatter(
        X[class_rows, proline_index],
        X[class_rows, hue_index],
        label=class_name,
        alpha=0.75,
    )

plt.xlabel("proline")
plt.ylabel("hue")
plt.title("Wine samples on mismatched feature scales")
plt.legend()
plt.show()

How does the plot work?

  • The two index() calls find the numeric positions of proline and hue.
  • The loop processes one class during each pass.
  • The class_rows mask selects only the samples from the current class.
  • The plt.scatter() call asks Matplotlib to draw those samples with their class label.
  • The plt.xlabel() call labels the horizontal axis.
  • The plt.ylabel() call labels the vertical axis.
  • The plt.legend() call connects each color to its class name.
  • The plt.show() call renders the completed chart.

Before you run the cell, predict whether the three classes will occupy separate regions.

  • Run the plotting cell using its play control.

You'll see proline on the horizontal axis. Hue appears on the vertical axis.

The legend identifies three class colors. The chart title reads Wine samples on mismatched feature scales.

That's your first visible result. The notebook now turns raw measurements into a class map.

Scatter plot missing or incomplete?

  • Confirm that the plotting cell appears after the dataset cell.
  • Check that proline matches the copied feature name.
  • Check that hue matches the copied feature name.

Need another pair of eyes? Help me troubleshoot why my Wine scatter plot is missing or incomplete.

Your notebook now contains the imports plus the dataset cells plus the plotting cell.

✔️ Awesome, I've got everything!

Your notebook loads the Wine dataset and displays the labeled scatter plot.

ⓧ I'd like to double check the full code

  • Compare your notebook from top to bottom against this reference:
import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

wine = load_wine()
X = wine.data
y = wine.target

print(f"Samples: {X.shape[0]}")
print(f"Features: {X.shape[1]}")
print(f"Classes: {list(wine.target_names)}")

proline_index = wine.feature_names.index("proline")
hue_index = wine.feature_names.index("hue")

for class_index, class_name in enumerate(wine.target_names):
    class_rows = y == class_index
    plt.scatter(
        X[class_rows, proline_index],
        X[class_rows, hue_index],
        label=class_name,
        alpha=0.75,
    )

plt.xlabel("proline")
plt.ylabel("hue")
plt.title("Wine samples on mismatched feature scales")
plt.legend()
plt.show()

What should this contain?

This reference preserves the import cell from the previous step. It adds the dataset summary plus the complete scatter plot from this step.

Your visual map is ready. Next, you'll test whether raw feature scales can mislead a K-nearest neighbors classifier.

Test an Unscaled KNN Baseline

Your scatter plot made the Wine dataset visible in your Google Colab notebook. The points now show how the three classes sit across proline and hue.

The next question is whether a distance-based classifier reads those measurements fairly. K-nearest neighbors (KNN) compares distances between samples.

A large raw range can overpower a smaller one before the model predicts a class. This step exposes that flaw with a deliberately unscaled baseline.

In this step, get ready to:
  • Measure the raw range of each selected feature.
  • Create a reproducible stratified split of the dataset.
  • Measure the unscaled KNN model's held-out accuracy.
Expose the mismatched feature ranges

A feature's range measures the distance from its smallest value to its largest value. Features with very different ranges can contribute unevenly to a distance calculation.

  • Use the code-cell control beneath the scatter plot output to add a new cell.
  • Calculate each selected feature's range by copying this code into the new cell:
proline_values = X[:, proline_index]
hue_values = X[:, hue_index]
proline_range = max(proline_values) - min(proline_values)
hue_range = max(hue_values) - min(hue_values)

print(f"Proline range: {proline_range:.2f}")
print(f"Hue range: {hue_range:.2f}")

What does this code do?

  • The proline_values variable selects every proline measurement.
  • The hue_values variable selects every hue measurement.
  • The range variables subtract each feature's minimum from its maximum.
  • The print lines make the difference between the raw scales visible.
  • Predict which feature has the wider range before you run the cell.
  • Run the cell using the control on its left.

You'll see Proline range: followed by a number. The next line starts with Hue range:.

The proline number is dramatically larger than the hue number. That comparison is your first concrete clue that raw distances can be misleading.

Ranges missing from the output?

If Colab reports an undefined name, rerun the earlier dataset cell. This restores the variables used by the range calculation.

Rerun the range cell after the earlier cells finish.

Ask for help diagnosing the missing range output.

Build the repeatable raw baseline

A train/test split holds back part of the dataset for an honest evaluation. Using stratification preserves each class's share in both groups.

  • Use the code-cell control beneath the range output to add another cell.
  • Create the repeatable split plus the raw KNN baseline by copying this code into the new cell:
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.30,
    random_state=42,
    stratify=y,
)

raw_model = KNeighborsClassifier(n_neighbors=5)
raw_model.fit(X_train, y_train)
raw_accuracy = raw_model.score(X_test, y_test)
print(f"Raw KNN accuracy: {raw_accuracy:.1%}")

How does the baseline work?

  • The test_size=0.30 setting reserves 30 percent of the samples for testing.
  • The random_state=42 setting makes the split repeatable.
  • The stratify=y setting preserves the class proportions.
  • The raw_model uses the five nearest training samples to classify each test sample.
  • The raw_accuracy variable stores the model's mean test accuracy.
  • Predict whether raw KNN can fully overcome the range mismatch before you run the cell.
  • Run the baseline cell using the control on its left.

You'll see Raw KNN accuracy: followed by the held-out score. The score is below a perfect result.

Some held-out samples remain misclassified. That shortfall is intentional.

You now have a reproducible baseline for a fair comparison. The raw model measured distance across the mismatched feature scales you exposed above.

Raw accuracy missing?

If a variable is undefined, rerun the notebook cells above in order. This restores the dataset plus the feature indexes before the split runs.

If the score line is missing, confirm the final print line remains inside the baseline cell.

Ask for help debugging the baseline cell.

✔️ Awesome, I've got everything!

Your notebook now includes the feature-range evidence plus the repeatable raw baseline. Both outputs are ready for the comparison ahead.

ⓧ I'd like to double check the full code

import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

wine = load_wine()
X = wine.data
y = wine.target

print(f"Samples: {X.shape[0]}")
print(f"Features: {X.shape[1]}")
print(f"Classes: {list(wine.target_names)}")

proline_index = wine.feature_names.index("proline")
hue_index = wine.feature_names.index("hue")

for class_index, class_name in enumerate(wine.target_names):
    class_rows = y == class_index
    plt.scatter(
        X[class_rows, proline_index],
        X[class_rows, hue_index],
        label=class_name,
        alpha=0.75,
    )

plt.xlabel("proline")
plt.ylabel("hue")
plt.title("Wine samples on mismatched feature scales")
plt.legend()
plt.show()

proline_values = X[:, proline_index]
hue_values = X[:, hue_index]
proline_range = max(proline_values) - min(proline_values)
hue_range = max(hue_values) - min(hue_values)

print(f"Proline range: {proline_range:.2f}")
print(f"Hue range: {hue_range:.2f}")

X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.30,
    random_state=42,
    stratify=y,
)

raw_model = KNeighborsClassifier(n_neighbors=5)
raw_model.fit(X_train, y_train)
raw_accuracy = raw_model.score(X_test, y_test)
print(f"Raw KNN accuracy: {raw_accuracy:.1%}")

Your unscaled KNN baseline now gives you evidence of the problem. Next, you'll correct how the model handles feature magnitudes. Then you'll compare the new score against this baseline.

Fix the Model with a Scaling Pipeline

Your Google Colab notebook now holds a repeatable KNN baseline. The feature-range check showed that large-scale measurements can dominate its distance calculations.

Those distorted distances can steer the classifier toward misleading neighbors. The model needs feature scaling before it measures closeness.

A pipeline keeps StandardScaler with KNN in one workflow. It learns scaling from the training data before it evaluates the held-out test data.

In this step, get ready to:
  • Combine StandardScaler with KNN in one pipeline.
  • Fit the pipeline on the existing training split.
  • Compare scaled accuracy with the raw baseline.
Build the scaled pipeline

The StandardScaler() step standardizes each feature using the training data. The pipeline sends those standardized values into KNeighborsClassifier(n_neighbors=5).

  • Add a new code cell directly below the cell that prints the raw KNN accuracy.
  • Populate the new cell by copying the code below:
scaled_model = make_pipeline(
    StandardScaler(),
    KNeighborsClassifier(n_neighbors=5),
)
scaled_model.fit(X_train, y_train)
scaled_accuracy = scaled_model.score(X_test, y_test)
improvement = scaled_accuracy - raw_accuracy

print(f"Scaled KNN accuracy: {scaled_accuracy:.1%}")
print(f"Accuracy change after scaling: {improvement:+.1%}")

What Does This Pipeline Do?

  • The make_pipeline function fixes the operation order. Scaling runs before classification.
  • The scaled_model.fit(X_train, y_train) line learns scaling from the training split. It trains KNN on the transformed values.
  • The scaled_model.score(X_test, y_test) line applies the learned transformation to the held-out features. It returns mean accuracy.
  • The improvement variable stores the difference between scaled accuracy and raw accuracy.
  • The final print statements make the comparison visible.
Run the accuracy comparison

The new cell uses the existing raw_accuracy value as its baseline. This keeps both results tied to the same train and test split.

Before you run the cell, do you expect scaling to change the held-out accuracy?

  • Run the new cell with its play button.

You should see Scaled KNN accuracy: on one line. You should see Accuracy change after scaling: on the next line.

That is the key fix complete: your classifier now measures neighbors after scaling every feature consistently.

Pipeline Cell Not Running?

  • Run the earlier notebook cells from top to bottom if the new cell reports that a previous variable is unavailable.
  • Compare the copied cell with the reference tab below if the pipeline cell reports a syntax problem.

Still stuck? Help me debug my scaling pipeline cell..

✔️ Awesome, I've got everything!

Your pipeline cell matches the target. The scaled comparison output is visible.

ⓧ I'd like to double check the full code

import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

wine = load_wine()
X = wine.data
y = wine.target

print(f"Samples: {X.shape[0]}")
print(f"Features: {X.shape[1]}")
print(f"Classes: {list(wine.target_names)}")

proline_index = wine.feature_names.index("proline")
hue_index = wine.feature_names.index("hue")

for class_index, class_name in enumerate(wine.target_names):
    class_rows = y == class_index
    plt.scatter(
        X[class_rows, proline_index],
        X[class_rows, hue_index],
        label=class_name,
        alpha=0.75,
    )

plt.xlabel("proline")
plt.ylabel("hue")
plt.title("Wine samples on mismatched feature scales")
plt.legend()
plt.show()

proline_values = X[:, proline_index]
hue_values = X[:, hue_index]
proline_range = max(proline_values) - min(proline_values)
hue_range = max(hue_values) - min(hue_values)

print(f"Proline range: {proline_range:.2f}")
print(f"Hue range: {hue_range:.2f}")

X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.30,
    random_state=42,
    stratify=y,
)

raw_model = KNeighborsClassifier(n_neighbors=5)
raw_model.fit(X_train, y_train)
raw_accuracy = raw_model.score(X_test, y_test)
print(f"Raw KNN accuracy: {raw_accuracy:.1%}")

scaled_model = make_pipeline(
    StandardScaler(),
    KNeighborsClassifier(n_neighbors=5),
)
scaled_model.fit(X_train, y_train)
scaled_accuracy = scaled_model.score(X_test, y_test)
improvement = scaled_accuracy - raw_accuracy

print(f"Scaled KNN accuracy: {scaled_accuracy:.1%}")
print(f"Accuracy change after scaling: {improvement:+.1%}")

Your scaled pipeline now reports its advantage over the raw baseline. Next, you'll use a confusion matrix to reveal which wine classes it predicts correctly.

Turn Results into a Recommendation

Your Google Colab notebook now reports the raw accuracy beside the result from your scaled KNN pipeline.

A single score hides which wine classes the model handles well. A confusion matrix reveals that class-level performance.

A concise executive summary connects the scale problem to the measured result. It turns your experiment into a recommendation someone can act on.

In this step, get ready to:
  • Display class-level prediction results in a confusion matrix.
  • Turn the measured accuracy change into an executive recommendation.
Inspect class-level predictions

The scaled model has already scored the complete test set. Individual predictions let you inspect how those results are distributed across the three wine classes.

  • Add a new code cell below the scaled accuracy cell in the notebook from earlier.
  • Paste this code into the new cell:
scaled_predictions = scaled_model.predict(X_test)
ConfusionMatrixDisplay.from_predictions(
    y_test,
    scaled_predictions,
    display_labels=wine.target_names,
)
plt.title("Scaled KNN confusion matrix")
plt.show()

What does this code do?

  • The scaled_predictions variable stores one predicted class for every sample in X_test.
  • The ConfusionMatrixDisplay.from_predictions method compares the predictions with the known labels in y_test.
  • The display_labels argument replaces numeric class values with the names from wine.target_names.
  • Counts on the diagonal represent correct predictions for each class.
  • Before you run this cell, predict where the largest counts will appear.
  • Run the code cell.

You should see a labeled three-class grid titled Scaled KNN confusion matrix. Its cells show how the scaled model's predictions line up with the held-out labels.

Matrix Not Appearing?

  • Run the earlier notebook cells in order if scaled_model, X_test, or y_test is missing.
  • Check that the final line is plt.show() if the matrix does not render.

Help me debug why my scaled KNN confusion matrix is not appearing in Google Colab.

Write the executive recommendation

The matrix shows how performance is distributed across classes. Your final output now needs to explain the original scale problem plus the measured accuracy change.

  • Add a new code cell below the confusion matrix cell.
  • Paste this executive summary code into the new cell:
print("Executive summary:")
print("- Distance-based models can be distorted by mismatched feature scales.")
print(f"- Scaling changed test accuracy from {raw_accuracy:.1%} to {scaled_accuracy:.1%}.")
print("- Recommendation: keep scaling and KNN together in one pipeline.")

How Does This Summary Work?

  • The first statement gives the result a clear reporting label.
  • The second statement names the feature-scale problem exposed by the experiment.
  • The formatted statement inserts the measured values from raw_accuracy and scaled_accuracy.
  • The final statement recommends keeping preprocessing inside the model workflow.
  • Before you run this final cell, predict which workflow the recommendation will keep.
  • Run the code cell.

You should see Executive summary: followed by three lines. Those lines identify the scale problem, report the observed accuracy change, and recommend keeping scaling with KNN in one pipeline.

Your experiment now ends with class-level evidence plus a recommendation that a reader can act on.

Summary Not Printing Correctly?

  • Run the earlier notebook cells in order if raw_accuracy or scaled_accuracy is missing.
  • Check the braces around each accuracy variable if the values do not appear inside the summary.

Help me debug why my executive summary does not show the raw and scaled accuracy values.

✔️ Awesome, I've got everything!

Your notebook now contains the complete Wine scaling experiment plus its final recommendation.

ⓧ I'd like to double check the full code

import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

print("Libraries imported successfully.")

wine = load_wine()
X = wine.data
y = wine.target

print(f"Samples: {X.shape[0]}")
print(f"Features: {X.shape[1]}")
print(f"Classes: {list(wine.target_names)}")

proline_index = wine.feature_names.index("proline")
hue_index = wine.feature_names.index("hue")

for class_index, class_name in enumerate(wine.target_names):
    class_rows = y == class_index
    plt.scatter(
        X[class_rows, proline_index],
        X[class_rows, hue_index],
        label=class_name,
        alpha=0.75,
    )

plt.xlabel("proline")
plt.ylabel("hue")
plt.title("Wine samples on mismatched feature scales")
plt.legend()
plt.show()

proline_values = X[:, proline_index]
hue_values = X[:, hue_index]
proline_range = max(proline_values) - min(proline_values)
hue_range = max(hue_values) - min(hue_values)

print(f"Proline range: {proline_range:.2f}")
print(f"Hue range: {hue_range:.2f}")

X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.30,
    random_state=42,
    stratify=y,
)

raw_model = KNeighborsClassifier(n_neighbors=5)
raw_model.fit(X_train, y_train)
raw_accuracy = raw_model.score(X_test, y_test)
print(f"Raw KNN accuracy: {raw_accuracy:.1%}")

scaled_model = make_pipeline(
    StandardScaler(),
    KNeighborsClassifier(n_neighbors=5),
)
scaled_model.fit(X_train, y_train)
scaled_accuracy = scaled_model.score(X_test, y_test)
improvement = scaled_accuracy - raw_accuracy

print(f"Scaled KNN accuracy: {scaled_accuracy:.1%}")
print(f"Accuracy change after scaling: {improvement:+.1%}")

scaled_predictions = scaled_model.predict(X_test)
ConfusionMatrixDisplay.from_predictions(
    y_test,
    scaled_predictions,
    display_labels=wine.target_names,
)
plt.title("Scaled KNN confusion matrix")
plt.show()

print("Executive summary:")
print("- Distance-based models can be distorted by mismatched feature scales.")
print(f"- Scaling changed test accuracy from {raw_accuracy:.1%} to {scaled_accuracy:.1%}.")
print("- Recommendation: keep scaling and KNN together in one pipeline.")

How to Use This Reference

This reference shows every notebook cell in cumulative order. Its final two sections match the confusion matrix and executive summary you added in this step.

Secret mission

Tune the Number of Neighbors

Your scaled classifier still depends on how many neighbors get a vote. Compare 3, 5, 7, and 9 on the same split. Identify the best tested configuration.

Clean Up Your Resources

Clean Up Your Resources

Your notebook uses Google Colab's free-of-charge service, so this project has no paid resources or ongoing project costs. Decide whether to keep your work available, pause your session, or delete the notebook.

Resources you used:

  • Your Wine Scaling Lab notebook stored in Google Drive.
  • Your temporary Google Colab runtime on version 2026.07.

Keep everything running

No action needed. Choose this if you want to share the scaling experiment or extend it with more model comparisons.

  • Keep the Wine Scaling Lab notebook in Google Drive.
  • Use the saved plots to demonstrate the effect of mismatched feature scales.
  • Share the notebook when you want someone else to review your scaling recommendation.
  • Continue from the saved cells if Colab replaces the temporary runtime.

Pause - I'll come back to this later

Pausing ends your active browser session while keeping the saved notebook available for your return.

  • Wait for your latest notebook changes to finish saving in Google Drive.
  • Close the browser tab containing Wine Scaling Lab.
  • Keep Wine Scaling Lab in Google Drive for your return.

Colab may delete the idle virtual machine. Your saved notebook remains in Google Drive.

Delete - I don't want to use this again

Deleting clears the saved project artifact from Google Drive. Closing the notebook tab leaves the temporary runtime idle.

Keep Anything You Need

Removing the notebook can feel final. Copy any plots or results you want to keep before you continue.

  • Go to Google Drive in your signed-in browser session.
  • Find the Wine Scaling Lab notebook in your files.
  • Remove the Wine Scaling Lab notebook from Google Drive.
  • Confirm the notebook no longer appears in your files.
  • Return to the Colab browser tab from earlier.
  • Close the browser tab containing Wine Scaling Lab.

Colab may delete the virtual machine after it becomes idle. Neither resource creates an ongoing project cost.

Nice Work!

Nice Work!

You did it! Your Wine Scaling Lab notebook in Google Colab now turns mismatched feature scales into a tested recommendation for a distance-based classifier.

You've learned how to:

  • Build an exploratory visualization that reveals the different scales of proline and hue across three wine classes.
  • Run a controlled model comparison that measures how feature scaling changes a K-nearest neighbors classifier on the same train/test split.
  • Turn accuracy plus a confusion matrix into an executive recommendation to keep preprocessing inside the pipeline.
  • Secret Mission: Compared four neighbor counts and selected the best tested scaled KNN configuration.

Ready to quiz yourself?