Lesson 16 of 16 · Level 4: Expert

Machine Learning with Python: Your First Model with scikit-learn

  • ⏱ 13 min read
  • ❓ 4 review questions
  • ✍️ Exercise with solution
In this lesson
  1. Which problems it solves
  2. The case: predicting a café's daily sales
  3. Step 1: split training and test data
  4. Step 2: train the model
  5. Step 3: evaluate
  6. Step 4: interpret and predict
  7. Overfitting: when the model memorizes
  8. Real case: planning the week's production
  9. Common scikit-learn errors
  10. Summary
  11. Course recap
What you'll learn
  • Understand what machine learning is and which problems it solves.
  • Train a regression model with scikit-learn to predict sales.
  • Evaluate it properly with test data, MAE and R², and compare it with a baseline.
  • Recognize overfitting and know your next step as a learner.
  • Use predictions for a business decision: how much to prepare each day of the week.

Machine learning is the branch of artificial intelligence in which a program learns patterns from examples instead of following hand-written rules. Behind an online store's recommendations, credit card fraud alerts and a grocery chain's demand forecasts are models like the one you'll train today. In this final lesson you'll bring everything together (pandas, functions and good habits) with a new library: scikit-learn.

80 / 20is the typical split: 80% of the data to train and 20% to test
25.6cups of average daily error for the model, versus 68 for the baseline
3steps to use any scikit-learn model: create it, fit and predict

Which problems it solves

Regression

Predicts a number.

Tomorrow's sales, a home's price, delivery time.

Classification

Predicts a category.

Will this customer cancel? Is this email spam? Is this transaction fraud?

Clustering (unsupervised)

Finds similar groups without labels.

Customer segments based on how they shop.

In the first two, the model learns from examples where you already know the answer (past sales, customers who already left). That's why they're called supervised problems.

The case: predicting a café's daily sales

pip install scikit-learn pandas

So you get exactly the same numbers, we'll generate a year of data with a fixed seed. Cups sold depend on the temperature (°F), whether it's a weekend and whether there was a promotion, plus a random part, just like real life:

import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
dates = pd.date_range("2025-01-01", periods=365, freq="D")
df = pd.DataFrame({
    "date": dates,
    "temperature": rng.uniform(54, 86, 365).round(1),
    "weekend": (dates.dayofweek >= 5).astype(int),
    "promo": rng.integers(0, 2, 365),
})
df["cups"] = (60 + 4.5 * df["temperature"] + 120 * df["weekend"]
              + 90 * df["promo"] + rng.normal(0, 40, 365)).round()
print(df.head())
        date  temperature  weekend  promo   cups
0 2025-01-01         78.8        0      1  510.0
1 2025-01-02         68.0        0      0  376.0
2 2025-01-03         81.5        0      1  579.0
3 2025-01-04         76.3        1      1  599.0
4 2025-01-05         57.0        1      0  399.0

In a real project you'd load your data from a CSV or a database (lessons 11 and 13). The columns you use to predict are called features (X), and what you want to predict is the target (y).

Step 1: split training and test data

The most important rule in machine learning: never evaluate a model on the same data it learned from. It's like taking a test with questions you memorized. You set aside part of the data that the model won't see until the end:

from sklearn.model_selection import train_test_split

X = df[["temperature", "weekend", "promo"]]
y = df["cups"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print(len(X_train), len(X_test))
292 73

80% of the days are for training and 20% for testing. random_state fixes the split so results are reproducible.

Step 2: train the model

Every scikit-learn model works the same way: you create it, train it with fit and predict with predict. We start with the simplest and most interpretable one, linear regression:

from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train, y_train)          # learn from the examples
pred = model.predict(X_test)         # predict the days it never saw

Step 3: evaluate

from sklearn.metrics import mean_absolute_error, r2_score

print("MAE:", round(mean_absolute_error(y_test, pred), 1))
print("R²:", round(r2_score(y_test, pred), 3))

# Baseline: always predict the average
baseline = np.full(len(y_test), y_train.mean())
print("Baseline MAE:", round(mean_absolute_error(y_test, baseline), 1))
MAE: 25.6
R²: 0.846
Baseline MAE: 68.0
  • MAE (mean absolute error): on average, the model is off by about 26 cups a day. It's measured in the same units as what you predict, so anyone can understand it.
  • R²: how much of the variation in sales the model explains. 1 would be perfect; 0, no better than the average. Here, 84.6%.
  • Baseline: if you always predicted the average, you'd be off by 68 cups a day. The model cuts the error by more than half. Without a baseline, you couldn't tell whether an MAE of 25.6 is good or bad.

