cswork / libraryOpen practice workspace

Interview guides · Red Alpha

Red Alpha Data Scientist Interview Questions & Guide 2026

Red Alpha data scientist prep on data pitfalls: selection bias, target leakage, and checking an outlier's provenance before you drop it.

The 60-second brief

A Data Scientist at Red Alpha plays a pivotal role in solving some of the nation's most complex and critical national security challenges. Operating primarily within the defense and intelligence sectors, Red Alpha delivers advanced technology solutions that transform massive, unstructured, and disparate datasets into actionable intelligence. As a Data Scientist, you will not just build standard models; you will design and deploy sophisticated algorithms that directly impact national security, mission planning, and strategic decision-making.

Allocate prep to your weakest link rather than your favourite topic. Of the three things that usually gate the outcome (SQL that is correct under messy joins, sound reasoning about experiments, and structured framing of an open-ended problem), candidates tend to over-invest in modelling theory and under-invest in framing.

Focus areas

  • Build point-in-time panels without lookahead
  • Measure shortfall against arrival, not VWAP
  • Model impact and borrow before claiming capacity

Overview

The impact of this position is profound. You will work alongside software engineers, systems architects, and mission analysts to create predictive models, natural language processing tools, and anomaly detection systems. The data environments you encounter are unique in their scale, sensitivity, and complexity, often requiring innovative approaches to data cleaning, feature engineering, and model deployment in secure, air-gapped environments.

Because Red Alpha operates primarily in the defense and intelligence space, all positions require an active TS/SCI clearance with a polygraph. Ensure your clearance details are up to date before your initial screen.

The Red Alpha interview process

Red Alpha candidates report 3 rounds · ≈ 3-5 weeks. The stages below are what candidates describe, not a published process.

Recruiter Call

Whoever runs this call is usually not a practitioner. They take notes, and a hiring manager skims those notes later, so the real question is whether your work survives being written down by someone outside the field. Test every project sentence against that: could a non-specialist repeat it correctly without knowing what a propensity score is? Carry a plain-language version of each project and one reason you want this particular role that you could not copy onto another application. Vagueness at this stage reads as inexperience, even when the underlying work was genuinely deep.

What to demonstrate

  • Whether a non-specialist can restate your projects accurately, since their paraphrase is what reaches the hiring manager
  • Whether your reason for wanting the role points at the work itself rather than the company's reputation
  • Whether your language signals the level being screened for: what you decided yourself versus what you were handed

How to prepare

  • Write a two-sentence, jargon-free version of each major project: the question nobody could answer, and the decision your work changed. Read it to someone outside data and have them repeat it back
  • Point your 'why this role' answer at something concrete in the job description or the product surface you would be working on, and keep it to two sentences
  • Have two questions ready about measurement: which metric the team is held to, and who acts on an analysis once it lands

Technical Assessment

This round decides whether someone can hand you a schema and a question and trust the number that comes back. Correctness under a clock is the bar, not clever syntax. The habit that separates strong from weak answers is checking the grain: after every join, know how many rows you expect and whether the count moved. Most wrong answers in this format are not wrong logic, they are a fan-out from a key that turned out not to be unique, or a filter applied before an aggregate when it belonged after. Say what you expect before you run it.

What to demonstrate

  • Whether your row counts survive each join, and whether you notice on your own when they do not
  • Deliberate handling of rows that fail to match, including whether the question needs an inner join or a left join with the non-matches kept and counted
  • Whether NULLs are treated on purpose, given that a NULL compares equal to nothing and that COUNT of a column skips it
  • Reaching a defensible answer inside the window instead of a refined one after it

How to prepare

  • Take a two-table schema, write a join that fans out on purpose, then fix it by collapsing the many-side to one row per key before joining. Repeat until the fix is reflex rather than recall.
  • Write a funnel as one query and print the distinct user count at each stage, then confirm each stage is a subset of the one above it rather than assuming it
  • Do a few timed runs in a plain text box with no autocomplete and no formatter, since assessment editors often have neither

Panel Interview

Where a loop ends with a senior leader, that conversation is rarely another skills test. The technical signal already exists by then, so the questions tend to open up: what you would look at first, where a metric you have heard about could mislead, what you would push back on. The decision being made is scope, which in practice means level and how much you would be trusted to own unsupervised. Treating it as a formality is the usual mistake. An open question late in the day is still being scored, and a vague answer reads as someone who has not run anything themselves.

