cswork / libraryOpen practice workspace

Interview guides · Target

Target Data Scientist Interview Questions & Guide 2026

Data scientist prep for Target candidates: drill A/B design, power and minimum detectable effect on original exercises.

The 60-second brief

A Data Scientist at Target plays a pivotal role in shaping the modern guest experience and driving operational efficiency across one of the world's largest retail ecosystems. Operating at an immense scale, data science at Target is not just about building predictive models; it is about translating massive streams of guest, inventory, and supply chain data into actionable strategic decisions. Whether optimizing supply chain logistics, refining search algorithms, personalizing marketing campaigns, or driving dynamic pricing models, your work directly impacts millions of guests daily.

When randomisation is off the table, the skill being checked is naming an identification strategy together with the assumption it rests on: parallel trends for difference-in-differences, relevance and exclusion for an instrument, overlap and conditional ignorability for matching. Say the assumption out loud and say how you would try to break it.

Focus areas

  • Separate true demand from stockout-censored sales
  • Join order, return and inventory grains without fan-out
  • Cohort revenue by first delivery, not signup

Overview

The scale and complexity of the problem spaces at Target make this role both highly challenging and deeply rewarding. You will work on sophisticated systems that balance digital and physical retail touchpoints. This requires building robust, production-ready machine learning pipelines that can handle massive throughput while maintaining high accuracy. You will collaborate closely with product managers, data engineers, and business leaders to turn complex data into clear business outcomes.

To succeed in this role, you must possess a strong foundation in statistical modeling, machine learning algorithms, and big data technologies, combined with a keen business intuition. values data scientists who do not just build models in isolation but who can design end-to-end systems, write clean, scalable code, and communicate complex technical concepts to non-technical stakeholders.

The Target interview process

Target candidates report 6 rounds · ≈ 4-6 weeks. The stages below are what candidates describe, not a published process.

Recruiter Screening

Data Scientist covers at least four different jobs: experimentation, product analytics, causal work on observational data, and applied modelling that ships into a system. A screening call is the cheapest place to find out which of them is being hired for, and doing that diagnosis openly reads as senior rather than fussy. Ask what the last few pieces of work on the team actually were, and roughly how a week splits between querying, modelling and stakeholder time. Then say which parts of that you have done and which you have not. Claiming the whole range is the fastest way to be caught one round later.

What to demonstrate

  • Whether you can distinguish the flavours of the role and locate your own experience inside one of them honestly
  • Whether you name what you have not done instead of stretching to cover every line of the posting
  • Whether your hard constraints (notice period, location, work authorisation, level) surface now rather than at offer stage

How to prepare

  • Map the last two years of your time into rough percentages across query writing, experiment design, modelling and stakeholder work, so a question about scope has a real answer
  • Mark every responsibility in the posting as done, adjacent or new, and prepare one sentence for each adjacent item naming the closest thing you have actually built
  • Decide which logistics are non-negotiable before the call so you can state them in one sentence rather than negotiating live

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

Machine Learning Discussion

Because the format is not fixed, prepare the reasoning rather than the ritual. Nearly every version of this round draws on the same underlying material: a design you can defend, a metric you can define exactly, an analysis whose assumptions you can state out loud. Only the wrapper changes, whether that is a take-home, a live case, a deep dive on past work, or a rough estimate on a whiteboard. Answers rehearsed to fit one shape stall the moment the shape differs. Practise naming the assumption behind a number, then saying how much the conclusion moves if that assumption is wrong.

What to demonstrate

  • Whether your justification for a method survives the question 'why not the simpler thing', including when the simpler thing would have worked
  • Precision under pressure: what exactly counts as an active user, a conversion or a success, over what window, with what exclusions
  • Whether you carry an argument through to a recommendation instead of stopping at a list of tradeoffs

How to prepare

  • For each project you plan to mention, write the metric definition in one sentence: numerator, denominator, time window, exclusions. Say it out loud once, because vagueness shows up in speech before it shows up on paper.
  • Rehearse the same project at three lengths: two minutes, ten minutes, and a deep dive on one technical decision. Cutting live is harder than it sounds.
  • For your headline result, write down what would have had to be true for it to be wrong, and how you ruled that out.

Project Discussion

An added round often puts you in front of someone outside the core hiring team: a partner engineer, a product owner, a domain expert, sometimes a more senior manager. The question they are really asking is not whether you can do the work but whether they would trust a number that came from you. That changes what a good answer looks like. Lead with what the decision cost and what it changed, keep the method available but not central, and be plain about the limits of your evidence. Overstating a result is the fastest way to lose this round.

What to demonstrate

  • Whether you can explain a technical choice to someone who will never read your code, without either flattening it into nothing or hiding inside jargon
  • Honesty about evidence strength: what the analysis establishes, what it only suggests, and what it cannot say at all
  • How you take disagreement, specifically whether you update on a good objection, hold your position with reasons, or fold on contact