1. Why do you evaluate the model on X_test instead of X_train?

  • Because X_test has more rows.
  • Because scikit-learn doesn't allow predictions on training data.
  • Because you need to measure how it predicts data it never saw, which is what will happen in real life.

A model can memorize its training data. Only test data it never saw shows whether it learned a useful pattern.

Step 4: interpret and predict

print("Intercept:", round(model.intercept_, 1))
for col, coef in zip(X.columns, model.coef_):
    print(f"{col:<13}{coef:>8.1f}")

new_day = pd.DataFrame({"temperature": [80], "weekend": [1], "promo": [1]})
print("Prediction:", round(model.predict(new_day)[0]))
Intercept: 57.6
temperature       4.5
weekend         121.2
promo            92.5
Prediction: 629

A linear regression reads like a recipe: each extra degree adds about 4.5 cups, a weekend adds 121 and a promotion adds 92. Notice that the model recovered almost exactly the values we used to generate the data (4.5, 120 and 90) without being told: it learned them from the examples. With real data you won't know the truth, but the coefficients tell you which factors matter most, which is valuable for the business: how much does a promotion really bring in?

2. According to the model, how many more cups can you expect on a day with a promotion compared with an identical day without one?

  • 4.5
  • About 92
  • 629

The promo coefficient (92.5) is the effect of going from 0 to 1 in that feature, with everything else held equal.

Overfitting: when the model memorizes

Let's try a more powerful model, a random forest, which combines hundreds of decision trees:

from sklearn.ensemble import RandomForestRegressor

forest = RandomForestRegressor(n_estimators=200, random_state=42)
forest.fit(X_train, y_train)

print("Training:", round(mean_absolute_error(y_train, forest.predict(X_train)), 1))
print("Test:    ", round(mean_absolute_error(y_test, forest.predict(X_test)), 1))
Training: 17.3
Test:     36.1

The forest does very well on data it has already seen (17.3) and much worse on new data (36.1), even worse than linear regression (25.6). That's overfitting: it also learned the noise in the training data. The moral: a more complex model isn't automatically better. Start with the simplest one, compare on the test data and keep whichever generalizes best.

Average error (MAE) on test data: shorter is better

Baseline68.0
Temp. only58.0
Forest36.1
Linear, 3 features25.6

The simplest model with the right features wins. "Temp. only" is the one-feature regression from the practice exercise.

Real case: planning the week's production

