AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Feature Engineering: Creating Better Input Data

📚 Machine Learning⏱️ 18 min read🎓 Grade 9
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 18 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

The Bengaluru House-Price Puzzle

Imagine you are given a spreadsheet of 500 flats for sale in Bengaluru, and your job is to build a program that predicts the price of a new flat before it goes on the market. Here is one row from that spreadsheet, exactly as it would appear in a real dataset:

size: "3 BHK"
total_sqft: "2100 - 2850"
location: "Jayanagar"
availability: "18-Dec"
built_year: 2015
price: 120  (in lakhs of rupees)

Now ask yourself a very concrete question: can a machine learning model do arithmetic on the text "3 BHK"? Can it multiply "2100 - 2850" by a weight and add it to something? It cannot. Every model you will meet in this course — whether it predicts a number (regression) or a category (classification) — ultimately does one thing underneath: it multiplies numbers by weights and adds them up. A model has no idea that "BHK" means "bedroom, hall, kitchen," and it cannot subtract a dash-separated range from anything. If you feed it this row exactly as it is, most of the useful information in it is completely invisible to the model.

Feature engineering is the practice of transforming raw data like this into a form a model can actually learn from — without changing what the data truthfully represents. It is arguably the single skill that separates a mediocre model from an excellent one, and it matters more than switching to a fancier algorithm. This chapter teaches you exactly how to do it, using this Bengaluru housing example as our running thread.

Recall: What Is a "Feature"?

A feature is simply one measurable input column that a model uses to make its prediction — the way "runs scored," "balls faced," and "wickets in hand" might be features for predicting a cricket team's final score. In our housing dataset, size, total_sqft, location, and built_year are all features, and price is the label — the answer the model is trying to learn to predict. Feature engineering never touches the label. It only reshapes the inputs so the relationship between inputs and label becomes something a model can find.

Why Raw Data Trips a Model Up

Look again at total_sqft: "2100 - 2850". A human reader understands this means the flat's area is somewhere between 2100 and 2850 square feet — probably because it spans a few similar floor plans. But to a computer, this is just a string of characters containing a space, a hyphen, and two number-like substrings. You cannot compare "2100 - 2850" to "1500 - 1650" and say which is bigger, the way you can compare 2475 and 1575. Until this text is converted into an actual number, the model cannot use area at all — one of the most important predictors of price in any housing dataset would simply sit unused.

The same problem hides inside "3 BHK". Somewhere in that string is the number of bedrooms — arguably the second most important predictor of price after area — but it is trapped inside text, mixed with letters that mean nothing numerically. Feature engineering is the process of freeing that trapped information.

Technique 1: Extracting Numbers Hidden Inside Text

The fix for "3 BHK" is straightforward once you see it: split the string on the space, take the first piece, and convert it to a number.

size_raw = "3 BHK"
bhk = int(size_raw.split(" ")[0])
print(bhk)
# Output: 3

Trace through it: size_raw.split(" ") breaks the string wherever a space appears, producing the list ["3", "BHK"]. Taking index [0] gives the text "3", and int(...) converts that text into the actual number 3. Now bhk is a genuine number the model can compare, add, and multiply — 3 is meaningfully bigger than 2, in a way "3 BHK" as a string never could be.

Technique 2: Combining Features to Create New Meaning

The total_sqft range needs a similar rescue, but with an extra step — turning a range into a single representative number, typically its midpoint:

sqft_raw = "2100 - 2850"
parts = sqft_raw.split(" - ")
low = float(parts[0])
high = float(parts[1])
avg_sqft = (low + high) / 2
print(avg_sqft)
# Output: 2475.0

Here, parts becomes ["2100", "2850"], low becomes 2100.0, high becomes 2850.0, and their average is (2100 + 2850) / 2 = 4950 / 2 = 2475.0. This new column, avg_sqft, did not exist in the original data at all — it is a derived feature, built by combining two raw values into one that carries more usable meaning than either half of the original string.

You can go one step further and derive a feature that combines two different columns: price per square foot, which real-estate analysts actually use to judge whether a flat is fairly priced.

price_lakhs = 120
avg_sqft = 2475.0
price_per_sqft = (price_lakhs * 100000) / avg_sqft
print(round(price_per_sqft, 2))
# Output: 4848.48

120 lakhs is 1,20,00,000 rupees. Dividing that by 2475 square feet gives approximately ₹4,848.48 per square foot. Watch what this single derived number does: it lets you compare a tiny 2 BHK and a huge 4 BHK on equal footing, something neither raw column could do alone.

Here is why this matters, shown with real numbers. Suppose four flats in our dataset have these raw and engineered values:

FlatRaw sizeRaw sqft rangeavg_sqft (engineered)Price (lakhs)price_per_sqft (engineered)
A"2 BHK""1000 - 1100"1050555238.10
B"3 BHK""1500 - 1650"1575855396.83
C"3 BHK""2100 - 2850"24751204848.48
D"4 BHK""2800 - 3000"29001455000.00