How to prepare

  • Write the two-sentence version of your most technical project for a non-specialist, then check that neither sentence needs a method name to make sense.
  • For one result you are proud of, write the strongest objection someone could raise and a response that concedes the part of it that is correct.
  • Prepare one decision that turned out to be wrong: how you found out, what it cost, and what you changed afterwards. A senior cross-functional interviewer asks for this more often than a technical one does.

Business Case Studies

A case has a fixed clock, and a good deal of what is being scored is how you spend it. Thirty to forty-five minutes buys one pass across the whole problem or a deep read of one part of it, and choosing between those is the work rather than a compromise forced on you. Announce the shape early: the structure you are using, the branch you think carries the decision, and what you are setting aside. An answer that is thorough for the first third and silent on the recommendation reads worse than one that is rougher throughout and lands.

What to demonstrate

  • Whether a visible structure appears in the opening minutes and survives the rest of the case
  • Whether the depth goes to the branch that carries the decision, rather than the branch you find most comfortable
  • Whether you say what you are leaving out and why, instead of quietly omitting it and hoping nobody asks

How to prepare

  • After each practice case, write down the branches you chose not to open and the reason for each, then check whether you said any of them out loud while the case was running. A branch you only cut privately reads to the interviewer as one you missed.
  • Redo a case you have already worked in half the time, deciding in advance which single branch you keep, then compare which version a listener would find more useful.
  • Write a two-sentence opening you can reuse, holding the restated question and your plan for the available time, and deliver it within the first ninety seconds of every practice run.

Bar-Raiser Evaluation

Before anything else, this round is a reading test. You are given a small schema and a question phrased in business language, and most of the difficulty sits in the gap between them. Who counts as an active user, does a refunded order still count as an order, is that date column an event time or a load time. Weak answers start typing immediately and compute something precise about the wrong population. Strong ones pin the definition in one sentence, name the column that encodes it, then write the query. On a timed assessment with nobody to tell, write the definition in a comment anyway.

What to demonstrate

  • Whether an ambiguous term becomes a specific column and filter before any computation happens
  • Whether you read the schema for keys and cardinality rather than only for column names
  • Whether the result answers the question at the grain it was asked at, per user or per session or per day

How to prepare

  • Take three metrics you already use and write down the exact filter and exact grain behind each, then practise stating one of them in a single sentence out loud
  • On a schema you have never seen, spend the first minute writing what one row of each table means and which key it is unique on, then predict which joins can duplicate rows
  • Rehearse a version where the definition changes halfway through, and edit the query you have instead of starting over

Where candidates lose points

Reading revenue or cohort value before the return window closes

Returns arrive days to weeks after delivery and vary enormously by category, so a two-week-old cohort reported on gross revenue is being compared with a mature cohort reported on something close to net. In apparel the gap between the two definitions is routinely twenty to forty percent of gross, more than the effect size of almost any experiment or campaign being evaluated. Attribute refunds back to the parent order's placed date rather than the refund date, hold the reporting lag at the 95th-percentile return-initiation lag for the category, and never let a fresh cohort's gross number sit in the same table as a mature cohort's net number.

Judging merchandising and recommendation changes on the surface they touch

Click-through or attributed revenue on a recommendation slot rises whenever the slot shows items the customer was going to buy anyway, so the surface metric measures capture rather than creation, and the units almost always come from a different slot, a search result or a later visit. The correct read is site-wide net revenue per session over a holdout, adjusted for returns, because surfacing more apparel or more discounted stock reliably moves both the return rate and the discount depth in the wrong direction while the click metric improves.

Reporting a p-value with no effect size or interval

Give the estimated difference with a confidence interval in the units the business cares about, then say whether that whole interval is worth acting on. A p-value only addresses whether you can rule out exactly zero; it says nothing about magnitude.

Writing SQL without stating NULL and tie-breaking behaviour

Before calling a query finished, say what it does with NULLs, ties and empty groups. NOT IN against a subquery containing a single NULL returns no rows at all, and RANK, DENSE_RANK and ROW_NUMBER differ precisely on ties, so name which one the question requires.

Questions & practice

Explain the bias-variance tradeoff and how regularization techniques like L1 and L2 affect model performance.

Medium · machine learning and modelling

Approach

  1. Frame the prediction: the label, the moment of prediction, and the action it triggers.
  2. Say how the offline result would be validated online before it is trusted.
  3. Check what information would not exist at prediction time, and exclude it.

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?

Permutation test for a geo holdout with pre-period adjustment

Hard · randomisation inference · difference-in-differences · geo experiment · blocking