A model is only worth something if it helps you decide. The owner of a café in Nashville, Tennessee, has the week's forecast and knows which days she'll run a promotion. With the trained model (continuing the lesson's code), she calculates how much to prepare:

week = pd.DataFrame({
    "day": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
    "temperature": [64, 70, 75, 73, 79, 82, 77],
    "weekend": [0, 0, 0, 0, 0, 1, 1],
    "promo": [0, 0, 1, 0, 1, 1, 0],
})
columns = ["temperature", "weekend", "promo"]
mae = mean_absolute_error(y_test, pred)                  # 25.6

week["prediction"] = model.predict(week[columns]).round()
week["prepare"] = (week["prediction"] + mae).round()    # a one-MAE cushion
print(week[["day", "temperature", "promo", "prediction", "prepare"]].to_string(index=False))
print("Expected cups this week:", int(week["prediction"].sum()))
day  temperature  promo  prediction  prepare
Mon           64      0       343.0    369.0
Tue           70      0       370.0    396.0
Wed           75      1       485.0    511.0
Thu           73      0       384.0    410.0
Fri           79      1       503.0    529.0
Sat           82      1       638.0    664.0
Sun           77      0       523.0    549.0
Expected cups this week: 3246

The prepare column adds a one-MAE "cushion" (25.6) to each prediction, because that's how far off the model is on average. That's a business decision, not a programming one: if leftovers get thrown out (pastries, cold brew), keep the cushion small; if running out loses customers, make it bigger. Look at Saturday: heat, a weekend and a promotion together produce the highest prediction (638).

Common scikit-learn errors

The first error nearly everyone hits appears when training on a single column:

ValueError: Expected a 2-dimensional container but got <class 'pandas.Series'> instead.

Models expect a table of features (2 dimensions), even if it has only one column. X_train["temperature"] is a Series (1 dimension); X_train[["temperature"]], with double brackets, is a one-column DataFrame.

Evaluating on the training data.

Split with train_test_split and measure only on X_test.

Predicting with different column names: "temp" instead of "temperature".

Use the same columns as in training; otherwise you'll see The feature names should match those that were passed during fit.

Training with empty cells.

Linear regression fails with Input X contains NaN. Clean or fill the data first with pandas (lesson 11).

Using the model outside the range of its data.

This model saw temperatures from 54°F to 86°F: don't trust what it predicts for a 100°F heat wave.

3. A model has an error of 5 on training data and 60 on test data. What does that mean?

  • It's overfitting: it memorized the training data and doesn't generalize.
  • It's excellent, because the training error is very low.
  • You should use the test data for training.

A big gap between training and test error is the classic sign of overfitting. A simpler model or more data usually helps.

4. You write LinearRegression().fit(X_train["temperature"], y_train) and get ValueError: Expected a 2-dimensional container…. How do you fix it?

  • Switch LinearRegression to RandomForestRegressor
  • Convert the temperatures to text
  • Use double brackets: X_train[["temperature"]]

Single brackets give you a Series (one dimension); double brackets give you a one-column DataFrame, the table of features every scikit-learn model expects.

Be careful with conclusions

A model learns from past data: if conditions change (a new competitor, a different season), its predictions can fail, so review it regularly. Correlation isn't causation. And if the data carries bias (for example, loan decisions made with unfair criteria), the model will learn it and repeat it.

Practice exercise: which features matter?

  1. Train a linear regression using only temperature and calculate its MAE and R² on the test data. Compare them with the three-feature model.
  2. With the three-feature model, predict cups for a 72°F weekday with a promotion.
  3. Write a function evaluate(model, columns) that trains and returns the test MAE, so you can compare models in one line.
See solution
m1 = LinearRegression().fit(X_train[["temperature"]], y_train)
p1 = m1.predict(X_test[["temperature"]])
print(round(mean_absolute_error(y_test, p1), 1), round(r2_score(y_test, p1), 3))
# 58.0 0.303  → much worse than with three features (25.6 and 0.846)

day = pd.DataFrame({"temperature": [72], "weekend": [0], "promo": [1]})
print(round(model.predict(day)[0]))
# 472

def evaluate(model, columns):
    model.fit(X_train[columns], y_train)
    return round(mean_absolute_error(y_test, model.predict(X_test[columns])), 1)

print(evaluate(LinearRegression(), ["temperature"]))                       # 58.0
print(evaluate(LinearRegression(), ["temperature", "weekend", "promo"]))   # 25.6

With temperature alone, the model can't tell weekends or promotions apart, and its error moves close to the baseline. Choosing good features usually improves a model far more than choosing a fancy algorithm.

Summary

  • Machine learning learns patterns from examples: regression for numbers, classification for categories.
  • Always split training and test data, and evaluate only on data the model never saw.
  • Every scikit-learn model works the same way: fit, predict and a metric like MAE or R².
  • Compare with a baseline and start with simple, interpretable models.
  • A big gap between training and test error signals overfitting.

Course recap

  • Level 1, Beginner: installation, variables, strings and conditionals.
  • Level 2, Intermediate: loops, data structures, functions, files and error handling.
  • Level 3, Advanced: classes, modules and environments, pandas, APIs and automation.
  • Level 4, Expert: SQL databases, matplotlib charts, web scraping and machine learning.

Congratulations, you've finished the Python course! You started with print("Hello, world!"), and now you can build programs that store data in a database, analyze it, chart it, collect it from the web and even learn from it.

🎓 Get your free certificate
  1. Create your free account, or log in if you already have one.
  2. At the end of each of the 16 lessons, click "Mark lesson as completed."
  3. When your progress reaches 100%, a "Download my certificate" button appears at the end of this lesson.

What's next? Go deeper into data with our free data analytics course, try web development with FastAPI or Django, or keep exploring scikit-learn's classification models. You'll find many more free courses in the course catalog.

You made it to the end of the course!

This is the last of the 16 lessons in Free Python Course for Beginners. If you worked through all of them, that’s real commitment.

🎓 Save your progress and get a free certificate

Create your account in 30 seconds, mark your lessons and download your certificate at the end.

Enroll for free