Python Interview Questions for Data Roles
The plain-Python coding tasks data-analyst and data-engineer interviews use, without pandas: counting, deduplicating, grouping, streaming large files, flattening JSON, joining, and the bugs interviewers plant. Each has a worked solution and the follow-up questions to expect.
GloryTecks Team
Training & Placement Faculty
Last updated: 24 September 2026
A live coding round for a data role may ask for plain Python, without pandas, to see whether you understand the operations underneath the libraries. The tasks look simple. What they test is whether you choose the right data structure, handle messy input, and know what happens when the data is 100 times bigger. This article collects ten such tasks, each with a solution that has been run, its cost, and the follow-up questions that naturally come next.
What this set is not
For conceptual Python questions (generators, decorators, the GIL and so on), use Python interview questions: top 50 with answers. For DataFrame questions, see pandas interview questions. Array questions are in NumPy interview questions for data roles.
Counting and deduplicating
1. Return the k most frequent values
from collections import Counter
def top_k(values, k):
return Counter(values).most_common(k)
top_k(["IN", "US", "IN", "UK", "US", "IN", "DE"], 2) # [('IN', 3), ('US', 2)]What it tests: knowing Counter instead of hand-writing a dictionary loop. Follow-ups: "How are ties ordered?" Elements with equal counts come out in the order they were first encountered (Python docs: collections), so say so, or sort explicitly if the interviewer wants alphabetical ties. "What if the values come from a 50 GB log?" Read it line by line and call counter.update() per line; memory then grows with the number of distinct values, not the file size.
2. Deduplicate records, keeping the latest per key
from datetime import datetime
def latest_per_key(records, key="customer_id", ts="updated_at"):
latest = {}
for r in records:
k = r[key]
if k not in latest or datetime.fromisoformat(r[ts]) > datetime.fromisoformat(latest[k][ts]):
latest[k] = r
return list(latest.values())
rows = [
{"customer_id": 7, "email": "old@x.com", "updated_at": "2026-03-01T09:00:00+05:30"},
{"customer_id": 9, "email": "a@y.com", "updated_at": "2026-03-02T10:00:00+05:30"},
{"customer_id": 7, "email": "new@x.com", "updated_at": "2026-03-04T08:30:00+05:30"},
]
latest_per_key(rows) # customer 7 -> new@x.com, customer 9 -> a@y.comWhat it tests: a single O(n) pass with a dictionary, instead of sorting or comparing every pair. The trap: comparing timestamps as strings. That works only while every value has the same format and offset. "2026-03-01T23:30:00+05:30" > "2026-03-01T19:00:00+00:00" is True as strings, but the first instant is actually earlier. Parse with datetime.fromisoformat, which understands UTC offsets (Python docs: datetime).
Grouping and aggregating
3. Total per group, without pandas
from collections import defaultdict
orders = [("north", 120), ("south", 80), ("north", 45), ("east", 60), ("south", 20)]
totals = defaultdict(int)
for region, amount in orders:
totals[region] += amount
dict(totals) # {'north': 165, 'south': 100, 'east': 60}Follow-up almost every time: "Why not itertools.groupby?" Because it groups consecutive items. The documentation says the input generally needs to be sorted on the same key first (Python docs: itertools). On the list above, unsorted groupby returns five groups, with "north" and "south" twice each. Sort first, and it works, at O(n log n) instead of O(n).
4. Aggregate a CSV that does not fit in memory
import csv
from collections import defaultdict
def read_rows(path):
with open(path, newline="", encoding="utf-8") as f:
yield from csv.DictReader(f) # one row at a time
def revenue_by_region(rows):
totals, skipped = defaultdict(float), 0
for row in rows:
if not row["amount"]: # blank amount: count it, don't crash
skipped += 1
continue
totals[row["region"]] += float(row["amount"])
return dict(totals), skipped
# revenue_by_region(read_rows("orders.csv"))
# for a file containing a row with an empty amount: ({'north': 120.5, 'south': 80.0, 'east': 60.0}, 1)What it tests: streaming with a generator instead of f.readlines() or loading a list, so memory stays flat whatever the file size. Details that score points: opening the file with newline="", as the csv documentation requires (Python docs: csv); counting bad rows instead of silently dropping them; and saying you would log or quarantine them in a real pipeline.
Reshaping and joining
5. Flatten nested JSON into dotted keys
def flatten(obj, prefix=""):
out = {}
if isinstance(obj, dict):
for k, v in obj.items():
out.update(flatten(v, f"{prefix}{k}."))
elif isinstance(obj, list):
for i, v in enumerate(obj):
out.update(flatten(v, f"{prefix}{i}."))
else:
out[prefix[:-1]] = obj
return out
event = {"id": 42, "user": {"id": 7, "geo": {"city": "Hyderabad"}},
"items": [{"sku": "A1", "qty": 2}, {"sku": "B3", "qty": 1}]}
flatten(event)
# {'id': 42, 'user.id': 7, 'user.geo.city': 'Hyderabad',
# 'items.0.sku': 'A1', 'items.0.qty': 2, 'items.1.sku': 'B3', 'items.1.qty': 1}Follow-ups: "How would you handle the list differently?" Often a list of items should become rows, one per item with the parent id repeated, rather than numbered columns; ask which the consumer needs. "What about very deep nesting?" Recursion depth is limited, so for untrusted input an explicit stack avoids a RecursionError.
6. Join two datasets on a key
customers = [{"id": 7, "name": "Asha"}, {"id": 9, "name": "Ravi"}]
orders = [{"order_id": 1, "customer_id": 7},
{"order_id": 2, "customer_id": 11}, # no such customer
{"order_id": 3, "customer_id": 9}]
by_id = {c["id"]: c for c in customers} # build once: O(m)
joined = [{**o, "name": by_id.get(o["customer_id"], {}).get("name")} for o in orders] # probe: O(n)
# order 2 gets name None: a left joinWhat it tests: that you build a dictionary on one side (a hash join, O(n + m)) instead of nested loops (O(n × m)). A dictionary lookup is O(1) on average (Python wiki: time complexity). Follow-ups: "Is this an inner or left join?" Left, because unmatched orders are kept with None; filter them out for inner. "What if customer ids are not unique?" The dictionary silently keeps the last one. Map each id to a list instead, or assert uniqueness first. Say which you would do.
Performance, and the bugs interviewers plant
7. "Why is this loop slow?"
seen_ids = list(range(100_000))
incoming_ids = [99_999, 5, 123_456]
matches = [e for e in incoming_ids if e in seen_ids] # scans the list for every check: O(n) each
seen_ids = set(seen_ids) # build the set once
matches = [e for e in incoming_ids if e in seen_ids] # O(1) average per check
matches # [99999, 5]x in list is O(n); x in set is O(1) on average (Python wiki: time complexity). Checking one value at the end of a 100,000-item list is thousands of times slower than the set lookup, and the gap grows with the list. Interviewers want to hear you name the complexity, not just "sets are faster".
8. Spot the bug: mutable defaults and shallow copies
def add_tag(record, tags=[]): # bug: one list shared by every call
tags.append(record["id"])
return tags
add_tag({"id": 1}) # [1]
add_tag({"id": 2}) # [1, 2] <- still holds the first call's tag
def add_tag(record, tags=None): # fix
tags = [] if tags is None else tags
tags.append(record["id"])
return tags
import copy
template = {"id": None, "tags": []}
row = copy.copy(template) # shallow: row["tags"] IS template["tags"]
row["tags"].append("vip")
template # {'id': None, 'tags': ['vip']} <- template changed
row = copy.deepcopy(template) # independent nested objectsBoth bugs corrupt data quietly in pipelines. The Python tutorial warns that a default value is evaluated only once, which matters for lists and dictionaries (Python tutorial: default arguments). A shallow copy shares nested objects with the original; a deep copy copies them recursively (Python docs: copy).
9. Sort by team ascending, then salary descending
from operator import itemgetter
emps = [("Asha", "data", 72000), ("Ravi", "bi", 65000),
("Meena", "data", 90000), ("Kiran", "bi", 65000)]
sorted(emps, key=lambda e: (e[1], -e[2])) # works because salary is numeric
# General version, for keys you cannot negate: sort in passes, least important first
s = sorted(emps, key=itemgetter(0)) # name (tie-breaker)
s = sorted(s, key=itemgetter(2), reverse=True) # salary, descending
s = sorted(s, key=itemgetter(1)) # team
# [('Kiran','bi',65000), ('Ravi','bi',65000), ('Meena','data',90000), ('Asha','data',72000)]The multi-pass version relies on Python’s sorts being stable, which the documentation guarantees (Python docs: sorting techniques). Mentioning stability, and why it makes the passes work, is what separates a good answer from a memorised one.
10. Moving average over a stream
from collections import deque
def moving_average(stream, n):
window, total = deque(maxlen=n), 0.0
for x in stream:
if len(window) == n:
total -= window[0] # value about to fall out of the window
window.append(x)
total += x
yield total / len(window)
list(moving_average([10, 20, 30, 40, 50], 3)) # [10.0, 15.0, 20.0, 30.0, 40.0]A deque with maxlen drops its oldest item automatically (Python docs: collections), and keeping a running total makes each step O(1) instead of re-summing the window. Follow-up: "What should the first two outputs be?" Here they average fewer than n values. Some teams want None until the window is full; ask.
How to work through a task in the room
- Restate the task and ask about the input: size, duplicates, missing values, sort order, types.
- Say the approach and its complexity before typing.
- Write the simple correct version first; mention the scalable version if the data could be large.
- Run through one small example by hand, including an edge case (empty input, a tie, a missing key).
- Name what you would add in production: logging bad rows, tests, type hints.
Can I use pandas in a Python coding round for a data role?
Should I also practise general algorithm problems?
How should I practise these?
Where to go next
The data structures, file handling, generators and standard-library modules used here are all covered in our Python programming course.
Sources
- collections — Container datatypes — Python documentation
- itertools — groupby — Python documentation
- csv — CSV File Reading and Writing — Python documentation
- datetime — Python documentation
- Default argument values — Python tutorial
- copy — Shallow and deep copy operations — Python documentation
- Sorting Techniques — Python documentation
- TimeComplexity — Python wiki
GloryTecks Team
Training & Placement Faculty
The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.
Want to master Interview Questions?
Join GloryTecks’ expert-led Python Programming training in Hyderabad with real-time projects and 100% placement support.
Data Science Interview Questions and Answers 2026
NextSQL Interview Questions: Top 50 with Answers
Related articles
Data Science Interview Questions and Answers 2026
A data science interview is several interviews in one: statistics, machine learning, coding, product sense and a take-home. This guide maps each round with model answers, then works a churn take-home on real telecom data, including the checks that separate a strong submission from a high score.
AWS Interview Questions for Data Engineers
AWS interview questions for data engineers on S3, Glue, Athena, Redshift, Kinesis, Step Functions and security, each with the points a strong answer includes, the follow-up to expect, and three end-to-end scenario questions.
Probability Interview Questions for Data Science
Probability questions reward one habit above all: check your answer two ways. Eight classic and data-based questions are solved here with exact fractions and with simulation, then on 12,330 real shopping sessions, where the binomial answer turned out wrong for a reason worth explaining in an interview.