geo_weekly has geo_id, week_start, sessions and net_revenue_cents for 40 geos over 8 pre-period weeks and 4 promotion weeks. Twenty geos were randomly assigned to run the promotion, twenty were held out, with assignment blocked into 20 pairs matched on pre-period sessions. The per-geo metric is net revenue per 1000 sessions. Estimate the difference-in-differences effect and test it with a permutation test you write from scratch, respecting the blocking. Report the estimate, a two-sided p-value, and a placebo test on the pre-period.

Approach

  1. Collapse to one pre value and one post value per geo first: revenue per 1000 sessions computed as sum(revenue) / sum(sessions) * 1000 within each period, never as the mean of weekly rates, which would weight a quiet week like a peak one.
  2. Compute delta_i = post_i - pre_i per geo, then the statistic as mean(delta) over treated minus mean(delta) over held-out. Pre-differencing absorbs the fixed level differences between geos, which is where most of the variance lives.
  3. Permute the way the assignment was actually made. Blocking into matched pairs means you flip the label within each of the 20 pairs independently, giving 2^20 possible relabelings, not C(40,20). Permuting freely across pairs tests a null the design does not correspond to and gives a p-value that is not calibrated.
  4. Draw 10000 relabelings with a seeded generator, recompute the identical statistic each time, and take p = (1 + count(|stat_perm| >= |stat_obs|)) / (1 + B). The plus-one is not cosmetic: it keeps the test valid at small B and makes p = 0 impossible.
  5. Run the placebo by splitting the 8 pre-period weeks into weeks 1 to 4 and 5 to 8, computing the same statistic and p-value with the real labels. A small placebo p-value means the matched pairs were not parallel before treatment and the headline estimate is not trustworthy.

Follow-up

  • The promotion ran during a week with a national holiday in some geos. How does that show up here, and what would you add to the design or the analysis?
  • With 40 geos, what effect size does this design actually detect? Sketch how you would answer that before running the test.
  • Revenue is still provisional because returns are open. What does that do to a four-week post window, and how long would you hold the readout?

Cluster bootstrap a confidence interval for revenue per session

Medium · bootstrap · ratio metrics · clustering · experimentation

sessions has session_id, customer_id, arm in {control, treatment}, net_revenue_cents (zero for non-converting sessions) and converted (bool). A customer contributes several sessions and is assigned to one arm. Estimate the difference between arms in revenue per session, defined as the ratio of summed revenue to session count, and return a 95 percent interval from a bootstrap you write yourself: no scipy.stats.bootstrap, no library CI helper. Resample at the level that respects the dependence in the data, use 2000 replicates, and report the point estimate alongside the interval.

Approach

  1. State the estimand as a ratio of totals, not a mean of per-customer ratios. Revenue per session is sum(revenue) / count(sessions); averaging each customer's own ratio weights a one-session customer the same as a forty-session customer and estimates a different quantity.
  2. Collapse to one row per customer with (revenue_sum_i, session_count_i) before resampling. The bootstrap then draws customers with replacement, which is what preserves the within-customer correlation that makes session-level resampling too optimistic.
  3. Resample within each arm separately, with the same number of customers as that arm has, so the arm sizes are fixed by design and only the sampling variability is resampled.
  4. For each replicate compute sum(revenue)/sum(sessions) per arm and take the difference; note that the number of sessions per replicate varies, which is correct for a ratio estimator and not a bug to patch.
  5. Report the percentile interval from the 2.5th and 97.5th quantiles of the replicate differences, and cross-check the width against the Taylor linearisation for a ratio, var(R) = n / ((n-1) * (sum N_i)^2) * sum (Y_i - R * N_i)^2.

Worked solution (35 min)

  1. Aggregate to customer level: revenue_sum, session_count, arm. Assert each customer_id appears in exactly one arm.
  2. Write the ratio estimator as a two-line function over a customer-level frame so the replicate loop and the point estimate call the identical code.
  3. Draw 2000 replicates per arm with rng.integers on positional indices, which is faster than df.sample and easier to seed reproducibly.
  4. Take the 2.5 and 97.5 percentiles of the 2000 differences; report the point estimate from the full data, not the bootstrap mean.
  5. Recompute the same interval resampling sessions instead of customers and print the ratio of the two widths as evidence for the design choice.

Expected result: A point estimate equal to the difference of ratio-of-totals between arms, and a percentile interval whose centre sits within Monte Carlo noise of that point estimate, with the session-level bootstrap interval visibly narrower than the customer-level one on any data where customers contribute multiple sessions.

Verify it yourself

  • Mean of the bootstrap replicates is close to the point estimate (bias well under the standard error)
  • The half-width agrees with the Taylor linearisation standard error to within a few percent
  • Re-running with the same seed reproduces the interval exactly
  • Forcing one session per customer makes the clustered and unclustered intervals coincide