Looking only at the raw size and total_sqft columns, there is no way to plot these four flats on a graph or compute a correlation with price — they are text. But once engineered, all four price-per-sqft values cluster tightly between roughly ₹4,800 and ₹5,400 — a real, learnable pattern that was completely invisible before engineering, and that a simple linear model can now pick up directly.

Seeing the Transformation

The diagram below shows the same simple model receiving the same house, once as raw text and once as engineered numbers. Nothing about the model changes — only the shape of its input does, and that alone decides whether it can find the pattern.

Raw Data size: "3 BHK" sqft: "2100 - 2850" location: "Jayanagar" built_year: 2015 Feature Engineering extract numbers from text combine into ratios bucket into ranges one-hot encode categories Engineered Features bhk = 3 avg_sqft = 2475 price_per_sqft ~ 4848 loc_Jayanagar = 1 age_bucket = "Moderate" ML Model (same model) Accurate Predictions Poor Predictions skip engineering: model sees text, not numbers Same model. Different input format. Very different result.

Technique 3: Turning Dates Into Meaningful Numbers

A raw date, like a birth date or a construction year, is often useless to a model exactly as stored. What usually matters is not the date itself but the gap between that date and now. If a dataset stores built_year: 2011, the model does not need the year 2011 — it needs to know the building is old:

current_year = 2026
built_year = 2011
house_age = current_year - built_year
print(house_age)
# Output: 15

2026 minus 2011 is 15. This one subtraction converts a fairly meaningless raw year into an age in years — a feature that behaves consistently across every row in the dataset, regardless of which year the flat happens to have been built in.

Technique 4: Bucketing (Binning) Continuous Values into Categories

Sometimes a precise number carries more detail than a model actually needs, and grouping it into ranges makes the pattern easier to learn — especially for simple models that work well with categories. We can bucket house_age into three bands:

def house_age_bucket(built_year, current_year=2026):
    age = current_year - built_year
    if age <= 5:
        return "New"
    elif age <= 15:
        return "Moderately Old"
    else:
        return "Old"

print(house_age_bucket(2022))
# age = 2026 - 2022 = 4  ->  4 <= 5  ->  "New"

print(house_age_bucket(2015))
# age = 2026 - 2015 = 11 -> not <= 5, but <= 15 -> "Moderately Old"

print(house_age_bucket(2005))
# age = 2026 - 2005 = 21 -> neither condition true -> "Old"

Trace the middle call carefully, since it is the one most students get wrong: age becomes 11. The first condition checks 11 <= 5, which is false, so Python moves to elif age <= 15, checks 11 <= 15, which is true, and returns "Moderately Old". The final case never even runs the check — it is the fallback when both prior conditions failed. Bucketing like this is a judgment call informed by domain knowledge: a real-estate analyst decides where the boundaries between "new," "moderately old," and "old" should sit, based on what actually affects price in that city — the code cannot invent those thresholds on its own.

Technique 5: Encoding Categories as Numbers (One-Hot Encoding)

The location column poses a different problem. Unlike total_sqft, it is not a number hiding inside text — it is a genuine category, like "Jayanagar" or "Whitefield." It would be tempting to just assign each location a number: Whitefield = 1, Indiranagar = 2, Jayanagar = 3. But this is a trap. Doing so tells the model that Jayanagar (3) is "three times" Whitefield (1), or that Indiranagar sits mathematically between them — relationships that do not exist in reality. Localities have no natural order or magnitude.

The fix is one-hot encoding: create one new column per category, and mark it 1 if that row belongs to that category, 0 otherwise.

def one_hot_location(location):
    locations = ["Whitefield", "Indiranagar", "Jayanagar"]
    return [1 if location == loc else 0 for loc in locations]

print(one_hot_location("Indiranagar"))
# Output: [0, 1, 0]

Trace it: the list comprehension walks through locations one entry at a time. For "Whitefield", is "Indiranagar" == "Whitefield"? No, so it appends 0. For "Indiranagar", the match is true, so it appends 1. For "Jayanagar", no match, appends 0. The result, [0, 1, 0], represents "Indiranagar" using three columns that each carry a fair, order-free yes/no signal — exactly the same way you'd tick one box on a form rather than write a number that implies ranking.

Flatlocationloc_Whitefieldloc_Indiranagarloc_Jayanagar
AWhitefield100
BIndiranagar010
CJayanagar001

Putting It All Together: One Row, Fully Engineered

Real feature engineering rarely applies just one technique — it chains several together on the same row. Here is a single function that engineers every feature for one house, using everything covered above:

def engineer_features(house):
    bhk = int(house["size"].split(" ")[0])
    parts = house["sqft_range"].split(" - ")
    avg_sqft = (float(parts[0]) + float(parts[1])) / 2
    price_per_sqft = (house["price_lakhs"] * 100000) / avg_sqft
    age_bucket = house_age_bucket(house["built_year"])
    loc_encoding = one_hot_location(house["location"])
    return {
        "bhk": bhk,
        "avg_sqft": avg_sqft,
        "price_per_sqft": round(price_per_sqft, 2),
        "age_bucket": age_bucket,
        "location_encoding": loc_encoding
    }