What to demonstrate

  • Whether your view of the business has anything specific behind it, given that you are working only from what is public and are expected to say so
  • Whether the scope of work you describe owning matches the scope of the role, instead of sitting a level below it
  • Whether you can disagree with something concrete and stay useful about it, rather than agreeing with everything said in the room
  • Whether your questions are ones only this person could answer, as opposed to ones the recruiter already covered

How to prepare

  • Build one view you could defend for two minutes using only public information: what the funnel probably looks like, which metric likely drives decisions, and where that metric could mislead. Being wrong for a stated reason survives this round; having no view does not
  • Write down the largest piece of work you have owned from question to decision, who else touched it, and what you decided alone, then check that it reads at the level you are interviewing for
  • Prepare one thing you would want changed if you joined and phrase it as a question rather than a verdict, so it opens a conversation instead of closing one

Where candidates lose points

Modelling transaction cost as a constant number of basis points, independent of order size and volatility.

Temporary market impact scales approximately with volatility times the square root of participation, that is, of order quantity divided by average daily volume, so cost per share rises as size rises rather than staying flat. A constant-bps assumption is roughly right for the small orders used to calibrate it and badly wrong for the size the strategy would actually run, which is how a book that backtests well at modest notional loses money at ten times the size. It also makes capacity unmeasurable, because capacity is exactly the notional at which marginal impact equals marginal alpha.

Judging execution quality against interval VWAP and treating a favourable number as proof of good trading.

Interval VWAP is a benchmark the trader partly determines: trading in line with volume tracks VWAP almost by construction, and stretching an order over a longer interval makes the benchmark easier while exposing the position to price drift that the benchmark never charges. Arrival price is the benchmark aligned with the decision, because it charges both the spread and the drift between decision and completion, including the unfilled remainder. Reporting both, and reporting the opportunity cost of unfilled quantity, is what separates a real TCA from a flattering one.

Naming a model class before naming the deployment constraints

Set out the latency budget, the label delay, the retraining cadence, the interpretability requirement and the number of labelled examples, then pick the model that fits them. A boosted-tree answer to a problem where each decision must be explained to the affected user is a well-executed answer to the wrong question.

Reading a dozen metrics with no multiplicity control

Nominate one primary metric before launch and treat the rest as guardrails or exploratory, with Bonferroni or Benjamini-Hochberg applied when you intend to make claims from them. Twenty independent tests at 0.05 under the null produce at least one false positive about 64 percent of the time.

Questions & practice

Describe a scenario where you would choose a deep learning approach over classical machine learning, and explain how you would justify the computational overhead.

Medium · machine learning and modelling

Approach

  1. Check what information would not exist at prediction time, and exclude it.
  2. Pick an evaluation metric that matches the cost of each error type, not a default.
  3. Frame the prediction: the label, the moment of prediction, and the action it triggers.

Follow-up

  • What would you monitor after launch to know the model is still valid?
  • How would you choose the decision threshold, and who owns that choice?

What statistical metrics would you use to evaluate the performance of an unsupervised clustering algorithm?

Medium · machine learning and modelling

Approach

  1. Pick an evaluation metric that matches the cost of each error type, not a default.
  2. Set a baseline first, so any model has something honest to beat.
  3. Check what information would not exist at prediction time, and exclude it.

Follow-up

  • Where could label leakage enter this setup?
  • How would you choose the decision threshold, and who owns that choice?

Sessionise a corrected fill stream into trading bursts

Hard · sessionisation · execution · vectorization

One trading day of execution_fill: fill_id, order_id, instrument_id, exec_ts (microsecond UTC venue time), received_ts, fill_qty, fill_px, venue_mic, liquidity_flag, is_correction, corrects_fill_id. About 9.4M rows, delivered in received_ts order, with roughly 0.3% corrections including chains and zero-quantity busts. Produce one row per (order_id, burst), where a burst is a maximal run of surviving fills whose consecutive exec_ts gap is at most 90 seconds, carrying start_ts, end_ts, n_fills, qty, quantity-weighted price and the added-liquidity share. No per-order Python loop.

Approach

  1. Resolve corrections with set arithmetic rather than iteration: any fill_id appearing in corrects_fill_id has been superseded, so drop those rows, then drop rows with fill_qty = 0 to remove busts, including a bust that supersedes a real fill. Chains need no special case, because every intermediate row is itself somebody's target.
  2. Sort by (order_id, exec_ts, fill_id). The file arrives in received_ts order and venue timestamps are not monotone in arrival, so sorting is load-bearing rather than tidy; tie-break on fill_id because exec_ts collides at microsecond resolution on active names.
  3. gap = df.groupby('order_id')['exec_ts'].diff(); new_burst = gap.isna() | (gap > Timedelta('90s')); burst_seq = new_burst.groupby(df.order_id).cumsum(). Two passes over an already-sorted frame, no Python-level iteration.
  4. Aggregate in one groupby([order_id, burst_seq]): min and max of exec_ts, size, sum of fill_qty, sum of fill_qty*fill_px, and sum of fill_qty where liquidity_flag == 'added'. Derive the weighted price after aggregation as notional over quantity, never as a mean of fill_px.
  5. Then the concentration flag: join each burst's quantity against the order's surviving total and the order's working span (last exec_ts minus first), and mark bursts holding over 40% of quantity in under 5% of the span. Those are the auction prints and the blocks, and they are the orders whose shortfall is driven by one decision rather than by the algo.

Worked solution (40 min)

  1. superseded = set(f.corrects_fill_id.dropna().astype('int64')); f = f[~f.fill_id.isin(superseded)]; f = f[f.fill_qty > 0].
  2. f = f.sort_values(['order_id','exec_ts','fill_id'], kind='mergesort').
  3. Build gap, new_burst and burst_seq as above, then assert burst_seq is 1 on each order's first surviving fill (gap.isna() makes new_burst True there, so the grouped cumsum is 1-based) and increases by exactly 1 at every boundary, with no gaps in the sequence.
  4. g = f.groupby(['order_id','burst_seq'], sort=False); build the aggregate frame, then wavg_px = notional_sum/qty_sum.
  5. Reconcile against parent_order.filled_qty and hand-check the three orders with the most bursts.

Expected result: One row per (order_id, burst_seq). Within an order the burst quantities sum to the surviving filled quantity, bursts are contiguous and non-overlapping in time, and consecutive bursts are separated by strictly more than 90 seconds. Most orders yield one to three bursts, with a heavy tail of several dozen belonging to all-day participation algos.

Verify it yourself

  • groupby('order_id').qty.sum() must equal parent_order.filled_qty for every order with status = 'filled'; a shortfall is an unresolved correction and an excess is a missed supersession.
  • start_ts of burst k+1 minus end_ts of burst k must always exceed 90 seconds by construction; any pair that does not means the sort is wrong.
  • Each burst's quantity-weighted price must lie between the minimum and maximum fill_px inside it.
  • Surviving fill count plus dropped count must equal the input row count.

Follow-up

  • Two bursts on one order are separated by 91 seconds. What does a 90-second threshold do to the distribution of bursts per order, and how would you pick the threshold from the data instead of by hand?
  • How would you sessionise across orders instead, over all fills in one instrument in one account, and what breaks when two strategies trade the same name in opposite directions?
  • One venue reports exec_ts in local time rather than UTC. What would that look like in the burst output, and which check catches it?

What are the primary architectural differences between SQL and NoSQL databases, and how do you decide which to use for a specific analytics application?

Medium · sql and data manipulation

Approach

  1. State the window function and its partition and ordering out loud before writing it.
  2. Say which table is the grain you start from, and join outward from it.
  3. Check whether any join is one-to-many before aggregating, or the sums inflate.

Follow-up

  • How would you verify this result without re-running the same query?
  • How does the query change if the join becomes one-to-many?

How do you ensure data quality and schema consistency when ingesting data from multiple external intelligence sources?

Medium · sql and data manipulation

Approach

  1. Check whether any join is one-to-many before aggregating, or the sums inflate.
  2. Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.
  3. State the window function and its partition and ordering out loud before writing it.

Follow-up

  • How does the query change if the join becomes one-to-many?
  • How would you verify this result without re-running the same query?

Sessionise child fills into execution bursts across venues

Medium · window-functions · sessionisation · gaps-and-islands · timezone

execution_fill holds fill_id, order_id, exec_ts TIMESTAMPTZ(6) in UTC, venue_mic, fill_qty, fill_px and liquidity_flag. Within each order_id, a burst is a maximal run of consecutive fills separated by under 90 seconds. Return one row per burst: order_id, burst sequence, first and last exec_ts, fill count, total quantity, quantity-weighted price, and the running share of parent_order.order_qty completed at the burst's end. Attach the exchange-local session date derived from instrument_master.primary_exchange_mic, not the UTC date.