Follow-up

  • Revenue per session is right-skewed and a single large basket moves the estimate. What capping rule would you pre-declare, and what estimand does capping change the answer to?
  • How would pre-period revenue per customer be used to tighten this interval, and what is the condition for that adjustment to stay unbiased?
  • Returns are still open on a third of these orders. What do you report today, and what do you refuse to report?

Write a PySpark script to handle missing values and perform feature scaling on a dataset with billions of rows.

Medium · sql and data manipulation

Approach

  1. Say which table is the grain you start from, and join outward from it.
  2. State the window function and its partition and ordering out loud before writing it.
  3. Handle the rows that do not match: a LEFT JOIN with a NULL check is usually the question.

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?

Write a function to detect and handle edge cases when parsing raw, malformed log data.

Medium · sql and data manipulation

Approach

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

Follow-up

  • How does the query change if the join becomes one-to-many?
  • What breaks if events arrive late or out of order?

Write a Python function to find the first non-repeating character in a string and return its index.

Medium · sql and data manipulation

Approach

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

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?

Daily visit-to-order conversion by device, excluding denied-consent sessions

Easy · funnel · aggregation · null handling

From fct_session (session_id, started_at_utc, device_type, entry_channel, order_id, tracking_consent, is_bot_flagged), return visit-to-order conversion for the last 28 days split by device_type and entry_channel. A session converts when order_id IS NOT NULL. Exclude is_bot_flagged = TRUE, and exclude tracking_consent = 'denied' from both numerator and denominator, reporting the denied share of non-bot sessions as its own column. Then produce the weekly figure from the same base. Output: day or week, device_type, entry_channel, sessions, orders, conversion_rate, denied_share.

Approach

  1. Filter once in a base CTE (is_bot_flagged = FALSE, started_at_utc in the window) and carry tracking_consent through, so the numerator and denominator can never drift apart in two separate scans.
  2. Aggregate with conditional counts in a single pass: COUNT(*) FILTER (WHERE tracking_consent <> 'denied') as sessions, and the same filter plus order_id IS NOT NULL as orders.
  3. Divide at the end with a NULLIF on the denominator and a numeric cast, because integer division floors a 2 percent rate to 0.
  4. For the weekly number, group the same base on date_trunc('week', started_at_utc) and re-divide the summed counts; an average of seven daily rates weights a quiet Tuesday the same as a peak Saturday.
  5. Emit denied_share = denied sessions / all non-bot sessions beside the rate, so a consent-banner change that moves the denominator is visible rather than being read as a behaviour change.

Worked solution (20 min)

  1. Write the base CTE with the bot filter and the date window, selecting session_id, started_at_utc, device_type, entry_channel, order_id, tracking_consent.
  2. Add the grouped aggregate with FILTER clauses for sessions, orders and denied sessions.
  3. Compute conversion_rate = orders::numeric / NULLIF(sessions, 0) and denied_share = denied::numeric / NULLIF(sessions + denied, 0).
  4. Duplicate the aggregate with date_trunc('week', started_at_utc) as the grain and confirm the weekly rate is derived from summed counts.
  5. Compute both weekly figures side by side and the covariance between daily sessions and daily rate, so the size of the gap is a measured number rather than an assumption.
  6. Spot-check one device and channel by hand for a single day against a raw COUNT.

Expected result: One row per (day, device_type, entry_channel) with sessions, orders, a conversion_rate between 0 and 1, and denied_share. The weekly rate equals SUM(orders)/SUM(sessions), and it exceeds the unweighted mean of the seven daily rates by exactly Cov(daily sessions, daily rate) / mean(daily sessions), the covariance taken over the seven days. So the two agree whenever the daily rates are flat, or whenever volume and rate are uncorrelated, however wide the traffic spread; they diverge only when volume and rate move together, which is the usual shape of a promotional weekend. Measure the gap on the actual week rather than asserting it is negligible or large.

Verify it yourself

  • SUM(sessions) across all rows for a day equals the count of non-bot, non-denied sessions started that day.
  • orders never exceeds sessions in any row.
  • The difference between the two weekly figures matches Cov(sessions, rate) / mean(sessions) to rounding, which confirms the gap is the volume-rate relationship and not an aggregation bug.
  • Replacing the seven daily rates with their mean (holding sessions fixed) collapses that difference to zero, which is the control that proves traffic dispersion alone does not create it.
  • denied_share summed with the retained share equals 1 for every day.

Follow-up

  • Denied-consent sessions can still place orders. What does excluding them do when you reconcile session-attributed orders against total orders in fct_order_line, and how do you present that gap?
  • Conversion rose on desktop and rose on mobile_web, but the blended rate fell. What happened, and what would you show to make it obvious?
  • How would you handle a session that starts at 23:55 and places its order at 00:04 the next day?

