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
- 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.
fit and predictWhich 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?
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?
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.
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?
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?
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.
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?
- Train a linear regression using only temperature and calculate its MAE and R² on the test data. Compare them with the three-feature model.
- With the three-feature model, predict cups for a 72°F weekday with a promotion.
- 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,predictand 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.
- Create your free account, or log in if you already have one.
- At the end of each of the 16 lessons, click "Mark lesson as completed."
- 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.
Create your account in 30 seconds, mark your lessons and download your certificate at the end.