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.
flowchart LR A["Business goal:
estimate district prices"] --> B["Supervised?
Yes - labeled data"] B --> C["Regression or classification?
Regression - predicting a number"] C --> D["Batch or online?
Batch - data is static"] D --> E["Pick a performance measure
(RMSE / MAE)"]
Selecting a performance measure
For regression problems, the typical measure is the Root Mean Square Error (RMSE):
RMSE(X, h) = sqrt( (1/m) * Σ (h(x_i) - y_i)² )RMSE(X, h) = sqrt( (1/m) * Σ (h(x_i) - y_i)² )mmis the number of instances you’re evaluating onh(x_i)h(x_i)is your model’s prediction for instanceiiy_iy_iis the true label for instanceii
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:
MAE(X, h) = (1/m) * Σ |h(x_i) - y_i|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.
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())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:
print(housing["ocean_proximity"].value_counts())print(housing["ocean_proximity"].value_counts())<1H OCEAN 9136
INLAND 6551
NEAR OCEAN 2658
NEAR BAY 2290
ISLAND 5
Name: ocean_proximity, dtype: int64<1H OCEAN 9136
INLAND 6551
NEAR OCEAN 2658
NEAR BAY 2290
ISLAND 5
Name: ocean_proximity, dtype: int64And 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 coffeeWas this page helpful?
Let us know how we did
