Skip to content

Framing an ML Problem & Getting the Data

What you’ll learn

  • how to turn a business request into a concrete ML problem statement
  • supervised vs. unsupervised, regression vs. classification, batch vs. online
  • why you pick a performance measure before you train anything
  • RMSE vs. MAE, and when to prefer one over the other
  • loading a dataset with pandas and taking a first look at its structure

The scenario

Imagine you’ve just joined a real-estate company. Your manager wants a model that predicts a district’s median house value from census data — population, median income, number of rooms, and so on — so a downstream investment system can decide where to buy.

Before writing a single line of model code, you need to answer some questions:

  • What’s the business objective? The prediction feeds another ML system, so getting the number right (not just a category) matters.
  • What’s the current solution? Experts estimate prices manually today, often off by more than 20%. That’s your baseline to beat.
  • Is there a continuous flow of data? No — census data is static, so plain batch learning is fine; no need for online learning.

Answering these turns “predict housing prices” into something precise: this is a supervised, multiple regression, univariate, batch-learning problem — supervised because each row has a known target (the price), regression because we predict a number, multiple because we use several features, and univariate because we only predict one value per district.

diagram Framing the problem mermaid
From a business question to a concrete ML task.

Selecting a performance measure

For regression problems, the typical measure is the Root Mean Square Error (RMSE):

text
RMSE(X, h) = sqrt( (1/m) * Σ (h(x_i) - y_i)² )
text
RMSE(X, h) = sqrt( (1/m) * Σ (h(x_i) - y_i)² )
  • mm is the number of instances you’re evaluating on
  • h(x_i)h(x_i) is your model’s prediction for instance ii
  • y_iy_i is the true label for instance ii

RMSE corresponds to the Euclidean (ℓ2) norm — it squares errors before averaging, so it punishes large errors more heavily. If your dataset has a lot of outlier districts, the Mean Absolute Error (MAE) — the ℓ1 or “Manhattan” norm — is often a better choice, since it treats every unit of error equally:

text
MAE(X, h) = (1/m) * Σ |h(x_i) - y_i|
text
MAE(X, h) = (1/m) * Σ |h(x_i) - y_i|

The higher the norm index, the more a metric focuses on large errors and ignores small ones — which is exactly why RMSE is more sensitive to outliers than MAE.

Loading the data

The dataset is the classic California Housing Prices dataset — one row per census “district,” with columns like longitudelongitude, latitudelatitude, housing_median_agehousing_median_age, total_roomstotal_rooms, total_bedroomstotal_bedrooms, populationpopulation, householdshouseholds, median_incomemedian_income, the target median_house_valuemedian_house_value, and a categorical ocean_proximityocean_proximity column.

load_housing.py
import os
import pandas as pd
 
HOUSING_PATH = os.path.join("datasets", "housing")
 
def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path, "housing.csv")
    return pd.read_csv(csv_path)
 
housing = load_housing_data()
print(housing.head())
print(housing.info())
load_housing.py
import os
import pandas as pd
 
HOUSING_PATH = os.path.join("datasets", "housing")
 
def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path, "housing.csv")
    return pd.read_csv(csv_path)
 
housing = load_housing_data()
print(housing.head())
print(housing.info())

head()head() shows the first few rows, and info()info() gives you row counts, dtypes, and how many non-null values each column has — a fast way to spot missing data. Here, total_bedroomstotal_bedrooms has fewer non-null values than the rest, which flags it for cleanup later.

For the one text column, value_counts()value_counts() shows which categories exist and how common each is:

Inspect the categorical column
print(housing["ocean_proximity"].value_counts())
Inspect the categorical column
print(housing["ocean_proximity"].value_counts())
text
<1H OCEAN    9136
INLAND       6551
NEAR OCEAN   2658
NEAR BAY     2290
ISLAND          5
Name: ocean_proximity, dtype: int64
text
<1H OCEAN    9136
INLAND       6551
NEAR OCEAN   2658
NEAR BAY     2290
ISLAND          5
Name: ocean_proximity, dtype: int64

And describe()describe() summarizes every numerical column (count, mean, std, min, quartiles, max) in one call — a quick way to spot capped or oddly-scaled attributes before you go any further.

🧪 Try It Yourself

Exercise 1 – Load a CSV with pandas

Exercise 2 – Count categories with value_counts

Exercise 3 – Summarize numerical columns

Next

Continue to Creating a Test Set (Avoiding Data Snooping) — before you explore this data any further, you need to set part of it aside and never look at it again until the very end.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did