← Robinhood Interview Insights

Robinhood·Data Scientist·Technical Phone Screen·Intermediate

Intermediate
May 2026

Summary

Robinhood data scientist interview with a combined SQL and Python problem built around a transactions dataset. The SQL part had two tasks back to back and the Python part was more of an algorithm efficiency question dressed up as analytics. Pretty dense for a single session.

Questions Asked (3)

Q1

Given a Users table and a Transactions table, write a single SQL query that returns each transaction with normalized sender and receiver IDs, the normalized amount, and the country of each party. Transactions with a negative amount have their direction reversed, so you need to handle that with a CASE expression.

Data ModelingProduct Analytics & Metrics
Author's notes

The direction-reversal rule tripped me up at first.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the schema and the normalization rules, then outline the SQL query structure: join Transactions to Users twice (for sender and receiver), use CASE to swap sender and receiver when amount is negative, and take the absolute value of the amount. Finally, select the country for each party from the joined Users table.

Pro tip: Mention that you would validate the normalization logic with edge cases (e.g., zero amounts, self-transactions) and consider performance implications of joining the Users table twice, suggesting indexes on user IDs.

1. Clarify schema and normalization rules

Ask about the table structures (column names, data types) and confirm that negative amounts indicate reversed direction. Ensure you understand what 'normalized' means: sender and receiver swapped, amount positive.

2. Plan the query structure

Decide to use a SELECT with CASE expressions for sender_id, receiver_id, and amount. Use two joins to the Users table: one for the original sender and one for the original receiver.

3. Write the CASE logic

For sender_id: CASE WHEN amount < 0 THEN receiver_id ELSE sender_id END. For receiver_id: CASE WHEN amount < 0 THEN sender_id ELSE receiver_id END. For amount: ABS(amount).

4. Join to Users for country information

Join the normalized sender_id to Users to get sender_country, and normalized receiver_id to Users to get receiver_country. Use aliases to avoid ambiguity.

5. Finalize and test

Combine all parts into a single query. Suggest testing with sample data including negative amounts to verify correctness.

Key Points to Mention

  • Use of CASE expressions to conditionally swap sender and receiver based on amount sign.
  • Application of ABS() to normalize the amount.
  • Self-join or multiple joins to the Users table to retrieve country for both parties.
  • Handling of edge cases such as zero amounts (direction unchanged) and self-transactions.
  • Performance considerations: indexing on user IDs and avoiding unnecessary columns.
  • Clarity on output columns: transaction_id, normalized_sender_id, normalized_receiver_id, normalized_amount, sender_country, receiver_country.

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

Q2

Using window functions on the normalized transaction data, produce a result showing each user's sent transaction count, their dense rank within their country by that count, and then filter to only the top 3 users per country.

Data ModelingAlgorithms & Data Structures
Author's notes

Window functions with a FILTER step at the end.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by aggregating the normalized transaction data to get each user's sent transaction count, then use a window function to compute a dense rank within each country based on that count. Finally, filter the results to only include users whose dense rank is 3 or less per country.

Pro tip: Clarify whether 'sent transaction count' should include only completed transactions or all attempts, and mention that dense rank handles ties without gaps, which is often preferred for top-N per group.

1. Aggregate sent transaction counts per user

Group the normalized transaction data by user ID and count the number of sent transactions, ensuring you filter for transactions where the user is the sender.

2. Join with user country information

Join the aggregated counts with a user dimension table to associate each user with their country.

3. Apply dense rank window function

Use DENSE_RANK() OVER (PARTITION BY country ORDER BY sent_count DESC) to rank users within each country based on their sent transaction count.

4. Filter to top 3 per country

Wrap the ranked result in a subquery or CTE and filter where the dense rank is less than or equal to 3.

5. Select final columns and order

Output user ID, country, sent transaction count, and dense rank, ordering by country and rank for readability.

Key Points to Mention

  • Use of DENSE_RANK() vs RANK() or ROW_NUMBER() and why dense rank is appropriate for top-N per group with ties.
  • Partitioning by country and ordering by sent transaction count descending.
  • Handling of ties: dense rank assigns the same rank to users with equal counts, and the next rank is consecutive.
  • Filtering after window function using a subquery or CTE because window functions cannot be used in WHERE clause directly.
  • Assumption about 'sent transaction count': whether it's distinct transactions or total amount, and if only successful transactions are counted.
  • Performance considerations: indexing on user ID and country, and potential use of QUALIFY in some SQL dialects.

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

Q3

Implement a Python function that takes an iterable of transaction records (each with an ID, a from:to user pair string, and an amount), applies the same direction-reversal rule for negative amounts, and returns the top 5 senders and top 5 receivers by transaction count. The solution should run in O(n) time and use O(u) extra space where u is the number of unique users.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

The O(n) / O(u) constraint is where this gets interesting.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Clarify the direction-reversal rule for negative amounts (e.g., if amount < 0, swap sender and receiver and use absolute amount). Then iterate through the records once, maintaining two hash maps (or Counters) to count transactions per sender and per receiver, and finally use heapq.nlargest to extract the top 5 from each map. This achieves O(n) time and O(u) space.

Pro tip: Mention that using collections.Counter and heapq.nlargest is both efficient and Pythonic, and explicitly state the time and space complexity to demonstrate awareness of scalability. Also, discuss edge cases like ties in counts and how to handle them deterministically.

1. Clarify the rules and assumptions

Confirm the direction-reversal rule for negative amounts: if amount < 0, swap the from and to users and treat the amount as positive. Also clarify input format (e.g., from:to string) and output format (e.g., list of top 5 user IDs with counts).

2. Choose data structures

Use two hash maps (e.g., collections.Counter) to count transactions per sender and per receiver. This allows O(1) average-time updates and O(u) space.

3. Single-pass iteration

Iterate through each transaction record once. For each, parse the from:to pair, apply the reversal rule if amount < 0, and increment the appropriate counters.

4. Extract top 5

Use heapq.nlargest(5, counter.items(), key=lambda x: x[1]) to get the top 5 senders and receivers by count. This runs in O(u log 5) which is effectively O(u).

5. Analyze complexity and edge cases

State that time is O(n) and space is O(u). Discuss handling ties (e.g., sort by count descending, then user ID ascending for determinism) and empty input.

Key Points to Mention

  • Direction-reversal rule: swap sender and receiver when amount is negative, and use absolute amount.
  • Use of hash maps (Counter) for O(1) updates and O(u) space.
  • Single-pass iteration to achieve O(n) time.
  • heapq.nlargest for efficient top-k extraction without full sort.
  • Time complexity: O(n + u log 5) ≈ O(n); space complexity: O(u).
  • Edge cases: ties in counts, empty input, negative amounts, and parsing the from:to string.

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