Six files, and only one opens in Excel
Kabir’s internship at TiffinGo starts next semester. To help him prepare, the data team sends a small practice dump: tiffingo_sample.zip. He unzips it on his laptop and finds six very different files. He tries to open all of them in Excel. Only one opens properly.
Sir, the CSV opened fine. The JSON file is one giant line of brackets, the log file is just text, and there’s a folder of photos and a PDF. How do I put this into one table?
You don’t, at least not directly. You’ve just met the three shapes of data: structured, semi-structured and unstructured. Each needs a different way to store, read and query.
Sir, in Lesson 1.1 you said most new data is unstructured. So is the CSV the odd one out?
In volume, yes. Photos, videos, chats and documents are the biggest part of the world’s data. But most business reports still run on structured tables. A big part of a data engineer’s job is turning the messy shapes into tidy tables.
So the JSON and the logs are somewhere in between?
Exactly. They have labels but no fixed table shape. Today you’ll flatten a nested JSON order into rows, pull fields out of log lines, turn support chats into numbers, and see what happens when an app update quietly changes the shape of the data.
Structured, semi-structured and unstructured data
Structured data conforms to a predefined schema of rows and columns with fixed data types, such as tables in a relational database. Semi-structured data does not follow a fixed tabular schema but contains tags, keys or markers that separate fields and describe hierarchy, so each record is self-describing; examples are JSON, XML, YAML and log files. Unstructured data has no predefined data model or organisation, such as free text, images, audio, video and scanned documents, and must be processed (parsed, tagged or analysed) before it can be queried.
Structured: neat rows and columns, every row the same shape. Semi-structured: every value has a label, but records can have different labels and lists inside them. Unstructured: no labels at all, like a photo or a paragraph of text. You have to look inside to find out what it says.
Words you’ll meet in this lesson
order_id: int, city: text, amount: decimal.{"city": "Pune"}.Common formats of each shape
| Structured | Database tables (MySQL, PostgreSQL, Oracle), CSV with a fixed header, spreadsheet tables, Parquet/ORC tables |
|---|---|
| Semi-structured | JSON, XML, YAML, Avro and Protobuf messages, server and app logs, emails (headers + body), NoSQL documents |
| Unstructured | Free text, PDFs and scanned documents, images (JPG, PNG), audio (MP3, WAV), video (MP4), CCTV feeds |
Three shapes of data at three levels
Beginner A spice rack, a tiffin box and a grocery bag
- Structured is a spice rack: every jar has a fixed place and a printed label. You find turmeric instantly. Example: a marks sheet, a bank statement table.
- Semi-structured is a tiffin box: each compartment is labelled (“dal”, “rice”), but one day there are three compartments and another day four. Example: a JSON message from an app.
- Unstructured is a bag of groceries: everything is in there, but there are no labels. You must look at each item to know what it is. Example: a photo, a voice note, a free-text review.
Quick test: can you write column names for it without looking at every item? Yes → structured. Names exist but vary → semi-structured. No names at all → unstructured.
Intermediate Schemas, nesting and how each shape is queried
- Schema: the description of fields and their types. Structured data has it before storing (schema-on-write); semi-structured data carries field names inside each record (self-describing); unstructured data has none.
- Nesting: JSON can hold lists inside records (an order with three items). To analyse it in a table, you flatten it: one row per item, repeating the order fields.
- Logs are text lines with a predictable pattern, so they are parsed into fields with split or regular expressions.
- Unstructured data is made usable by adding metadata (file name, size, upload time, camera details) and by extracting features: words from text, labels from images, transcripts from audio.
- Query tools: SQL for tables; JSON functions in databases, MongoDB queries or Spark for semi-structured; search engines (Elasticsearch, OpenSearch) and ML models for unstructured.
Advanced Schema drift, variant columns and embeddings
- Schema drift: semi-structured sources change silently: new keys, missing keys, renamed fields, a number arriving as text. Pipelines need schema checks and data contracts (an agreed schema between the team producing data and the team using it).
- Warehouses store JSON natively: Snowflake’s
VARIANT, BigQuery’sJSONandSTRUCT/ARRAYtypes, and Spark’sstruct,arrayandmapcolumns let you query nested data without flattening everything first. - Explode carefully: flattening two independent arrays in one record multiplies rows (3 items × 2 coupons = 6 rows), which silently double-counts money.
- Unstructured data for AI: text, images and audio are converted into embeddings (lists of numbers that capture meaning) and stored in vector indexes, so you can search “photos of burnt rotis” or “complaints about late delivery” by meaning.
- Size vs value: unstructured data is the largest share of storage, but structured data often drives most decisions. Plan storage costs and processing effort for each shape separately.
Exam tip: Draw a three-column table: definition, schema, examples, storage, tools to process, ease of analysis. Give two examples of each kind from one business (for example, a bank: transactions table, mobile-app JSON logs, cheque images). That earns full marks on most “types of big data” questions.
Three shapes, compared
Structured
Fixed columns and types. Easy to store, index and query with SQL. Bank transactions, orders, marks, inventory.
Rows and columnsSemi-structured
Labels inside each record, shape can vary, can be nested. App events, API responses, server logs, sensor messages.
Keys and nestingUnstructured
No predefined fields. Needs extra processing (OCR, speech-to-text, image models, text analysis) before analysis.
No fixed modelSide by side
| Structured | Semi-structured | Unstructured | |
|---|---|---|---|
| Schema | Fixed, defined before storing | Flexible, inside each record | None |
| Example | Orders table | App event JSON, logs | Review text, menu photos, call recordings |
| Stored in | RDBMS, warehouse, Parquet tables | Document DBs (MongoDB), lake files, Kafka topics | Object storage (S3, ADLS, GCS), HDFS |
| Queried with | SQL | JSON functions, Spark, MongoDB queries | Search engines, ML models, OCR, speech-to-text |
| Ease of analysis | Easiest | Medium: parse and flatten first | Hardest: extract features first |
| Share of total volume | Smallest | Large | Largest (most estimates put it at a big majority) |
Where data comes from
| Source | What it produces | Usual shape |
|---|---|---|
| Business transactions | Orders, payments, invoices, bookings | Structured |
| Machines and apps | Server logs, clickstreams, API responses | Semi-structured |
| Sensors and IoT | GPS pings, smart-meter readings, temperature | Semi-structured (often time series) |
| People | Reviews, chats, emails, social posts, photos, voice notes | Unstructured |
| Documents | Scanned forms, contracts, invoices as PDF | Unstructured |
The same fact can arrive in all three shapes: “order O1043 cost ₹420” can be a row in a table, a field in a JSON event, and a sentence in a customer’s complaint email. Good pipelines link them with a common key such as the order id.
One food order in every shape
Structured: one row per order
Every row has the same three columns, and the types are known. This is the easiest shape: load it into a database and query it with SQL.import csv, io text = "order_id,city,amount\nO1043,Pune,420\nO1044,Mumbai,250\n" for row in csv.DictReader(io.StringIO(text)): print(row["order_id"], row["city"], int(row["amount"]))O1043 Pune 420 O1044 Mumbai 250Semi-structured: an order with a list inside
The JSON carries its own field names and contains a nestedimport json event = json.loads('{"order_id": "O1043", "city": "Pune", "items": [{"dish": "thali", "qty": 1, "price": 220}, {"dish": "lassi", "qty": 2, "price": 100}]}') for item in event["items"]: print(event["order_id"], item["dish"], item["qty"] * item["price"])O1043 thali 220 O1043 lassi 200itemslist. Looping over the list flattens it into one row per item, ready for a table.Semi-structured: a log line with a pattern
A log line is text, but it follows a pattern: date, time, thenline = "2026-10-19 13:02:11 rider=R77 order=O1043 lat=18.5204 lon=73.8567 status=PICKED" parts = line.split() fields = dict(p.split("=") for p in parts[2:]) print(parts[0], parts[1], fields["order"], fields["status"])2026-10-19 13:02:11 O1043 PICKEDkey=valuepairs. Splitting by spaces and=turns it into fields.Unstructured: a free-text review
The review has no fields. Here a simple keyword list extracts labels; real systems use language models to detect sentiment and topics. The result is structured:review = "Thali was great but the delivery was very late and the lassi was warm" words = review.lower().split() labels = [w for w in ["late", "cold", "warm", "great", "spilled"] if w in words] print("labels:", labels)labels: ['late', 'warm', 'great']order_id, label.Unstructured: a photo of the food
A 3 MB photo arrives with the review. It is stored as a file in object storage, and a row of metadata is saved: file path, size, upload time, order id. An image model can add labels such as “thali, spilled”.
The photo itself never goes into a table. Its path and labels do, so analysts can count “spilled food” complaints by kitchen.Link everything by order id
Now all shapes can be joined: the order row, its items from JSON, the rider’s pickup time from logs, the review labels and the photo metadata, all keyed by
This is the daily work of a data engineer: bring every shape into a structured, joinable form without losing the raw originals.O1043.
One order: what Riya sees, and what the data looks like
Step through the six frames. Left: Riya ordering food. Right: the shape of the data each moment creates.
- Riya builds an order: one thali and two lassis, ₹420 in total.
- The app confirms order O1043 from Annapurna Kitchen.
- A rider picks it up and the map shows him moving towards her.
- She gives three stars, writes a short review and adds a photo.
- She tells support the lassi spilled and gets a ₹100 refund.
- At the end of the month the app shows her favourite dish and how often she ordered.
- The order becomes one structured row: same columns as every other order.
- The app also sends a semi-structured JSON event with nested user details and a list of items.
- The rider’s phone writes a log line every five seconds: text with a fixed pattern of key=value pairs.
- The review text and the photo are unstructured. The text is analysed for labels; the photo is stored as a file with a metadata row.
- The support chat is free text too. A model turns it into a structured ticket: topic, refund, resolved.
- All shapes are linked by
order_id, giving one complete view of the order. The raw files are kept for reprocessing.
Real-time scenarios: working with every shape
Three live scenarios: flattening nested JSON into a table, turning a flood of support chats into numbers during an outage, and an advanced one about an app update that silently changes the data.
Real-time scenario 1Flatten a nested order into a table
TiffinGo’s app sends every order as nested JSON. The finance dashboard needs a flat table. Watch the three steps every pipeline uses, and the classic mistake to avoid at the end.
- A JSON order holds a list of three items. A table cell can hold only one value, so the list must be turned into rows.
- Copy the top-level fields (order id and city) that apply to the whole order.
- Explode the
itemslist: one row per item, repeating the order fields on each row. - Keep missing or null fields as NULL. The coupon was not used, so it stays empty; never fill in a guess.
- Now ordinary SQL works. Compute totals from item rows (qty × price). If an order-level total were repeated on every row, summing it would triple-count the order.
Outage night: turning 1,000 chats a minute into numbers
At 20:14 support chats explode. Nobody can read a thousand messages a minute. Watch unstructured text become a structured count that tells engineers exactly what broke.
- Chats suddenly jump from about 60 to 1,000 a minute. They are free text in English, Hindi and Hinglish, full of typos.
- Each chat goes through a language model that outputs a topic, the language and a confidence score: structured fields from unstructured text.
- The labelled chats are counted per topic every minute. “Payment failed” is far above everything else.
- An alert fires because payment-failure chats are ten times their normal level. Engineers find one bank’s gateway timing out.
- Traffic is moved to another gateway, failures drop, and refund questions rise, so the app shows a banner instead of agents answering each one. (Numbers are illustrative.)
Schema drift: the app update that broke the dashboard
Semi-structured data is flexible, which is also its danger. Watch a small app change silently break a pipeline, and the three defences real teams use.
- A new app version starts sending
amountas text with a rupee sign and renamescitytocity_name. Nobody tells the data team. - The pipeline fails to convert “₹420” to a number and finds no
city. Many orders are dropped or lose their city, and the morning dashboard shows revenue down 40%. - Schema check: every incoming batch is compared with the expected schema (types and required fields). The drift is caught before anything is published.
- Quarantine: rows that fail go to a separate table for inspection, while good rows keep flowing. The owning team gets an alert.
- Data contract: the app and data teams agree the event schema, version it, and check it automatically before an app release. Renames need notice; new optional fields are fine.
Where each shape shows up at work
1. Banks: structured core, unstructured edges
Account and transaction tables are strictly structured. Around them sit cheque images, KYC documents (scanned Aadhaar and PAN copies), call-centre recordings and emails, which are processed with OCR and speech-to-text before analysis.
2. Shopping apps: oceans of JSON events
Every view, search, add-to-cart and checkout is a JSON event with nested product lists. They are streamed through Kafka, stored raw in the lake, and flattened into tables for funnel and recommendation analysis.
3. Every tech company: logs for monitoring
Servers write millions of log lines an hour. They are parsed into fields (time, service, status code, latency) and searched in tools like Elasticsearch/OpenSearch or a log platform to find errors in seconds.
4. Hospitals and insurance: documents and images
Discharge summaries, prescriptions, lab reports and X-rays are unstructured. Insurers extract fields from claim documents to speed up approvals, while keeping the originals for audit.
Sort records by shape, and read each one
Four records arrive at a data platform. The program decides the shape of each and prints a short summary. Then try the challenges: flatten nested JSON, parse a log line, label free text and catch schema drift.
import json
records = [
("table_row", ("O1043", "Pune", 420)),
("json", '{"order_id": "O1043", "items": [{"dish": "thali"}, {"dish": "lassi"}]}'),
("log", "13:02:11 rider=R77 status=PICKED"),
("text", "Thali was great but the lassi was warm"),
]
for kind, value in records:
if kind == "table_row":
print("STRUCTURED :", value, "-> 3 fixed columns")
elif kind == "json":
event = json.loads(value)
print("SEMI-STRUCTURED:", list(event), "-> keys inside the record,", len(event["items"]), "nested items")
elif kind == "log":
fields = dict(p.split("=") for p in value.split()[1:])
print("SEMI-STRUCTURED:", fields, "-> parsed from a pattern")
else:
print("UNSTRUCTURED :", len(value.split()), "words, no fields until we extract some")event = json.loads(value)json.loadsturns JSON text into Python dictionaries and lists. The field names come from the data itself.fields = dict(p.split("=") for p in value.split()[1:])Split the line by spaces, skip the time, then split eachkey=valuepair. This is log parsing.print("UNSTRUCTURED :", len(value.split()), "words, no fields until we extract some")Plain text has no fields. All we can do without extra work is count words.
Run it on your PC: create and read all three shapes
This program creates a sample_data folder with a CSV (structured), a JSON-lines file and a log file (semi-structured) and a text file of reviews (unstructured). Then it reads each one the right way: csv, json, a regular expression and simple text analysis. Only the Python standard library is used. Save it as three_shapes.py in an empty folder and run it, then open the files it creates.
Create and read structured, semi-structured and unstructured files
"""Create one sample file of each shape in a folder, then read each one the right way."""
import csv, json, os, re
os.makedirs("sample_data", exist_ok=True)
# 1. Structured: a CSV with a fixed header
with open("sample_data/orders.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["order_id", "city", "amount"])
w.writerows([["O1", "Pune", 420], ["O2", "Mumbai", 250], ["O3", "Pune", 180]])
# 2. Semi-structured: JSON lines with nesting and a missing key
events = [
{"order_id": "O1", "items": [{"dish": "thali", "qty": 1}, {"dish": "lassi", "qty": 2}]},
{"order_id": "O2", "items": [{"dish": "dosa", "qty": 1}], "coupon": "LUNCH20"},
]
with open("sample_data/events.jsonl", "w") as f:
for e in events:
f.write(json.dumps(e) + "\n")
# 3. Semi-structured: log lines with a pattern
with open("sample_data/app.log", "w") as f:
f.write("2026-10-19 13:02:11 INFO order=O1 status=PLACED ms=120\n")
f.write("2026-10-19 13:02:15 ERROR order=O2 status=PAY_FAILED ms=3020\n")
f.write("2026-10-19 13:02:19 INFO order=O3 status=PLACED ms=95\n")
# 4. Unstructured: free-text reviews
with open("sample_data/reviews.txt", "w") as f:
f.write("Loved the thali, will order again!\n")
f.write("Food was cold and delivery very late.\n")
print("Files created:", sorted(os.listdir("sample_data")))
# ---- read each one the right way
with open("sample_data/orders.csv") as f:
rows = list(csv.DictReader(f))
total = sum(int(r["amount"]) for r in rows)
print(f"\n[structured] {len(rows)} rows, columns {list(rows[0])}, total Rs {total}")
flat = []
with open("sample_data/events.jsonl") as f:
for line in f:
e = json.loads(line)
for item in e["items"]:
flat.append((e["order_id"], item["dish"], item["qty"], e.get("coupon")))
print(f"[semi-structured JSON] flattened into {len(flat)} item rows:")
for r in flat:
print(" ", r)
pattern = re.compile(r"(\S+ \S+) (\w+)\s+order=(\w+) status=(\w+) ms=(\d+)")
with open("sample_data/app.log") as f:
parsed = [pattern.match(line).groups() for line in f]
errors = [p for p in parsed if p[1] == "ERROR"]
print(f"[semi-structured log] {len(parsed)} lines parsed, {len(errors)} error: {errors[0][2]} {errors[0][3]}")
negative = {"cold", "late", "spilled", "wrong"}
with open("sample_data/reviews.txt") as f:
for line in f:
words = set(re.findall(r"[a-z]+", line.lower()))
label = "complaint" if words & negative else "praise"
print(f"[unstructured] {label:9} <- {line.strip()}")
# In a terminal: python three_shapes.pyRecorded from a real run with Python 3.13 (any Python 3.8+ gives the same output). Notice that the JSON flattening turned 2 events into 3 item rows and filled the missing coupon with None, the regular expression pulled five fields out of each log line, and the reviews became a structured label.
Flattening a nested order, one line at a time
An order with two items becomes two flat rows. Watch the inner loop run once per item and the rows list grow, with the order id repeated on each row.
Worked examples: real problems, solved and traced in memory
Four tasks a data engineer does with mixed data, from sorting files by shape to catching schema drift. Each runs live and is traced.
Sort a folder by shape of data
Problem statement
A shared folder has files from many teams. Count how many are structured, semi-structured and unstructured, based on the extension.
sales.csv, events.json, app.log, cctv.mp4, invoice.pdf, stock.parquet, review.txt, config.xml
{'structured': 2, 'semi-structured': 3, 'unstructured': 3}Approach
- Map each known extension to a shape.
- Take the text after the last dot of each file name.
- Count with a dictionary; unknown extensions count as unstructured.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Turn nested JSON orders into item rows
Problem statement
Each order is JSON with a list of items. Produce one row per item with order id, city, dish and line total, then print revenue per dish.
2 orders with 3 items in total
{'thali': 660, 'lassi': 200}Approach
- Parse each JSON string with
json.loads. - Loop over the nested items and build one row per item.
- Group the rows by dish and add the line totals.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Error rate per minute from server logs
Problem statement
Parse log lines into minute and level, then print how many requests and errors happened in each minute and the error percentage.
6 log lines over 2 minutes
13:02 3 requests, 33 % errors 13:03 3 requests, 67 % errors
Approach
- Split each line: the first part is the time, the second the level.
- Use the first five characters of the time (HH:MM) as the minute.
- Count totals and errors per minute in two dictionaries.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Validate a batch of events against a schema
Problem statement
A batch of JSON events must match the expected schema. Send each event to accept or quarantine, and summarise the problems found.
expected: order_id (str), amount (int), city (str) · 4 events
Accepted : ['O1', 'O4']
Quarantined: ['O2', 'O3']
Problems : {'bad type amount': 1, 'missing city': 1}Approach
- For each event, find missing keys and keys with the wrong type.
- If any problem exists, quarantine the event and record why.
- Count each kind of problem across the batch.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check yourself
Quick revision
Lesson 2.1 in one screen
- Structured: fixed schema, rows and columns (SQL tables, CSV). Easiest to query.
- Semi-structured: labels inside each record, flexible and nested (JSON, XML, logs, Avro). Parse and flatten.
- Unstructured: no data model (text, images, audio, video, PDFs). Add metadata and extract features.
- Flatten / explode nested lists into one row per item; never sum a repeated parent value.
- Logs are parsed with split or regular expressions; text becomes labels with keywords or language models.
- Schema drift = fields change silently. Defend with schema checks, quarantine tables and data contracts.
- Warehouses store JSON natively (VARIANT, JSON, STRUCT/ARRAY); AI search uses embeddings of unstructured data.
Interview and viva questions
Differentiate structured, semi-structured and unstructured data with examples.
Structured data follows a fixed schema of rows and columns with defined types, for example a bank transactions table. Semi-structured data has tags or keys that describe fields but no fixed table shape, and records can be nested or vary, for example JSON app events, XML files or server logs. Unstructured data has no predefined model, for example images, videos, audio recordings, scanned PDFs and free-text reviews. Structured data is queried directly with SQL; semi-structured data is parsed and flattened; unstructured data needs feature extraction (OCR, speech-to-text, NLP, image models) before analysis.
Is a CSV file always structured?
Usually yes, when it has a consistent header and every row has the same columns and types. A CSV with inconsistent columns, embedded free text or mixed types behaves more like semi-structured data and needs validation before loading.
Why is semi-structured data popular in big data?
It is flexible: producers can add fields without changing a central schema, nested data maps naturally to real objects (an order with items), and formats like JSON are easy for applications to produce. The trade-off is that consumers must handle missing, new and changed fields.
How do you process unstructured data at scale?
Store the raw files in object storage, record metadata for each file, then run extraction jobs in parallel (for example with Spark or a cloud service): OCR for documents, speech-to-text for audio, image classification for photos and language models for text. Save the extracted fields as structured tables linked by ids, and keep the originals for reprocessing.
What is schema drift and how do you handle it?
Schema drift is an unannounced change in the structure of incoming data, such as a renamed field, a new field or a changed type. Handle it with schema validation on every batch, quarantine tables for failing records, alerts on row counts and nulls, parsers that accept known versions, and versioned data contracts agreed with the producing team.
Practice problems
Exercises on reading, flattening and validating mixed data. The last two are business tasks.
Count JSON keys
Print the keys of a JSON order and how many there are.
{"id": "O1", "city": "Pune", "amount": 420}['id', 'city', 'amount'] 3
Show solution and explanation
import json
o = json.loads('{"id": "O1", "city": "Pune", "amount": 420}')
print(list(o), len(o))How it works: json.loads returns a dictionary; list(o) gives its keys.
Shape of each value
For each value, print “structured row” for a tuple, “semi-structured” for a dict and “unstructured” for a string.
(1, "Pune"), {"a": 1}, "nice food"(1, 'Pune') -> structured row
{'a': 1} -> semi-structured
nice food -> unstructuredShow solution and explanation
for v in [(1, "Pune"), {"a": 1}, "nice food"]:
if isinstance(v, tuple):
print(v, "-> structured row")
elif isinstance(v, dict):
print(v, "-> semi-structured")
else:
print(v, "-> unstructured")How it works: isinstance checks the type of each value.
Read a CSV text
Read the CSV text and print the total amount.
id,amount / O1,120 / O2,80
Total: 200
Show solution and explanation
import csv, io
text = "id,amount\nO1,120\nO2,80\n"
rows = list(csv.DictReader(io.StringIO(text)))
print("Total:", sum(int(r["amount"]) for r in rows))How it works: csv.DictReader uses the header row as keys; values arrive as text, so convert with int.
Flatten items
Print one line per item: order id, dish, qty.
{"id": "O5", "items": [{"dish": "dosa", "qty": 2}, {"dish": "chai", "qty": 1}]}O5 dosa 2 O5 chai 1
Show solution and explanation
order = {"id": "O5", "items": [{"dish": "dosa", "qty": 2}, {"dish": "chai", "qty": 1}]}
for it in order["items"]:
print(order["id"], it["dish"], it["qty"])How it works: Loop over the nested list and repeat the parent id on each line.
Parse key=value logs
Turn each log line into a dictionary and print the riders who picked orders.
3 lines
['R1', 'R3']
Show solution and explanation
lines = ["rider=R1 status=PICKED", "rider=R2 status=WAITING", "rider=R3 status=PICKED"]
picked = []
for ln in lines:
f = dict(p.split("=") for p in ln.split())
if f["status"] == "PICKED":
picked.append(f["rider"])
print(picked)How it works: Each key=value pair becomes a dictionary entry.
Keys across versions
Events from two app versions have different keys. Print keys common to both, keys only in v1 and keys only in v2.
v1: id, amount, city · v2: id, amount, city_name, coupon
Common : ['amount', 'id'] Only v1: ['city'] Only v2: ['city_name', 'coupon']
Show solution and explanation
v1 = {"id", "amount", "city"}
v2 = {"id", "amount", "city_name", "coupon"}
print("Common :", sorted(v1 & v2))
print("Only v1:", sorted(v1 - v2))
print("Only v2:", sorted(v2 - v1))How it works: Set operations quickly show renamed (city → city_name) and new (coupon) fields.
Null rate per field
For each field, print the percentage of records where it is missing or None.
4 records
id 0% missing city 50% missing coupon 75% missing
Show solution and explanation
records = [{"id": 1, "city": "Pune", "coupon": None}, {"id": 2, "city": None},
{"id": 3, "city": "Thane", "coupon": "X"}, {"id": 4}]
fields = ["id", "city", "coupon"]
for f in fields:
missing = sum(1 for r in records if r.get(f) is None)
print(f"{f:7} {missing / len(records) * 100:.0f}% missing")How it works: A sudden jump in a null rate is often the first sign of schema drift.
Storage by shape
A company stores 2 TB of tables, 30 TB of JSON logs and 400 TB of images and video. Print the percentage of each.
2, 30, 400 TB
structured 0.5% semi-structured 6.9% unstructured 92.6%
Show solution and explanation
sizes = {"structured": 2, "semi-structured": 30, "unstructured": 400}
total = sum(sizes.values())
for k, v in sizes.items():
print(f"{k:16} {v / total * 100:5.1f}%")How it works: Unstructured data dominates storage, even when most reports come from the small structured share.
Complaint share
Out of reviews labelled by a model, print the share of each complaint type among complaints only.
late 420, cold 180, wrong item 90, praise 1310
late 61% cold 26% wrong item 13%
Show solution and explanation
labels = {"late": 420, "cold": 180, "wrong item": 90, "praise": 1310}
complaints = {k: v for k, v in labels.items() if k != "praise"}
total = sum(complaints.values())
for k, v in sorted(complaints.items(), key=lambda x: -x[1]):
print(f"{k:10} {v / total * 100:.0f}%")How it works: Text has become numbers a manager can act on: most complaints are about late delivery.
Real-world scenario questions (asked in company interviews)
Situations with mixed data that come up in real teams and interviews. Decide your answer first, then open the model answer.
Load a JSON API into the warehouse
A partner sends orders through an API as JSON. Each order has a nested list of items and an optional coupon object. Your manager wants revenue per dish in the warehouse by tomorrow.
The interviewer is checking: do you know how to land, flatten and validate semi-structured data?
Show a strong answer
- Land the raw JSON as-is in the lake (bronze) so you can always reprocess.
- Define the target tables:
orders(one row per order) andorder_items(one row per item, withorder_id). - Explode
itemsintoorder_items; keep the coupon fields as nullable columns. - Validate types and required fields; send failing rows to a quarantine table.
- Compute revenue from
order_items(qty × price), not from a repeated order total.
Avoid: flattening straight into one wide table and summing a repeated order-level amount.
What are customers complaining about?
You have 2 lakh free-text reviews from the last month in English, Hindi and Hinglish. The product head wants the top five complaint themes by city.
The interviewer is checking: can you turn unstructured text into a structured, trustworthy result?
Show a strong answer
- Store the raw reviews with order id, city and date (metadata).
- Clean the text (lower-case, remove emojis or map them, normalise common spellings).
- Classify each review into themes with a language model or a trained classifier; record the label and a confidence score.
- Check a random sample by hand to measure accuracy before trusting the numbers.
- Aggregate counts per theme per city, and show example reviews behind each number.
Avoid: counting single keywords like “bad” and presenting it as insight without checking accuracy.
Revenue dropped 40% overnight
The morning dashboard shows revenue down 40%, but the business team says orders were normal. An app release went out yesterday.
The interviewer is checking: do you suspect the data before the business, and know how to prevent it next time?
Show a strong answer
First, compare raw event counts with orders in the database: if raw counts are normal, the pipeline lost data. Check rejected and quarantined rows and diff the event schema before and after the release; you are likely to find a renamed or retyped field. Fix the parser to handle both versions, backfill the affected day from the raw zone, and publish a corrected dashboard. To prevent repeats: schema checks on every batch, quarantine instead of silent drops, alerts on row-count changes, and a versioned data contract checked before app releases.
Avoid: telling leadership that revenue fell, or patching the dashboard by hand.
Where should each kind of data live?
A new ed-tech start-up has student records, quiz-attempt events from the app, lecture videos and doubt-chat messages. They ask you where to store each.
The interviewer is checking: can you match each shape to sensible storage?
Show a strong answer
- Student records (structured, updated often) → a relational database such as PostgreSQL.
- Quiz events (semi-structured, high volume) → a stream (Kafka) into the data lake as JSON, then flattened Parquet tables for analysis.
- Lecture videos (unstructured, huge) → object storage plus a CDN for streaming; a metadata table for titles, durations and tags.
- Doubt chats (unstructured text) → stored raw, labelled by topic, and searchable through a search engine.
Avoid: putting videos inside database tables, or forcing every event into a fixed table schema on day one.