← Thumbtack Interview Insights

Thumbtack·Data Scientist·Technical Phone Screen·Intermediate

IntermediatePrefer not to say
Jun 2026Remote

Summary

Technical phone screen for a Data Scientist role at Thumbtack. The questions were heavily focused on Python internals and data engineering fundamentals, more CS-heavy than I expected for a DS position. Left feeling like I'd done okay on the high-level stuff but probably got too vague on some of the implementation details.

Questions Asked (5)

Q1

Compare Python lists and dicts across append, insert, lookup, update, and delete operations. What are the average and worst-case time complexities, and what are the memory and ordering implications in CPython 3?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

I knew the O(1) amortized append for lists and O(1) average lookup for dicts, but fumbled a bit on worst-case dict behavior (hash collisions, O(n)).

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Structure your answer by first defining the operations (append, insert, lookup, update, delete) and then comparing lists and dicts for each, specifying average and worst-case time complexities. Highlight CPython 3 implementation details like dynamic arrays for lists and hash tables for dicts, and discuss memory overhead and ordering guarantees.

Pro tip: Emphasize that dicts are optimized for fast lookups and have insertion-order preservation since Python 3.7, while lists are better for ordered sequences and index-based access. Mention that worst-case O(n) for dict operations is rare but possible with hash collisions, and that lists have amortized O(1) append but O(n) insert/delete at arbitrary positions.

1. Define operations and data structures

Briefly explain that lists are dynamic arrays and dicts are hash tables in CPython 3. Clarify the operations to compare: append, insert, lookup, update, delete.

2. Compare time complexities

For each operation, state the average and worst-case time complexity for lists and dicts. For example, list append is amortized O(1) average, O(n) worst-case due to resizing; dict lookup is O(1) average, O(n) worst-case with collisions.

3. Discuss memory implications

Explain that lists store pointers to objects contiguously, with overallocation for amortized appends, while dicts use a hash table with open addressing, incurring higher memory overhead per entry due to hash, key, and value storage.

4. Address ordering guarantees

Note that lists maintain insertion order by index. Dicts preserve insertion order as an implementation detail since Python 3.7 (and guaranteed in 3.7+), but this is not the same as sorted order.

5. Summarize trade-offs and use cases

Conclude with when to use each: lists for ordered sequences and index-based access, dicts for fast key-based lookups and mappings. Mention that dicts are not suitable for positional insert/delete.

Key Points to Mention

  • List append is amortized O(1) average, O(n) worst-case due to resizing; dict insert/update is O(1) average, O(n) worst-case with hash collisions.
  • List insert/delete at arbitrary index is O(n) due to shifting elements; dict delete is O(1) average, O(n) worst-case.
  • List lookup by index is O(1); dict lookup by key is O(1) average, O(n) worst-case.
  • Memory: lists have lower overhead per element but may overallocate; dicts have higher overhead per entry due to hash table structure.
  • Ordering: lists are ordered by index; dicts preserve insertion order since Python 3.7.
  • CPython 3 dicts use open addressing with perturbed probing; lists use dynamic array with over-allocation.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Write concise code showing how to append an element to a list and update a value in a dict.

Algorithms & Data Structures
Author's notes

Pretty easy, no issues.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the programming language (likely Python) and then write concise code that demonstrates appending to a list and updating a dictionary. Explain the code briefly, highlighting key operations and any relevant considerations like mutability and time complexity.

Pro tip: Mention that lists are mutable and appending is O(1) amortized, while dict updates are O(1) average case, showing awareness of performance. Also, use descriptive variable names to make the code self-documenting.

1. Clarify language and assumptions

Confirm the programming language (e.g., Python) and any constraints, such as whether the list and dict already exist.

2. Write code for list append

Use the appropriate method (e.g., list.append() in Python) to add an element to the end of the list.

3. Write code for dict update

Assign a new value to an existing key or use the update method to modify the dictionary.

4. Explain the code

Briefly describe what each line does and mention any relevant properties like mutability or time complexity.

5. Consider edge cases

