When I finished my first machine learning lectures, I wanted a project that used everything I had learned: data loading, preprocessing, train-test split, scaling, training, and evaluation. So I built a small but complete project: predicting whether a customer will purchase a product, based on their age, salary, experience, and gender.
The dataset is tiny (8 customers), which makes it perfect for learning — you can follow every number by hand.
The data
| Age | Salary | Experience | Gender | Purchase |
|---|---|---|---|---|
| 25 | 500 | 2 | Male | 0 |
| 32 | 800 | 5 | Female | 1 |
| 45 | 1200 | 15 | Male | 1 |
| 22 | 400 | 1 | Female | 0 |
| 38 | 950 | 8 | Male | 1 |
| 28 | 600 | 3 | Female | 0 |
| 50 | 1500 | 20 | Male | 1 |
| 30 | 700 | 4 | Female | 1 |
Purchase is the target: 1 means the customer bought, 0 means they didn't. The rest are the features.
Step 1: Load the data and encode the categories
Models work with numbers, so the Gender column needs encoding: Male = 0, Female = 1.
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score data = { 'Age': [25, 32, 45, 22, 38, 28, 50, 30], 'Salary': [500, 800, 1200, 400, 950, 600, 1500, 700], 'Experience': [2, 5, 15, 1, 8, 3, 20, 4], 'Gender': ['Male', 'Female', 'Male', 'Female', 'Male', 'Female', 'Male', 'Female'], 'Purchase': [0, 1, 1, 0, 1, 0, 1, 1] } df = pd.DataFrame(data) df['Gender'] = df['Gender'].map({'Male': 0, 'Female': 1}) Step 2: Split features and target, then train/test
X = df[['Age', 'Salary', 'Experience', 'Gender']] y = df['Purchase'] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # Train shape: (6, 4) — Test shape: (2, 4) test_size=0.2 means 20% of the data goes to testing. random_state=42 makes the split reproducible.
Step 3: Scale the features
Age ranges up to 50, Salary up to 1500. Without scaling, the bigger numbers dominate the model. StandardScaler fixes that — and the key rule is: fit only on the training data, then transform both sets.
scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # never fit_transform on test data! Step 4: Train and evaluate
Since the target is yes/no, this is a classification problem, and logistic regression is the natural first model:
model = LogisticRegression() model.fit(X_train, y_train) y_pred = model.predict(X_test) print("Actual: ", list(y_test)) # [0, 1] print("Predicted:", list(y_pred)) # [0, 1] print("Accuracy: ", accuracy_score(y_test, y_pred)) # 1.0 The model predicted both test customers correctly: accuracy 1.0.
Step 5: Predict a new customer
Now the fun part — a brand-new customer: age 36, salary 950, 6 years of experience, female. The new data must go through the same scaler (transform, not fit):
new_customer = [[36, 950, 6, 1]] new_customer_scaled = scaler.transform(new_customer) prediction = model.predict(new_customer_scaled) print("Approved" if prediction[0] == 1 else "Not Approved") # Approved Result: Approved (Yes) — the model predicts this customer will purchase.
What I learned
- Preprocessing is half the project. Encoding categories and scaling features matter as much as the model itself.
- Never fit on test data. Fitting the scaler on test data leaks information and gives fake results.
- Small data teaches the pipeline. With 8 rows I could trace every step; the same pipeline scales to thousands of rows.
- Accuracy on 2 test samples is not proof. A 1.0 here is encouraging, not conclusive — real evaluation needs more data and more metrics (precision, recall, confusion matrix).
If you are learning ML, build this project yourself and change one thing at a time: try a different random_state, add a feature, or compare logistic regression with a decision tree. That is where the real learning happens.