← Capital One Interview Insights

Capital One·Data Scientist·Technical Phone Screen·Senior

Senior
May 2026

Summary

Capital One data scientist technical screen, all four questions were SQL plus pandas combos on the same schema. Pretty intense for a phone screen, felt more like a take-home crammed into a live session.

Questions Asked (4)

Q1

Given a customers table with missing age values, impute each null using the median age within that customer's tier. If a tier has no non-null ages to compute a median from, fall back to the global median. Return customer_id and the imputed age.

Data ModelingProduct Analytics & Metrics
Author's notes

This one tripped me up more than it should have.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the table schema and the definition of 'tier'. Then outline a two-level aggregation: compute median age per tier and the global median, and use COALESCE or a CASE expression to fill nulls with the tier median, falling back to the global median when the tier median is null. Finally, return customer_id and the imputed age.

Pro tip: Mention that you would validate the imputation by checking the distribution of imputed values and ensuring no tier with all nulls is left unfilled. Also note that using a window function like PERCENTILE_CONT with PARTITION BY tier can compute tier medians in a single pass, but you must handle the fallback carefully.

1. Understand the data and requirements

Clarify the table structure: customer_id, age, tier. Confirm that 'tier' is a column and that missing ages are represented as NULL. Ask if there are any edge cases, such as tiers with all NULL ages.

2. Compute tier-level medians

Use a window function or subquery to calculate the median age for each tier, ignoring NULLs. For example, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY age) OVER (PARTITION BY tier).

3. Compute global median

Calculate the median age across the entire table, ignoring NULLs. This will be used as a fallback when a tier has no non-null ages.

4. Impute missing ages

For each row, if age is NULL, replace it with the tier median if available; otherwise use the global median. Use COALESCE(tier_median, global_median) or a CASE expression.

5. Return the result

Select customer_id and the imputed age (original age if not null, else the imputed value). Optionally, validate that no NULLs remain and that imputed values are reasonable.

Key Points to Mention

  • Use of window functions like PERCENTILE_CONT for median calculation
  • Handling of NULLs in median computation (they are ignored by default)
  • Fallback logic: COALESCE or CASE WHEN tier_median IS NULL THEN global_median
  • Partitioning by tier to compute tier-specific medians
  • Ensuring the final output has no NULL ages
  • Potential performance considerations: computing global median once, avoiding repeated subqueries

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

Q2

You have a staging_events table that may contain new rows or updated versions of existing rows relative to a live events table. Write an upsert: insert rows with event_ids not already in events, and for duplicate event_ids with differing values, keep only the row with the latest event_date. Return the final deduplicated events table.

Data ModelingTechnical Trade-offs
Author's notes

The dedup condition caught me a bit.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the schema and business rules, then outline a step-by-step SQL solution using a UNION of both tables, a window function to rank rows by event_date, and a filter to keep the latest. Finally, discuss trade-offs and edge cases to demonstrate depth.

Pro tip: Mention that you would validate the upsert with a quick count check and consider idempotency to ensure the operation can be safely re-run without duplicating data.

1. Clarify Requirements and Schema

Ask about the table structures, primary keys, and how to handle ties in event_date. Confirm that 'latest' means the most recent event_date and that all columns should be updated.

2. Combine Staging and Live Data

Use a UNION ALL to stack staging_events and events, ensuring column alignment. This creates a unified dataset for deduplication.

3. Rank Rows by Recency

Apply a window function like ROW_NUMBER() OVER (PARTITION BY event_id ORDER BY event_date DESC) to assign a rank to each row within each event_id.

4. Filter to Keep Latest

Select only rows where the rank equals 1, which gives the most recent version of each event. This handles both new inserts and updates.

5. Discuss Trade-offs and Edge Cases

Address performance implications (e.g., full table scan vs. incremental), tie-breaking rules (e.g., using another column), and how to handle deletions if needed.

Key Points to Mention

  • Use of UNION ALL to combine datasets without removing duplicates prematurely.
  • Window functions (ROW_NUMBER or RANK) for deduplication based on event_date.
  • Handling ties in event_date by defining a secondary sort key or business rule.
  • Idempotency and transaction safety to allow re-running the upsert.
  • Performance considerations: indexing on event_id and event_date, and avoiding full table scans.
  • Validation steps: comparing row counts and checking for duplicates post-upsert.

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