Design a dynamic pricing strategy for clearance items that maximizes revenue while ensuring inventory is cleared by a specific date.

Medium · product sense and metrics

Approach

  1. Decompose the metric into the rates that drive it, and say which one you would check first.
  2. Restate the decision this analysis has to support, and who acts on the answer.
  3. Fix the population and the time window before naming any metric.

Follow-up

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

How would you design an end-to-end recommendation engine for Target’s online grocery section to increase average order value?

Medium · product sense and metrics

Approach

  1. Restate the decision this analysis has to support, and who acts on the answer.
  2. State what result would change your recommendation, so the answer is falsifiable.
  3. Decompose the metric into the rates that drive it, and say which one you would check first.

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?

How would you design and analyze an A/B test for a new checkout interface, and what primary and secondary metrics would you track?

Medium · a/b testing and experimentation

Approach

  1. Name the guardrails that would stop a launch even on a positive primary result.
  2. Decide the analysis before seeing data, including how long it runs and when you look.
  3. Name the randomisation unit first; it decides the variance and what the test can detect.

Follow-up

  • What would you do if you could not randomise at all?
  • How would you handle interference between treated and control units?

Describe how you would evaluate the performance of a recommendation system when online A/B testing is not immediately feasible.

Medium · a/b testing and experimentation

Approach

  1. Name the guardrails that would stop a launch even on a positive primary result.
  2. Say whether units interfere with each other, and switch design if they do.
  3. Decide the analysis before seeing data, including how long it runs and when you look.

Follow-up

  • What would you conclude if the result is positive but the test is underpowered?
  • What would you do if you could not randomise at all?

Put a defensible number on sales lost to stockouts

Hard · censoring · proxy metrics · bias

Buying wants a figure for sales lost to stockouts last season to size next season's buy. What is observable is fct_inventory_snapshot (snapshot_date, sku_id, node_id, minutes_unavailable, was_listed, forecast_units, gross_units_sold, units_cancelled_oos, available_to_promise_units) and fct_order_line. Deliverable: state plainly why the quantity is not measurable from this data, define the proxy you would publish, name the direction and rough size of its bias, and say what the number may and may not be used for.

Approach

  1. Say the unmeasurable part first. Recorded sales are the minimum of demand and availability, so a day with zero available units produces a zero that is indistinguishable from a day of zero demand. Lost sales are a counterfactual, not a column, and no amount of aggregation over an end-of-day snapshot recovers it exactly.
  2. Estimate the demand rate on clean periods only. Take SKU-node-days with was_listed = TRUE and minutes_unavailable = 0, fit a daily rate with day-of-week and promotion indicators, and pool to category level for the intermittent long tail where a per-SKU mean is noise. Then apply the rate to the unavailable fraction: lost units is approximately rate times minutes_unavailable / 1440, summed over the season, plus units_cancelled_oos which is the one piece actually observed.
  3. State the biases with their signs; they do not all point the same way. Upward: substitution, because a customer who buys the next size or colour was never lost, so a SKU-level sum counts demand the style captured. Downward: the rate is fitted only on SKU-node-days with minutes_unavailable = 0, and when outages are demand-driven, those are by construction the days demand stayed under available_to_promise_units, so the fitted rate sits below the SKU's true mean and every unavailable hour is priced too cheaply. Also downward: customers who stopped searching because the item had been gone for weeks, and baskets abandoned entirely when the missing item was the reason for the visit. The precondition is worth stating, because it is what makes the censoring bite: if a SKU is unavailable because an inbound shipment slipped, outage timing is independent of that day's demand and this component goes away.
  4. Size the censoring instead of asserting it. Partial-outage days, where minutes_unavailable is between 1 and 1439, are the only window onto demand on days that do run out: take the units sold during the in-stock hours, reweight by the SKU's intraday sales profile rather than dividing by the in-stock fraction flat, and compare the implied daily rate with the rate fitted on fully-in-stock days. That ratio is the size of the downward pull and belongs next to the estimate, so the direction is never published without a magnitude.
  5. Publish a bracket, not a point, and say which error it brackets. The SKU-level sum and the same calculation rolled to style_id (a style counts as available when any child SKU is available at that node) bracket the substitution component only; the censoring in the fitted rate moves both ends down together, so this is not a confidence interval on lost sales. units_cancelled_oos is a hard observed floor under both. Report the share of SKU-level lost units whose sibling SKUs were in stock that day, because that share is the part most likely to be substitution.
  6. Scope the use explicitly. The number is adequate for ranking SKUs and nodes for a buy increase and for the availability guardrail on sell-through. It is not adequate to be added to actual revenue and presented as revenue the business would have had, and it is not identified at all for a SKU unavailable for more than a few weeks, where there is no recent clean period to fit on.
  7. Express the result in contribution margin rather than revenue, since the decision it feeds is a buy quantity and the relevant cost of over-buying is an end-of-season markdown unit.