Mention what happens if the key doesn't exist (e.g., it gets added) or if the list is full (not applicable in Python).

Key Points to Mention

  • Lists are mutable and dynamic; append adds to the end.
  • Dictionaries are mutable; updating a key changes its value or adds a new key-value pair.
  • Time complexity: list append is O(1) amortized, dict update is O(1) average.
  • Use of built-in methods like append() and direct assignment or update().
  • Code readability: use meaningful variable names and comments if needed.
  • Potential pitfalls: modifying a list while iterating, or using a non-hashable key in a dict.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

What is the difference between JSON and CSV formats, and when would you choose JSON over CSV? Consider nesting, schema changes over time, interoperability, and compression.

Technical Trade-offsData Modeling
Author's notes

This one I actually liked.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by defining JSON and CSV in terms of their structural differences, then systematically compare them across the four dimensions mentioned: nesting, schema evolution, interoperability, and compression. Conclude with practical scenarios where JSON is preferred, especially in data science contexts like handling semi-structured data or APIs.

Pro tip: Emphasize that the choice often depends on the downstream use case: CSV for tabular analytics and bulk storage, JSON for hierarchical data and flexible schemas. Mention that hybrid approaches (e.g., JSON Lines) can offer the best of both worlds.

1. Define the formats

Briefly explain that CSV is a flat, tabular format with rows and columns, while JSON is a hierarchical, key-value format supporting nested structures.

2. Compare nesting capabilities

Discuss that JSON natively supports nested objects and arrays, making it ideal for representing complex relationships, whereas CSV requires flattening or multiple tables.

3. Analyze schema flexibility

Explain that JSON is schema-less and can easily accommodate evolving fields, while CSV has a fixed column structure that requires careful handling of schema changes.

4. Evaluate interoperability and compression

Mention that CSV is universally supported by spreadsheets and databases, but JSON is standard for web APIs and NoSQL databases. For compression, both compress well, but JSON's repetitive keys can benefit from specialized compression like gzip.

5. Decide when to choose JSON

Conclude that JSON is preferable when data is hierarchical, schema is dynamic, or when integrating with web services; CSV is better for simple tabular data and bulk analytics.

Key Points to Mention

  • Nesting: JSON supports nested structures; CSV is flat and requires flattening or multiple files.
  • Schema evolution: JSON is flexible and can handle added/removed fields; CSV requires schema migration or careful parsing.
  • Interoperability: CSV is widely supported by spreadsheets and SQL databases; JSON is standard for APIs and NoSQL.
  • Compression: Both compress well, but JSON's verbose keys may lead to larger files; however, gzip or columnar formats can mitigate.
  • Use cases: JSON for semi-structured data, APIs, and hierarchical data; CSV for tabular data, bulk storage, and analytics.
  • Hybrid formats: JSON Lines (newline-delimited JSON) combines JSON's flexibility with CSV's line-by-line processing.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

Show Python code to stream-read a JSON Lines file line by line using json.loads, read a CSV using csv.DictReader, and use pandas read_csv with chunksize to compute the sum of a numeric column without loading the full file into memory.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

The JSON Lines part was fine.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the file formats and the goal of memory-efficient processing. Then, for each format, outline the streaming approach: line-by-line with json.loads for JSONL, csv.DictReader for CSV, and pandas read_csv with chunksize for large CSV. Emphasize that all methods avoid loading the full file into memory and include code snippets for each.

Pro tip: Mention that for JSONL, you should handle potential malformed lines with try/except to avoid crashing on bad data, and for pandas chunksize, you can use a running sum to accumulate the total without storing all chunks.

1. Clarify requirements and constraints

Confirm the file formats, the numeric column name, and that memory efficiency is critical. Ask if error handling for malformed lines is needed.

2. Stream JSONL with json.loads

Open the file and iterate over each line, parsing with json.loads. Process each JSON object immediately and discard it to keep memory low.

3. Read CSV with csv.DictReader

Use csv.DictReader to iterate over rows as dictionaries, accessing the numeric column by key. This reads the file line by line without loading it entirely.