Approach

  1. Order fills inside each parent and take LAG(exec_ts) OVER (PARTITION BY order_id ORDER BY exec_ts, fill_id). The fill_id tie-break is load-bearing: an algo slicing into a venue produces genuine microsecond ties, and without a deterministic second key the burst boundaries move between runs.
  2. Flag a boundary where the lag is NULL or the gap exceeds 90 seconds, then convert flags to a dense burst id with SUM(is_new_burst) OVER (PARTITION BY order_id ORDER BY exec_ts, fill_id ROWS UNBOUNDED PRECEDING). One pass, no self-join, and it degrades gracefully on a single-fill order.
  3. Aggregate to burst grain with SUM(fill_qty * fill_px) / SUM(fill_qty), not AVG(fill_px); averaging prices weights a 100-share child the same as a 50,000-share block and misstates the burst by the spread.
  4. Add the completion curve as a second window over the aggregated bursts: SUM(burst_qty) OVER (PARTITION BY order_id ORDER BY burst_seq ROWS UNBOUNDED PRECEDING) / order_qty. Doing it after aggregation keeps it at the grain you are reporting.
  5. Resolve the session date through a MIC-to-IANA-zone lookup and (exec_ts AT TIME ZONE zone)::date. The UTC date happens to coincide with the local session date for US cash equities and most Asian cash sessions, but an Australian morning sits on the previous UTC day, and a futures trade date that rolls at 17:00 US Central does not align with either.

Worked solution (35 min)

  1. Write the LAG and boundary flag, then the running SUM that produces burst_seq.
  2. Aggregate to (order_id, burst_seq) with quantity-weighted price and first/last exec_ts.
  3. Join parent_order for order_qty and add the cumulative completion share window.
  4. Join instrument_master for primary_exchange_mic, map to a zone, and compute the local session date.
  5. Verify the islands: every gap between one burst's end and the next burst's start must exceed 90 seconds.

Expected result: One row per (order_id, burst_seq), bursts disjoint and ordered in time, completion share monotonically non-decreasing within an order and ending at filled_qty / order_qty.

Verify it yourself

  • Summed burst fill counts per order equal that order's fill row count.
  • Summed burst quantity per order equals parent_order.filled_qty.
  • Every inter-burst gap is strictly greater than 90 seconds; every intra-burst gap is under it.
  • For at least one non-US venue in the sample, the exchange-local session date differs from exec_ts::date.

Follow-up

  • Two venues report the same execution microsecond with different received_ts. Which timestamp do you sort by for sessionisation, and which for latency analysis?
  • Derive the 90-second threshold from the data instead of asserting it. What distribution would you look at, and what would tell you the threshold is wrong?
  • How does the burst structure change under a POV algo versus a close-auction order, and what would you expect the completion curve to look like in each?

How do you prioritize your work when faced with competing deadlines from multiple high-priority projects?

Medium · product sense and metrics

Approach

  1. Fix the population and the time window before naming any metric.
  2. Name one primary metric, then the guardrail that stops it being gamed.
  3. State what result would change your recommendation, so the answer is falsifiable.

Follow-up

  • How would you detect that the metric is being gamed rather than genuinely improving?
  • Which segment would you cut first, and what would that rule out?

How would you optimize a PySpark job that is experiencing severe performance bottlenecks due to data skew?

Medium · product sense and metrics

Approach

  1. Restate the decision this analysis has to support, and who acts on the answer.
  2. Decompose the metric into the rates that drive it, and say which one you would check first.
  3. Name one primary metric, then the guardrail that stops it being gamed.

Follow-up

  • How would you detect that the metric is being gamed rather than genuinely improving?
  • What would you do if the primary metric and the guardrail moved in opposite directions?

Describe your approach to designing an ETL pipeline that ingests heterogeneous, unstructured data streams in near real-time.

Medium

Approach

  1. State your assumptions explicitly before working the problem.
  2. Clarify what is being asked and what a complete answer would contain.
  3. Say what you would check first and why it is the highest-information step.

Follow-up

  • How would you know your answer was wrong?
  • What assumption would you test first?

Design a firm-level client-outcome metric that survives audit

Hard · survivorship · aum-weighting · guardrails · client-outcomes