house_C = {
    "size": "3 BHK",
    "sqft_range": "2100 - 2850",
    "price_lakhs": 120,
    "built_year": 2015,
    "location": "Jayanagar"
}

print(engineer_features(house_C))
# Output: {'bhk': 3, 'avg_sqft': 2475.0, 'price_per_sqft': 4848.48,
#          'age_bucket': 'Moderately Old', 'location_encoding': [0, 0, 1]}

Every value in that output was traced earlier in this chapter: bhk from the split-and-convert in Technique 1, avg_sqft and price_per_sqft from the combined ratios in Technique 2, age_bucket from the boundary logic in Technique 4, and location_encoding from the comprehension in Technique 5. What began as five text-and-number fields a model could barely use has become five numeric signals it can multiply, compare, and learn from directly.

Misconception 1: "Feature Engineering Is the Same as Data Cleaning"

These are two different jobs that happen back to back, and mixing them up is one of the most common mistakes at this stage. Data cleaning fixes what is wrong with the data — a missing price, a negative square footage that was mistyped, a duplicate row. Feature engineering assumes the data is already correct and asks a different question: is this correct data in the most useful shape for a model? A perfectly clean, error-free "3 BHK" string is still useless to a model until it becomes the number 3. Cleaning removes noise; feature engineering unlocks meaning. A dataset can be spotlessly clean and still perform badly in a model simply because nobody engineered its features.

Misconception 2: "More Features Always Mean a Smarter Model"

It is tempting to think that throwing in every possible column — the flat's floor number, the seller's phone number's last digit, the exact day of the week it was listed — can only help, since the model can supposedly "ignore" whatever is useless. In practice, this often backfires, particularly with the simple models taught at this stage. A model with limited data has to estimate a weight for every single feature you give it. If you hand it a feature with no real relationship to price — say, the last digit of a phone number — it may still find some accidental, meaningless pattern in the specific rows it was trained on, and confidently apply that fake pattern to new houses it has never seen. This is why feature engineering is not just "add more columns" — it is "add the columns that carry a genuine, explainable relationship to what you're predicting," guided by understanding the domain, not by convenience.

Where This Fits in the Bigger Picture

If you have studied the AI Project Cycle — the stages of Problem Scoping, Data Acquisition, Data Exploration, Modelling, and Evaluation — feature engineering sits at the boundary between Data Exploration and Modelling. It is the last thing you do to your data before a model ever sees it, and it is also the step most likely to determine whether that model succeeds or fails. Two students using the identical algorithm on the identical raw dataset can get very different results purely because one of them engineered better features than the other. This is precisely why experienced practitioners often say that whoever understands the data best — not whoever knows the fanciest algorithm — usually builds the better model.

Check Your Understanding

  1. A dataset column stores exam attendance as the string "42 out of 45 days". Write the extraction logic (in words or code) to turn this into a single number a model could use, and explain what that number represents.
  2. For the input size_raw = "5 BHK", trace through the code in Technique 1 step by step and state the final value of bhk.
  3. A friend proposes encoding three exam grades — "Pass," "Merit," "Distinction" — using 1, 2, 3, arguing it is simpler than one-hot encoding. Explain, using the ideas from this chapter, why this is actually acceptable in this case but would not be acceptable for encoding city names like "Delhi," "Mumbai," "Chennai." (Hint: think about whether the categories have a natural order.)
  4. Using house_age_bucket exactly as defined in this chapter, what does house_age_bucket(2001) return, and what does house_age_bucket(2026) return? Show the arithmetic for both.
  5. A classmate says, "I already removed all the missing values from my dataset, so my features are ready for the model." Identify the misconception in this statement and correct it in one or two sentences.

Summary

  • A feature is a model's input column; feature engineering reshapes raw values into a form a model can actually compute with, without changing what they represent.
  • Extraction pulls a hidden number out of text, such as turning "3 BHK" into the integer 3.
  • Derived (combined) features, such as avg_sqft from a range or price_per_sqft from two different columns, often reveal patterns that no single raw column shows on its own.
  • Dates are usually engineered into a gap from the present, such as converting a birth year or built year into an age.
  • Bucketing groups a continuous number into meaningful ranges using boundaries chosen from domain knowledge, not guesswork.
  • One-hot encoding represents an unordered category safely as several 0/1 columns, avoiding the false sense of order or magnitude that plain integer codes would introduce.
  • Data cleaning fixes errors; feature engineering creates usable meaning — they are different steps, and doing one does not substitute for the other.
  • Adding irrelevant features does not automatically help a model, and can actively mislead it by letting it latch onto accidental, meaningless patterns.
← Decision Trees: Making Predictions with Tree LogicCross-Validation: Testing Model Reliability →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn