Python coding round for data roles, eight easy problems solved
In this article (7 sections)
Related programme: the Data Science course (6 months; ₹1,30,000 (inclusive of GST) · EMI from ₹5,000/month).
Python coding rounds for data analyst and data scientist roles are usually easier than software engineering rounds: 30–45 minutes, one to three problems on dictionaries, strings, lists and sorting, often dressed up as data tasks such as counting events, grouping records or parsing logs. You are judged on correct, readable code in plain Python (pandas is often not allowed), handling of edge cases, and a short explanation of time complexity. The eight problems below are typical, each solved and tested in one script, followed by the follow-up the interviewer is likely to ask.
How to run the round
Read the problem back in your own words, ask about edge cases (empty input, ties, duplicates), say your approach and its complexity before typing, write the code, then test it with a normal case and an edge case aloud. Silence while typing is the most common way to lose points on an otherwise correct answer.
The eight problems
- 1Top-k frequent items. Given a list of product IDs viewed, return the k most viewed, breaking ties alphabetically.
- 2First non-repeating character. Return the first character in a string that appears exactly once, or None.
- 3Group anagrams. Group words that are anagrams of each other.
- 4Merge overlapping intervals. Given session start and end minutes, merge overlaps.
- 5Two-sum on transaction amounts. Return indices of two amounts that sum to a target.
- 6Moving average. Return the average of each window of size k.
- 7Parse a log line. Extract status codes and count 5xx errors per endpoint.
- 8Flatten a nested dictionary. Turn nested keys into dotted keys, as when flattening JSON for a table.
Solutions, tested together
from collections import Counter, defaultdict
def top_k(items, k):
counts = Counter(items)
return [item for item, _ in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))[:k]]
def first_unique(s):
counts = Counter(s)
return next((ch for ch in s if counts[ch] == 1), None)
def group_anagrams(words):
groups = defaultdict(list)
for w in words:
groups["".join(sorted(w))].append(w)
return list(groups.values())
def merge_intervals(intervals):
merged = []
for start, end in sorted(intervals):
if merged and start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
return merged
def two_sum(amounts, target):
seen = {}
for i, a in enumerate(amounts):
if target - a in seen:
return seen[target - a], i
seen[a] = i
return None
def moving_average(values, k):
if k <= 0 or k > len(values):
return []
window = sum(values[:k])
out = [window / k]
for i in range(k, len(values)):
window += values[i] - values[i - k]
out.append(window / k)
return out
def errors_by_endpoint(lines):
errors = Counter()
for line in lines:
parts = line.split()
if len(parts) < 3 or not parts[2].isdigit():
continue # skip malformed lines instead of crashing
endpoint, status = parts[1], int(parts[2])
if 500 <= status <= 599:
errors[endpoint] += 1
return dict(errors)
def flatten(d, prefix=""):
flat = {}
for key, value in d.items():
name = f"{prefix}.{key}" if prefix else key
if isinstance(value, dict) and value:
flat.update(flatten(value, name))
else:
flat[name] = value
return flat
assert top_k(["p2", "p1", "p2", "p3", "p1", "p4"], 2) == ["p1", "p2"]
assert first_unique("swiss") == "w" and first_unique("aabb") is None
assert sorted(map(sorted, group_anagrams(["listen", "silent", "enlist", "rat", "tar", "art", "dog"]))) == \
[["art", "rat", "tar"], ["dog"], ["enlist", "listen", "silent"]]
assert merge_intervals([(1, 3), (8, 10), (2, 6), (10, 12)]) == [[1, 6], [8, 12]]
assert two_sum([500, 1200, 300, 900], 1200) == (2, 3)
assert moving_average([10, 20, 30, 40], 2) == [15.0, 25.0, 35.0]
assert moving_average([1, 2], 5) == []
logs = ["GET /pay 502", "GET /pay 200", "POST /cart 500", "bad line", "GET /pay 503"]
assert errors_by_endpoint(logs) == {"/pay": 2, "/cart": 1}
assert flatten({"user": {"id": 7, "geo": {"city": "Noida"}}, "ok": True}) == \
{"user.id": 7, "user.geo.city": "Noida", "ok": True}
print("all eight solutions pass")Complexity and follow-ups
| Problem | Complexity | Likely follow-up | Good response |
|---|---|---|---|
| Top-k | O(n + m log m), m distinct items | "What if there are millions of distinct items?" | Use heapq.nsmallest(k, …) for O(m log k), or count in a streaming or distributed way. |
| First unique | O(n) | "Characters arrive as a stream?" | Keep counts plus an ordered structure of candidates; drop them when they repeat. |
| Anagrams | O(n · w log w) | "Faster key?" | A 26-count tuple for lowercase letters gives O(n · w). |
| Intervals | O(n log n) | "Should touching intervals merge?" | Ask. Here 10 and 10 merge because of <=; change to < if not. |
| Two-sum | O(n) | "Return all pairs?" | Store lists of indices per value; watch duplicates and the same element used twice. |
| Moving average | O(n) | "Why not sum each window?" | That is O(n·k); the running window subtracts the value leaving and adds the new one. |
| Log parsing | O(n) | "Malformed lines?" | They are skipped rather than crashing the job; count them and report it. |
| Flatten | O(total keys) | "Lists inside?" | Decide a convention such as items.0.sku, and ask whether the consumer expects that. |
Talking through one problem aloud
Here is how problem 4 sounds when done well: "So I get a list of sessions as start and end minutes, possibly unsorted, and I return merged blocks. Should intervals that only touch — one ending at 10, the next starting at 10 — merge? I'll assume yes. Approach: sort by start, walk once, and either extend the last merged block or start a new one. Sorting dominates, so O(n log n) time and O(n) for the output. Testing with the example… and an empty list returns an empty list." That is under a minute, and it answers the three things an interviewer is scoring before any code appears: understanding, assumptions and complexity.
What loses points
- Starting to code without confirming the tie-breaking rule in problem 1 or the touching-interval rule in problem 4.
- Using a list for membership checks inside a loop (
if x in seen_list), which quietly makes problem 5 O(n²). - Not testing an empty or tiny input. The
moving_average([1, 2], 5)case is the one interviewers try. - Clever one-liners that you cannot explain. Readable code with names wins.
Practice plan
Solve each problem once from scratch without looking, time yourself at 10 minutes each, and say the complexity aloud. Then do the same problems in SQL or pandas where it makes sense — top-k and moving averages are classic — and compare with the pandas interview bank and SQL window functions. Python fundamentals for data work are taught from the first module of the Data Science programme.
Frequently asked questions
Are data science coding rounds as hard as software engineering ones?
Usually not. Data roles tend to ask easy-to-medium problems on dictionaries, strings, lists and sorting, often with a data flavour such as counting, grouping or parsing logs. Interviewers care about correct, readable code, edge cases and a sensible explanation of time complexity.
Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.
This article is part of our Data Science programme — 6 months. From data foundations to machine learning, deep learning and deployment.
Explore Data Science