Q3

For the 7-day window ending today, compute net revenue per tier. Purchases count positive, refunds negative, and non-monetary events like page views should be excluded. Use the imputed ages from the first question and restrict to customers aged 18 to 65. Return tier, total revenue for the window, and distinct customer count.

Product Analytics & MetricsData Modeling
Author's notes

Stacking the imputed age logic from part A into this query is where things got messy for me live.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, filter the events to the last 7 days and exclude non-monetary events, keeping only purchases and refunds. Then join with the customer age data (using imputed ages) and restrict to customers aged 18-65. Finally, aggregate net revenue (purchases positive, refunds negative) and distinct customer count per tier.

Pro tip: Clarify whether 'net revenue' should be calculated as the sum of purchase amounts minus refund amounts, and ensure that refunds are correctly attributed to the original purchase's tier if tier can change. Also, confirm that the 7-day window includes today and that 'today' is based on the event timestamp's timezone.

1. Filter events by date and type

Restrict the event data to the 7-day window ending today and exclude non-monetary events like page views, keeping only purchases and refunds.

2. Join with customer age data

Join the filtered events with the customer table that contains imputed ages, ensuring you use the imputed ages from the first question.

3. Filter by age range

Restrict the joined data to customers aged 18 to 65 inclusive.

4. Compute net revenue and distinct customers

For each tier, calculate net revenue by summing purchase amounts (positive) and refund amounts (negative), and count distinct customers who made at least one purchase or refund.

5. Output results

Return a table with tier, total net revenue for the window, and distinct customer count, ordered by tier or revenue as needed.

Key Points to Mention

  • Definition of net revenue: purchases as positive, refunds as negative.
  • Exclusion of non-monetary events such as page views.
  • Use of imputed ages from the first question and age restriction 18-65.
  • Time window: last 7 days including today, based on event timestamp.
  • Distinct customer count: count unique customers per tier who had any monetary event in the window.
  • Handling of refunds: ensure they are matched to the correct tier, possibly using the original purchase's tier if tier can change.

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

Q4

Among customers who had at least one monetary event in the last 7 days, what fraction also had any event in the prior 7-day window? Return one row per tier with the retention rate. Also describe what indexes you'd add and how you'd test correctness.

Product Analytics & MetricsTechnical Trade-offsA/B Testing & Experimentation
Author's notes

The retention definition here is a bit unusual since it's looking backward at a prior window rather than forward, so I had to read it twice.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clarifying the definitions of 'monetary event' and 'any event' and the tier dimension, then outline a SQL solution using two 7-day windows and a self-join or conditional aggregation. Finally, discuss indexing strategies and a testing plan to validate correctness.

Pro tip: Emphasize that the retention rate should be computed as the number of customers with a monetary event in the last 7 days who also had any event in the prior 7 days, divided by the total number of customers with a monetary event in the last 7 days, grouped by tier. Also, mention that using a calendar table or date spine can help handle missing dates and ensure accurate window calculations.

1. Clarify definitions and requirements

Confirm what constitutes a 'monetary event' and 'any event', the definition of 'tier', and the exact time windows (e.g., last 7 days from today, prior 7-day window).

2. Design the SQL query

Use conditional aggregation or a self-join to identify customers with a monetary event in the last 7 days and check if they had any event in the prior 7 days, then compute the fraction per tier.

3. Optimize with indexes

Recommend indexes on (customer_id, event_date, event_type) and (tier, event_date) to speed up filtering and joins, and consider partitioning by date if the table is large.

4. Test correctness

Validate with edge cases (e.g., customers with events exactly on the boundary dates), compare against a manual calculation on a small sample, and check for NULLs or missing tiers.

5. Communicate results and assumptions

Present the retention rate per tier and clearly state any assumptions made (e.g., event_date is a date not timestamp, tiers are mutually exclusive).

Key Points to Mention

  • Definition of monetary event and any event (e.g., transaction vs. login)
  • Use of date functions like DATE_SUB or BETWEEN to define 7-day windows
  • Handling of customers with no prior events (they count as not retained)
  • Grouping by tier and calculating the fraction as retained / total monetary customers
  • Index recommendations: composite indexes on (customer_id, event_date) and (tier, event_date)
  • Testing approach: unit tests with known data, boundary conditions, and performance checks

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