The firm wants one number reported monthly to the board for whether clients are getting what they were sold. The proposal is: AUM held in accounts whose trailing 36-month net-of-fee return beats their own benchmark_id, divided by AUM in accounts with at least 36 months of history. Using account_mandate (benchmark_id, inception_date, funded_date, close_date, status, mgmt_fee_bps, perf_fee_rate, high_water_mark_base, effective_from, effective_to) and position_daily, critique it, name three ways it rises without any client being better off, and specify the guardrails and the exact denominator you would publish.

Approach

  1. Attack the denominator first, because that is where this metric is won or lost. Requiring 36 months of history excludes accounts that closed during the window, and accounts close disproportionately after bad performance. Fix it by including accounts whose close_date falls inside the window, measured to their close, and carrying them for 36 months afterwards, so the series cannot be improved by attrition.
  2. Name the three inflation paths concretely. First, survivorship through the close_date exclusion. Second, benchmark choice: benchmark_id is per account and amendable, and account_mandate is SCD2, so an amendment silently restates history unless the metric evaluates each date against the benchmark in force via effective_from and effective_to, with a count of amendments published beside the ratio. Third, AUM-weight concentration: one large mandate can carry the number, so publish the effective number of accounts as the inverse Herfindahl of AUM weights next to it.
  3. Set the guardrails from the spine: cross-sectional dispersion of trailing 12-month net return within each strategy composite, and trailing 12-month dollar redemption rate. Dispersion catches the case where the headline is met by a favoured subset, and the redemption rate catches the attrition path from the client's side of the relationship.
  4. Fix the numerator's arithmetic. Net of fee means management fee accrued plus performance fee crystallised, and under a high-water mark with a hurdle two accounts in one strategy legitimately pay different fees, so compute per account from its own terms rather than deducting a composite fee. Then decide the return convention deliberately: time-weighted answers whether the manager delivered, money-weighted answers whether the client made money, and publishing only one answers only one question.
  5. State the residual honestly. Even a clean version measures relative return, not whether the product matched the client's purpose. Pair it with a small set of hard flags from the same tables, such as breaches of max_tracking_error_bps or max_single_name_weight_pct and recon_status values other than 'matched', rather than pretending one ratio covers the question.

Worked solution (45 min)

  1. Build the account-month panel from account_mandate with SCD2 ranges resolved, tagging each account-month with the benchmark_id in force that month rather than the current one.
  2. Compute per-account trailing 36-month net-of-fee time-weighted return and the matching benchmark return over the same dates, including accounts closed inside the window up to their close_date.
  3. Compute the headline ratio under three denominators, surviving accounts only, surviving plus closed-to-date, and surviving plus closed with a 36-month tail, and tabulate the gaps.
  4. Compute the guardrails: within-composite standard deviation of trailing 12-month net return, trailing 12-month dollar redemption rate, and the effective number of accounts as one over the sum of squared AUM weights.
  5. Recompute the headline as of a month six months in the past and compare it to what was published then; any drift is a restatement and needs a named cause.

Expected result: A published specification plus evidence that the survivorship gap is material, with the surviving-only version typically reading several percentage points above the closed-inclusive version, and a board card carrying four figures on one line: the ratio, the effective number of accounts, within-composite dispersion and the trailing 12-month dollar redemption rate.

Verify it yourself

  • Surviving-only and closed-inclusive versions must differ. Identical values mean either no account closed in the window or close_date is not populated.
  • The effective number of accounts must be below the raw account count, and far below it when one mandate dominates the AUM.
  • Re-running a past month must reproduce the number published then. If it does not, something restated: a benchmark amendment, a late fee accrual, or a reconciliation break.
  • Each account's net return must reconcile to its gross return minus its own accrued fees; a composite-level fee deduction will not tie under a high-water mark.

Follow-up

  • An account funded 20 months ago has beaten its benchmark throughout and is excluded by the 36-month rule. Is that the right treatment, and what would including it cost you?
  • Two accounts in the same composite differ by 180 basis points over 12 months. List the legitimate causes before calling it an error.
  • How does the published ratio behave in a month when the firm funds one very large new mandate, and what should the board see alongside it?

Turnover doubled in a week with unchanged positions

Medium · definition drift · turnover · data semantics · reconciliation

Annualized one-way turnover for a strategy reads 368 percent this month against 180 percent last month. Position_daily shows end-of-day quantities and market_value_base following their usual pattern, and neither the signal nor portfolio construction was touched. Tables: execution_fill (fill_id, order_id, side, fill_qty, fill_px, is_correction, corrects_fill_id, exec_ts, currency) and position_daily (business_date, account_id, instrument_id, quantity, market_value_base). The catalogue definition is: monthly sum of min(total buy notional, total sell notional), over average end-of-day gross market value, times 12. Find the cause and prove it.

Approach

  1. Split the ratio and chart the numerator and the denominator as separate monthly series before interpreting either. A ratio that moved tells you nothing about which side moved, and the two sides have unrelated failure modes.
  2. Read the magnitude as evidence. A jump close to a factor of two, in a book whose buy and sell notional are nearly balanced, is the signature of min(buy, sell) having become buy plus sell. No strategy change produces a suspiciously round multiple in a week.
  3. Reconstruct the numerator yourself from the catalogue definition and compare it with the reported figure for both months. If your recomputation matches last month and not this month, the expression changed, and the scheduled job's query or its version history says when.
  4. Rule out the competing mechanism before closing. Correction rows arrive as new rows referencing corrects_fill_id, so a naive SUM(fill_qty) counts the correction and never removes the original. Size the corrected notional separately instead of assuming it is small.
  5. Reconcile to the book of record. Day-over-day change in position_daily.quantity per instrument must equal that day's net signed fills, which bounds true traded quantity independently of whichever expression the report used.

Follow-up

  • Correction rows turn out to be 0.8 percent of notional. Write the numerator expression that handles them correctly in one pass.
  • Your reconciliation against position_daily leaves a residual on two instruments. Which legitimate causes would you rule out before calling it a data bug?
  • What would you add to the pipeline so a metric definition cannot change without the change being visible in the report itself?

Suggested practice checklist

For someone who can already write the query and train the model but stalls when asked what to measure or whether a change is worth making. Metric definition and case structure come first; the technical work is kept as maintenance rather than the centre of the week.

Day 1 — Metric anatomy

  • For three products you use daily, write one primary metric, two input metrics that plausibly move it, and one guardrail that would catch a cheap way of moving the primary at the cost of the product.
  • For one of them, specify the metric precisely enough that two analysts would return the same number: numerator, denominator, unit of observation, time window, and how returning and deleted accounts are treated.
  • Pick a ratio metric and write what happens to it when the denominator shrinks for reasons unrelated to the numerator, with a concrete example of that happening.

Deliverable: A one-page metric tree for one product, with the primary metric written as an unambiguous spec.

Day 2 — Diagnosing a drop without guessing

  • Take the prompt "weekly active users fell 8 percent week over week" and write the segmentation plan before proposing any cause: platform, region, tenure cohort, acquisition channel, and whether the movement sits in the numerator or in a changed denominator.
  • List the instrumentation failures that manufacture fake drops (a client release that stopped firing an event, a bot filter change, a shifted date boundary or timezone) and write the query that rules out each one.
  • Rehearse stating the boring explanations first, seasonality and day-of-week composition, before reaching for a product cause.

Deliverable: A drop-diagnosis checklist short enough to recite from memory in under a minute.

Day 3 — Should we build it

  • Take a feature idea and write it as a bet: what you believe is true, what would have to be true for it to pay off, the metric that would confirm it, and the effect size that would justify the engineering cost.
  • Size the opportunity top-down and bottom-up, then reconcile the two numbers in writing instead of quoting whichever is friendlier.
  • Write the counter-metric that would make you kill the feature even if it wins on the primary metric.

Deliverable: A one-page product memo ending in a decision rather than a list of considerations.

Day 4 — The places aggregate numbers lie

  • Construct a Simpson's paradox numerically: two segments where the treatment wins within each segment yet loses overall, and identify the shift in segment weights that causes it.
  • Take a heavy right-tailed quantity such as revenue per user and write why the mean is the wrong summary, which percentile you would report instead, and what a moving mean with a stable median tells you.
  • Write your definition of a session for the product from day one, then name two real behaviours it misclassifies.

Deliverable: One page holding a worked Simpson's paradox table and a session definition with its two known failure cases.

Day 5 — Technical maintenance, aimed at metrics

  • Solve four timed SQL prompts that all end in a ratio metric, so the question of grain stays live in every answer.
  • Compute a 95 percent confidence interval for a proportion on a small sample, and state why the normal approximation is unreliable when either np or n(1 minus p) falls below roughly 10, along with which interval you would use instead.
  • Take one metric from your day-one tree, write the query that computes it correctly, then write the query that computes it wrong in the most plausible way and explain how you would notice.