Worked solution (40 min)

  1. Filter fct_inventory_snapshot to was_listed = TRUE and minutes_unavailable = 0, and fit a daily demand rate per SKU-node with day-of-week and promotion indicators, pooling to category_l2 for SKUs below a stated volume floor.
  2. For every SKU-node-day with minutes_unavailable greater than zero, compute expected units as rate times minutes_unavailable / 1440, sum across the season, and add SUM(units_cancelled_oos).
  3. Size the censoring: for SKU-node-days with minutes_unavailable between 1 and 1439, take the units sold during the in-stock hours, reweight by the SKU's intraday sales profile, and compare the implied daily rate with the rate fitted in step 1.
  4. Repeat the whole calculation at style_id grain, treating a style as available at a node on a day when any child SKU had available_to_promise_units greater than zero, to produce the substitution-corrected end of the bracket.
  5. Compute the substitution share: the fraction of SKU-level lost units on days when a sibling SKU at the same node was available.
  6. Convert both ends of the bracket to contribution margin using unit_paid_price_cents less unit_cost_cents from the SKU's own in-stock sales in the same season.

Expected result: A bracketed estimate expressed in contribution margin: SKU-level and style-level unmet demand as the two ends of the substitution bracket, observed units_cancelled_oos as a hard floor under both, published with the substitution share, the partial-outage rate ratio, and an explicit statement that the demand rate is fitted only on days the SKU did not stock out and is therefore biased downward, against the substitution bias rather than with it.

Verify it yourself

  • The style-level estimate must be less than or equal to the SKU-level estimate; if it is not, the style availability roll-up is wrong.
  • units_cancelled_oos is directly observed and must sit at or below the final estimate.
  • Any SKU with minutes_unavailable equal to zero across the whole season must contribute exactly zero lost units.
  • The implied daily rate on partial-outage days, reweighted by the intraday profile, should sit at or above the rate fitted on fully-in-stock days; the gap is the censoring, and on the SKUs where it is large the published estimate is understated by roughly that ratio.
  • Hold out a random set of in-stock SKU-days, blank their sales, run the method on them, and confirm it recovers their actual units within a stated tolerance. This validates the rate model on in-stock days only and cannot detect the censoring, which is what the partial-outage comparison is for.

Follow-up

  • How does the estimate change when was_listed = FALSE rather than the SKU being out of stock? Are those the same loss?
  • The buy team wants one number, not a range. What do you hand them and what do you write next to it?
  • How would you validate the substitution correction using data you already have, without running an experiment?

App conversion fell overnight while orders held flat

Easy · funnel decomposition · instrumentation · consent

Visit-to-order conversion on ios_app fell from 3.1% to 2.4% between Tuesday and Wednesday; desktop, mobile_web and android_app are flat. You have fct_session (session_id, customer_id, order_id, device_type, entry_channel, campaign_id, landing_path, tracking_consent, is_bot_flagged, product_views, add_to_cart_events, checkout_starts) and fct_order_line (order_id, channel, placed_at_utc, quantity). No app build shipped that day, but a consent banner change went out across all surfaces on Tuesday evening. Deliverable: one page splitting the 0.7 point drop into real demand and denominator composition, with the evidence for each.

Approach

  1. Check the numerator outside the session table first: count distinct order_id in fct_order_line where channel = 'ios' per day. If orders are flat, no demand was lost and the whole question is about the denominator or the stitching between the two tables.
  2. Profile the denominator by composition, not size: sessions per day on ios_app split by tracking_consent and by is_bot_flagged. A session with tracking_consent = 'denied' can still produce an order, but it can never be joined to one, so it is a guaranteed zero in the numerator and must be excluded from both sides.
  3. Recompute the rate with denied-consent and bot-flagged sessions removed from numerator and denominator, and report the denied share as its own series so the exclusion is visible rather than hidden.
  4. Walk the in-session funnel on the cleaned population: product_views per session, add_to_cart_events per session, checkout_starts per session, orders per checkout_start. A break confined to the last ratio points at checkout or payment; a uniform sag across all four points at traffic quality or mix.
  5. Cut the residual by entry_channel, campaign_id and landing_path to separate a single campaign or deep-link path from a surface-wide change, then state the remaining real effect with its size.

Follow-up

  • The denied-consent sessions still buy. How would you estimate the orders they placed, and would you put that estimate in the published conversion rate or beside it?
  • If orders had also fallen but only on one entry_channel, what would you check before blaming the app?
  • How do you keep this class of break from reading as a business result next time: what monitor would have fired on Tuesday evening?