4. Use pandas read_csv with chunksize

Specify chunksize to get an iterator of DataFrames. For each chunk, compute the sum of the numeric column and accumulate the total. This avoids loading the full dataset into memory.

5. Summarize and discuss trade-offs

Highlight that all methods are memory-efficient but differ in speed and convenience. Mention that pandas chunksize is convenient for complex operations, while manual parsing gives more control.

Key Points to Mention

  • Memory efficiency: streaming avoids loading entire file into RAM.
  • Error handling: try/except for json.loads to skip malformed lines.
  • csv.DictReader returns dictionaries, making column access easy.
  • pandas chunksize returns an iterator of DataFrames; use a running sum.
  • Trade-offs: pandas is faster for large data but has overhead; manual parsing is lightweight.
  • Use of generators or iterators to process data lazily.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

How would you handle malformed rows, missing or NaN values, bad encodings, and numeric overflow when processing large files? Also, what chunk size would you use for a 10 GB file on a machine with 16 GB RAM, and is there a non-pandas streaming alternative?

System DesignTechnical Trade-offsData Modeling
Author's notes

This is where things got a bit scattered for me.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Structure your answer by first addressing data quality issues (malformed rows, missing/NaN, encodings, overflow) with concrete strategies, then discuss chunk sizing for a 10GB file on 16GB RAM, and finally present non-pandas streaming alternatives. Emphasize trade-offs between robustness, performance, and memory usage, and tailor your answer to Thumbtack's data scale and business needs.

Pro tip: Mention that you'd log and quarantine bad rows for later inspection rather than silently dropping them, and that you'd validate chunk size empirically by monitoring memory usage during a test run. This shows production maturity and avoids one-size-fits-all answers.

1. Handle malformed rows and missing/NaN values

Use error handling (e.g., try/except, on_bad_lines='skip' or custom parser) to skip or quarantine malformed rows. For missing/NaN, decide between imputation, dropping, or flagging based on downstream use, and use pandas' na_values and keep_default_na to control parsing.

2. Address bad encodings and numeric overflow

Detect encoding with chardet or use errors='replace'/'ignore' in open(), and consider converting to UTF-8. For numeric overflow, specify dtypes (e.g., float64, int64) or use object dtype for large integers, and validate ranges to avoid silent overflow.

3. Determine chunk size for 10GB file on 16GB RAM

Aim for chunks that fit comfortably in memory, e.g., 100MB–500MB (roughly 1–5 million rows depending on width). Start with 200MB and adjust based on available RAM and processing overhead; monitor memory to avoid swapping.

4. Discuss non-pandas streaming alternatives

Mention alternatives like Dask, Polars (lazy), Vaex, or plain Python with csv module and generators. For truly large files, consider PyArrow or database bulk loaders, and highlight trade-offs in speed, memory, and ease of use.

5. Tie back to Thumbtack's context and trade-offs

Relate choices to Thumbtack's data volume and real-time needs, emphasizing robustness and reproducibility. Discuss trade-offs: skipping bad rows vs. failing fast, chunk size vs. I/O overhead, and pandas vs. streaming libraries.

Key Points to Mention

  • Use of on_bad_lines parameter in pandas read_csv (or error_bad_lines in older versions) and logging bad rows for auditing.
  • Handling missing data: distinguish between NaN, None, and empty strings; use pandas' na_values and keep_default_na; consider imputation vs. dropping based on analysis goals.
  • Encoding detection and conversion: chardet, errors='replace', and ensuring UTF-8 for downstream compatibility.
  • Numeric overflow prevention: specify dtypes, use float64 for large integers, or object dtype; validate ranges.
  • Chunk size calculation: rule of thumb 10-20% of available RAM (e.g., 1.6-3.2GB) but start smaller (e.g., 200MB) and tune; use chunksize parameter in pandas.
  • Non-pandas alternatives: Dask (parallel, out-of-core), Polars (lazy, fast), Vaex (memory-mapped), and Python's csv module with generators for minimal memory.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.