Deliverable: Four solved prompts plus a matched correct and plausible-wrong query for one metric.

Day 6 — Turning engineering work into data science stories

  • Write three project stories as situation, decision, trade-off, outcome, each carrying one number and one thing you got wrong.
  • For the story you will lead with, prepare an answer to "what would you do differently" that names a decision you made, not a constraint you were handed.
  • Practise the sentence that reframes a systems project as a question project: the question the work answered, ahead of the pipeline it shipped.

Deliverable: Three written stories with the lead story delivered aloud and timed under four minutes.

Day 7 — Mock case and gap list

  • Run a 40-minute mock case with someone playing a product manager who pushes back on your metric choice, and record it.
  • Listen back and mark every moment you proposed a solution before the success metric existed.
  • Rewrite those moments as the question you should have asked, and rehearse the first 90 seconds of the case until scoping comes before solving.

Deliverable: A recorded case plus a rewritten opening 90 seconds.

Your part, not the team's

Most data work is done by groups, so an interviewer has to work out which piece was yours. An answer that runs on 'we' for several minutes gets interrupted with a question about what you personally did, and by then the answer sounds defensive even when it is true. Mark your own contribution as you go, and name the parts that belonged to someone else instead of leaving them ambiguous. Keep a few specifics back as well, like the name of the metric or who actually objected, so a probe can be answered with something you had not already said.

How do you approach working with incomplete or highly noisy datasets where domain expertise is limited?

Medium · behavioural and stakeholder questions

Approach

  1. Pick a story where you drove the decision, not one where you observed it.
  2. State the situation in two sentences and spend the rest on your reasoning.
  3. Quantify the outcome, including what you would not claim credit for.

Follow-up

  • How did you know the outcome was caused by your change?
  • What would you do differently if you ran that project again?

Tell me about a time a model you built failed to perform as expected in production. How did you identify the issue, and what steps did you take to resolve it?

Medium · behavioural and stakeholder questions

Approach

  1. State the situation in two sentences and spend the rest on your reasoning.
  2. Close with what you would do differently, concretely.
  3. Name the disagreement or constraint, and how you resolved it with evidence.

Follow-up

  • What would you do differently if you ran that project again?
  • How did you know the outcome was caused by your change?

Disagreeing with a product manager over an account leaderboard

Medium · stakeholder disagreement · attribution · dispersion

A product manager wants to ship a client portal widget ranking every separately managed account against its peers in the same strategy composite, by trailing 12-month net return. You believe the ranking will mostly order accounts by mandate mechanics rather than by anything a client can act on. You have position_daily, account_mandate (SCD2) and benchmark returns for 140 accounts in the composite. Build the case and bring a counter-proposal you would ship. The product manager has a launch date and a client asking for exactly this.

Approach

  1. What is probed: whether you can lose the feature and keep the working relationship, meaning your disagreement arrives with a shippable alternative rather than as a veto.
  2. Measure the dispersion before arguing about it. Compute the cross-sectional standard deviation of trailing 12-month net return across the 140 accounts. If it is small, the product manager is right and you are not, and you want to know that before the meeting rather than during it.
  3. Decompose the dispersion into causes you can name from the tables: time spent ramping between funded_date and the date gross exposure reached 90 percent of target_gross_exposure_pct, average cash weight over the period, restricted names via position_daily.is_restricted, single-name cap differences across account_mandate SCD2 versions, and fee schedule including whether a performance fee crystallised above the high-water mark. Report the share each explains and the residual.
  4. Convert the finding into the client's decision, because that is what moves a product manager. If most dispersion is mandate mechanics, the leaderboard tells a client to change managers when the honest action is to relax a constraint or fund fully. A wrong action is an argument; a noisy statistic is a preference.
  5. Bring the alternative that keeps the launch date: the same widget, showing the account's return against its own benchmark and its own constraint set, with a named driver line such as your restricted list cost 34 bps, instead of a rank. It answers what the client actually asked and it survives a phone call.
  6. Pre-commit to being wrong. If the residual dominates the decomposition, the leaderboard is measuring something real, and saying so in the same memo is what makes the rest of it credible next time.

Follow-up

  • Dispersion is 180 bps and mandate mechanics explain 40 percent of it. What do you ship?
  • The client asked for a rank by name. Do they get one, and what do you put next to it?
  • How do you keep this from becoming a standing veto on anything this product manager proposes?