Suggested practice checklist

Four days spend equal time on query work, statistics, modelling and product judgement at deliberately shallow depth, which produces a scored map of where you actually stand. The last three days spend everything on the two areas the role weights most, and close by re-running day one to measure movement.

Day 1 — Breadth pass: query fluency

  • Solve six prompts spanning aggregation, joins, window functions and date arithmetic in 60 minutes total, stopping at 10 minutes each whether or not it works, and mark every prompt as solved, solved slowly, or stuck.
  • For each unsolved prompt write the single blocking sentence (I lost the grain, I did not know the frame clause, I could not express the date boundary) instead of reading the solution.
  • Translate one pandas transformation you know well into SQL and one SQL query into pandas, checking that both return the same row count and the same totals.

Deliverable: A scored six-row table, one line per prompt, saved for the day-seven re-run.

Day 2 — Breadth pass: statistics and inference

  • Answer ten short questions in writing with nothing open: what a p-value is conditional on, what a 95 percent interval covers across repeated samples, when a paired test is the right one, what the bootstrap estimates, why multiple comparisons inflate false positives, how controlling the family-wise error rate differs from controlling the false discovery rate, what power depends on, what a missed real effect costs a product, the three situations where the central limit theorem does not rescue you (small n, very heavy tails, dependent observations), and what a standard error is the standard deviation of.
  • Grade yourself against a reference and count only the answers that were exactly right, not the ones that were nearly right.
  • Rewrite the two weakest answers the following morning from memory in full sentences.

Deliverable: Ten graded answers with an honest count of exact hits.

Day 3 — Breadth pass: modelling

  • Take one tabular dataset end to end in 90 minutes: a leakage-safe split, a baseline that is not a model (majority class or historical mean), one regularized linear model, one gradient-boosted tree, and a single evaluation metric chosen before you look at any result.
  • Write why that metric fits the cost structure: precision at a fixed recall for alerting, calibration for anything feeding a price or a threshold, ranking metrics for retrieval, and note that area under the ROC curve is insensitive to class balance in a way that can flatter a rare-positive problem.
  • Name the leak you were most likely to introduce (an encoding fit on all rows before splitting, or a feature computed after the label's timestamp) and write the check that would have caught it.

Deliverable: A notebook whose first cell states the metric and the baseline, plus two lines on what beat what and by how much.

Day 4 — Breadth pass: product judgement

  • Answer three case prompts aloud at 15 minutes each, timing how long passes before you state a success metric.
  • For one case write the first segmentation you would run and the row counts you expect per segment, so that a tiny segment cannot quietly drive the conclusion.
  • Take a metric definition you did not write, from a public dashboard, a textbook, or documentation you already have open, and list every place two analysts implementing it would diverge: which rows the denominator admits, whether the unit is an account or a person, what the time window is anchored to, and what happens to data that arrives late. Then write the one question that would close the largest of those gaps.

Deliverable: Three recorded case answers plus an ambiguity list for a metric someone else defined, ending in the single question you would ask about it.

Day 5 — Depth, first area

  • Rank the four areas by how many bullet points in the role description each one covers, pick the top one, and spend the entire day inside it.
  • Work the six hardest problems you can find in that area and for each write the generalizable move you should have reached for first, rather than the answer.
  • Re-solve the two you failed the same evening with notes closed.

Deliverable: Six generalizable moves written as instructions to yourself, not as solutions.

Day 6 — Depth, second area, and the seam between them

  • Repeat the depth protocol on the second-ranked area with the same six-problem structure.
  • Construct one problem that requires both areas at once, for example a metric redefinition whose effect you must validate with a test whose readout you then have to query.
  • Solve your own combined problem end to end and note where the handoff between the two areas cost you time.

Deliverable: One combined problem, solved end to end, with the handoff failure written down.

Day 7 — Integration and re-measurement

  • Re-run the six prompts from day one under the same clock and compare both correctness and time.
  • Run a 60-minute mixed mock that moves between areas without warning, since switching cost is what breadth passes do not train.
  • Write the two areas you would still fail on, and the sentence you will use in the interview when you hit one of them.

Deliverable: A before-and-after score table plus a written plan for the two remaining gaps.

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.

State the impact of your model without overclaiming

Hard · attribution · confounding · self-assessment

Writing your own review, you want to claim the reorder model you shipped in February. Since launch, demand-weighted in-stock rate on covered categories rose from ninety-one to ninety-five percent and net merchandise revenue on them is up eight percent year over year. In the same window a supplier consolidation shortened lead times, two categories were re-merchandised, and the fiscal calendar shifted the promotional week. Nobody will check your number. Write the impact claim you would actually put in the document, and explain what you did to earn the right to it.

Approach

  1. Decide what is attributable before deciding what is impressive. Three known changes move the same metrics in the same direction, so the year-over-year revenue figure is an upper bound rather than an estimate of your effect.
  2. Find the cleanest contrast still available after the fact: categories the model covers against comparable uncovered ones over the same weeks. That is a difference-in-differences read, its identifying assumption is parallel pre-trends, so show the pre-period rather than assert it.
  3. Claim the mechanism you can trace end to end. The model changed reorder points, reorder points changed minutes_unavailable, availability changed units sold. Each link is measurable, and a confounder has to break a specific one of them.
  4. Lead with the in-stock movement and put revenue second, because availability is closest to the thing you changed and hardest for the supplier consolidation to explain away.
  5. Write the confounders into the claim instead of omitting them. A claim that survives a reader naming the consolidation is worth more than a larger one you retract when they do.
  6. State the counterfactual you cannot rule out, and the design you would run next time to rule it out.

Follow-up

  • Your manager wants one number for the promotion packet. Which one, and what sentence goes with it?
  • A peer claims the same revenue for the re-merchandising work. How do you resolve that without both of you shrinking your claims to nothing?

Explain a forecast interval to an executive placing a buy

Medium · uncertainty communication · asymmetric loss · newsvendor

You forecast season demand for a style with a twelve-week lead time. The point forecast is 40,000 units and your eighty percent interval runs 26,000 to 61,000. The executive placing the purchase order asks for one number and says intervals are not actionable. Unsold units are marked down at end of season. A stockout loses the sale and censors the demand history you will forecast on next year. In four minutes, with no slides, give the executive a buy quantity and explain the uncertainty in terms they can act on.

Approach

  1. Name a quantity first, then explain it. Refusing to give a number reads as evasion, and the buy gets placed anyway without your input in it.
  2. Translate the interval into the decision. One unit too many costs the markdown loss; one unit too few costs the lost contribution plus the censoring it writes into next year's history. Those costs are not equal, so the right order quantity is not the median of the forecast.
  3. State the critical ratio rather than asserting a quantile: order the q-quantile of the demand distribution where q = Cu / (Cu + Co), Cu being contribution lost per unit short and Co being unit cost minus salvage recovery per unsold unit. This holds for a single ordering opportunity with no mid-season replenishment and a known salvage value.
  4. Express the remaining uncertainty as consequences rather than as a range: at your recommended quantity, the probability of selling out before end of season and the expected markdown units if demand lands near the top of the interval.
  5. Offer the lever that actually shrinks the interval: a smaller initial buy with an in-season reorder if any supplier lead time allows it, or a first-two-weeks sell-through read that updates the quantity before the second tranche.
  6. Agree what goes in writing: the number, the quantile it corresponds to, and the two per-unit costs it depends on, so the number survives being repeated without you.

Follow-up

  • What evidence would make you raise the number, and how quickly could you get it?
  • Supply offers a second buy at week six with a shorter lead time but ten percent higher unit cost. How does that change your recommendation?

Defend a promotion read that contradicts the revenue number

Medium · incrementality · pull-forward · stakeholder pushback

You analysed a site-wide twenty-percent weekend event. Revenue over the event week rose eleven percent year over year, contribution margin per delivered order fell, and the two weeks after the event ran below the pre-period baseline. The merchandising lead who owns the event calendar has already circulated the revenue figure. You have ten minutes at a planning meeting attended by that lead and their director. Deliver the finding and the recommendation you would make for the next event, and describe how you handle the pushback in the room.

Approach

  1. Before the meeting, write the finding as one sentence about the event, not about the person who owns it: the event moved price and timing, not demand. Keeping the event owner's judgement out of the sentence is what keeps the holdout you want on the table.
  2. Open with the number they already have, then decompose revenue into traffic, conversion, units per order, net price per unit and keep rate, and show which term moved. If price per unit fell and units per order held, the revenue gain was volume bought at a lower price.
  3. Quantify the post-period dip as the pull-forward estimate, and state the measurement window explicitly. The window has to extend past the event because the demand was borrowed from the weeks after it.
  4. Convert to incremental contribution margin. At gross margin m on list price and discount depth d, holding contribution flat requires a unit lift of d / (m - d), valid when m > d, when the discount applies to every unit sold, when unit cost is constant, and when nothing was cannibalised from full-price SKUs. Compare the observed lift to that threshold.
  5. Say what would change your mind and propose the design that would settle it: a geo or customer holdout on the next event, with the number of units and weeks it needs.
  6. End on the next event, not a verdict on the last one, so the meeting produces a decision rather than a defence.

Follow-up

  • The lead says the event acquired new customers whose lifetime value justifies the margin loss. How would you test that claim with dim_customer and fct_order_line?
  • Your director wants one number to take to finance. Which one, and what caveat travels with it?